From 5cf748d1352e29ed439efb1ce9227ad22b3013c4 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 27 Aug 2014 09:55:39 -0500 Subject: [PATCH 001/368] Initial work --- common/database.cpp | 390 ++++++++++++++++++++++++++++++++++++++++++++ common/database.h | 2 + world/net.cpp | 3 + zone/zonedb.cpp | 7 +- 4 files changed, 399 insertions(+), 3 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 8d27d5b46..b90340ad9 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -18,6 +18,7 @@ #include "../common/debug.h" #include "../common/rulesys.h" #include +#include #include #include #include @@ -771,6 +772,395 @@ void Database::GetCharName(uint32 char_id, char* name) { strcpy(name, row[0]); } +static inline void loadbar(unsigned int x, unsigned int n, unsigned int w = 50) +{ + if ((x != n) && (x % (n / 100 + 1) != 0)) return; + + float ratio = x / (float)n; + int c = ratio * w; + + std::cout << std::setw(3) << (int)(ratio * 100) << "% ["; + for (int x = 0; xchecksum); + printf("name: %s \n", pp->name); + printf("last_name: %s \n", pp->last_name); + printf("gender: %i \n", pp->gender); + printf("race: %i \n", pp->race); + printf("class_: %i \n", pp->class_); + printf("unknown0112: %i \n", pp->unknown0112); + printf("level: %i \n", pp->level); + + printf("\n=== BIND DATA (Array Size 5) ===\n"); + for (i = 0; i < 5; i++){ + printf("Bind Num: %i ZoneID: %u x: %f y: %f z: %f heading: %f \n", i, pp->binds[i].zoneId, pp->binds[i].x, pp->binds[i].y, pp->binds[i].z, pp->binds[i].heading); + } + printf("\n"); + + printf("deity: %u \n", pp->deity); + printf("guild_id: %u \n", pp->guild_id); + printf("birthday: %u \n", pp->birthday); + printf("lastlogin: %u \n", pp->lastlogin); + printf("timePlayedMin: %u \n", pp->timePlayedMin); + printf("pvp: %u \n", pp->pvp); + printf("level2: %u \n", pp->level2); + printf("anon: %u \n", pp->anon); + printf("gm: %u \n", pp->gm); + printf("guildrank: %u \n", pp->guildrank); + printf("guildbanker: %u \n", pp->guildbanker); + printf("unknown0246[6]: %u \n", pp->unknown0246[6]); + printf("intoxication: %u \n", pp->intoxication); + + printf("\n=== Spell Slot Refresh spellSlotRefresh[MAX_PP_MEMSPELL] ===\n"); + for (i = 0; i < MAX_PP_MEMSPELL; i++){ + printf("Slot: %i Value: %u \n", i, pp->spellSlotRefresh[i]); + } + printf("\n\n"); + + printf("abilitySlotRefresh: %u \n", pp->abilitySlotRefresh); + printf("haircolor: %u \n", pp->haircolor); + printf("beardcolor: %u \n", pp->beardcolor); + printf("eyecolor1: %u \n", pp->eyecolor1); + printf("eyecolor2: %u \n", pp->eyecolor2); + printf("hairstyle: %u \n", pp->hairstyle); + printf("beard: %u \n", pp->beard); + printf("ability_time_seconds: %u \n", pp->ability_time_seconds); + printf("ability_number: %u \n", pp->ability_number); + printf("ability_time_minutes: %u \n", pp->ability_time_minutes); + printf("ability_time_hours: %u \n", pp->ability_time_hours); + + printf("\n=== Color Material Data ===\n"); + for (i = 0; i < 10; i++){ + printf("Slot: %i Blue: %u Green: %u Red: %i Use_Tint: %u Color: %u \n", i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); + } + printf("\n\n"); + + printf("\n=== AA Data ===\n"); + for (i = 0; i < MAX_PP_AA_ARRAY; i++){ + printf("ID: %u Value %u\n", pp->aa_array[i].AA, pp->aa_array[i].value); + } + printf("\n\n"); + + printf("%i unknown2384\n", pp->unknown2384); + printf("servername: %s \n", pp->servername); + printf("title: %s \n", pp->title); + printf("suffix: %s \n", pp->suffix); + printf("guildid2: %u \n", pp->guildid2); + printf("exp: %u \n", pp->exp); + printf("unknown2492: %u \n", pp->unknown2492); + printf("points: %u \n", pp->points); + printf("mana: %u \n", pp->mana); + printf("cur_hp: %u \n", pp->cur_hp); + printf("unknown2508: %u \n", pp->unknown2508); + printf("STR: %u \n", pp->STR); + printf("STA: %u \n", pp->STA); + printf("CHA: %u \n", pp->CHA); + printf("DEX: %u \n", pp->DEX); + printf("INT: %u \n", pp->INT); + printf("AGI: %u \n", pp->AGI); + printf("WIS: %u \n", pp->WIS); + printf("face: %u \n", pp->face); + printf("unknown2541[47]: %u \n", pp->unknown2541[47]); + printf("languages[MAX_PP_LANGUAGE]: %u \n", pp->languages[MAX_PP_LANGUAGE]); + + printf("\n=== languages[MAX_PP_LANGUAGE] ===\n"); + for (i = 0; i < MAX_PP_LANGUAGE; i++){ + printf("ID: %u Value: %u \n", i, pp->languages[i]); + } + printf("\n\n"); + + printf("unknown2616[4]: %u \n", pp->unknown2616[4]); + + printf("\n=== Spell Book ===\n"); + for (i = 0; i < MAX_PP_SPELLBOOK; i++){ + printf("Spell Book Slot: %i Spell: %u \n", i, pp->spell_book[i]); + } + printf("\n\n"); + + printf("unknown4540[128]: %u \n", pp->unknown4540[128]); + + printf("\n=== mem_spells[MAX_PP_MEMSPELL] ===\n"); + for (i = 0; i < MAX_PP_MEMSPELL; i++){ + printf("ID: %u Value: %u \n", i, pp->mem_spells[i]); + } + printf("\n\n"); + + printf("unknown4704[32]: %u \n", pp->unknown4704[32]); + printf("y: %4.2f \n", pp->y); + printf("x: %4.2f \n", pp->x); + printf("z: %4.2f \n", pp->z); + printf("heading: %4.2f \n", pp->heading); + printf("unknown4752[4]: %u \n", pp->unknown4752[4]); + printf("platinum: %u \n", pp->platinum); + printf("gold: %u \n", pp->gold); + printf("silver: %u \n", pp->silver); + printf("copper: %u \n", pp->copper); + printf("platinum_bank: %u \n", pp->platinum_bank); + printf("gold_bank: %u \n", pp->gold_bank); + printf("silver_bank: %u \n", pp->silver_bank); + printf("copper_bank: %u \n", pp->copper_bank); + printf("platinum_cursor: %u \n", pp->platinum_cursor); + printf("gold_cursor: %u \n", pp->gold_cursor); + printf("silver_cursor: %u \n", pp->silver_cursor); + printf("copper_cursor: %u \n", pp->copper_cursor); + printf("platinum_shared: %u \n", pp->platinum_shared); + printf("unknown4808[24]: %u \n", pp->unknown4808[24]); + + printf("\n=== skills[MAX_PP_SKILL] ===\n"); + for (i = 0; i < MAX_PP_SKILL; i++){ + printf("ID: %u Value: %u \n", i, pp->skills[i]); + } + printf("\n\n"); + + printf("unknown5132[184]: %u \n", pp->unknown5132[184]); + printf("pvp2: %u \n", pp->pvp2); + printf("unknown5420: %u \n", pp->unknown5420); + printf("pvptype: %u \n", pp->pvptype); + printf("unknown5428: %u \n", pp->unknown5428); + printf("ability_down: %u \n", pp->ability_down); + printf("unknown5436[8]: %u \n", pp->unknown5436[8]); + printf("autosplit: %u \n", pp->autosplit); + printf("unknown5448[8]: %u \n", pp->unknown5448[8]); + printf("zone_change_count: %u \n", pp->zone_change_count); + printf("unknown5460[16]: %u \n", pp->unknown5460[16]); + printf("drakkin_heritage: %u \n", pp->drakkin_heritage); + printf("drakkin_tattoo: %u \n", pp->drakkin_tattoo); + printf("drakkin_details: %u \n", pp->drakkin_details); + printf("expansions: %u \n", pp->expansions); + printf("toxicity: %u \n", pp->toxicity); + printf("unknown5496: %s \n", pp->unknown5496); + printf("hunger_level: %i \n", pp->hunger_level); + printf("thirst_level: %i \n", pp->thirst_level); + printf("ability_up: %u \n", pp->ability_up); + printf("unknown5524: %s \n", pp->unknown5524); + printf("zone_id: %u \n", pp->zone_id); + printf("zoneInstance: %u \n", pp->zoneInstance); + + // SpellBuff_Struct buffs[BUFF_COUNT]; + + printf("groupMembers: %s \n", pp->groupMembers); + printf("unknown6428: %s \n", pp->unknown6428); + printf("entityid: %u \n", pp->entityid); + printf("leadAAActive: %u \n", pp->leadAAActive); + printf("unknown7092: %u \n", pp->unknown7092); + printf("ldon_points_guk: %i \n", pp->ldon_points_guk); + printf("ldon_points_mir: %i \n", pp->ldon_points_mir); + printf("ldon_points_mmc: %i \n", pp->ldon_points_mmc); + printf("ldon_points_ruj: %i \n", pp->ldon_points_ruj); + printf("ldon_points_tak: %i \n", pp->ldon_points_tak); + printf("ldon_points_available: %i \n", pp->ldon_points_available); + printf("ldon_wins_guk: %i \n", pp->ldon_wins_guk); + printf("ldon_wins_mir: %i \n", pp->ldon_wins_mir); + printf("ldon_wins_mmc: %i \n", pp->ldon_wins_mmc); + printf("ldon_wins_ruj: %i \n", pp->ldon_wins_ruj); + printf("ldon_wins_tak: %i \n", pp->ldon_wins_tak); + printf("ldon_losses_guk: %i \n", pp->ldon_losses_guk); + printf("ldon_losses_mir: %i \n", pp->ldon_losses_mir); + printf("ldon_losses_mmc: %i \n", pp->ldon_losses_mmc); + printf("ldon_losses_ruj: %i \n", pp->ldon_losses_ruj); + printf("ldon_losses_tak: %i \n", pp->ldon_losses_tak); + printf("unknown7160[72]: %u \n", pp->unknown7160[72]); + printf("tribute_time_remaining: %u \n", pp->tribute_time_remaining); + printf("showhelm: %u \n", pp->showhelm); + printf("career_tribute_points: %u \n", pp->career_tribute_points); + printf("unknown7244: %u \n", pp->unknown7244); + printf("tribute_points: %u \n", pp->tribute_points); + printf("unknown7252: %u \n", pp->unknown7252); + printf("tribute_active: %u \n", pp->tribute_active); + + printf("\n=== Tribute_Struct tributes[EmuConstants::TRIBUTE_SIZE] ===\n"); + for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ + printf("ID: %u Tribute: %u Tier: %u \n", i, pp->tributes[i].tribute, pp->tributes[i].tier); + } + printf("\n\n"); + + // Tribute_Struct tributes[EmuConstants::TRIBUTE_SIZE]; + // /*7264*/ Disciplines_Struct disciplines; + + printf("\n=== Disciplines_Struct disciplines ===\n"); + for (i = 0; i < MAX_PP_DISCIPLINES; i++){ + printf("ID: %u Disc Value: %u \n", i, pp->disciplines.values[i]); + } + printf("\n\n"); + + printf("\n=== recastTimers[MAX_RECAST_TYPES] ===\n"); + for (i = 0; i < MAX_RECAST_TYPES; i++){ + printf("ID: %u Value: %u \n", i, pp->recastTimers[i]); + } + printf("\n\n"); + + printf("unknown7780: %s \n", pp->unknown7780); + printf("endurance: %u \n", pp->endurance); + printf("group_leadership_exp: %u \n", pp->group_leadership_exp); + printf("raid_leadership_exp: %u \n", pp->raid_leadership_exp); + printf("group_leadership_points: %u \n", pp->group_leadership_points); + printf("raid_leadership_points: %u \n", pp->raid_leadership_points); + + // LeadershipAA_Struct leader_abilities; + + printf("\n=== LeadershipAA_Struct leader_abilities ===\n"); + for (i = 0; i <= MAX_LEADERSHIP_AA_ARRAY; i++){ + printf("ID: %u Rank: %u \n", i, pp->leader_abilities.ranks[i]); + } + printf("\n\n"); + + printf("unknown8088[132]: %u \n", pp->unknown8088[132]); + printf("air_remaining: %u \n", pp->air_remaining); + printf("PVPKills: %u \n", pp->PVPKills); + printf("PVPDeaths: %u \n", pp->PVPDeaths); + printf("PVPCurrentPoints: %u \n", pp->PVPCurrentPoints); + printf("PVPCareerPoints: %u \n", pp->PVPCareerPoints); + printf("PVPBestKillStreak: %u \n", pp->PVPBestKillStreak); + printf("PVPWorstDeathStreak: %u \n", pp->PVPWorstDeathStreak); + printf("PVPCurrentKillStreak: %u \n", pp->PVPCurrentKillStreak); + + // PVPStatsEntry_Struct PVPLastKill; + + printf("\n=== PVPStatsEntry_Struct PVPLastKill ===\n"); + printf("Char Name: %s Level: %u Race: %u Class: %u Zone: %u Time: %u Points: %u \n", pp->PVPLastKill.Name, pp->PVPLastKill.Level, pp->PVPLastKill.Race, pp->PVPLastKill.Class, pp->PVPLastKill.Zone, pp->PVPLastKill.Time, pp->PVPLastKill.Points); + printf("\n\n"); + + // /*8304*/ PVPStatsEntry_Struct PVPLastDeath; + + printf("\n=== PVPStatsEntry_Struct PVPLastDeath ===\n"); + printf("Char Name: %s Level: %u Race: %u Class: %u Zone: %u Time: %u Points: %u \n", pp->PVPLastDeath.Name, pp->PVPLastDeath.Level, pp->PVPLastDeath.Race, pp->PVPLastDeath.Class, pp->PVPLastDeath.Zone, pp->PVPLastDeath.Time, pp->PVPLastDeath.Points); + printf("\n\n"); + + printf("PVPNumberOfKillsInLast24Hours: %u \n", pp->PVPNumberOfKillsInLast24Hours); + + // PVPStatsEntry_Struct PVPRecentKills[50]; + + printf("\n===PVPStatsEntry_Struct PVPRecentKills[50] ===\n"); + for (i = 0; i < 50; i++){ + printf("ID: %u Char Name: %s Level: %u Race: %u Class: %u Zone: %u Time: %u Points: %u \n", i, pp->PVPRecentKills[i].Name, pp->PVPRecentKills[i].Level, pp->PVPRecentKills[i].Race, pp->PVPRecentKills[i].Class, pp->PVPRecentKills[i].Zone, pp->PVPRecentKills[i].Time, pp->PVPRecentKills[i].Points); + } + printf("\n\n"); + + printf("aapoints_spent: %u \n", pp->aapoints_spent); + printf("expAA: %u \n", pp->expAA); + printf("aapoints: %u \n", pp->aapoints); + printf("unknown12844[36]: %u \n", pp->unknown12844[36]); + + // Bandolier_Struct bandoliers[EmuConstants::BANDOLIERS_COUNT]; + + printf("\n=== Bandolier_Struct bandoliers[EmuConstants::BANDOLIERS_COUNT] ===\n"); + uint16 si = 0; + for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ + // BandolierItem_Struct items[EmuConstants::BANDOLIER_SIZE]; + for (si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ + printf("ID: %u item_id: %u icon: %u items name: %s name: %s\n", i, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].items[si].item_name, pp->bandoliers[i].name); + } + } + printf("\n\n"); + + printf("unknown14160[4506]: %u \n", pp->unknown14160[4506]); + + // SuspendedMinion_Struct SuspendedMinion; // No longer in use (In DB) + + // printf("\n=== SuspendedMinion_Struct SuspendedMinion ===\n"); + // printf("SpellID: %u HP: %u Mana: %u \n", i, pp->leader_abilities.ranks[i]); + + // printf("\n\n"); + + printf("timeentitledonaccount: %u \n", pp->timeentitledonaccount); + + // PotionBelt_Struct potionbelt; //there should be 3 more of these + + printf("\n=== PotionBelt_Struct potionbelt ===\n"); + for (i = 0; i < EmuConstants::POTION_BELT_SIZE; i++){ + printf("ID: %u Icon: %u Item ID: %u Item Name: %s\n", i, pp->potionbelt.items[i].icon, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].item_name); + } + printf("\n\n"); + + printf("unknown19568[8]: %u \n", pp->unknown19568[8]); + printf("currentRadCrystals: %u \n", pp->currentRadCrystals); + printf("careerRadCrystals: %u \n", pp->careerRadCrystals); + printf("currentEbonCrystals: %u \n", pp->currentEbonCrystals); + printf("careerEbonCrystals: %u \n", pp->careerEbonCrystals); + printf("groupAutoconsent: %u \n", pp->groupAutoconsent); + printf("raidAutoconsent: %u \n", pp->raidAutoconsent); + printf("guildAutoconsent: %u \n", pp->guildAutoconsent); + printf("unknown19595[5]: %u \n", pp->unknown19595[5]); + printf("RestTimer: %u \n", pp->RestTimer); + + + printf("\n"); + } + + mysql_free_result(result2); + } + } + mysql_free_result(result); + } + return true; +} + bool Database::LoadVariables() { char *query = nullptr; diff --git a/common/database.h b/common/database.h index f912c18d1..a22e6d406 100644 --- a/common/database.h +++ b/common/database.h @@ -217,6 +217,8 @@ public: uint32 GetRaidID(const char* name); const char *GetRaidLeaderName(uint32 rid); + bool Database::CheckDatabaseConversions(); + /* * Database Variables */ diff --git a/world/net.cpp b/world/net.cpp index 4bfdc3c1a..f308fc1be 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -276,6 +276,9 @@ int main(int argc, char** argv) { _log(WORLD__INIT, "HTTP world service disabled."); } + _log(WORLD__INIT, "Checking Database Conversions.."); + database.CheckDatabaseConversions(); + _log(WORLD__INIT, "Loading variables.."); database.LoadVariables(); _log(WORLD__INIT, "Loading zones.."); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 787e3a94e..725f6395a 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -858,9 +858,10 @@ void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) } bool ZoneDatabase::GetCharacterInfoForLogin(const char* name, uint32* character_id, -char* current_zone, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, -uint32* pplen, uint32* guilddbid, uint8* guildrank, -uint8 *class_, uint8 *level, bool *LFP, bool *LFG, uint8 *NumXTargets, uint8 *firstlogon) { + char* current_zone, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, + uint32* pplen, uint32* guilddbid, uint8* guildrank, + uint8 *class_, uint8 *level, bool *LFP, bool *LFG, uint8 *NumXTargets, uint8 *firstlogon) { + char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; uint32 querylen; From 4071d88290d32e66c10f60ce85178e2d20bea292 Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 31 Aug 2014 02:53:59 -0500 Subject: [PATCH 002/368] At point of commit: Basic character data, currency and AA are being loaded/saved from the database, currently working on the rest right now. - Character blob removed from load for testing. Lots of cleanup yet to be done so don't judge code yet. Saves: - Two FULL saves when looting a corpse, this has been reduced to just currency saves on initial loot and trimmed to one save since AddToMoneyPP did it already - Every time a player moves coin with any situation (Splits/Trades/Merchant/Skills/Bank Coin Exchange/Coin Moves), a full save is made, this is now just a currency save - Every time a player skilled up at a skill vendor, a full blob save hit was made, this is not just a currency hit --- common/database.cpp | 389 +++++++++++++++++++++++++-- common/debug.h | 18 +- common/shareddb.cpp | 5 +- world/net.cpp | 6 +- zone/aa.cpp | 6 +- zone/client.cpp | 160 +++++------ zone/client.h | 4 + zone/client_logs.cpp | 5 +- zone/client_packet.cpp | 221 +++++++--------- zone/client_process.cpp | 2 +- zone/command.cpp | 4 +- zone/corpse.cpp | 3 +- zone/zonedb.cpp | 569 +++++++++++++++++++++++++++++++++++++++- zone/zonedb.h | 15 +- zone/zoning.cpp | 2 +- 15 files changed, 1140 insertions(+), 269 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index b90340ad9..92df542f8 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -80,7 +80,7 @@ bool Database::Connect(const char* host, const char* user, const char* passwd, c LogFile->write(EQEMuLog::Error, "Failed to connect to database: Error: %s", errbuf); HandleMysqlError(errnum); - return false; + return false; } else { @@ -555,7 +555,7 @@ bool Database::DeleteCharacter(char *name) return true; } -// Store new character information into the character_ and inventory tables +/* This only for new Character creation storing */ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext) { char query[256+sizeof(PlayerProfile_Struct)*2+sizeof(ExtendedProfile_Struct)*2+5]; @@ -639,7 +639,6 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inven i++; } - return true; } @@ -772,8 +771,7 @@ void Database::GetCharName(uint32 char_id, char* name) { strcpy(name, row[0]); } -static inline void loadbar(unsigned int x, unsigned int n, unsigned int w = 50) -{ +static inline void loadbar(unsigned int x, unsigned int n, unsigned int w = 50) { if ((x != n) && (x % (n / 100 + 1) != 0)) return; float ratio = x / (float)n; @@ -787,31 +785,46 @@ static inline void loadbar(unsigned int x, unsigned int n, unsigned int w = 50) bool Database::CheckDatabaseConversions() { /* Set all of this ugliness */ - char errbuf[MYSQL_ERRMSG_SIZE]; char errbuf2[MYSQL_ERRMSG_SIZE]; - char *query = 0; char *query2 = 0; - uint32 querylen; uint32 querylen2; - MYSQL_RES *result; MYSQL_RES *result2; - MYSQL_ROW row2; MYSQL_ROW row; + char errbuf[MYSQL_ERRMSG_SIZE]; char errbuf2[MYSQL_ERRMSG_SIZE]; char errbuf3[MYSQL_ERRMSG_SIZE]; + char *query = 0; char *query2 = 0; char *query3 = 0; + uint32 querylen; uint32 querylen2; uint32 querylen3; + MYSQL_RES *result; MYSQL_RES *result2; MYSQL_RES *result3; + MYSQL_ROW row; MYSQL_ROW row2; MYSQL_ROW row3; unsigned long* lengths; PlayerProfile_Struct* pp; uint32 pplen = 0; uint32 i; - + int character_id = 0; + int account_id = 0; int number_of_characters = 0; - int printppdebug = 1; + int printppdebug = 0; int runconvert = 0; - printf("CheckDatabase Running.... \n"); + /* Check For Legacy Storage Method */ + std::string rquery = StringFormat("SELECT `profile` FROM `character_` LIMIT 1"); + auto results = QueryDatabase(rquery); + for (auto row = results.begin(); row != results.end(); ++row) { + runconvert = 1; + printf("\n\n::: Legacy Character Data Binary Blob Storage Detected... \n"); + printf("----------------------------------------------------------\n\n"); + printf(" Database currently has character data being stored via \n"); + printf(" the legacy character storage method and will proceed with converting...\n\n"); + printf(" It is recommended that you backup your database \n"); + printf(" before continuing the automatic conversion proces...\n\n"); + printf("----------------------------------------------------------\n\n"); + std::cout << "Press ENTER to continue....." << std::endl << std::endl; + std::cin.ignore(1); + } - printf("Running character binary blob to database conversion... \n", number_of_characters); - /* Get the number of characters */ - querylen = MakeAnyLenString(&query, "SELECT COUNT(`id`) FROM `character_`"); - if (RunQuery(query, querylen, errbuf, &result)) { - row = mysql_fetch_row(result); - number_of_characters = atoi(row[0]); - printf("Number of Characters in Database: %i \n", number_of_characters); - safe_delete_array(query); - mysql_free_result(result); + if (runconvert == 1){ + printf("Running character binary blob to database conversion... \n", number_of_characters); + /* Get the number of characters */ + rquery = StringFormat("SELECT COUNT(`id`) FROM `character_`"); + results = QueryDatabase(rquery); + for (auto row = results.begin(); row != results.end(); ++row) { + number_of_characters = atoi(row[0]); + printf("Number of Characters in Database: %i \n", number_of_characters); + } } // querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); @@ -821,12 +834,13 @@ bool Database::CheckDatabaseConversions() { safe_delete_array(query); while (row = mysql_fetch_row(result)) { char_iter_count++; - querylen2 = MakeAnyLenString(&query2, "SELECT `id`, `profile`, `name`, `level` FROM `character_` WHERE `id` = %i", atoi(row[0])); + querylen2 = MakeAnyLenString(&query2, "SELECT `id`, `profile`, `name`, `level`, `account_id` FROM `character_` WHERE `id` = %i", atoi(row[0])); if (RunQuery(query2, querylen2, errbuf2, &result2)){ safe_delete_array(query2); row2 = mysql_fetch_row(result2); - pp = (PlayerProfile_Struct*)row2[1]; - + pp = (PlayerProfile_Struct*)row2[1]; + character_id = atoi(row[0]); + account_id = atoi(row2[4]); /* Verify PP Integrity */ lengths = mysql_fetch_lengths(result2); if (lengths[1] == sizeof(PlayerProfile_Struct)) { @@ -842,11 +856,325 @@ bool Database::CheckDatabaseConversions() { /* Loading Status on conversion */ if (runconvert == 1){ std::cout << "\r" << char_iter_count << "/" << number_of_characters << " " << std::flush; - loadbar(char_iter_count, number_of_characters, 50); + loadbar(char_iter_count, number_of_characters, 50); + + /* Run Currency Convert */ + std::string rquery = StringFormat("REPLACE INTO `character_currency` (id, platinum, gold, silver, copper," + "platinum_bank, gold_bank, silver_bank, copper_bank," + "platinum_cursor, gold_cursor, silver_cursor, copper_cursor, " + "radiant_crystals, career_radiant_crystals, ebon_crystals, career_ebon_crystals)" + "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u)", + character_id, + pp->platinum, + pp->gold, + pp->silver, + pp->copper, + pp->platinum_bank, + pp->gold_bank, + pp->silver_bank, + pp->copper_bank, + pp->platinum_cursor, + pp->gold_cursor, + pp->silver_cursor, + pp->copper_cursor, + pp->currentRadCrystals, + pp->careerRadCrystals, + pp->currentEbonCrystals, + pp->careerEbonCrystals + ); + auto results = QueryDatabase(rquery); + + /* Run Character Data Convert */ + rquery = StringFormat( + "REPLACE INTO `character_data` (" + " id, " + " account_id, " + " `name`, " + " last_name, " + " gender, " + " race, " + " class, " + " `level`, " + " deity, " + " birthday, " + " last_login, " + " time_played, " + " pvp_status, " + " level2, " + " anon, " + " gm, " + " intoxication, " + " hair_color, " + " beard_color, " + " eye_color_1, " + " eye_color_2, " + " hair_style, " + " beard, " + " ability_time_seconds, " + " ability_number, " + " ability_time_minutes, " + " ability_time_hours, " + " title, " + " suffix, " + " exp, " + " points, " + " mana, " + " cur_hp, " + " str, " + " sta, " + " cha, " + " dex, " + " `int`, " + " agi, " + " wis, " + " face, " + " y, " + " x, " + " z, " + " heading, " + " pvp2, " + " pvp_type, " + " autosplit_enabled, " + " zone_change_count, " + " drakkin_heritage, " + " drakkin_tattoo, " + " drakkin_details, " + " toxicity, " + " hunger_level, " + " thirst_level, " + " ability_up, " + " zone_id, " + " zone_instance, " + " leadership_exp_on, " + " ldon_points_guk, " + " ldon_points_mir, " + " ldon_points_mmc, " + " ldon_points_ruj, " + " ldon_points_tak, " + " ldon_points_available, " + " tribute_time_remaining, " + " show_helm, " + " career_tribute_points, " + " tribute_points, " + " tribute_active, " + " endurance, " + " group_leadership_exp, " + " raid_leadership_exp, " + " group_leadership_points, " + " raid_leadership_points, " + " air_remaining, " + " pvp_kills, " + " pvp_deaths, " + " pvp_current_points, " + " pvp_career_points, " + " pvp_best_kill_streak, " + " pvp_worst_death_streak, " + " pvp_current_kill_streak, " + " aa_points_spent, " + " aa_exp, " + " aa_points, " + " group_auto_consent, " + " raid_auto_consent, " + " guild_auto_consent, " + " RestTimer) " + "VALUES (" + "%u," // id " id, " + "%u," // account_id " account_id, " + "'%s'," // `name` pp->name, " `name`, " + "'%s'," // last_name pp->last_name, " last_name, " + "%u," // gender pp->gender, " gender, " + "%u," // race pp->race, " race, " + "%u," // class pp->class_, " class, " + "%u," // `level` pp->level, " `level`, " + "%u," // deity pp->deity, " deity, " + "%u," // birthday pp->birthday, " birthday, " + "%u," // last_login pp->lastlogin, " last_login, " + "%u," // time_played pp->timePlayedMin, " time_played, " + "%u," // pvp_status pp->pvp, " pvp_status, " + "%u," // level2 pp->level2, " level2, " + "%u," // anon pp->anon, " anon, " + "%u," // gm pp->gm, " gm, " + "%u," // intoxication pp->intoxication, " intoxication, " + "%u," // hair_color pp->haircolor, " hair_color, " + "%u," // beard_color pp->beardcolor, " beard_color, " + "%u," // eye_color_1 pp->eyecolor1, " eye_color_1, " + "%u," // eye_color_2 pp->eyecolor2, " eye_color_2, " + "%u," // hair_style pp->hairstyle, " hair_style, " + "%u," // beard pp->beard, " beard, " + "%u," // ability_time_seconds pp->ability_time_seconds, " ability_time_seconds, " + "%u," // ability_number pp->ability_number, " ability_number, " + "%u," // ability_time_minutes pp->ability_time_minutes, " ability_time_minutes, " + "%u," // ability_time_hours pp->ability_time_hours, " ability_time_hours, " + "'%s'," // title pp->title, " title, " + "'%s'," // suffix pp->suffix, " suffix, " + "%u," // exp pp->exp, " exp, " + "%u," // points pp->points, " points, " + "%u," // mana pp->mana, " mana, " + "%u," // cur_hp pp->cur_hp, " cur_hp, " + "%u," // str pp->STR, " str, " + "%u," // sta pp->STA, " sta, " + "%u," // cha pp->CHA, " cha, " + "%u," // dex pp->DEX, " dex, " + "%u," // `int` pp->INT, " `int`, " + "%u," // agi pp->AGI, " agi, " + "%u," // wis pp->WIS, " wis, " + "%u," // face pp->face, " face, " + "%f," // y pp->y, " y, " + "%f," // x pp->x, " x, " + "%f," // z pp->z, " z, " + "%f," // heading pp->heading, " heading, " + "%u," // pvp2 pp->pvp2, " pvp2, " + "%u," // pvp_type pp->pvptype, " pvp_type, " + "%u," // autosplit_enabled pp->autosplit, " autosplit_enabled, " + "%u," // zone_change_count pp->zone_change_count, " zone_change_count, " + "%u," // drakkin_heritage pp->drakkin_heritage, " drakkin_heritage, " + "%u," // drakkin_tattoo pp->drakkin_tattoo, " drakkin_tattoo, " + "%u," // drakkin_details pp->drakkin_details, " drakkin_details, " + "%u," // toxicity pp->toxicity, " toxicity, " + "%u," // hunger_level pp->hunger_level, " hunger_level, " + "%u," // thirst_level pp->thirst_level, " thirst_level, " + "%u," // ability_up pp->ability_up, " ability_up, " + "%u," // zone_id pp->zone_id, " zone_id, " + "%u," // zone_instance pp->zoneInstance, " zone_instance, " + "%u," // leadership_exp_on pp->leadAAActive, " leadership_exp_on, " + "%u," // ldon_points_guk pp->ldon_points_guk, " ldon_points_guk, " + "%u," // ldon_points_mir pp->ldon_points_mir, " ldon_points_mir, " + "%u," // ldon_points_mmc pp->ldon_points_mmc, " ldon_points_mmc, " + "%u," // ldon_points_ruj pp->ldon_points_ruj, " ldon_points_ruj, " + "%u," // ldon_points_tak pp->ldon_points_tak, " ldon_points_tak, " + "%u," // ldon_points_available pp->ldon_points_available, " ldon_points_available, " + "%u," // tribute_time_remaining pp->tribute_time_remaining, " tribute_time_remaining, " + "%u," // show_helm pp->showhelm, " show_helm, " + "%u," // career_tribute_points pp->career_tribute_points, " career_tribute_points, " + "%u," // tribute_points pp->tribute_points, " tribute_points, " + "%u," // tribute_active pp->tribute_active, " tribute_active, " + "%u," // endurance pp->endurance, " endurance, " + "%u," // group_leadership_exp pp->group_leadership_exp, " group_leadership_exp, " + "%u," // raid_leadership_exp pp->raid_leadership_exp, " raid_leadership_exp, " + "%u," // group_leadership_points pp->group_leadership_points, " group_leadership_points, " + "%u," // raid_leadership_points pp->raid_leadership_points, " raid_leadership_points, " + "%u," // air_remaining pp->air_remaining, " air_remaining, " + "%u," // pvp_kills pp->PVPKills, " pvp_kills, " + "%u," // pvp_deaths pp->PVPDeaths, " pvp_deaths, " + "%u," // pvp_current_points pp->PVPCurrentPoints, " pvp_current_points, " + "%u," // pvp_career_points pp->PVPCareerPoints, " pvp_career_points, " + "%u," // pvp_best_kill_streak pp->PVPBestKillStreak, " pvp_best_kill_streak, " + "%u," // pvp_worst_death_streak pp->PVPWorstDeathStreak, " pvp_worst_death_streak, " + "%u," // pvp_current_kill_streak pp->PVPCurrentKillStreak, " pvp_current_kill_streak, " + "%u," // aa_points_spent pp->aapoints_spent, " aa_points_spent, " + "%u," // aa_exp pp->expAA, " aa_exp, " + "%u," // aa_points pp->aapoints, " aa_points, " + "%u," // group_auto_consent pp->groupAutoconsent, " group_auto_consent, " + "%u," // raid_auto_consent pp->raidAutoconsent, " raid_auto_consent, " + "%u," // guild_auto_consent pp->guildAutoconsent, " guild_auto_consent, " + "%u" // RestTimer pp->RestTimer, " RestTimer) " + ")", + character_id, + account_id, + pp->name, + pp->last_name, + pp->gender, + pp->race, + pp->class_, + pp->level, + pp->deity, + pp->birthday, + pp->lastlogin, + pp->timePlayedMin, + pp->pvp, + pp->level2, + pp->anon, + pp->gm, + pp->intoxication, + pp->haircolor, + pp->beardcolor, + pp->eyecolor1, + pp->eyecolor2, + pp->hairstyle, + pp->beard, + pp->ability_time_seconds, + pp->ability_number, + pp->ability_time_minutes, + pp->ability_time_hours, + pp->title, + pp->suffix, + pp->exp, + pp->points, + pp->mana, + pp->cur_hp, + pp->STR, + pp->STA, + pp->CHA, + pp->DEX, + pp->INT, + pp->AGI, + pp->WIS, + pp->face, + pp->y, + pp->x, + pp->z, + pp->heading, + pp->pvp2, + pp->pvptype, + pp->autosplit, + pp->zone_change_count, + pp->drakkin_heritage, + pp->drakkin_tattoo, + pp->drakkin_details, + pp->toxicity, + pp->hunger_level, + pp->thirst_level, + pp->ability_up, + pp->zone_id, + pp->zoneInstance, + pp->leadAAActive, + pp->ldon_points_guk, + pp->ldon_points_mir, + pp->ldon_points_mmc, + pp->ldon_points_ruj, + pp->ldon_points_tak, + pp->ldon_points_available, + pp->tribute_time_remaining, + pp->showhelm, + pp->career_tribute_points, + pp->tribute_points, + pp->tribute_active, + pp->endurance, + pp->group_leadership_exp, + pp->raid_leadership_exp, + pp->group_leadership_points, + pp->raid_leadership_points, + pp->air_remaining, + pp->PVPKills, + pp->PVPDeaths, + pp->PVPCurrentPoints, + pp->PVPCareerPoints, + pp->PVPBestKillStreak, + pp->PVPWorstDeathStreak, + pp->PVPCurrentKillStreak, + pp->aapoints_spent, + pp->expAA, + pp->aapoints, + pp->groupAutoconsent, + pp->raidAutoconsent, + pp->guildAutoconsent, + pp->RestTimer + ); + results = QueryDatabase(rquery); + /* Run AA Convert */ + for (i = 0; i < MAX_PP_AA_ARRAY; i++){ + if (pp->aa_array[i].AA > 0 && pp->aa_array[i].value > 0){ + std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" + " VALUES (%u, %u, %u, %u)", + character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); + auto results = QueryDatabase(rquery); + } + } + printf("\n\nCharacter blob conversion complete, continuing world bootup...\n"); } /* Print out the entire Player Profile for testing */ - if (printppdebug == 1){ + if (printppdebug == 1) { printf("ID: %i \n", atoi(row[0])); printf("checksum: %i \n", pp->checksum); printf("name: %s \n", pp->name); @@ -947,7 +1275,7 @@ bool Database::CheckDatabaseConversions() { printf("\n=== mem_spells[MAX_PP_MEMSPELL] ===\n"); for (i = 0; i < MAX_PP_MEMSPELL; i++){ - printf("ID: %u Value: %u \n", i, pp->mem_spells[i]); + printf("ID: %u Value: %u \n", i, pp->mem_spells[i]); } printf("\n\n"); @@ -957,6 +1285,7 @@ bool Database::CheckDatabaseConversions() { printf("z: %4.2f \n", pp->z); printf("heading: %4.2f \n", pp->heading); printf("unknown4752[4]: %u \n", pp->unknown4752[4]); + printf("platinum: %u \n", pp->platinum); printf("gold: %u \n", pp->gold); printf("silver: %u \n", pp->silver); @@ -972,6 +1301,8 @@ bool Database::CheckDatabaseConversions() { printf("platinum_shared: %u \n", pp->platinum_shared); printf("unknown4808[24]: %u \n", pp->unknown4808[24]); + + printf("\n=== skills[MAX_PP_SKILL] ===\n"); for (i = 0; i < MAX_PP_SKILL; i++){ printf("ID: %u Value: %u \n", i, pp->skills[i]); @@ -1140,7 +1471,7 @@ bool Database::CheckDatabaseConversions() { printf("unknown19568[8]: %u \n", pp->unknown19568[8]); printf("currentRadCrystals: %u \n", pp->currentRadCrystals); - printf("careerRadCrystals: %u \n", pp->careerRadCrystals); + printf("careerRadCrystals: %u \n", pp->careerRadCrystals); printf("currentEbonCrystals: %u \n", pp->currentEbonCrystals); printf("careerEbonCrystals: %u \n", pp->careerEbonCrystals); printf("groupAutoconsent: %u \n", pp->groupAutoconsent); diff --git a/common/debug.h b/common/debug.h index 6184e4cbc..5e2fff368 100644 --- a/common/debug.h +++ b/common/debug.h @@ -80,14 +80,15 @@ public: ~EQEMuLog(); enum LogIDs { - Status = 0, //this must stay the first entry in this list - Normal, - Error, - Debug, - Quest, - Commands, - Crash, - MaxLogID + Status = 0, /* This must stay the first entry in this list */ + Normal, /* Normal Logs */ + Error, /* Error Logs */ + Debug, /* Debug Logs */ + Quest, /* Quest Logs */ + Commands, /* Issued Comamnds */ + Crash, /* Crash Logs */ + Save, /* Client Saves */ + MaxLogID /* Max, used in functions to get the max log ID */ }; //these are callbacks called for each @@ -113,6 +114,7 @@ private: Mutex MOpen; Mutex MLog[MaxLogID]; FILE* fp[MaxLogID]; + /* LogStatus: bitwise variable 1 = output to file 2 = output to stdout diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 8e999da54..c10e87856 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "shareddb.h" #include "mysql.h" @@ -1270,9 +1271,11 @@ bool SharedDatabase::SetPlayerProfile(uint32 account_id, uint32 charid, PlayerPr uint32 affected_rows = 0; bool ret = false; + clock_t t = std::clock(); /* Function timer start */ if (RunQuery(query, SetPlayerProfile_MQ(&query, account_id, charid, pp, inv, ext, current_zone, current_instance, MaxXTargets), errbuf, 0, &affected_rows)) { ret = (affected_rows != 0); } + LogFile->write(EQEMuLog::Status, "SharedDatabase::SetPlayerProfile SetPlayerProfile_MQ done... Took %f seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC); if (!ret) { LogFile->write(EQEMuLog::Error, "SetPlayerProfile query '%s' %s", query, errbuf); @@ -1292,7 +1295,7 @@ uint32 SharedDatabase::SetPlayerProfile_MQ(char** query, uint32 account_id, uint if (!current_instance) current_instance = pp->zoneInstance; - if(strlen(pp->name) == 0) // Sanity check in case pp never loaded + if(strlen(pp->name) == 0) /* Sanity check in case pp never loaded */ return false; end += sprintf(end, "UPDATE character_ SET timelaston=unix_timestamp(now()),name=\'%s\', zonename=\'%s\', zoneid=%u, instanceid=%u, x = %f, y = %f, z = %f, profile=\'", pp->name, GetZoneName(current_zone), current_zone, current_instance, pp->x, pp->y, pp->z); diff --git a/world/net.cpp b/world/net.cpp index f308fc1be..2d186c1af 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -222,9 +222,8 @@ int main(int argc, char** argv) { else if (strcasecmp(argv[1], "flag") == 0) { if (argc == 4) { if (Seperator::IsNumber(argv[3])) { - if (atoi(argv[3]) >= 0 && atoi(argv[3]) <= 255) { - if (database.SetAccountStatus(argv[2], atoi(argv[3]))) { + if (database.SetAccountStatus(argv[2], atoi(argv[3]))){ std::cout << "Account flagged: Username='" << argv[2] << "', status=" << argv[3] << std::endl; return 0; } @@ -277,8 +276,7 @@ int main(int argc, char** argv) { } _log(WORLD__INIT, "Checking Database Conversions.."); - database.CheckDatabaseConversions(); - + database.CheckDatabaseConversions(); _log(WORLD__INIT, "Loading variables.."); database.LoadVariables(); _log(WORLD__INIT, "Loading zones.."); diff --git a/zone/aa.cpp b/zone/aa.cpp index 2964dc6f3..390174cf5 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1048,8 +1048,12 @@ void Client::BuyAA(AA_Action* action) mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level + 1); m_pp.aapoints -= real_cost; + + /* Do Player Profile rank calculations and set player profile */ + SaveAA(); + /* Save to Database to avoid having to write the whole AA array to the profile, only write changes*/ + // database.SaveCharacterAA(this->CharacterID(), aa2->id, (cur_level + 1)); - Save(); if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (aa2->hotkey_sid == 4294967295u)) && ((aa2->max_level == (cur_level + 1)) && aa2->sof_next_id)){ SendAA(aa2->id); diff --git a/zone/client.cpp b/zone/client.cpp index 37740b472..0065d31c5 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -479,60 +479,27 @@ void Client::ReportConnectingState() { }; } -bool Client::Save(uint8 iCommitNow) { -#if 0 -// Orig. Offset: 344 / 0x00000000 -// Length: 36 / 0x00000024 - unsigned char rawData[36] = +inline double clock_diff_to_sec(long clock_diff) { - 0x0D, 0x30, 0xE1, 0x30, 0x1E, 0x10, 0x22, 0x10, 0x20, 0x10, 0x21, 0x10, 0x1C, 0x20, 0x1F, 0x10, - 0x7C, 0x10, 0x68, 0x10, 0x51, 0x10, 0x78, 0x10, 0xBD, 0x10, 0xD2, 0x10, 0xCD, 0x10, 0xD1, 0x10, - 0x01, 0x10, 0x6D, 0x10 -} ; - for (int tmp = 0;tmp <=35;tmp++){ - m_pp.unknown0256[89+tmp] = rawData[tmp]; - } -#endif + return double(clock_diff) / CLOCKS_PER_SEC; +} - if(!ClientDataLoaded()) - return false; - - m_pp.x = x_pos; - m_pp.y = y_pos; - m_pp.z = z_pos; - m_pp.guildrank=guildrank; - m_pp.heading = heading; - - // Temp Hack for signed values until we get the root of the problem changed over to signed... - if (m_pp.copper < 0) { m_pp.copper = 0; } - if (m_pp.silver < 0) { m_pp.silver = 0; } - if (m_pp.gold < 0) { m_pp.gold = 0; } - if (m_pp.platinum < 0) { m_pp.platinum = 0; } - if (m_pp.copper_bank < 0) { m_pp.copper_bank = 0; } - if (m_pp.silver_bank < 0) { m_pp.silver_bank = 0; } - if (m_pp.gold_bank < 0) { m_pp.gold_bank = 0; } - if (m_pp.platinum_bank < 0) { m_pp.platinum_bank = 0; } - - - int spentpoints=0; - for(int a=0;a < MAX_PP_AA_ARRAY;a++) { +bool Client::SaveAA(){ + clock_t t = std::clock(); /* Function timer start */ + /* Save Player AA */ + int spentpoints = 0; + for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { uint32 points = aa[a]->value; - if(points > HIGHEST_AA_VALUE) // Unifying this - { + if (points > HIGHEST_AA_VALUE) { aa[a]->value = HIGHEST_AA_VALUE; points = HIGHEST_AA_VALUE; } - if (points > 0) - { - SendAA_Struct* curAA = zone->FindAA(aa[a]->AA-aa[a]->value+1); - if(curAA) - { - for (int rank=0; rank::iterator RequiredLevel = AARequiredLevelAndCost.find(aa[a]->AA-aa[a]->value + 1 + rank); - - if(RequiredLevel != AARequiredLevelAndCost.end()) - { + if (points > 0) { + SendAA_Struct* curAA = zone->FindAA(aa[a]->AA - aa[a]->value + 1); + if (curAA) { + for (int rank = 0; rank::iterator RequiredLevel = AARequiredLevelAndCost.find(aa[a]->AA - aa[a]->value + 1 + rank); + if (RequiredLevel != AARequiredLevelAndCost.end()) { spentpoints += RequiredLevel->second.Cost; } else @@ -541,36 +508,58 @@ bool Client::Save(uint8 iCommitNow) { } } } - - m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; - - if (GetHP() <= 0) { - m_pp.cur_hp = GetMaxHP(); + m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; + for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { + if (aa[a]->AA > 0 && aa[a]->value){ + std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" + " VALUES (%u, %u, %u, %u)", + character_id, a, aa[a]->AA, aa[a]->value); + auto results = database.QueryDatabase(rquery); + } } - else - m_pp.cur_hp = GetHP(); + LogFile->write(EQEMuLog::Status, "Issuing Client AA Save... CID: %i Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + return true; +} + +bool Client::Save(uint8 iCommitNow) { + if(!ClientDataLoaded()) + return false; + + clock_t t = std::clock(); /* Function timer start */ + + /* Wrote current basics to PP for saves */ + m_pp.x = x_pos; + m_pp.y = y_pos; + m_pp.z = z_pos; + m_pp.guildrank = guildrank; + m_pp.heading = heading; + + /* Mana and HP */ + if (GetHP() <= 0) { m_pp.cur_hp = GetMaxHP(); } + else { m_pp.cur_hp = GetHP(); } m_pp.mana = cur_mana; m_pp.endurance = cur_end; + /* Save Character Currency */ + database.SaveCharacterCurrency(this->CharacterID(), &m_pp); + + /* Save Character AA */ + SaveAA(); + + /* Save Character Buffs */ database.SaveBuffs(this); + /* Total Time Played */ TotalSecondsPlayed += (time(nullptr) - m_pp.lastlogin); m_pp.timePlayedMin = (TotalSecondsPlayed / 60); m_pp.RestTimer = rest_timer.GetRemainingTime() / 1000; - if(GetMercInfo().MercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)) - GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); - - if(GetMercTimer()->Enabled()) { - GetMercInfo().MercTimerRemaining = GetMercTimer()->GetRemainingTime(); - } - - if (GetMerc() && !dead) { - - } else { - memset(&m_mercinfo, 0, sizeof(struct MercInfo)); - } + /* Save Mercs */ + if (GetMercInfo().MercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)){ GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); } + if(GetMercTimer()->Enabled()) { GetMercInfo().MercTimerRemaining = GetMercTimer()->GetRemainingTime(); } + if (GetMerc() && !dead) { } + else { memset(&m_mercinfo, 0, sizeof(struct MercInfo)); } m_pp.lastlogin = time(nullptr); if (pQueuedSaveWorkID) { @@ -591,19 +580,17 @@ bool Client::Save(uint8 iCommitNow) { } database.SavePetInfo(this); - if(tribute_timer.Enabled()) { - m_pp.tribute_time_remaining = tribute_timer.GetRemainingTime(); - } else { - m_pp.tribute_time_remaining = 0xFFFFFFFF; - m_pp.tribute_active = 0; - } + if(tribute_timer.Enabled()) { m_pp.tribute_time_remaining = tribute_timer.GetRemainingTime(); } + else { m_pp.tribute_time_remaining = 0xFFFFFFFF; m_pp.tribute_active = 0; } p_timers.Store(&database); -// printf("Dumping inventory on save:\n"); -// m_inv.dumpEntireInventory(); - + /* Save Character Task */ SaveTaskState(); + + /* Save Character Data */ + database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp); + if (iCommitNow <= 1) { char* query = 0; uint32_breakdown workpt; @@ -632,7 +619,7 @@ bool Client::Save(uint8 iCommitNow) { /* Mirror Character Data */ database.StoreCharacterLookup(this->CharacterID()); - + LogFile->write(EQEMuLog::Status, "Client::Save %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; } @@ -2115,7 +2102,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper = copperpp; if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } silver -= copper; @@ -2130,7 +2117,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper += (silver-(m_pp.silver*10)); if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } @@ -2150,7 +2137,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper += coppertest; if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } @@ -2168,7 +2155,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { if(updateclient) SendMoneyUpdate(); RecalcWeight(); - Save(); + SaveCurrency(); return true; } } @@ -2234,7 +2221,7 @@ void Client::AddMoneyToPP(uint64 copper, bool updateclient){ RecalcWeight(); - Save(); + SaveCurrency(); LogFile->write(EQEMuLog::Debug, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); } @@ -2270,7 +2257,7 @@ void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 plat SendMoneyUpdate(); RecalcWeight(); - Save(); + SaveCurrency(); #if (EQDEBUG>=5) LogFile->write(EQEMuLog::Debug, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", @@ -2807,11 +2794,6 @@ void Client::SetMaterial(int16 in_slot, uint32 item_id) { m_pp.item_material[MaterialArms] = item->Material; else if (in_slot==MainWrist1) m_pp.item_material[MaterialWrist] = item->Material; - /* - // non-live behavior - else if (in_slot==SLOT_BRACER02) - m_pp.item_material[MaterialWrist] = item->Material; - */ else if (in_slot==MainHands) m_pp.item_material[MaterialHands] = item->Material; else if (in_slot==MainLegs) @@ -5734,7 +5716,7 @@ void Client::AddCrystals(uint32 Radiant, uint32 Ebon) m_pp.currentEbonCrystals += Ebon; m_pp.careerEbonCrystals += Ebon; - Save(); + SaveCurrency(); SendCrystalCounts(); } diff --git a/zone/client.h b/zone/client.h index d3aed5540..a490d73c4 100644 --- a/zone/client.h +++ b/zone/client.h @@ -313,6 +313,10 @@ public: bool Save(uint8 iCommitNow); // 0 = delayed, 1=async now, 2=sync now void SaveBackup(); + /* New PP Save Functions */ + bool SaveCurrency(){ return database.SaveCharacterCurrency(this->CharacterID(), &m_pp); } + bool SaveAA(); + inline bool ClientDataLoaded() const { return client_data_loaded; } inline bool Connected() const { return (client_state == CLIENT_CONNECTED); } inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); } diff --git a/zone/client_logs.cpp b/zone/client_logs.cpp index e51a51996..7ce3d5aab 100644 --- a/zone/client_logs.cpp +++ b/zone/client_logs.cpp @@ -33,14 +33,12 @@ void ClientLogs::subscribe(EQEMuLog::LogIDs id, Client *c) { if(c == nullptr) return; - //make sure they arnt allready subscribed. - std::vector::iterator cur,end; cur = entries[id].begin(); end = entries[id].end(); for(; cur != end; ++cur) { if(*cur == c) { - printf("%s was allready subscribed to %d\n", c->GetName(), id); + printf("%s was already subscribed to %d\n", c->GetName(), id); return; } } @@ -100,6 +98,7 @@ void ClientLogs::msg(EQEMuLog::LogIDs id, const char *buf) { for(; cur != end; ++cur) { if(!(*cur)->InZone()) continue; + (*cur)->Message(CLIENT_LOG_CHANNEL, buf); } } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 91d99490f..abf044caa 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8698,6 +8698,8 @@ void Client::DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { break; } case DBA_b1_Entity_Client_Save: { + clock_t t = std::clock(); /* Function timer start */ + char errbuf[MYSQL_ERRMSG_SIZE]; uint32 affected_rows = 0; DBAsyncQuery* dbaq = dbaw->PopAnswer(); @@ -8713,6 +8715,8 @@ void Client::DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { Message(13, "errbuf: %s", errbuf); } pQueuedSaveWorkID = 0; + + LogFile->write(EQEMuLog::Status, "Client::DBAWComplete Save Character Async done... Took %f seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC); break; } default: { @@ -8761,12 +8765,17 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { } } - // This should be a part of the PlayerProfile BLOB, but we don't want to modify that - // The player inspect message is retrieved from the db on load, then saved as new updates come in..no mods to Client::Save() - database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); + /* Load Character Currency into PP */ + database.LoadCharacterCurrency(CharacterID(), &m_pp); + /* Load Character Data from DB into PP */ + database.LoadCharacterData(CharacterID(), &m_pp); + /* Move to another method when can, this is pointless... */ + database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); + /* Load Character Currency*/ + database.LoadCharacterCurrency(CharacterID(), &m_pp); conn_state = PlayerProfileLoaded; - + /* Set Current zone */ m_pp.zone_id = zone->GetZoneID(); m_pp.zoneInstance = zone->GetInstanceID(); @@ -8820,7 +8829,7 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { base_race = m_pp.race; gender = m_pp.gender; base_gender = m_pp.gender; - deity = m_pp.deity;//FYI: DEITY_AGNOSTIC = 396; still valid? + deity = m_pp.deity; //FYI: DEITY_AGNOSTIC = 396; still valid? haircolor = m_pp.haircolor; beardcolor = m_pp.beardcolor; eyecolor1 = m_pp.eyecolor1; @@ -8833,8 +8842,7 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { drakkin_details = m_pp.drakkin_details; //if we zone in with invalid Z, fix it. - if (zone->zonemap != nullptr) { - + if (zone->zonemap != nullptr) { Map::Vertex me; me.x = GetX(); me.y = GetY(); @@ -8871,19 +8879,13 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { if (m_pp.gm && admin < minStatusToBeGM) m_pp.gm = 0; - if (m_pp.platinum < 0 || m_pp.gold < 0 || m_pp.silver < 0 || m_pp.copper < 0 ) - { - m_pp.platinum = 0; - m_pp.gold = 0; - m_pp.silver = 0; - m_pp.copper = 0; - } + + /* Load Guild */ if (!IsInAGuild()) { m_pp.guild_id = GUILD_NONE; } - else - { + else { m_pp.guild_id = GuildID(); if(zone->GetZoneID() == RuleI(World, GuildBankZoneID)) @@ -8895,120 +8897,89 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { switch (race) { case OGRE: - size = 9; - break; + size = 9; break; case TROLL: - size = 8; - break; - case VAHSHIR: - case BARBARIAN: - size = 7; - break; - case HUMAN: - case HIGH_ELF: - case ERUDITE: - case IKSAR: - case DRAKKIN: - size = 6; - break; + size = 8; break; + case VAHSHIR: case BARBARIAN: + size = 7; break; + case HUMAN: case HIGH_ELF: case ERUDITE: case IKSAR: case DRAKKIN: + size = 6; break; case HALF_ELF: - size = 5.5; - break; - case WOOD_ELF: - case DARK_ELF: - case FROGLOK: - size = 5; - break; + size = 5.5; break; + case WOOD_ELF: case DARK_ELF: case FROGLOK: + size = 5; break; case DWARF: - size = 4; - break; + size = 4; break; case HALFLING: - size = 3.5; - break; + size = 3.5; break; case GNOME: - size = 3; - break; + size = 3; break; default: size = 0; } - //validate skills - //im not sure I follow this logic... commenting for now... - /* - if(Admin() < minStatusToHaveInvalidSkills) { - SkillType sk; - for (sk = _1H_BLUNT; sk <= HIGHEST_SKILL; sk = (SkillType)(sk+1)) { - //int cap = GetSkillCap(sk-1); - int cap = MaxSkill(sk-1, GetClass(), GetLevel()); - if (cap >= 254) - m_pp.skills[sk] = cap; - } - } - */ - - //validate adventure points, this cap is arbitrary at 2,000,000,000 - if(m_pp.ldon_points_guk < 0) - m_pp.ldon_points_guk = 0; - if(m_pp.ldon_points_guk > 0x77359400) - m_pp.ldon_points_guk = 0x77359400; - if(m_pp.ldon_points_mir < 0) - m_pp.ldon_points_mir = 0; - if(m_pp.ldon_points_mir > 0x77359400) - m_pp.ldon_points_mir = 0x77359400; - if(m_pp.ldon_points_mmc < 0) - m_pp.ldon_points_mmc = 0; - if(m_pp.ldon_points_mmc > 0x77359400) - m_pp.ldon_points_mmc = 0x77359400; - if(m_pp.ldon_points_ruj < 0) - m_pp.ldon_points_ruj = 0; - if(m_pp.ldon_points_ruj > 0x77359400) - m_pp.ldon_points_ruj = 0x77359400; - if(m_pp.ldon_points_tak < 0) - m_pp.ldon_points_tak = 0; - if(m_pp.ldon_points_tak > 0x77359400) - m_pp.ldon_points_tak = 0x77359400; - if(m_pp.ldon_points_available < 0) - m_pp.ldon_points_available = 0; - if(m_pp.ldon_points_available > 0x77359400) - m_pp.ldon_points_available = 0x77359400; - + /* Check for Invalid points */ + if (m_pp.ldon_points_guk < 0 || m_pp.ldon_points_guk > 2000000000){ m_pp.ldon_points_guk = 0; } + if (m_pp.ldon_points_mir < 0 || m_pp.ldon_points_mir > 2000000000){ m_pp.ldon_points_mir = 0; } + if (m_pp.ldon_points_mmc < 0 || m_pp.ldon_points_mmc > 2000000000){ m_pp.ldon_points_mmc = 0; } + if (m_pp.ldon_points_ruj < 0 || m_pp.ldon_points_ruj > 2000000000){ m_pp.ldon_points_ruj = 0; } + if (m_pp.ldon_points_tak < 0 || m_pp.ldon_points_tak > 2000000000){ m_pp.ldon_points_tak = 0; } + if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000){ m_pp.ldon_points_available = 0; } + if(GetSkill(SkillSwimming) < 100) - SetSkill(SkillSwimming,100); + SetSkill(SkillSwimming, 100); - //pull AAs from the PP - for(uint32 a=0; a < MAX_PP_AA_ARRAY; a++){ - //set up our AA pointer + + + /* Load Character AA's */ + //database.LoadCharacterAA(this->CharacterID(), &m_pp, &aa, &aa_points); + + /* Initialize AA's */ + for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ aa[a] = &m_pp.aa_array[a]; - - + } + std::string query = StringFormat( + "SELECT " + "slot, " + "aa_id, " + "aa_value " + "FROM " + "`character_alternate_abilities` " + "WHERE `id` = %i ORDER BY `slot`", this->CharacterID()); + auto results = database.QueryDatabase(query); int si = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + si = atoi(row[0]); + m_pp.aa_array[si].AA = atoi(row[1]); + m_pp.aa_array[si].value = atoi(row[1]); + aa[si]->AA = atoi(row[1]); + aa[si]->value = atoi(row[2]); + } + for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ uint32 id = aa[a]->AA; //watch for invalid AA IDs - if(id == aaNone) + if (id == aaNone) continue; - if(id >= aaHighestID) { + if (id >= aaHighestID) { + aa[a]->AA = aaNone; + aa[a]->value = 0; + continue; + } + if (aa[a]->value == 0) { + aa[a]->AA = aaNone; + continue; + } + if (aa[a]->value > HIGHEST_AA_VALUE) { aa[a]->AA = aaNone; aa[a]->value = 0; continue; } - //watch for invalid AA values - if(aa[a]->value == 0) { - aa[a]->AA = aaNone; - continue; - } - if(aa[a]->value > HIGHEST_AA_VALUE) { - aa[a]->AA = aaNone; - aa[a]->value = 0; - continue; - } - - if(aa[a]->value > 1) //hack in some stuff for sony's new AA method (where each level of each aa.has a seperate ID) - aa_points[(id - aa[a]->value +1)] = aa[a]->value; + if (aa[a]->value > 1) /* hack in some stuff for sony's new AA method (where each level of each aa.has a seperate ID) */ + aa_points[(id - aa[a]->value + 1)] = aa[a]->value; else aa_points[id] = aa[a]->value; } - if(SPDAT_RECORDS > 0) { for(uint32 z=0;z 0){ @@ -9128,6 +9101,7 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { m_pp.z = zone->newzone_data.safe_z; } + /* Get Expansions from variables table and ship via PP */ char val[20] = {0}; if (database.GetVariable("Expansions", val, 20)) m_pp.expansions = atoi(val); @@ -9143,14 +9117,11 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { if(IsValidSpell(m_pp.mem_spells[i])) m_pp.spellSlotRefresh[i] = p_timers.GetRemainingTime(pTimerSpellStart + m_pp.mem_spells[i]) * 1000; - if(m_pp.class_==SHADOWKNIGHT || m_pp.class_==PALADIN) - { + /* Ability slot refresh send SK/PAL */ + if(m_pp.class_==SHADOWKNIGHT || m_pp.class_==PALADIN) { uint32 abilitynum=0; - if(m_pp.class_==SHADOWKNIGHT) - abilitynum = pTimerHarmTouch; - else - abilitynum = pTimerLayHands; - + if (m_pp.class_ == SHADOWKNIGHT){ abilitynum = pTimerHarmTouch; } + else{ abilitynum = pTimerLayHands; } uint32 remaining = p_timers.GetRemainingTime(abilitynum); if(remaining > 0 && remaining < 15300) @@ -9174,16 +9145,16 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; - // Reset rest timer if the durations have been lowered in the database + /* Reset rest timer if the durations have been lowered in the database */ if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) m_pp.RestTimer = 0; - //This checksum should disappear once dynamic structs are in... each struct strategy will do it + /* This checksum should disappear once dynamic structs are in... each struct strategy will do it */ CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct)-4); outapp = new EQApplicationPacket(OP_PlayerProfile,sizeof(PlayerProfile_Struct)); - // The entityid field in the Player Profile is used by the Client in relation to Group Leadership AA + /* The entityid field in the Player Profile is used by the Client in relation to Group Leadership AA */ m_pp.entityid = GetID(); memcpy(outapp->pBuffer,&m_pp,outapp->size); outapp->priority = 6; @@ -9193,9 +9164,11 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { rest_timer.Start(m_pp.RestTimer * 1000); database.LoadPetInfo(this); - //this was moved before the spawn packets are sent - //in hopes that it adds more consistency... - //Remake pet + /* + This was moved before the spawn packets are sent + in hopes that it adds more consistency... + Remake pet + */ if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) { MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size); @@ -9208,7 +9181,7 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { } m_petinfo.SpellID = 0; } - // Moved here so it's after where we load the pet data. + /* Moved here so it's after where we load the pet data. */ if(!GetAA(aaPersistentMinion)) memset(&m_suspendedminion, 0, sizeof(PetInfo)); @@ -12894,13 +12867,13 @@ void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app) { SummonItem(RuleI(Zone, EbonCrystalItemID), GetEbonCrystals()); m_pp.currentEbonCrystals = 0; m_pp.careerEbonCrystals = 0; - Save(); + SaveCurrency(); SendCrystalCounts(); } else { SummonItem(RuleI(Zone, EbonCrystalItemID), cr->amount); m_pp.currentEbonCrystals -= cr->amount; m_pp.careerEbonCrystals -= cr->amount; - Save(); + SaveCurrency(); SendCrystalCounts(); } } else if(cr->type == 4) { @@ -12908,13 +12881,13 @@ void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app) { SummonItem(RuleI(Zone, RadiantCrystalItemID), GetRadiantCrystals()); m_pp.currentRadCrystals = 0; m_pp.careerRadCrystals = 0; - Save(); + SaveCurrency(); SendCrystalCounts(); } else { SummonItem(RuleI(Zone, RadiantCrystalItemID), cr->amount); m_pp.currentRadCrystals -= cr->amount; m_pp.careerRadCrystals -= cr->amount; - Save(); + SaveCurrency(); SendCrystalCounts(); } } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index e39cef0bd..fd8cb3740 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1582,7 +1582,7 @@ void Client::OPMoveCoin(const EQApplicationPacket* app) safe_delete(outapp); } - Save(); + SaveCurrency(); } void Client::OPGMTraining(const EQApplicationPacket *app) diff --git a/zone/command.cpp b/zone/command.cpp index b14a173bc..011cb0f09 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6273,13 +6273,13 @@ void command_setcrystals(Client *c, const Seperator *sep) { t->SetRadiantCrystals(atoi(sep->arg[2])); t->SendCrystalCounts(); - t->Save(); + t->SaveCurrency(); } else if(!strcasecmp(sep->arg[1], "ebon")) { t->SetEbonCrystals(atoi(sep->arg[2])); t->SendCrystalCounts(); - t->Save(); + t->SaveCurrency(); } else { diff --git a/zone/corpse.cpp b/zone/corpse.cpp index aa7841a64..083dc0b12 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -971,8 +971,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a } RemoveCash(); - Save(); - client->Save(); + Save(); } outapp->priority = 6; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 725f6395a..e7bad016c 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -5,6 +5,7 @@ #include "../common/extprofile.h" #include "../common/guilds.h" #include "../common/rulesys.h" +#include "../common/rdtsc.h" #include "zone.h" #include "client.h" #include "merc.h" @@ -13,6 +14,7 @@ #include #include #include +#include extern Zone* zone; @@ -893,12 +895,575 @@ bool ZoneDatabase::GetCharacterInfoForLogin(const char* name, uint32* character_ #define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1)) + +bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "`name`, " + "last_name, " + "gender, " + "race, " + "class, " + "`level`, " + "deity, " + "birthday, " + "last_login, " + "time_played, " + "pvp_status, " + "level2, " + "anon, " + "gm, " + "intoxication, " + "hair_color, " + "beard_color, " + "eye_color_1, " + "eye_color_2, " + "hair_style, " + "beard, " + "ability_time_seconds, " + "ability_number, " + "ability_time_minutes, " + "ability_time_hours, " + "title, " + "suffix, " + "exp, " + "points, " + "mana, " + "cur_hp, " + "str, " + "sta, " + "cha, " + "dex, " + "`int`, " + "agi, " + "wis, " + "face, " + "y, " + "x, " + "z, " + "heading, " + "pvp2, " + "pvp_type, " + "autosplit_enabled, " + "zone_change_count, " + "drakkin_heritage, " + "drakkin_tattoo, " + "drakkin_details, " + "toxicity, " + "hunger_level, " + "thirst_level, " + "ability_up, " + "zone_id, " + "zone_instance, " + "leadership_exp_on, " + "ldon_points_guk, " + "ldon_points_mir, " + "ldon_points_mmc, " + "ldon_points_ruj, " + "ldon_points_tak, " + "ldon_points_available, " + "tribute_time_remaining, " + "show_helm, " + "career_tribute_points, " + "tribute_points, " + "tribute_active, " + "endurance, " + "group_leadership_exp, " + "raid_leadership_exp, " + "group_leadership_points, " + "raid_leadership_points, " + "air_remaining, " + "pvp_kills, " + "pvp_deaths, " + "pvp_current_points, " + "pvp_career_points, " + "pvp_best_kill_streak, " + "pvp_worst_death_streak, " + "pvp_current_kill_streak, " + "aa_points_spent, " + "aa_exp, " + "aa_points, " + "group_auto_consent, " + "raid_auto_consent, " + "guild_auto_consent, " + "RestTimer " + "FROM " + "character_data " + "WHERE `id` = %i ", character_id); + auto results = database.QueryDatabase(query); int r = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + strcpy(pp->name, row[r]); r++; + strcpy(pp->last_name, row[r]); r++; + pp->gender = atoi(row[r]); r++; + pp->race = atoi(row[r]); r++; + pp->class_ = atoi(row[r]); r++; + pp->level = atoi(row[r]); r++; + pp->deity = atoi(row[r]); r++; + pp->birthday = atoi(row[r]); r++; + pp->lastlogin = atoi(row[r]); r++; + pp->timePlayedMin = atoi(row[r]); r++; + pp->pvp = atoi(row[r]); r++; + pp->level2 = atoi(row[r]); r++; + pp->anon = atoi(row[r]); r++; + pp->gm = atoi(row[r]); r++; + pp->intoxication = atoi(row[r]); r++; + pp->haircolor = atoi(row[r]); r++; + pp->beardcolor = atoi(row[r]); r++; + pp->eyecolor1 = atoi(row[r]); r++; + pp->eyecolor2 = atoi(row[r]); r++; + pp->hairstyle = atoi(row[r]); r++; + pp->beard = atoi(row[r]); r++; + pp->ability_time_seconds = atoi(row[r]); r++; + pp->ability_number = atoi(row[r]); r++; + pp->ability_time_minutes = atoi(row[r]); r++; + pp->ability_time_hours = atoi(row[r]); r++; + strcpy(pp->title, row[r]); r++; + strcpy(pp->suffix, row[r]); r++; + pp->exp = atoi(row[r]); r++; + pp->points = atoi(row[r]); r++; + pp->mana = atoi(row[r]); r++; + pp->cur_hp = atoi(row[r]); r++; + pp->STR = atoi(row[r]); r++; + pp->STA = atoi(row[r]); r++; + pp->CHA = atoi(row[r]); r++; + pp->DEX = atoi(row[r]); r++; + pp->INT = atoi(row[r]); r++; + pp->AGI = atoi(row[r]); r++; + pp->WIS = atoi(row[r]); r++; + pp->face = atoi(row[r]); r++; + pp->y = atof(row[r]); r++; + pp->x = atof(row[r]); r++; + pp->z = atof(row[r]); r++; + pp->heading = atof(row[r]); r++; + pp->pvp2 = atoi(row[r]); r++; + pp->pvptype = atoi(row[r]); r++; + pp->autosplit = atoi(row[r]); r++; + pp->zone_change_count = atoi(row[r]); r++; + pp->drakkin_heritage = atoi(row[r]); r++; + pp->drakkin_tattoo = atoi(row[r]); r++; + pp->drakkin_details = atoi(row[r]); r++; + pp->toxicity = atoi(row[r]); r++; + pp->hunger_level = atoi(row[r]); r++; + pp->thirst_level = atoi(row[r]); r++; + pp->ability_up = atoi(row[r]); r++; + pp->zone_id = atoi(row[r]); r++; + pp->zoneInstance = atoi(row[r]); r++; + pp->leadAAActive = atoi(row[r]); r++; + pp->ldon_points_guk = atoi(row[r]); r++; + pp->ldon_points_mir = atoi(row[r]); r++; + pp->ldon_points_mmc = atoi(row[r]); r++; + pp->ldon_points_ruj = atoi(row[r]); r++; + pp->ldon_points_tak = atoi(row[r]); r++; + pp->ldon_points_available = atoi(row[r]); r++; + pp->tribute_time_remaining = atoi(row[r]); r++; + pp->showhelm = atoi(row[r]); r++; + pp->career_tribute_points = atoi(row[r]); r++; + pp->tribute_points = atoi(row[r]); r++; + pp->tribute_active = atoi(row[r]); r++; + pp->endurance = atoi(row[r]); r++; + pp->group_leadership_exp = atoi(row[r]); r++; + pp->raid_leadership_exp = atoi(row[r]); r++; + pp->group_leadership_points = atoi(row[r]); r++; + pp->raid_leadership_points = atoi(row[r]); r++; + pp->air_remaining = atoi(row[r]); r++; + pp->PVPKills = atoi(row[r]); r++; + pp->PVPDeaths = atoi(row[r]); r++; + pp->PVPCurrentPoints = atoi(row[r]); r++; + pp->PVPCareerPoints = atoi(row[r]); r++; + pp->PVPBestKillStreak = atoi(row[r]); r++; + pp->PVPWorstDeathStreak = atoi(row[r]); r++; + pp->PVPCurrentKillStreak = atoi(row[r]); r++; + pp->aapoints_spent = atoi(row[r]); r++; + pp->expAA = atoi(row[r]); r++; + pp->aapoints = atoi(row[r]); r++; + pp->groupAutoconsent = atoi(row[r]); r++; + pp->raidAutoconsent = atoi(row[r]); r++; + pp->guildAutoconsent = atoi(row[r]); r++; + pp->RestTimer = atoi(row[r]); r++; + LogFile->write(EQEMuLog::Status, "Loading Character Data for character ID: %i, done", character_id); + } + return true; +} + +bool ZoneDatabase::LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "platinum, " + "gold, " + "silver, " + "copper, " + "platinum_bank, " + "gold_bank, " + "silver_bank, " + "copper_bank, " + "platinum_cursor, " + "gold_cursor, " + "silver_cursor, " + "copper_cursor, " + "radiant_crystals, " + "career_radiant_crystals," + "ebon_crystals, " + "career_ebon_crystals " + "FROM " + "character_currency " + "WHERE `id` = %i ", character_id); + auto results = database.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + pp->platinum = atoi(row[0]); + pp->gold = atoi(row[1]); + pp->silver = atoi(row[2]); + pp->copper = atoi(row[3]); + pp->platinum_bank = atoi(row[4]); + pp->gold_bank = atoi(row[5]); + pp->silver_bank = atoi(row[6]); + pp->copper_bank = atoi(row[7]); + pp->platinum_cursor = atoi(row[8]); + pp->gold_cursor = atoi(row[9]); + pp->silver_cursor = atoi(row[10]); + pp->copper_cursor = atoi(row[11]); + pp->currentRadCrystals = atoi(row[12]); + pp->careerRadCrystals = atoi(row[13]); + pp->currentEbonCrystals = atoi(row[14]); + pp->careerEbonCrystals = atoi(row[15]); + LogFile->write(EQEMuLog::Status, "Loading Currency for character ID: %i, done", character_id); + } + return true; +} + +bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp){ + clock_t t = std::clock(); /* Function timer start */ + std::string query = StringFormat( + "REPLACE INTO `character_data` (" + " id, " + " account_id, " + " `name`, " + " last_name, " + " gender, " + " race, " + " class, " + " `level`, " + " deity, " + " birthday, " + " last_login, " + " time_played, " + " pvp_status, " + " level2, " + " anon, " + " gm, " + " intoxication, " + " hair_color, " + " beard_color, " + " eye_color_1, " + " eye_color_2, " + " hair_style, " + " beard, " + " ability_time_seconds, " + " ability_number, " + " ability_time_minutes, " + " ability_time_hours, " + " title, " + " suffix, " + " exp, " + " points, " + " mana, " + " cur_hp, " + " str, " + " sta, " + " cha, " + " dex, " + " `int`, " + " agi, " + " wis, " + " face, " + " y, " + " x, " + " z, " + " heading, " + " pvp2, " + " pvp_type, " + " autosplit_enabled, " + " zone_change_count, " + " drakkin_heritage, " + " drakkin_tattoo, " + " drakkin_details, " + " toxicity, " + " hunger_level, " + " thirst_level, " + " ability_up, " + " zone_id, " + " zone_instance, " + " leadership_exp_on, " + " ldon_points_guk, " + " ldon_points_mir, " + " ldon_points_mmc, " + " ldon_points_ruj, " + " ldon_points_tak, " + " ldon_points_available, " + " tribute_time_remaining, " + " show_helm, " + " career_tribute_points, " + " tribute_points, " + " tribute_active, " + " endurance, " + " group_leadership_exp, " + " raid_leadership_exp, " + " group_leadership_points, " + " raid_leadership_points, " + " air_remaining, " + " pvp_kills, " + " pvp_deaths, " + " pvp_current_points, " + " pvp_career_points, " + " pvp_best_kill_streak, " + " pvp_worst_death_streak, " + " pvp_current_kill_streak, " + " aa_points_spent, " + " aa_exp, " + " aa_points, " + " group_auto_consent, " + " raid_auto_consent, " + " guild_auto_consent, " + " RestTimer) " + "VALUES (" + "%i," // id " id, " + "%i," // account_id " account_id, " + "'%s'," // `name` pp->name, " `name`, " + "'%s'," // last_name pp->last_name, " last_name, " + "%i," // gender pp->gender, " gender, " + "%i," // race pp->race, " race, " + "%i," // class pp->class_, " class, " + "%i," // `level` pp->level, " `level`, " + "%i," // deity pp->deity, " deity, " + "%i," // birthday pp->birthday, " birthday, " + "%i," // last_login pp->lastlogin, " last_login, " + "%i," // time_played pp->timePlayedMin, " time_played, " + "%i," // pvp_status pp->pvp, " pvp_status, " + "%i," // level2 pp->level2, " level2, " + "%i," // anon pp->anon, " anon, " + "%i," // gm pp->gm, " gm, " + "%i," // intoxication pp->intoxication, " intoxication, " + "%i," // hair_color pp->haircolor, " hair_color, " + "%i," // beard_color pp->beardcolor, " beard_color, " + "%i," // eye_color_1 pp->eyecolor1, " eye_color_1, " + "%i," // eye_color_2 pp->eyecolor2, " eye_color_2, " + "%i," // hair_style pp->hairstyle, " hair_style, " + "%i," // beard pp->beard, " beard, " + "%i," // ability_time_seconds pp->ability_time_seconds, " ability_time_seconds, " + "%i," // ability_number pp->ability_number, " ability_number, " + "%i," // ability_time_minutes pp->ability_time_minutes, " ability_time_minutes, " + "%i," // ability_time_hours pp->ability_time_hours, " ability_time_hours, " + "'%s'," // title pp->title, " title, " " + "'%s'," // suffix pp->suffix, " suffix, " + "%i," // exp pp->exp, " exp, " + "%i," // points pp->points, " points, " + "%i," // mana pp->mana, " mana, " + "%i," // cur_hp pp->cur_hp, " cur_hp, " + "%i," // str pp->STR, " str, " + "%i," // sta pp->STA, " sta, " + "%i," // cha pp->CHA, " cha, " + "%i," // dex pp->DEX, " dex, " + "%i," // `int` pp->INT, " `int`, " + "%i," // agi pp->AGI, " agi, " + "%i," // wis pp->WIS, " wis, " + "%i," // face pp->face, " face, " + "%f," // y pp->y, " y, " + "%f," // x pp->x, " x, " + "%f," // z pp->z, " z, " + "%f," // heading pp->heading, " heading, " + "%i," // pvp2 pp->pvp2, " pvp2, " + "%i," // pvp_type pp->pvptype, " pvp_type, " + "%i," // autosplit_enabled pp->autosplit, " autosplit_enabled, " + "%i," // zone_change_count pp->zone_change_count, " zone_change_count, " + "%i," // drakkin_heritage pp->drakkin_heritage, " drakkin_heritage, " + "%i," // drakkin_tattoo pp->drakkin_tattoo, " drakkin_tattoo, " + "%i," // drakkin_details pp->drakkin_details, " drakkin_details, " + "%i," // toxicity pp->toxicity, " toxicity, " + "%i," // hunger_level pp->hunger_level, " hunger_level, " + "%i," // thirst_level pp->thirst_level, " thirst_level, " + "%i," // ability_up pp->ability_up, " ability_up, " + "%i," // zone_id pp->zone_id, " zone_id, " + "%i," // zone_instance pp->zoneInstance, " zone_instance, " + "%i," // leadership_exp_on pp->leadAAActive, " leadership_exp_on, " + "%i," // ldon_points_guk pp->ldon_points_guk, " ldon_points_guk, " + "%i," // ldon_points_mir pp->ldon_points_mir, " ldon_points_mir, " + "%i," // ldon_points_mmc pp->ldon_points_mmc, " ldon_points_mmc, " + "%i," // ldon_points_ruj pp->ldon_points_ruj, " ldon_points_ruj, " + "%i," // ldon_points_tak pp->ldon_points_tak, " ldon_points_tak, " + "%i," // ldon_points_available pp->ldon_points_available, " ldon_points_available, " + "%i," // tribute_time_remaining pp->tribute_time_remaining, " tribute_time_remaining, " + "%i," // show_helm pp->showhelm, " show_helm, " + "%i," // career_tribute_points pp->career_tribute_points, " career_tribute_points, " + "%i," // tribute_points pp->tribute_points, " tribute_points, " + "%i," // tribute_active pp->tribute_active, " tribute_active, " + "%i," // endurance pp->endurance, " endurance, " + "%i," // group_leadership_exp pp->group_leadership_exp, " group_leadership_exp, " + "%i," // raid_leadership_exp pp->raid_leadership_exp, " raid_leadership_exp, " + "%i," // group_leadership_points pp->group_leadership_points, " group_leadership_points, " + "%i," // raid_leadership_points pp->raid_leadership_points, " raid_leadership_points, " + "%i," // air_remaining pp->air_remaining, " air_remaining, " + "%i," // pvp_kills pp->PVPKills, " pvp_kills, " + "%i," // pvp_deaths pp->PVPDeaths, " pvp_deaths, " + "%i," // pvp_current_points pp->PVPCurrentPoints, " pvp_current_points, " + "%i," // pvp_career_points pp->PVPCareerPoints, " pvp_career_points, " + "%i," // pvp_best_kill_streak pp->PVPBestKillStreak, " pvp_best_kill_streak, " + "%i," // pvp_worst_death_streak pp->PVPWorstDeathStreak, " pvp_worst_death_streak, " + "%i," // pvp_current_kill_streak pp->PVPCurrentKillStreak, " pvp_current_kill_streak, " + "%i," // aa_points_spent pp->aapoints_spent, " aa_points_spent, " + "%i," // aa_exp pp->expAA, " aa_exp, " + "%i," // aa_points pp->aapoints, " aa_points, " + "%i," // group_auto_consent pp->groupAutoconsent, " group_auto_consent, " + "%i," // raid_auto_consent pp->raidAutoconsent, " raid_auto_consent, " + "%i," // guild_auto_consent pp->guildAutoconsent, " guild_auto_consent, " + "%i" // RestTimer pp->RestTimer, " RestTimer) " + ")", + character_id, + account_id, + pp->name, + pp->last_name, + pp->gender, + pp->race, + pp->class_, + pp->level, + pp->deity, + pp->birthday, + pp->lastlogin, + pp->timePlayedMin, + pp->pvp, + pp->level2, + pp->anon, + pp->gm, + pp->intoxication, + pp->haircolor, + pp->beardcolor, + pp->eyecolor1, + pp->eyecolor2, + pp->hairstyle, + pp->beard, + pp->ability_time_seconds, + pp->ability_number, + pp->ability_time_minutes, + pp->ability_time_hours, + pp->title, + pp->suffix, + pp->exp, + pp->points, + pp->mana, + pp->cur_hp, + pp->STR, + pp->STA, + pp->CHA, + pp->DEX, + pp->INT, + pp->AGI, + pp->WIS, + pp->face, + pp->y, + pp->x, + pp->z, + pp->heading, + pp->pvp2, + pp->pvptype, + pp->autosplit, + pp->zone_change_count, + pp->drakkin_heritage, + pp->drakkin_tattoo, + pp->drakkin_details, + pp->toxicity, + pp->hunger_level, + pp->thirst_level, + pp->ability_up, + pp->zone_id, + pp->zoneInstance, + pp->leadAAActive, + pp->ldon_points_guk, + pp->ldon_points_mir, + pp->ldon_points_mmc, + pp->ldon_points_ruj, + pp->ldon_points_tak, + pp->ldon_points_available, + pp->tribute_time_remaining, + pp->showhelm, + pp->career_tribute_points, + pp->tribute_points, + pp->tribute_active, + pp->endurance, + pp->group_leadership_exp, + pp->raid_leadership_exp, + pp->group_leadership_points, + pp->raid_leadership_points, + pp->air_remaining, + pp->PVPKills, + pp->PVPDeaths, + pp->PVPCurrentPoints, + pp->PVPCareerPoints, + pp->PVPBestKillStreak, + pp->PVPWorstDeathStreak, + pp->PVPCurrentKillStreak, + pp->aapoints_spent, + pp->expAA, + pp->aapoints, + pp->groupAutoconsent, + pp->raidAutoconsent, + pp->guildAutoconsent, + pp->RestTimer + ); + auto results = database.QueryDatabase(query); + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + return true; +} + +bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp){ + if (pp->copper < 0) { pp->copper = 0; } + if (pp->silver < 0) { pp->silver = 0; } + if (pp->gold < 0) { pp->gold = 0; } + if (pp->platinum < 0) { pp->platinum = 0; } + if (pp->copper_bank < 0) { pp->copper_bank = 0; } + if (pp->silver_bank < 0) { pp->silver_bank = 0; } + if (pp->gold_bank < 0) { pp->gold_bank = 0; } + if (pp->platinum_bank < 0) { pp->platinum_bank = 0; } + std::string query = StringFormat( + "REPLACE INTO `character_currency` (id, platinum, gold, silver, copper," + "platinum_bank, gold_bank, silver_bank, copper_bank," + "platinum_cursor, gold_cursor, silver_cursor, copper_cursor, " + "radiant_crystals, career_radiant_crystals, ebon_crystals, career_ebon_crystals)" + "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u)", + character_id, + pp->platinum, + pp->gold, + pp->silver, + pp->copper, + pp->platinum_bank, + pp->gold_bank, + pp->silver_bank, + pp->copper_bank, + pp->platinum_cursor, + pp->gold_cursor, + pp->silver_cursor, + pp->copper_cursor, + pp->currentRadCrystals, + pp->careerRadCrystals, + pp->currentEbonCrystals, + pp->careerEbonCrystals); + auto results = database.QueryDatabase(query); + LogFile->write(EQEMuLog::Status, "Saving Currency for character ID: %i, done", character_id); + return true; +} + +bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level){ + std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, aa_id, aa_value)" + " VALUES (%u, %u, %u)", + character_id, aa_id, current_level); + auto results = QueryDatabase(rquery); + LogFile->write(EQEMuLog::Status, "Saving AA for character ID: %i, aa_id: %u current_level: %i", character_id, aa_id, current_level); + return true; +} + // Process results of GetCharacterInfoForLogin() // Query this processes: SELECT id,profile,zonename,x,y,z,guild,guildrank,extprofile,class,level FROM character_ WHERE id=%i bool ZoneDatabase::GetCharacterInfoForLogin_result(MYSQL_RES* result, uint32* character_id, char* current_zone, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32* pplen, uint32* guilddbid, uint8* guildrank, - uint8 *class_, uint8 *level, bool *LFP, bool *LFG, uint8 *NumXTargets, uint8* firstlogon) { + uint8 *class_, uint8 *level, bool *LFP, bool *LFG, uint8 *NumXTargets, uint8* firstlogon) { MYSQL_ROW row; unsigned long* lengths; @@ -908,7 +1473,7 @@ bool ZoneDatabase::GetCharacterInfoForLogin_result(MYSQL_RES* result, lengths = mysql_fetch_lengths(result); if (pp && pplen) { if (lengths[1] == sizeof(PlayerProfile_Struct)) { - memcpy(pp, row[1], sizeof(PlayerProfile_Struct)); + // memcpy(pp, row[1], sizeof(PlayerProfile_Struct)); } else { LogFile->write(EQEMuLog::Error, "Player profile length mismatch in GetCharacterInfo Expected: %i, Got: %i", sizeof(PlayerProfile_Struct), lengths[1]); diff --git a/zone/zonedb.h b/zone/zonedb.h index 218654dc1..d0e70b995 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -252,8 +252,9 @@ public: bool GetAccountInfoForLogin(uint32 account_id, int16* admin = 0, char* account_name = 0, uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = 0); bool GetAccountInfoForLogin_result(MYSQL_RES* result, int16* admin = 0, char* account_name = 0, - uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = nullptr, - uint32* account_creation = 0); + uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = nullptr, + uint32* account_creation = 0); + bool GetCharacterInfoForLogin_result(MYSQL_RES* result, uint32* character_id = 0, char* current_zone = 0, PlayerProfile_Struct* pp = 0, Inventory* inv = 0, ExtendedProfile_Struct *ext = 0, uint32* pplen = 0, uint32* guilddbid = 0, uint8* guildrank = 0, uint8 *class_= 0, uint8 *level = 0, bool *LFP = 0, @@ -268,6 +269,16 @@ public: void SavePetInfo(Client *c); void RemoveTempFactions(Client *c); + /* Player Profile Loaders */ + bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); + + /* Player Profile Saves */ + + bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); + bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp); + bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); + /* * Character Inventory */ diff --git a/zone/zoning.cpp b/zone/zoning.cpp index b50e43d33..1b64fef3c 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -354,7 +354,7 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc m_pp.zoneInstance = instance_id; //Force a save so its waiting for them when they zone - Save(2); + Save(2); if (zone_id == zone->GetZoneID() && instance_id == zone->GetInstanceID()) { // No need to ask worldserver if we're zoning to ourselves (most From 5d8ea5752d075ddff043667a625219a47dc57c4a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 31 Aug 2014 03:23:42 -0500 Subject: [PATCH 003/368] Added automatic table creation in conversion process. More will come when more tables are added --- common/database.cpp | 337 ++++++++++++++++++++++++++++++++------------ 1 file changed, 247 insertions(+), 90 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 92df542f8..0996f6b0c 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -825,6 +825,162 @@ bool Database::CheckDatabaseConversions() { number_of_characters = atoi(row[0]); printf("Number of Characters in Database: %i \n", number_of_characters); } + + /* Check for table `character_data` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_data'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_data` doesn't exist... creating..."); + rquery = StringFormat( + " CREATE TABLE `character_data` ( " + " `id` int(11) NOT NULL AUTO_INCREMENT, " + " `account_id` int(11) NOT NULL DEFAULT '0', " + " `name` varchar(64) NOT NULL, " + " `last_name` varchar(64) NOT NULL, " + " `gender` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `race` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `class` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `deity` tinyint(11) UNSIGNED NOT NULL, " + " `birthday` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `last_login` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `time_played` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `pvp_status` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `level2` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `anon` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `gm` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `intoxication` int(11) UNSIGNED NOT NULL, " + " `hair_color` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `beard_color` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `eye_color_1` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `eye_color_2` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `hair_style` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `beard` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ability_time_seconds` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ability_number` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ability_time_minutes` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ability_time_hours` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `title` varchar(32) NOT NULL, " + " `suffix` varchar(32) NOT NULL, " + " `exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `mana` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `cur_hp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `str` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `sta` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `cha` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `dex` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `int` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `agi` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `wis` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `face` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `y` float NOT NULL DEFAULT '0', " + " `x` float NOT NULL DEFAULT '0', " + " `z` float NOT NULL DEFAULT '0', " + " `heading` float NOT NULL DEFAULT '0', " + " `pvp2` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `pvp_type` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `autosplit_enabled` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `zone_change_count` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `drakkin_heritage` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `drakkin_tattoo` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `drakkin_details` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `toxicity` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `hunger_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `thirst_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ability_up` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `zone_id` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `zone_instance` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `leadership_exp_on` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ldon_points_guk` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ldon_points_mir` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ldon_points_mmc` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ldon_points_ruj` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ldon_points_tak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ldon_points_available` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `tribute_time_remaining` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `show_helm` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `career_tribute_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `tribute_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `tribute_active` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `endurance` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `group_leadership_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `raid_leadership_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `group_leadership_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `raid_leadership_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `air_remaining` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `pvp_kills` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `pvp_deaths` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `pvp_current_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `pvp_career_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `pvp_best_kill_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `pvp_worst_death_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `pvp_current_kill_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `aa_points_spent` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `aa_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `aa_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `group_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `raid_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `guild_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `RestTimer` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " PRIMARY KEY (`id`), " + " UNIQUE KEY `name` (`name`), " + " KEY `account_id` (`account_id`) " + " ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; " + ); + results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_currency` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_currency'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_currency` doesn't exist... creating..."); + rquery = StringFormat( + " CREATE TABLE `character_currency` ( " + " `id` int(11) NOT NULL AUTO_INCREMENT, " + " `platinum` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `gold` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `silver` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `copper` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `platinum_bank` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `gold_bank` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `silver_bank` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `copper_bank` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `platinum_cursor` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `gold_cursor` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `silver_cursor` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `copper_cursor` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `radiant_crystals` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `career_radiant_crystals` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ebon_crystals` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `career_ebon_crystals` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " PRIMARY KEY (`id`), " + " KEY `id` (`id`) " + " ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; " + ); + results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_alternate_abilities` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_alternate_abilities'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_alternate_abilities` doesn't exist... creating..."); + rquery = StringFormat( + " CREATE TABLE `character_alternate_abilities` ( " + " `id` int(11) NOT NULL AUTO_INCREMENT, " + " `slot` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `aa_id` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `aa_value` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + " PRIMARY KEY(`id`,`slot`), " + " KEY `id` (`id`) " + " ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " + ); + results = QueryDatabase(rquery); + printf(" done...\n"); + } + printf("Starting conversion...\n\n"); } // querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); @@ -978,96 +1134,96 @@ bool Database::CheckDatabaseConversions() { " guild_auto_consent, " " RestTimer) " "VALUES (" - "%u," // id " id, " - "%u," // account_id " account_id, " - "'%s'," // `name` pp->name, " `name`, " - "'%s'," // last_name pp->last_name, " last_name, " - "%u," // gender pp->gender, " gender, " - "%u," // race pp->race, " race, " - "%u," // class pp->class_, " class, " - "%u," // `level` pp->level, " `level`, " - "%u," // deity pp->deity, " deity, " - "%u," // birthday pp->birthday, " birthday, " - "%u," // last_login pp->lastlogin, " last_login, " - "%u," // time_played pp->timePlayedMin, " time_played, " - "%u," // pvp_status pp->pvp, " pvp_status, " - "%u," // level2 pp->level2, " level2, " - "%u," // anon pp->anon, " anon, " - "%u," // gm pp->gm, " gm, " - "%u," // intoxication pp->intoxication, " intoxication, " - "%u," // hair_color pp->haircolor, " hair_color, " - "%u," // beard_color pp->beardcolor, " beard_color, " - "%u," // eye_color_1 pp->eyecolor1, " eye_color_1, " - "%u," // eye_color_2 pp->eyecolor2, " eye_color_2, " - "%u," // hair_style pp->hairstyle, " hair_style, " - "%u," // beard pp->beard, " beard, " - "%u," // ability_time_seconds pp->ability_time_seconds, " ability_time_seconds, " - "%u," // ability_number pp->ability_number, " ability_number, " - "%u," // ability_time_minutes pp->ability_time_minutes, " ability_time_minutes, " - "%u," // ability_time_hours pp->ability_time_hours, " ability_time_hours, " - "'%s'," // title pp->title, " title, " - "'%s'," // suffix pp->suffix, " suffix, " - "%u," // exp pp->exp, " exp, " - "%u," // points pp->points, " points, " - "%u," // mana pp->mana, " mana, " - "%u," // cur_hp pp->cur_hp, " cur_hp, " - "%u," // str pp->STR, " str, " - "%u," // sta pp->STA, " sta, " - "%u," // cha pp->CHA, " cha, " - "%u," // dex pp->DEX, " dex, " - "%u," // `int` pp->INT, " `int`, " - "%u," // agi pp->AGI, " agi, " - "%u," // wis pp->WIS, " wis, " - "%u," // face pp->face, " face, " - "%f," // y pp->y, " y, " - "%f," // x pp->x, " x, " - "%f," // z pp->z, " z, " - "%f," // heading pp->heading, " heading, " - "%u," // pvp2 pp->pvp2, " pvp2, " - "%u," // pvp_type pp->pvptype, " pvp_type, " - "%u," // autosplit_enabled pp->autosplit, " autosplit_enabled, " - "%u," // zone_change_count pp->zone_change_count, " zone_change_count, " - "%u," // drakkin_heritage pp->drakkin_heritage, " drakkin_heritage, " - "%u," // drakkin_tattoo pp->drakkin_tattoo, " drakkin_tattoo, " - "%u," // drakkin_details pp->drakkin_details, " drakkin_details, " - "%u," // toxicity pp->toxicity, " toxicity, " - "%u," // hunger_level pp->hunger_level, " hunger_level, " - "%u," // thirst_level pp->thirst_level, " thirst_level, " - "%u," // ability_up pp->ability_up, " ability_up, " - "%u," // zone_id pp->zone_id, " zone_id, " - "%u," // zone_instance pp->zoneInstance, " zone_instance, " - "%u," // leadership_exp_on pp->leadAAActive, " leadership_exp_on, " - "%u," // ldon_points_guk pp->ldon_points_guk, " ldon_points_guk, " - "%u," // ldon_points_mir pp->ldon_points_mir, " ldon_points_mir, " - "%u," // ldon_points_mmc pp->ldon_points_mmc, " ldon_points_mmc, " - "%u," // ldon_points_ruj pp->ldon_points_ruj, " ldon_points_ruj, " - "%u," // ldon_points_tak pp->ldon_points_tak, " ldon_points_tak, " - "%u," // ldon_points_available pp->ldon_points_available, " ldon_points_available, " - "%u," // tribute_time_remaining pp->tribute_time_remaining, " tribute_time_remaining, " - "%u," // show_helm pp->showhelm, " show_helm, " - "%u," // career_tribute_points pp->career_tribute_points, " career_tribute_points, " - "%u," // tribute_points pp->tribute_points, " tribute_points, " - "%u," // tribute_active pp->tribute_active, " tribute_active, " - "%u," // endurance pp->endurance, " endurance, " - "%u," // group_leadership_exp pp->group_leadership_exp, " group_leadership_exp, " - "%u," // raid_leadership_exp pp->raid_leadership_exp, " raid_leadership_exp, " - "%u," // group_leadership_points pp->group_leadership_points, " group_leadership_points, " - "%u," // raid_leadership_points pp->raid_leadership_points, " raid_leadership_points, " - "%u," // air_remaining pp->air_remaining, " air_remaining, " - "%u," // pvp_kills pp->PVPKills, " pvp_kills, " - "%u," // pvp_deaths pp->PVPDeaths, " pvp_deaths, " - "%u," // pvp_current_points pp->PVPCurrentPoints, " pvp_current_points, " - "%u," // pvp_career_points pp->PVPCareerPoints, " pvp_career_points, " - "%u," // pvp_best_kill_streak pp->PVPBestKillStreak, " pvp_best_kill_streak, " - "%u," // pvp_worst_death_streak pp->PVPWorstDeathStreak, " pvp_worst_death_streak, " - "%u," // pvp_current_kill_streak pp->PVPCurrentKillStreak, " pvp_current_kill_streak, " - "%u," // aa_points_spent pp->aapoints_spent, " aa_points_spent, " - "%u," // aa_exp pp->expAA, " aa_exp, " - "%u," // aa_points pp->aapoints, " aa_points, " - "%u," // group_auto_consent pp->groupAutoconsent, " group_auto_consent, " - "%u," // raid_auto_consent pp->raidAutoconsent, " raid_auto_consent, " - "%u," // guild_auto_consent pp->guildAutoconsent, " guild_auto_consent, " - "%u" // RestTimer pp->RestTimer, " RestTimer) " + "%u," // id + "%u," // account_id + "'%s'," // `name` pp->name, + "'%s'," // last_name pp->last_name, + "%u," // gender pp->gender, + "%u," // race pp->race, + "%u," // class pp->class_, + "%u," // `level` pp->level, + "%u," // deity pp->deity, + "%u," // birthday pp->birthday, + "%u," // last_login pp->lastlogin, + "%u," // time_played pp->timePlayedMin, + "%u," // pvp_status pp->pvp, + "%u," // level2 pp->level2, + "%u," // anon pp->anon, + "%u," // gm pp->gm, + "%u," // intoxication pp->intoxication, + "%u," // hair_color pp->haircolor, + "%u," // beard_color pp->beardcolor, + "%u," // eye_color_1 pp->eyecolor1, + "%u," // eye_color_2 pp->eyecolor2, + "%u," // hair_style pp->hairstyle, + "%u," // beard pp->beard, + "%u," // ability_time_seconds pp->ability_time_seconds, + "%u," // ability_number pp->ability_number, + "%u," // ability_time_minutes pp->ability_time_minutes, + "%u," // ability_time_hours pp->ability_time_hours, + "'%s'," // title pp->title, + "'%s'," // suffix pp->suffix, + "%u," // exp pp->exp, + "%u," // points pp->points, + "%u," // mana pp->mana, + "%u," // cur_hp pp->cur_hp, + "%u," // str pp->STR, + "%u," // sta pp->STA, + "%u," // cha pp->CHA, + "%u," // dex pp->DEX, + "%u," // `int` pp->INT, + "%u," // agi pp->AGI, + "%u," // wis pp->WIS, + "%u," // face pp->face, + "%f," // y pp->y, + "%f," // x pp->x, + "%f," // z pp->z, + "%f," // heading pp->heading, + "%u," // pvp2 pp->pvp2, + "%u," // pvp_type pp->pvptype, + "%u," // autosplit_enabled pp->autosplit, + "%u," // zone_change_count pp->zone_change_count, + "%u," // drakkin_heritage pp->drakkin_heritage, + "%u," // drakkin_tattoo pp->drakkin_tattoo, + "%u," // drakkin_details pp->drakkin_details, + "%u," // toxicity pp->toxicity, + "%u," // hunger_level pp->hunger_level, + "%u," // thirst_level pp->thirst_level, + "%u," // ability_up pp->ability_up, + "%u," // zone_id pp->zone_id, + "%u," // zone_instance pp->zoneInstance, + "%u," // leadership_exp_on pp->leadAAActive, + "%u," // ldon_points_guk pp->ldon_points_guk, + "%u," // ldon_points_mir pp->ldon_points_mir, + "%u," // ldon_points_mmc pp->ldon_points_mmc, + "%u," // ldon_points_ruj pp->ldon_points_ruj, + "%u," // ldon_points_tak pp->ldon_points_tak, + "%u," // ldon_points_available pp->ldon_points_available, + "%u," // tribute_time_remaining pp->tribute_time_remaining, + "%u," // show_helm pp->showhelm, + "%u," // career_tribute_points pp->career_tribute_points, + "%u," // tribute_points pp->tribute_points, + "%u," // tribute_active pp->tribute_active, + "%u," // endurance pp->endurance, + "%u," // group_leadership_exp pp->group_leadership_exp, + "%u," // raid_leadership_exp pp->raid_leadership_exp, + "%u," // group_leadership_points pp->group_leadership_points, + "%u," // raid_leadership_points pp->raid_leadership_points, + "%u," // air_remaining pp->air_remaining, + "%u," // pvp_kills pp->PVPKills, + "%u," // pvp_deaths pp->PVPDeaths, + "%u," // pvp_current_points pp->PVPCurrentPoints, + "%u," // pvp_career_points pp->PVPCareerPoints, + "%u," // pvp_best_kill_streak pp->PVPBestKillStreak, + "%u," // pvp_worst_death_streak pp->PVPWorstDeathStreak, + "%u," // pvp_current_kill_streak pp->PVPCurrentKillStreak, + "%u," // aa_points_spent pp->aapoints_spent, + "%u," // aa_exp pp->expAA, + "%u," // aa_points pp->aapoints, + "%u," // group_auto_consent pp->groupAutoconsent, + "%u," // raid_auto_consent pp->raidAutoconsent, + "%u," // guild_auto_consent pp->guildAutoconsent, + "%u" // RestTimer pp->RestTimer, ")", character_id, account_id, @@ -1161,6 +1317,7 @@ bool Database::CheckDatabaseConversions() { pp->RestTimer ); results = QueryDatabase(rquery); + /* Run AA Convert */ for (i = 0; i < MAX_PP_AA_ARRAY; i++){ if (pp->aa_array[i].AA > 0 && pp->aa_array[i].value > 0){ From 8dda7ddd04fab80b3dd9796ff476612ff073a57e Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 31 Aug 2014 05:52:36 -0500 Subject: [PATCH 004/368] Added the following tables to player profile automatic conversion during world bootup: `character_bind_home`; `character_alternate_abilities`; `character_currency`; `character_data`; `character_spells`; `character_memmed_spells`; `character_disciplines`; --- common/database.cpp | 366 +++++++++++++++++++++++++++++------------ zone/client_packet.cpp | 29 +--- 2 files changed, 265 insertions(+), 130 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 0996f6b0c..a5bce5a52 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -831,9 +831,9 @@ bool Database::CheckDatabaseConversions() { results = QueryDatabase(rquery); if (results.RowCount() == 0){ printf("Table: `character_data` doesn't exist... creating..."); - rquery = StringFormat( + rquery = StringFormat( " CREATE TABLE `character_data` ( " - " `id` int(11) NOT NULL AUTO_INCREMENT, " + " `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " " `account_id` int(11) NOT NULL DEFAULT '0', " " `name` varchar(64) NOT NULL, " " `last_name` varchar(64) NOT NULL, " @@ -928,7 +928,7 @@ bool Database::CheckDatabaseConversions() { " KEY `account_id` (`account_id`) " " ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; " ); - results = QueryDatabase(rquery); + QueryDatabase(rquery); printf(" done...\n"); } /* Check for table `character_currency` */ @@ -938,7 +938,7 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_currency` doesn't exist... creating..."); rquery = StringFormat( " CREATE TABLE `character_currency` ( " - " `id` int(11) NOT NULL AUTO_INCREMENT, " + " `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " " `platinum` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `gold` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `silver` int(11) UNSIGNED NOT NULL DEFAULT 0, " @@ -958,8 +958,8 @@ bool Database::CheckDatabaseConversions() { " PRIMARY KEY (`id`), " " KEY `id` (`id`) " " ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; " - ); - results = QueryDatabase(rquery); + ); + QueryDatabase(rquery); printf(" done...\n"); } /* Check for table `character_alternate_abilities` */ @@ -969,23 +969,130 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_alternate_abilities` doesn't exist... creating..."); rquery = StringFormat( " CREATE TABLE `character_alternate_abilities` ( " - " `id` int(11) NOT NULL AUTO_INCREMENT, " + " `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " " `slot` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " " `aa_id` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " " `aa_value` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " " PRIMARY KEY(`id`,`slot`), " " KEY `id` (`id`) " " ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " - ); - results = QueryDatabase(rquery); + ); + QueryDatabase(rquery); printf(" done...\n"); } + /* Check for table `character_bind_home` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_bind_home'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_bind_home` doesn't exist... creating..."); + rquery = StringFormat( + " CREATE TABLE `character_bind_home` ( " + " `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + " `zone_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + " `instance_id` mediumint(11) UNSIGNED NOT NULL DEFAULT '0', " + " `x` float NOT NULL DEFAULT '0', " + " `y` float NOT NULL DEFAULT '0', " + " `z` float NOT NULL DEFAULT '0', " + " `heading` float NOT NULL DEFAULT '0', " + " PRIMARY KEY(`id`), " + " KEY `id` (`id`) " + " ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ); + QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_languages` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_languages'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_languages` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_languages` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`lang_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`value` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`, `lang_id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ); + QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_skills` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_skills'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_skills` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_skills` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`skill_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`value` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`, `skill_id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ); + QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_spells` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_spells'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_spells` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_spells` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`slot_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`spell_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`, `slot_id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ); + QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_memmed_spells` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_memmed_spells'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_memmed_spells` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_memmed_spells` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`slot_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`spell_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`, `slot_id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ); + QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_disciplines` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_disciplines'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_disciplines` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_disciplines` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`disc_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`, `disc_id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ); + QueryDatabase(rquery); + printf(" done...\n"); + } + + /* Done */ printf("Starting conversion...\n\n"); } // querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); int char_iter_count = 0; - querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); + querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` >= 61238 LIMIT 100"); if (RunQuery(query, querylen, errbuf, &result)) { safe_delete_array(query); while (row = mysql_fetch_row(result)) { @@ -1012,7 +1119,7 @@ bool Database::CheckDatabaseConversions() { /* Loading Status on conversion */ if (runconvert == 1){ std::cout << "\r" << char_iter_count << "/" << number_of_characters << " " << std::flush; - loadbar(char_iter_count, number_of_characters, 50); + loadbar(char_iter_count, number_of_characters, 50); /* Run Currency Convert */ std::string rquery = StringFormat("REPLACE INTO `character_currency` (id, platinum, gold, silver, copper," @@ -1037,9 +1144,9 @@ bool Database::CheckDatabaseConversions() { pp->careerRadCrystals, pp->currentEbonCrystals, pp->careerEbonCrystals - ); + ); auto results = QueryDatabase(rquery); - + /* Run Character Data Convert */ rquery = StringFormat( "REPLACE INTO `character_data` (" @@ -1134,96 +1241,96 @@ bool Database::CheckDatabaseConversions() { " guild_auto_consent, " " RestTimer) " "VALUES (" - "%u," // id - "%u," // account_id - "'%s'," // `name` pp->name, - "'%s'," // last_name pp->last_name, - "%u," // gender pp->gender, - "%u," // race pp->race, - "%u," // class pp->class_, - "%u," // `level` pp->level, - "%u," // deity pp->deity, - "%u," // birthday pp->birthday, - "%u," // last_login pp->lastlogin, - "%u," // time_played pp->timePlayedMin, - "%u," // pvp_status pp->pvp, - "%u," // level2 pp->level2, - "%u," // anon pp->anon, - "%u," // gm pp->gm, - "%u," // intoxication pp->intoxication, - "%u," // hair_color pp->haircolor, - "%u," // beard_color pp->beardcolor, - "%u," // eye_color_1 pp->eyecolor1, - "%u," // eye_color_2 pp->eyecolor2, - "%u," // hair_style pp->hairstyle, - "%u," // beard pp->beard, - "%u," // ability_time_seconds pp->ability_time_seconds, - "%u," // ability_number pp->ability_number, - "%u," // ability_time_minutes pp->ability_time_minutes, - "%u," // ability_time_hours pp->ability_time_hours, - "'%s'," // title pp->title, - "'%s'," // suffix pp->suffix, - "%u," // exp pp->exp, - "%u," // points pp->points, - "%u," // mana pp->mana, - "%u," // cur_hp pp->cur_hp, - "%u," // str pp->STR, - "%u," // sta pp->STA, - "%u," // cha pp->CHA, - "%u," // dex pp->DEX, - "%u," // `int` pp->INT, - "%u," // agi pp->AGI, - "%u," // wis pp->WIS, - "%u," // face pp->face, - "%f," // y pp->y, - "%f," // x pp->x, - "%f," // z pp->z, - "%f," // heading pp->heading, - "%u," // pvp2 pp->pvp2, - "%u," // pvp_type pp->pvptype, - "%u," // autosplit_enabled pp->autosplit, - "%u," // zone_change_count pp->zone_change_count, - "%u," // drakkin_heritage pp->drakkin_heritage, - "%u," // drakkin_tattoo pp->drakkin_tattoo, - "%u," // drakkin_details pp->drakkin_details, - "%u," // toxicity pp->toxicity, - "%u," // hunger_level pp->hunger_level, - "%u," // thirst_level pp->thirst_level, - "%u," // ability_up pp->ability_up, - "%u," // zone_id pp->zone_id, - "%u," // zone_instance pp->zoneInstance, - "%u," // leadership_exp_on pp->leadAAActive, - "%u," // ldon_points_guk pp->ldon_points_guk, - "%u," // ldon_points_mir pp->ldon_points_mir, - "%u," // ldon_points_mmc pp->ldon_points_mmc, - "%u," // ldon_points_ruj pp->ldon_points_ruj, - "%u," // ldon_points_tak pp->ldon_points_tak, - "%u," // ldon_points_available pp->ldon_points_available, - "%u," // tribute_time_remaining pp->tribute_time_remaining, - "%u," // show_helm pp->showhelm, - "%u," // career_tribute_points pp->career_tribute_points, - "%u," // tribute_points pp->tribute_points, - "%u," // tribute_active pp->tribute_active, - "%u," // endurance pp->endurance, - "%u," // group_leadership_exp pp->group_leadership_exp, - "%u," // raid_leadership_exp pp->raid_leadership_exp, - "%u," // group_leadership_points pp->group_leadership_points, - "%u," // raid_leadership_points pp->raid_leadership_points, - "%u," // air_remaining pp->air_remaining, - "%u," // pvp_kills pp->PVPKills, - "%u," // pvp_deaths pp->PVPDeaths, - "%u," // pvp_current_points pp->PVPCurrentPoints, - "%u," // pvp_career_points pp->PVPCareerPoints, - "%u," // pvp_best_kill_streak pp->PVPBestKillStreak, - "%u," // pvp_worst_death_streak pp->PVPWorstDeathStreak, - "%u," // pvp_current_kill_streak pp->PVPCurrentKillStreak, - "%u," // aa_points_spent pp->aapoints_spent, - "%u," // aa_exp pp->expAA, - "%u," // aa_points pp->aapoints, - "%u," // group_auto_consent pp->groupAutoconsent, - "%u," // raid_auto_consent pp->raidAutoconsent, - "%u," // guild_auto_consent pp->guildAutoconsent, - "%u" // RestTimer pp->RestTimer, + "%u," // id + "%u," // account_id + "'%s'," // `name` + "'%s'," // last_name + "%u," // gender + "%u," // race + "%u," // class + "%u," // `level` + "%u," // deity + "%u," // birthday + "%u," // last_login + "%u," // time_played + "%u," // pvp_status + "%u," // level2 + "%u," // anon + "%u," // gm + "%u," // intoxication + "%u," // hair_color + "%u," // beard_color + "%u," // eye_color_1 + "%u," // eye_color_2 + "%u," // hair_style + "%u," // beard + "%u," // ability_time_seconds + "%u," // ability_number + "%u," // ability_time_minutes + "%u," // ability_time_hours + "'%s'," // title + "'%s'," // suffix + "%u," // exp + "%u," // points + "%u," // mana + "%u," // cur_hp + "%u," // str + "%u," // sta + "%u," // cha + "%u," // dex + "%u," // `int` + "%u," // agi + "%u," // wis + "%u," // face + "%f," // y + "%f," // x + "%f," // z + "%f," // heading + "%u," // pvp2 + "%u," // pvp_type + "%u," // autosplit_enabled + "%u," // zone_change_count + "%u," // drakkin_heritage + "%u," // drakkin_tattoo + "%u," // drakkin_details + "%u," // toxicity + "%u," // hunger_level + "%u," // thirst_level + "%u," // ability_up + "%u," // zone_id + "%u," // zone_instance + "%u," // leadership_exp_on + "%u," // ldon_points_guk + "%u," // ldon_points_mir + "%u," // ldon_points_mmc + "%u," // ldon_points_ruj + "%u," // ldon_points_tak + "%u," // ldon_points_available + "%u," // tribute_time_remaining + "%u," // show_helm + "%u," // career_tribute_points + "%u," // tribute_points + "%u," // tribute_active + "%u," // endurance + "%u," // group_leadership_exp + "%u," // raid_leadership_exp + "%u," // group_leadership_points + "%u," // raid_leadership_points + "%u," // air_remaining + "%u," // pvp_kills + "%u," // pvp_deaths + "%u," // pvp_current_points + "%u," // pvp_career_points + "%u," // pvp_best_kill_streak + "%u," // pvp_worst_death_streak + "%u," // pvp_current_kill_streak + "%u," // aa_points_spent + "%u," // aa_exp + "%u," // aa_points + "%u," // group_auto_consent + "%u," // raid_auto_consent + "%u," // guild_auto_consent + "%u" // RestTimer ")", character_id, account_id, @@ -1321,13 +1428,58 @@ bool Database::CheckDatabaseConversions() { /* Run AA Convert */ for (i = 0; i < MAX_PP_AA_ARRAY; i++){ if (pp->aa_array[i].AA > 0 && pp->aa_array[i].value > 0){ - std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" + rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" " VALUES (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); - auto results = QueryDatabase(rquery); + QueryDatabase(rquery); + } + } + + /* Run Bind Home Convert */ + rquery = StringFormat("REPLACE INTO `character_bind_home` (id, zone_id, instance_id, x, y, z, heading)" + " VALUES (%u, %u, %u, %f, %f, %f, %f)", + character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); + QueryDatabase(rquery); + + /* Run Language Convert */ + for (i = 0; i < MAX_PP_LANGUAGE; i++){ + if (pp->languages[i] > 0){ + rquery = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, i, pp->languages[i]); + QueryDatabase(rquery); + } + } + + /* Run Skill Convert */ + for (i = 0; i < MAX_PP_SKILL; i++){ + if (pp->skills[i] > 0){ + rquery = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, i, pp->skills[i]); + QueryDatabase(rquery); + } + } + + /* Run Spell Convert */ + for (i = 0; i < MAX_PP_SPELLBOOK; i++){ + if (pp->spell_book[i] > 0 && pp->spell_book[i] != 4294967295){ + rquery = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->spell_book[i]); + QueryDatabase(rquery); + } + } + + /* Run Max Memmed Spell Convert */ + for (i = 0; i < MAX_PP_MEMSPELL; i++){ + if (pp->mem_spells[i] > 0){ + rquery = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->mem_spells[i]); + QueryDatabase(rquery); + } + } + + /* Run Discipline Convert */ + for (i = 0; i < MAX_PP_DISCIPLINES; i++){ + if (pp->disciplines.values[i] > 0){ + rquery = StringFormat("REPLACE INTO `character_disciplines` (id, disc_id) VALUES (%u, %u)", character_id, pp->disciplines.values[i]); + QueryDatabase(rquery); } } - printf("\n\nCharacter blob conversion complete, continuing world bootup...\n"); } /* Print out the entire Player Profile for testing */ @@ -1646,6 +1798,8 @@ bool Database::CheckDatabaseConversions() { } mysql_free_result(result); } + if (runconvert == 1){ printf("\n\nCharacter blob conversion complete, continuing world bootup...\n"); } + return true; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index abf044caa..da5e62dc5 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8786,31 +8786,15 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { if(!RuleB(Character, MaintainIntoxicationAcrossZones)) m_pp.intoxication = 0; - //uint32 aalen = database.GetPlayerAlternateAdv(account_id, name, &aa); - //if (aalen == 0) { - // std::cout << "Client dropped: !GetPlayerAlternateAdv, name=" << name << std::endl; - // return false; - //} - - - - //////////////////////////////////////////////////////////// // Player Profile Packet - // Try to find the EQ ID for the guild, if doesnt exist, guild has been deleted. - - // Clear memory, but leave it in the DB (no reason not to, guild might be restored?) - strcpy(name, m_pp.name); - strcpy(lastname, m_pp.last_name); if((m_pp.x == -1 && m_pp.y == -1 && m_pp.z == -1)||(m_pp.x == -2 && m_pp.y == -2 && m_pp.z == -2)) { m_pp.x = zone->safe_x(); m_pp.y = zone->safe_y(); m_pp.z = zone->safe_z(); } - //these now come from the database, and it is the authority. - if(class_ > 0) - m_pp.class_ = class_; - else - class_ = m_pp.class_; + + class_ = m_pp.class_; + if(level > 0) { if(m_pp.level != level) { //they changed their level in the database... not ideal, but oh well.. @@ -8882,9 +8866,7 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { /* Load Guild */ - if (!IsInAGuild()) { - m_pp.guild_id = GUILD_NONE; - } + if (!IsInAGuild()) { m_pp.guild_id = GUILD_NONE; } else { m_pp.guild_id = GuildID(); @@ -9483,8 +9465,7 @@ void Client::CompleteConnect() { } /* Sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead */ - entity_list.SendZoneAppearance(this); - + entity_list.SendZoneAppearance(this); /* Sends the Nimbus particle effects (up to 3) for any mob using them */ entity_list.SendNimbusEffects(this); From f8439fd6e6165748ceed3345eb94ad8279584b6b Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 31 Aug 2014 07:52:52 -0500 Subject: [PATCH 005/368] Made many adjustments to character load code. Removed bool Client::FinishConnState2(DBAsyncWork* dbaw) Removed all async character loads Removed bool GetAccountInfoForLogin Removed bool GetAccountInfoForLogin_result Removed bool GetCharacterInfoForLogin_result Removed bool GetCharacterInfoForLogin Added: bool LoadCharacterFactionValues(uint32 character_id, faction_map & val_list); bool LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp); --- common/database.cpp | 2 +- zone/client_packet.cpp | 1064 +++++++++++++++++++--------------------- zone/effects.cpp | 4 - zone/zonedb.cpp | 249 ++-------- zone/zonedb.h | 18 +- 5 files changed, 540 insertions(+), 797 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index a5bce5a52..429da0e42 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1092,7 +1092,7 @@ bool Database::CheckDatabaseConversions() { // querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); int char_iter_count = 0; - querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` >= 61238 LIMIT 100"); + querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` >= 61238 LIMIT 1"); if (RunQuery(query, querylen, errbuf, &result)) { safe_delete_array(query); while (row = mysql_fetch_row(result)) { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index da5e62dc5..9b2af6c45 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -538,29 +538,511 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) client->Disconnect(); } - char* query = 0; - uint32_breakdown workpt; - workpt.b4() = DBA_b4_Entity; - workpt.w2_3() = GetID(); - workpt.b1() = DBA_b1_Entity_Client_InfoForLogin; - DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Read); - dbaw->AddQuery(1, &query, MakeAnyLenString(&query, - "SELECT status,name,lsaccount_id,gmspeed,revoked,hideme,time_creation FROM account WHERE id=%i", - account_id)); - //DO NOT FORGET TO EDIT ZoneDatabase::GetCharacterInfoForLogin if you change this - dbaw->AddQuery(2, &query, MakeAnyLenString(&query, - "SELECT id,profile,zonename,x,y,z,guild_id,rank,extprofile,class,level,lfp,lfg,instanceid,xtargets,firstlogon" - " FROM character_ LEFT JOIN guild_members ON id=char_id WHERE id=%i", - character_id)); - dbaw->AddQuery(3, &query, MakeAnyLenString(&query, - "SELECT faction_id,current_value FROM faction_values WHERE temp = 0 AND char_id = %i", - character_id)); - if (!(pDBAsyncWorkID = dbasync->AddWork(&dbaw))) { - safe_delete(dbaw); - LogFile->write(EQEMuLog::Error,"dbasync->AddWork() returned false, client crash"); - client_state = CLIENT_KICKED; - return; + uint32 pplen = 0; + EQApplicationPacket* outapp = 0; + MYSQL_RES* result = 0; + bool loaditems = 0; + uint32 i; + std::string query; + unsigned long* lengths; + + /* Set item materials */ + for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) + if (m_pp.item_tint[i].rgb.use_tint == 1) + m_pp.item_tint[i].rgb.use_tint = 0xFF; + + uint32 cid = CharacterID(); + character_id = cid; + + database.RemoveTempFactions(this); + database.LoadCharacterFactionValues(cid, factionvalues); + /* Load Character Account Data: Temp until I move */ + query = StringFormat("SELECT `status`, `name`, `lsaccount_id`, `gmspeed`, `revoked`, `hideme` FROM `account` WHERE `id` = %i", this->AccountID()); + auto results = database.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + if (admin){ admin = atoi(row[0]); } + if (account_name){ strcpy(account_name, row[1]); } + if (lsaccountid && atoi(row[2]) > 0){ lsaccountid = atoi(row[2]); } + else{ lsaccountid = 0; } + if (gmspeed){ gmspeed = atoi(row[3]); } + if (revoked){ revoked = atoi(row[4]); } + if (gmhideme){ gmhideme = atoi(row[5]); } + if (account_creation){ account_creation = atoul(row[6]); } } + /* Load Character Legacy Data: Temp until I move */ + query = StringFormat("id,profile,zonename,x,y,z,guild_id,rank,extprofile,class,level,lfp,lfg,instanceid,xtargets,firstlogon FROM character_ LEFT JOIN guild_members ON id=char_id WHERE id=%i", cid); + results = database.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + m_pp.lastlogin = time(nullptr); + + if (RuleB(Character, SharedBankPlat)) + m_pp.platinum_shared = database.GetSharedPlatinum(database.GetAccountIDByChar(cid)); + + if (guildrank) { + if (row[7] != nullptr) + guildrank = atoi(row[7]); + else + guildrank = GUILD_RANK_NONE; + } + // if (ext) { SetExtendedProfile(ext, row[8], lengths[8]); } + if (level){ level = atoi(row[10]); } + if (LFP){ LFP = atoi(row[11]); } + if (LFG){ LFG = atoi(row[12]); } + if (firstlogon){ firstlogon = atoi(row[15]); } + } + /* Load Character Inventory */ + loaditems = database.GetInventory(cid, &m_inv); + /* Load Character Currency into PP */ + database.LoadCharacterCurrency(cid, &m_pp); + /* Load Character Data from DB into PP */ + database.LoadCharacterData(cid, &m_pp); + /* Move to another method when can, this is pointless... */ + database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); + /* Load Character Currency */ + database.LoadCharacterCurrency(cid, &m_pp); + /* Load Character Skills */ + database.LoadCharacterSkills(cid, &m_pp); + /* Load Character Disciplines */ + database.LoadCharacterDisciplines(cid, &m_pp); + + if (gmhideme) { trackable = false; } + + conn_state = PlayerProfileLoaded; + + /* Set Current zone */ + // m_pp.zone_id = zone->GetZoneID(); + // m_pp.zoneInstance = zone->GetInstanceID(); + + TotalSecondsPlayed = m_pp.timePlayedMin * 60; + + max_AAXP = RuleI(AA, ExpPerPoint); + + if (!RuleB(Character, MaintainIntoxicationAcrossZones)) + m_pp.intoxication = 0; + + if ((m_pp.x == -1 && m_pp.y == -1 && m_pp.z == -1) || (m_pp.x == -2 && m_pp.y == -2 && m_pp.z == -2)) { + m_pp.x = zone->safe_x(); + m_pp.y = zone->safe_y(); + m_pp.z = zone->safe_z(); + } + + /* If too far below ground, then fix */ + float ground_z = GetGroundZ(m_pp.x, m_pp.y, m_pp.z); + if (m_pp.z < (ground_z - 500)) + m_pp.z = ground_z; + + class_ = m_pp.class_; + + level = m_pp.level; + x_pos = m_pp.x; + y_pos = m_pp.y; + z_pos = m_pp.z; + heading = m_pp.heading; + race = m_pp.race; + base_race = m_pp.race; + gender = m_pp.gender; + base_gender = m_pp.gender; + deity = m_pp.deity; //FYI: DEITY_AGNOSTIC = 396; still valid? + haircolor = m_pp.haircolor; + beardcolor = m_pp.beardcolor; + eyecolor1 = m_pp.eyecolor1; + eyecolor2 = m_pp.eyecolor2; + hairstyle = m_pp.hairstyle; + luclinface = m_pp.face; + beard = m_pp.beard; + drakkin_heritage = m_pp.drakkin_heritage; + drakkin_tattoo = m_pp.drakkin_tattoo; + drakkin_details = m_pp.drakkin_details; + + if (m_pp.gm && admin < minStatusToBeGM) + m_pp.gm = 0; + + /* Load Guild */ + if (!IsInAGuild()) { m_pp.guild_id = GUILD_NONE; } + else { + m_pp.guild_id = GuildID(); + + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID)) + GuildBanker = (guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || guild_mgr.GetBankerFlag(CharacterID())); + } + + m_pp.guildbanker = GuildBanker; + + switch (race) + { + case OGRE: + size = 9; break; + case TROLL: + size = 8; break; + case VAHSHIR: case BARBARIAN: + size = 7; break; + case HUMAN: case HIGH_ELF: case ERUDITE: case IKSAR: case DRAKKIN: + size = 6; break; + case HALF_ELF: + size = 5.5; break; + case WOOD_ELF: case DARK_ELF: case FROGLOK: + size = 5; break; + case DWARF: + size = 4; break; + case HALFLING: + size = 3.5; break; + case GNOME: + size = 3; break; + default: + size = 0; + } + + /* Check for Invalid points */ + if (m_pp.ldon_points_guk < 0 || m_pp.ldon_points_guk > 2000000000){ m_pp.ldon_points_guk = 0; } + if (m_pp.ldon_points_mir < 0 || m_pp.ldon_points_mir > 2000000000){ m_pp.ldon_points_mir = 0; } + if (m_pp.ldon_points_mmc < 0 || m_pp.ldon_points_mmc > 2000000000){ m_pp.ldon_points_mmc = 0; } + if (m_pp.ldon_points_ruj < 0 || m_pp.ldon_points_ruj > 2000000000){ m_pp.ldon_points_ruj = 0; } + if (m_pp.ldon_points_tak < 0 || m_pp.ldon_points_tak > 2000000000){ m_pp.ldon_points_tak = 0; } + if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000){ m_pp.ldon_points_available = 0; } + + if (GetSkill(SkillSwimming) < 100) + SetSkill(SkillSwimming, 100); + + /* Initialize AA's : Move to function eventually */ + for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ aa[a] = &m_pp.aa_array[a]; } + query = StringFormat( + "SELECT " + "slot, " + "aa_id, " + "aa_value " + "FROM " + "`character_alternate_abilities` " + "WHERE `id` = %u ORDER BY `slot`", this->CharacterID()); + results = database.QueryDatabase(query); i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); + m_pp.aa_array[i].AA = atoi(row[1]); + m_pp.aa_array[i].value = atoi(row[1]); + aa[i]->AA = atoi(row[1]); + aa[i]->value = atoi(row[2]); + } + for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ + uint32 id = aa[a]->AA; + //watch for invalid AA IDs + if (id == aaNone) + continue; + if (id >= aaHighestID) { + aa[a]->AA = aaNone; + aa[a]->value = 0; + continue; + } + if (aa[a]->value == 0) { + aa[a]->AA = aaNone; + continue; + } + if (aa[a]->value > HIGHEST_AA_VALUE) { + aa[a]->AA = aaNone; + aa[a]->value = 0; + continue; + } + + if (aa[a]->value > 1) /* hack in some stuff for sony's new AA method (where each level of each aa.has a seperate ID) */ + aa_points[(id - aa[a]->value + 1)] = aa[a]->value; + else + aa_points[id] = aa[a]->value; + } + + if (SPDAT_RECORDS > 0) + { + for (uint32 z = 0; z= (uint32)SPDAT_RECORDS) + UnmemSpell(z, false); + } + + database.LoadBuffs(this); + uint32 max_slots = GetMaxBuffSlots(); + for (int i = 0; i < max_slots; i++) { + if (buffs[i].spellid != SPELL_UNKNOWN) { + m_pp.buffs[i].spellid = buffs[i].spellid; + m_pp.buffs[i].bard_modifier = 10; + m_pp.buffs[i].slotid = 2; + m_pp.buffs[i].player_id = 0x2211; + m_pp.buffs[i].level = buffs[i].casterlevel; + m_pp.buffs[i].effect = 0; + m_pp.buffs[i].duration = buffs[i].ticsremaining; + m_pp.buffs[i].counters = buffs[i].counters; + } + else { + m_pp.buffs[i].spellid = SPELLBOOK_UNKNOWN; + m_pp.buffs[i].bard_modifier = 10; + m_pp.buffs[i].slotid = 0; + m_pp.buffs[i].player_id = 0; + m_pp.buffs[i].level = 0; + m_pp.buffs[i].effect = 0; + m_pp.buffs[i].duration = 0; + m_pp.buffs[i].counters = 0; + } + } + } + + /* Load Character Key Ring */ + KeyRingLoad(); + + /* Send Group Members via PP */ + uint32 groupid = database.GetGroupID(GetName()); + Group* group = nullptr; + if (groupid > 0){ + group = entity_list.GetGroupByID(groupid); + if (!group) { //nobody from our is here... start a new group + group = new Group(groupid); + if (group->GetID() != 0) + entity_list.AddGroup(group, groupid); + else //error loading group members... + { + delete group; + group = nullptr; + } + } //else, somebody from our group is already here... + + if (group) + group->UpdatePlayer(this); + else + database.SetGroupID(GetName(), 0, CharacterID()); //cannot re-establish group, kill it + + } + else { //no group id + //clear out the group junk in our PP + uint32 xy = 0; + for (xy = 0; xy < MAX_GROUP_MEMBERS; xy++) + memset(m_pp.groupMembers[xy], 0, 64); + } + + if (group){ + // If the group leader is not set, pull the group leader infomrmation from the database. + if (!group->GetLeader()){ + char ln[64]; + char MainTankName[64]; + char AssistName[64]; + char PullerName[64]; + char NPCMarkerName[64]; + GroupLeadershipAA_Struct GLAA; + memset(ln, 0, 64); + strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, &GLAA)); + Client *c = entity_list.GetClientByName(ln); + if (c) + group->SetLeader(c); + + group->SetMainTank(MainTankName); + group->SetMainAssist(AssistName); + group->SetPuller(PullerName); + group->SetNPCMarker(NPCMarkerName); + group->SetGroupAAs(&GLAA); + + //group->NotifyMainTank(this, 1); + //group->NotifyMainAssist(this, 1); + //group->NotifyPuller(this, 1); + + // If we are the leader, force an update of our group AAs to other members in the zone, in case + // we purchased a new one while out-of-zone. + if (group->IsLeader(this)) + group->SendLeadershipAAUpdate(); + + } + LFG = false; + } + +#ifdef BOTS + Bot::LoadAndSpawnAllZonedBots(this); +#endif + + CalcBonuses(); + if (m_pp.cur_hp <= 0) + m_pp.cur_hp = GetMaxHP(); + + SetHP(m_pp.cur_hp); + Mob::SetMana(m_pp.mana); + SetEndurance(m_pp.endurance); + + if (IsLFP()) { + // Update LFP in case any (or all) of our group disbanded while we were zoning. + UpdateLFP(); + } + + if (m_pp.z <= zone->newzone_data.underworld) { + m_pp.x = zone->newzone_data.safe_x; + m_pp.y = zone->newzone_data.safe_y; + m_pp.z = zone->newzone_data.safe_z; + } + + /* Get Expansions from variables table and ship via PP */ + char val[20] = { 0 }; + if (database.GetVariable("Expansions", val, 20)) + m_pp.expansions = atoi(val); + else + m_pp.expansions = 0x3FF; + + p_timers.SetCharID(CharacterID()); + if (!p_timers.Load(&database)) { + LogFile->write(EQEMuLog::Error, "Unable to load ability timers from the database for %s (%i)!", GetCleanName(), CharacterID()); + } + + for (unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) + if (IsValidSpell(m_pp.mem_spells[i])) + m_pp.spellSlotRefresh[i] = p_timers.GetRemainingTime(pTimerSpellStart + m_pp.mem_spells[i]) * 1000; + + /* Ability slot refresh send SK/PAL */ + if (m_pp.class_ == SHADOWKNIGHT || m_pp.class_ == PALADIN) { + uint32 abilitynum = 0; + if (m_pp.class_ == SHADOWKNIGHT){ abilitynum = pTimerHarmTouch; } + else{ abilitynum = pTimerLayHands; } + + uint32 remaining = p_timers.GetRemainingTime(abilitynum); + if (remaining > 0 && remaining < 15300) + m_pp.abilitySlotRefresh = remaining * 1000; + else + m_pp.abilitySlotRefresh = 0; + } + +#ifdef _EQDEBUG + printf("Dumping inventory on load:\n"); + m_inv.dumpEntireInventory(); +#endif + + //lost in current PP + // strcpy(m_pp.servername,"eqemulator"); + + m_pp.air_remaining = 60; //Reset to max so they dont drown on zone in if its underwater + + if (zone->IsPVPZone()) + m_pp.pvp = 1; + + m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; + + /* Reset rest timer if the durations have been lowered in the database */ + if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) + m_pp.RestTimer = 0; + + /* This checksum should disappear once dynamic structs are in... each struct strategy will do it */ + CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct)-4); + + outapp = new EQApplicationPacket(OP_PlayerProfile, sizeof(PlayerProfile_Struct)); + + /* The entityid field in the Player Profile is used by the Client in relation to Group Leadership AA */ + m_pp.entityid = GetID(); + memcpy(outapp->pBuffer, &m_pp, outapp->size); + outapp->priority = 6; + FastQueuePacket(&outapp); + + if (m_pp.RestTimer) + rest_timer.Start(m_pp.RestTimer * 1000); + + database.LoadPetInfo(this); + /* + This was moved before the spawn packets are sent + in hopes that it adds more consistency... + Remake pet + */ + if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) + { + MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size); + if (GetPet() && GetPet()->IsNPC()) { + NPC *pet = GetPet()->CastToNPC(); + pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items); + pet->CalcBonuses(); + pet->SetHP(m_petinfo.HP); + pet->SetMana(m_petinfo.Mana); + } + m_petinfo.SpellID = 0; + } + /* Moved here so it's after where we load the pet data. */ + if (!GetAA(aaPersistentMinion)) + memset(&m_suspendedminion, 0, sizeof(PetInfo)); + + /* Server Zone Entry Packet */ + outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); + ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer; + + FillSpawnStruct(&sze->player, CastToMob()); + sze->player.spawn.curHp = 1; + sze->player.spawn.NPC = 0; + sze->player.spawn.z += 6; //arbitrary lift, seems to help spawning under zone. + outapp->priority = 6; + FastQueuePacket(&outapp); + + /* Zone Spawns Packet */ + entity_list.SendZoneSpawnsBulk(this); + entity_list.SendZoneCorpsesBulk(this); + entity_list.SendZonePVPUpdates(this); //hack until spawn struct is fixed. + + /* Time of Day packet */ + outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); + TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; + zone->zone_time.getEQTimeOfDay(time(0), tod); + outapp->priority = 6; + FastQueuePacket(&outapp); + + /* Tribute Packets */ + DoTributeUpdate(); + if (m_pp.tribute_active) { + //restart the tribute timer where we left off + tribute_timer.Start(m_pp.tribute_time_remaining); + } + + /* + Character Inventory Packet + this is not quite where live sends inventory, they do it after tribute + */ + if (loaditems) { //dont load if a length error occurs + BulkSendInventoryItems(); + + // Send stuff on the cursor which isnt sent in bulk + iter_queue it; + for (it = m_inv.cursor_begin(); it != m_inv.cursor_end(); ++it) { + // First item cursor is sent in bulk inventory packet + if (it == m_inv.cursor_begin()) + continue; + const ItemInst *inst = *it; + SendItemPacket(MainCursor, inst, ItemPacketSummonItem); + } + } + + /* Task Packets */ + LoadClientTaskState(); + + if (GetClientVersion() >= EQClientRoF) + { + outapp = new EQApplicationPacket(OP_ReqNewZone, 0); + Handle_Connect_OP_ReqNewZone(outapp); + safe_delete(outapp); + } + + if (ClientVersionBit & BIT_UnderfootAndLater) + { + outapp = new EQApplicationPacket(OP_XTargetResponse, 8); + outapp->WriteUInt32(GetMaxXTargets()); + outapp->WriteUInt32(0); + FastQueuePacket(&outapp); + } + + /* + Weather Packet + This shouldent be moved, this seems to be what the client + uses to advance to the next state (sending ReqNewZone) + */ + outapp = new EQApplicationPacket(OP_Weather, 12); + Weather_Struct *ws = (Weather_Struct *)outapp->pBuffer; + ws->val1 = 0x000000FF; + if (zone->zone_weather == 1) + ws->type = 0x31; // Rain + if (zone->zone_weather == 2) + { + outapp->pBuffer[8] = 0x01; + ws->type = 0x02; + } + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + SetAttackTimer(); + + conn_state = ZoneInfoSent; + return; } @@ -8692,11 +9174,6 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) void Client::DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { Entity::DBAWComplete(workpt_b1, dbaw); switch (workpt_b1) { - case DBA_b1_Entity_Client_InfoForLogin: { - if (!FinishConnState2(dbaw)) - client_state = CLIENT_ERROR; - break; - } case DBA_b1_Entity_Client_Save: { clock_t t = std::clock(); /* Function timer start */ @@ -8726,539 +9203,6 @@ void Client::DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { } } -bool Client::FinishConnState2(DBAsyncWork* dbaw) { - uint32 pplen = 0; - DBAsyncQuery* dbaq = 0; - EQApplicationPacket* outapp = 0; - MYSQL_RES* result = 0; - bool loaditems = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - uint32 i; - - for (i=1; i<=3; i++) { - dbaq = dbaw->PopAnswer(); - if (!dbaq) { - std::cout << "Error in FinishConnState2(): dbaq==0" << std::endl; - return false; - } - if (!dbaq->GetAnswer(errbuf, &result)) { - std::cout << "Error in FinishConnState2(): !dbaq[" << dbaq->QPT() << "]->GetAnswer(): " << errbuf << std::endl; - return false; - } - if (dbaq->QPT() == 1) { - database.GetAccountInfoForLogin_result(result, 0, account_name, &lsaccountid, &gmspeed, &revoked, &gmhideme, &account_creation); - if(gmhideme) - { - trackable = false; - } - } - else if (dbaq->QPT() == 2) { - loaditems = database.GetCharacterInfoForLogin_result(result, 0, 0, &m_pp, &m_inv, &m_epp, &pplen, &guild_id, &guildrank, &class_, &level, &LFP, &LFG, &MaxXTargets, &firstlogon); - } - else if (dbaq->QPT() == 3) { - database.RemoveTempFactions(this); - database.LoadFactionValues_result(result, factionvalues); - } - else { - std::cout << "Error in FinishConnState2(): dbaq->PQT() unknown" << std::endl; - return false; - } - } - - /* Load Character Currency into PP */ - database.LoadCharacterCurrency(CharacterID(), &m_pp); - /* Load Character Data from DB into PP */ - database.LoadCharacterData(CharacterID(), &m_pp); - /* Move to another method when can, this is pointless... */ - database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); - /* Load Character Currency*/ - database.LoadCharacterCurrency(CharacterID(), &m_pp); - - conn_state = PlayerProfileLoaded; - /* Set Current zone */ - m_pp.zone_id = zone->GetZoneID(); - m_pp.zoneInstance = zone->GetInstanceID(); - - TotalSecondsPlayed = m_pp.timePlayedMin * 60; - - max_AAXP = RuleI(AA, ExpPerPoint); - - if(!RuleB(Character, MaintainIntoxicationAcrossZones)) - m_pp.intoxication = 0; - - if((m_pp.x == -1 && m_pp.y == -1 && m_pp.z == -1)||(m_pp.x == -2 && m_pp.y == -2 && m_pp.z == -2)) { - m_pp.x = zone->safe_x(); - m_pp.y = zone->safe_y(); - m_pp.z = zone->safe_z(); - } - - - class_ = m_pp.class_; - - if(level > 0) { - if(m_pp.level != level) { - //they changed their level in the database... not ideal, but oh well.. - m_pp.exp = GetEXPForLevel(level); - m_pp.level = level; - } - } else { - level = m_pp.level; - } - - x_pos = m_pp.x; - y_pos = m_pp.y; - z_pos = m_pp.z; - heading = m_pp.heading; - race = m_pp.race; - base_race = m_pp.race; - gender = m_pp.gender; - base_gender = m_pp.gender; - deity = m_pp.deity; //FYI: DEITY_AGNOSTIC = 396; still valid? - haircolor = m_pp.haircolor; - beardcolor = m_pp.beardcolor; - eyecolor1 = m_pp.eyecolor1; - eyecolor2 = m_pp.eyecolor2; - hairstyle = m_pp.hairstyle; - luclinface = m_pp.face; - beard = m_pp.beard; - drakkin_heritage = m_pp.drakkin_heritage; - drakkin_tattoo = m_pp.drakkin_tattoo; - drakkin_details = m_pp.drakkin_details; - - //if we zone in with invalid Z, fix it. - if (zone->zonemap != nullptr) { - Map::Vertex me; - me.x = GetX(); - me.y = GetY(); - me.z = GetZ() + (GetSize() == 0.0 ? 6 : GetSize()); - - Map::Vertex hit; - - if (zone->zonemap->FindBestZ(me, &hit) == BEST_Z_INVALID) - { -#if EQDEBUG >= 5 - LogFile->write(EQEMuLog::Debug, "Player %s started below the zone trying to fix! (%.3f, %.3f, %.3f)", GetName(), me.x, me.y, me.z); -#endif - me.z += 200; //arbitrary # - if (zone->zonemap->FindBestZ(me, &hit) != BEST_Z_INVALID) - { - //+10 so they dont stick in the ground - SendTo(me.x, me.y, hit.z + 10); - m_pp.z = hit.z + 10; - } - else - { - //one more, desperate try - me.z += 2000; - if (zone->zonemap->FindBestZ(me, &hit) != BEST_Z_INVALID) - { - //+10 so they dont stick in the ground - SendTo(me.x, me.y, hit.z + 10); - m_pp.z = hit.z + 10; - } - } - } - } - - if (m_pp.gm && admin < minStatusToBeGM) - m_pp.gm = 0; - - - - /* Load Guild */ - if (!IsInAGuild()) { m_pp.guild_id = GUILD_NONE; } - else { - m_pp.guild_id = GuildID(); - - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID)) - GuildBanker = (guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || guild_mgr.GetBankerFlag(CharacterID())); - } - - m_pp.guildbanker = GuildBanker; - - switch (race) - { - case OGRE: - size = 9; break; - case TROLL: - size = 8; break; - case VAHSHIR: case BARBARIAN: - size = 7; break; - case HUMAN: case HIGH_ELF: case ERUDITE: case IKSAR: case DRAKKIN: - size = 6; break; - case HALF_ELF: - size = 5.5; break; - case WOOD_ELF: case DARK_ELF: case FROGLOK: - size = 5; break; - case DWARF: - size = 4; break; - case HALFLING: - size = 3.5; break; - case GNOME: - size = 3; break; - default: - size = 0; - } - - /* Check for Invalid points */ - if (m_pp.ldon_points_guk < 0 || m_pp.ldon_points_guk > 2000000000){ m_pp.ldon_points_guk = 0; } - if (m_pp.ldon_points_mir < 0 || m_pp.ldon_points_mir > 2000000000){ m_pp.ldon_points_mir = 0; } - if (m_pp.ldon_points_mmc < 0 || m_pp.ldon_points_mmc > 2000000000){ m_pp.ldon_points_mmc = 0; } - if (m_pp.ldon_points_ruj < 0 || m_pp.ldon_points_ruj > 2000000000){ m_pp.ldon_points_ruj = 0; } - if (m_pp.ldon_points_tak < 0 || m_pp.ldon_points_tak > 2000000000){ m_pp.ldon_points_tak = 0; } - if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000){ m_pp.ldon_points_available = 0; } - - if(GetSkill(SkillSwimming) < 100) - SetSkill(SkillSwimming, 100); - - - - /* Load Character AA's */ - //database.LoadCharacterAA(this->CharacterID(), &m_pp, &aa, &aa_points); - - /* Initialize AA's */ - for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ - aa[a] = &m_pp.aa_array[a]; - } - std::string query = StringFormat( - "SELECT " - "slot, " - "aa_id, " - "aa_value " - "FROM " - "`character_alternate_abilities` " - "WHERE `id` = %i ORDER BY `slot`", this->CharacterID()); - auto results = database.QueryDatabase(query); int si = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - si = atoi(row[0]); - m_pp.aa_array[si].AA = atoi(row[1]); - m_pp.aa_array[si].value = atoi(row[1]); - aa[si]->AA = atoi(row[1]); - aa[si]->value = atoi(row[2]); - } - for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ - uint32 id = aa[a]->AA; - //watch for invalid AA IDs - if (id == aaNone) - continue; - if (id >= aaHighestID) { - aa[a]->AA = aaNone; - aa[a]->value = 0; - continue; - } - if (aa[a]->value == 0) { - aa[a]->AA = aaNone; - continue; - } - if (aa[a]->value > HIGHEST_AA_VALUE) { - aa[a]->AA = aaNone; - aa[a]->value = 0; - continue; - } - - if (aa[a]->value > 1) /* hack in some stuff for sony's new AA method (where each level of each aa.has a seperate ID) */ - aa_points[(id - aa[a]->value + 1)] = aa[a]->value; - else - aa_points[id] = aa[a]->value; - } - - if(SPDAT_RECORDS > 0) - { - for(uint32 z=0;z= (uint32)SPDAT_RECORDS) - UnmemSpell(z, false); - } - - database.LoadBuffs(this); - uint32 max_slots = GetMaxBuffSlots(); - for(int i = 0; i < max_slots; i++) { - if(buffs[i].spellid != SPELL_UNKNOWN) { - m_pp.buffs[i].spellid = buffs[i].spellid; - m_pp.buffs[i].bard_modifier = 10; - m_pp.buffs[i].slotid = 2; - m_pp.buffs[i].player_id = 0x2211; - m_pp.buffs[i].level = buffs[i].casterlevel; - m_pp.buffs[i].effect = 0; - m_pp.buffs[i].duration = buffs[i].ticsremaining; - m_pp.buffs[i].counters = buffs[i].counters; - } else { - m_pp.buffs[i].spellid = SPELLBOOK_UNKNOWN; - m_pp.buffs[i].bard_modifier = 10; - m_pp.buffs[i].slotid = 0; - m_pp.buffs[i].player_id = 0; - m_pp.buffs[i].level = 0; - m_pp.buffs[i].effect = 0; - m_pp.buffs[i].duration = 0; - m_pp.buffs[i].counters = 0; - } - } - } - - /* Load Character Key Ring */ - KeyRingLoad(); - - /* Send Group Members via PP */ - uint32 groupid = database.GetGroupID(GetName()); - Group* group = nullptr; - if(groupid > 0){ - group = entity_list.GetGroupByID(groupid); - if(!group) { //nobody from our is here... start a new group - group = new Group(groupid); - if(group->GetID() != 0) - entity_list.AddGroup(group, groupid); - else //error loading group members... - { - delete group; - group = nullptr; - } - } //else, somebody from our group is already here... - - if(group) - group->UpdatePlayer(this); - else - database.SetGroupID(GetName(), 0, CharacterID()); //cannot re-establish group, kill it - - } else { //no group id - //clear out the group junk in our PP - uint32 xy=0; - for(xy=0;xy < MAX_GROUP_MEMBERS;xy++) - memset(m_pp.groupMembers[xy], 0, 64); - } - - if(group){ - // If the group leader is not set, pull the group leader infomrmation from the database. - if(!group->GetLeader()){ - char ln[64]; - char MainTankName[64]; - char AssistName[64]; - char PullerName[64]; - char NPCMarkerName[64]; - GroupLeadershipAA_Struct GLAA; - memset(ln, 0, 64); - strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, &GLAA)); - Client *c = entity_list.GetClientByName(ln); - if(c) - group->SetLeader(c); - - group->SetMainTank(MainTankName); - group->SetMainAssist(AssistName); - group->SetPuller(PullerName); - group->SetNPCMarker(NPCMarkerName); - group->SetGroupAAs(&GLAA); - - //group->NotifyMainTank(this, 1); - //group->NotifyMainAssist(this, 1); - //group->NotifyPuller(this, 1); - - // If we are the leader, force an update of our group AAs to other members in the zone, in case - // we purchased a new one while out-of-zone. - if(group->IsLeader(this)) - group->SendLeadershipAAUpdate(); - - } - LFG = false; - } - -#ifdef BOTS - Bot::LoadAndSpawnAllZonedBots(this); -#endif - - CalcBonuses(); - if (m_pp.cur_hp <= 0) - m_pp.cur_hp = GetMaxHP(); - - SetHP(m_pp.cur_hp); - Mob::SetMana(m_pp.mana); - SetEndurance(m_pp.endurance); - - if(IsLFP()) { - // Update LFP in case any (or all) of our group disbanded while we were zoning. - UpdateLFP(); - } - - if(m_pp.z <= zone->newzone_data.underworld) { - m_pp.x = zone->newzone_data.safe_x; - m_pp.y = zone->newzone_data.safe_y; - m_pp.z = zone->newzone_data.safe_z; - } - - /* Get Expansions from variables table and ship via PP */ - char val[20] = {0}; - if (database.GetVariable("Expansions", val, 20)) - m_pp.expansions = atoi(val); - else - m_pp.expansions = 0x3FF; - - p_timers.SetCharID(CharacterID()); - if(!p_timers.Load(&database)) { - LogFile->write(EQEMuLog::Error, "Unable to load ability timers from the database for %s (%i)!", GetCleanName(), CharacterID()); - } - - for(unsigned int i =0 ; i < MAX_PP_MEMSPELL; ++i) - if(IsValidSpell(m_pp.mem_spells[i])) - m_pp.spellSlotRefresh[i] = p_timers.GetRemainingTime(pTimerSpellStart + m_pp.mem_spells[i]) * 1000; - - /* Ability slot refresh send SK/PAL */ - if(m_pp.class_==SHADOWKNIGHT || m_pp.class_==PALADIN) { - uint32 abilitynum=0; - if (m_pp.class_ == SHADOWKNIGHT){ abilitynum = pTimerHarmTouch; } - else{ abilitynum = pTimerLayHands; } - - uint32 remaining = p_timers.GetRemainingTime(abilitynum); - if(remaining > 0 && remaining < 15300) - m_pp.abilitySlotRefresh = remaining * 1000; - else - m_pp.abilitySlotRefresh = 0; - } - -#ifdef _EQDEBUG - printf("Dumping inventory on load:\n"); - m_inv.dumpEntireInventory(); -#endif - -//lost in current PP -// strcpy(m_pp.servername,"eqemulator"); - - m_pp.air_remaining = 60; //Reset to max so they dont drown on zone in if its underwater - - if(zone->IsPVPZone()) - m_pp.pvp=1; - - m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; - - /* Reset rest timer if the durations have been lowered in the database */ - if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) - m_pp.RestTimer = 0; - - /* This checksum should disappear once dynamic structs are in... each struct strategy will do it */ - CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct)-4); - - outapp = new EQApplicationPacket(OP_PlayerProfile,sizeof(PlayerProfile_Struct)); - - /* The entityid field in the Player Profile is used by the Client in relation to Group Leadership AA */ - m_pp.entityid = GetID(); - memcpy(outapp->pBuffer,&m_pp,outapp->size); - outapp->priority = 6; - FastQueuePacket(&outapp); - - if(m_pp.RestTimer) - rest_timer.Start(m_pp.RestTimer * 1000); - - database.LoadPetInfo(this); - /* - This was moved before the spawn packets are sent - in hopes that it adds more consistency... - Remake pet - */ - if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) - { - MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size); - if (GetPet() && GetPet()->IsNPC()) { - NPC *pet = GetPet()->CastToNPC(); - pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items); - pet->CalcBonuses(); - pet->SetHP(m_petinfo.HP); - pet->SetMana(m_petinfo.Mana); - } - m_petinfo.SpellID = 0; - } - /* Moved here so it's after where we load the pet data. */ - if(!GetAA(aaPersistentMinion)) - memset(&m_suspendedminion, 0, sizeof(PetInfo)); - - /* Server Zone Entry Packet */ - outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); - ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer; - - FillSpawnStruct(&sze->player,CastToMob()); - sze->player.spawn.curHp=1; - sze->player.spawn.NPC=0; - sze->player.spawn.z += 6; //arbitrary lift, seems to help spawning under zone. - outapp->priority = 6; - FastQueuePacket(&outapp); - - /* Zone Spawns Packet */ - entity_list.SendZoneSpawnsBulk(this); - entity_list.SendZoneCorpsesBulk(this); - entity_list.SendZonePVPUpdates(this); //hack until spawn struct is fixed. - - /* Time of Day packet */ - outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); - TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; - zone->zone_time.getEQTimeOfDay(time(0), tod); - outapp->priority = 6; - FastQueuePacket(&outapp); - - /* Tribute Packets */ - DoTributeUpdate(); - if(m_pp.tribute_active) { - //restart the tribute timer where we left off - tribute_timer.Start(m_pp.tribute_time_remaining); - } - - /* - Character Inventory Packet - this is not quite where live sends inventory, they do it after tribute - */ - if (loaditems) { //dont load if a length error occurs - BulkSendInventoryItems(); - - // Send stuff on the cursor which isnt sent in bulk - iter_queue it; - for (it=m_inv.cursor_begin();it!=m_inv.cursor_end();++it) { - // First item cursor is sent in bulk inventory packet - if (it==m_inv.cursor_begin()) - continue; - const ItemInst *inst=*it; - SendItemPacket(MainCursor, inst, ItemPacketSummonItem); - } - } - - /* Task Packets */ - LoadClientTaskState(); - - if (GetClientVersion() >= EQClientRoF) - { - outapp = new EQApplicationPacket(OP_ReqNewZone, 0); - Handle_Connect_OP_ReqNewZone(outapp); - safe_delete(outapp); - } - - if(ClientVersionBit & BIT_UnderfootAndLater) - { - outapp = new EQApplicationPacket(OP_XTargetResponse, 8); - outapp->WriteUInt32(GetMaxXTargets()); - outapp->WriteUInt32(0); - FastQueuePacket(&outapp); - } - - /* - Weather Packet - This shouldent be moved, this seems to be what the client - uses to advance to the next state (sending ReqNewZone) - */ - outapp = new EQApplicationPacket(OP_Weather, 12); - Weather_Struct *ws = (Weather_Struct *) outapp->pBuffer; - ws->val1 = 0x000000FF; - if (zone->zone_weather == 1) - ws->type = 0x31; // Rain - if (zone->zone_weather == 2) - { - outapp->pBuffer[8] = 0x01; - ws->type = 0x02; - } - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - SetAttackTimer(); - - conn_state = ZoneInfoSent; - - return true; -} - /* Finish client connecting state */ void Client::CompleteConnect() { UpdateWho(); diff --git a/zone/effects.cpp b/zone/effects.cpp index a24a6b39b..5e07d5f9a 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -616,13 +616,9 @@ bool Client::TrainDiscipline(uint32 itemid) { } void Client::SendDisciplineUpdate() { - //this dosent seem to work right now - EQApplicationPacket app(OP_DisciplineUpdate, sizeof(Disciplines_Struct)); Disciplines_Struct *d = (Disciplines_Struct*)app.pBuffer; - //dunno why I dont just send the one from m_pp memcpy(d, &m_pp.disciplines, sizeof(m_pp.disciplines)); - QueuePacket(&app); } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index e7bad016c..4eb0e64ab 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -370,41 +370,6 @@ void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug){ safe_delete_array(bugtext); } - -bool ZoneDatabase::GetAccountInfoForLogin_result(MYSQL_RES* result, int16* admin, char* account_name, uint32* lsaccountid, uint8* gmspeed, bool* revoked,bool* gmhideme, uint32* account_creation) { - MYSQL_ROW row; - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if (admin) - *admin = atoi(row[0]); - if (account_name) - strcpy(account_name, row[1]); - if (lsaccountid) { - - if (row[2]) - *lsaccountid = atoi(row[2]); - else - *lsaccountid = 0; - - - } - if (gmspeed) - *gmspeed = atoi(row[3]); - if (revoked) - *revoked = atoi(row[4]); - if(gmhideme) - *gmhideme = atoi(row[5]); - if(account_creation) - *account_creation = atoul(row[6]); - - return true; - } - else { - return false; - } -} - - bool ZoneDatabase::SetSpecialAttkFlag(uint8 id, const char* flag) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; @@ -859,43 +824,8 @@ void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) } -bool ZoneDatabase::GetCharacterInfoForLogin(const char* name, uint32* character_id, - char* current_zone, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, - uint32* pplen, uint32* guilddbid, uint8* guildrank, - uint8 *class_, uint8 *level, bool *LFP, bool *LFG, uint8 *NumXTargets, uint8 *firstlogon) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 querylen; - MYSQL_RES *result; - - bool ret = false; - - //DO NOT FORGET TO EDIT Client::Handle_Connect_OP_ZoneEntry if you change this. - - if (character_id && *character_id) { - // searching by ID should be a lil bit faster - querylen = MakeAnyLenString(&query, "SELECT id,profile,zonename,x,y,z,guild_id,rank,extprofile,class,level,lfp,lfg,instanceid,xtargets,firstlogon FROM character_ LEFT JOIN guild_members ON id=char_id WHERE id=%i", *character_id); - } - else { - querylen = MakeAnyLenString(&query, "SELECT id,profile,zonename,x,y,z,guild_id,rank,extprofile,class,level,lfp,lfg,instanceid,xtargets,firstlogon FROM character_ LEFT JOIN guild_members ON id=char_id WHERE name='%s'", name); - } - - if (RunQuery(query, querylen, errbuf, &result)) { - ret = GetCharacterInfoForLogin_result(result, character_id, current_zone, pp, inv, ext, pplen, guilddbid, guildrank, class_, level, LFP, LFG, NumXTargets, firstlogon); - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "GetCharacterInfoForLogin query '%s' %s", query, errbuf); - } - - safe_delete_array(query); - return ret; -} - #define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1)) - bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat( "SELECT " @@ -1085,6 +1015,38 @@ bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* return true; } +bool ZoneDatabase::LoadCharacterFactionValues(uint32 character_id, faction_map & val_list) { + std::string query = StringFormat("SELECT faction_id,current_value FROM faction_values WHERE char_id = %i", character_id); + auto results = database.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { val_list[atoi(row[0])] = atoi(row[1]); } + return true; +} + +bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "disc_id " + "FROM " + "`character_disciplines`" + "WHERE `id` = %u ORDER BY `disc_id`", character_id); + auto results = database.QueryDatabase(query); int i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { pp->disciplines.values[i] = atoi(row[0]); i++; } + return true; +} + +bool ZoneDatabase::LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "skill_id, " + "`value` " + "FROM " + "`character_skills` " + "WHERE `id` = %u ORDER BY `skill_id`", character_id); + auto results = database.QueryDatabase(query); int i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); pp->skills[i] = atoi(row[1]); } + return true; +} + bool ZoneDatabase::LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat( "SELECT " @@ -1458,103 +1420,6 @@ bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 cur return true; } -// Process results of GetCharacterInfoForLogin() -// Query this processes: SELECT id,profile,zonename,x,y,z,guild,guildrank,extprofile,class,level FROM character_ WHERE id=%i -bool ZoneDatabase::GetCharacterInfoForLogin_result(MYSQL_RES* result, - uint32* character_id, char* current_zone, PlayerProfile_Struct* pp, Inventory* inv, - ExtendedProfile_Struct *ext, uint32* pplen, uint32* guilddbid, uint8* guildrank, - uint8 *class_, uint8 *level, bool *LFP, bool *LFG, uint8 *NumXTargets, uint8* firstlogon) { - - MYSQL_ROW row; - unsigned long* lengths; - - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - lengths = mysql_fetch_lengths(result); - if (pp && pplen) { - if (lengths[1] == sizeof(PlayerProfile_Struct)) { - // memcpy(pp, row[1], sizeof(PlayerProfile_Struct)); - } else { - LogFile->write(EQEMuLog::Error, "Player profile length mismatch in GetCharacterInfo Expected: %i, Got: %i", - sizeof(PlayerProfile_Struct), lengths[1]); - return false; - } - - *pplen = lengths[1]; - pp->zone_id = GetZoneID(row[2]); - pp->zoneInstance = atoi(row[13]); - - pp->x = atof(row[3]); - pp->y = atof(row[4]); - pp->z = atof(row[5]); - - pp->lastlogin = time(nullptr); - - if (pp->x == -1 && pp->y == -1 && pp->z == -1) - GetSafePoints(pp->zone_id, database.GetInstanceVersion(pp->zoneInstance), &pp->x, &pp->y, &pp->z); - } - - uint32 char_id = atoi(row[0]); - if (RuleB(Character, SharedBankPlat)) - pp->platinum_shared = database.GetSharedPlatinum(GetAccountIDByChar(char_id)); - if (character_id) - *character_id = char_id; - if (current_zone) - strcpy(current_zone, row[2]); - - if (guilddbid) { - if(row[6] != nullptr) - *guilddbid = atoi(row[6]); - else - *guilddbid = GUILD_NONE; - } - if (guildrank) { - if(row[7] != nullptr) - *guildrank = atoi(row[7]); - else - *guildrank = GUILD_RANK_NONE; - } - - if(ext) { - //SetExtendedProfile handles any conversion - SetExtendedProfile(ext, row[8], lengths[8]); - } - - if(class_) - *class_ = atoi(row[9]); - - if(level) - *level = atoi(row[10]); - - if(LFP) - *LFP = atoi(row[11]); - - if(LFG) - *LFG = atoi(row[12]); - - if(NumXTargets) - { - *NumXTargets = atoi(row[14]); - } - - - if(firstlogon) - { - *firstlogon = atoi(row[15]); - } - - // Fix use_tint, previously it was set to 1 for a dyed slot, client wants it set to 0xFF - for(int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) - if(pp->item_tint[i].rgb.use_tint == 1) - pp->item_tint[i].rgb.use_tint = 0xFF; - - // Retrieve character inventory - return GetInventory(char_id, inv); - } - - return false; -} - bool ZoneDatabase::NoRentExpired(const char* name){ char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; @@ -2812,31 +2677,6 @@ bool ZoneDatabase::SetZoneTZ(uint32 zoneid, uint32 version, uint32 tz) { } //End new timezone functions. -/* - solar: this is never actually called, client_process starts an async query - instead and uses GetAccountInfoForLogin_result to process it.. - */ -bool ZoneDatabase::GetAccountInfoForLogin(uint32 account_id, int16* admin, char* account_name, uint32* lsaccountid, uint8* gmspeed, bool* revoked,bool* gmhideme) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT status, name, lsaccount_id, gmspeed, revoked, hideme FROM account WHERE id=%i", account_id), errbuf, &result)) { - safe_delete_array(query); - bool ret = GetAccountInfoForLogin_result(result, admin, account_name, lsaccountid, gmspeed, revoked,gmhideme); - mysql_free_result(result); - return ret; - } - else - { - std::cerr << "Error in GetAccountInfoForLogin query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - return false; -} - void ZoneDatabase::RefreshGroupFromDB(Client *c){ if(!c){ return; @@ -3568,31 +3408,6 @@ bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race return true; } -bool ZoneDatabase::LoadFactionValues(uint32 char_id, faction_map & val_list) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT faction_id,current_value FROM faction_values WHERE char_id = %i",char_id), errbuf, &result)) { - safe_delete_array(query); - bool ret = LoadFactionValues_result(result, val_list); - mysql_free_result(result); - return ret; - } - else { - std::cerr << "Error in LoadFactionValues query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } - return false; -} - -bool ZoneDatabase::LoadFactionValues_result(MYSQL_RES* result, faction_map & val_list) { - MYSQL_ROW row; - while((row = mysql_fetch_row(result))) { - val_list[atoi(row[0])] = atoi(row[1]); - } - return true; -} - //o-------------------------------------------------------------- //| Name: GetFactionName; rembrant, Dec. 16 //o-------------------------------------------------------------- diff --git a/zone/zonedb.h b/zone/zonedb.h index d0e70b995..ce32e919f 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -249,20 +249,7 @@ public: void StoreCharacterLookup(uint32 char_id); bool SetServerFilters(char* name, ServerSideFilters_Struct *ssfs); uint32 GetServerFilters(char* name, ServerSideFilters_Struct *ssfs); - bool GetAccountInfoForLogin(uint32 account_id, int16* admin = 0, char* account_name = 0, - uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = 0); - bool GetAccountInfoForLogin_result(MYSQL_RES* result, int16* admin = 0, char* account_name = 0, - uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = nullptr, - uint32* account_creation = 0); - bool GetCharacterInfoForLogin_result(MYSQL_RES* result, uint32* character_id = 0, char* current_zone = 0, - PlayerProfile_Struct* pp = 0, Inventory* inv = 0, ExtendedProfile_Struct *ext = 0, uint32* pplen = 0, - uint32* guilddbid = 0, uint8* guildrank = 0, uint8 *class_= 0, uint8 *level = 0, bool *LFP = 0, - bool *LFG = 0, uint8 *NumXTargets = 0, uint8* firstlogon = 0); - bool GetCharacterInfoForLogin(const char* name, uint32* character_id = 0, char* current_zone = 0, - PlayerProfile_Struct* pp = 0, Inventory* inv = 0, ExtendedProfile_Struct *ext = 0, uint32* pplen = 0, - uint32* guilddbid = 0, uint8* guildrank = 0, uint8 *class_ = 0, uint8 *level = 0, bool *LFP = 0, - bool *LFG = 0, uint8 *NumXTargets = 0, uint8* firstlogon = 0); void SaveBuffs(Client *c); void LoadBuffs(Client *c); void LoadPetInfo(Client *c); @@ -270,6 +257,9 @@ public: void RemoveTempFactions(Client *c); /* Player Profile Loaders */ + bool LoadCharacterFactionValues(uint32 character_id, faction_map & val_list); + bool LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); @@ -321,8 +311,6 @@ public: bool GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction = 0); // neotokyo: improve faction handling bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // rembrant, needed for factions Dec, 16 2001 bool LoadFactionData(); - bool LoadFactionValues(uint32 char_id, faction_map & val_list); - bool LoadFactionValues_result(MYSQL_RES* result, faction_map & val_list); /* * AAs From ca430e24942aa3d82837be7b604ecabf85976f01 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 31 Aug 2014 17:52:43 -0500 Subject: [PATCH 006/368] Fix void Database::GetCharName(uint32 char_id, char* name) Increased MAX_PP_SPELLBOOK to 720 for UF/RoF Increased MAX_PP_MEMSPELL to 12 Implemented up to 12 spell slots Fix for public_note default value in bool BaseGuildManager::DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank) Updated all CastSpell entries to use the appropriate slot type defines located now in zone/common.h Fixed Guild Loading from character_data Fixed #guild list Refactored Merchantlist loading Refactored Temp Merchantlist loading Gutted most of dbasync Added: LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp); LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp); LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp); LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp); SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot); SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); Removed Zone::LoadTempMerchantData_result(MYSQL_RES* result) Removed Zone::LoadMerchantData_result(MYSQL_RES* result) Removed SharedDatabase::GetPlayerProfile Removed SharedDatabase::SetPlayerProfile Removed SharedDatabase::SetPlayerProfile_MQ Removed Zone::DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) from zone.cpp --- common/database.cpp | 23 +-- common/eq_packet_structs.h | 4 +- common/guild_base.cpp | 2 +- common/patches/underfoot.cpp | 3 +- common/patches/underfoot_structs.h | 3 +- common/shareddb.cpp | 100 ------------ common/shareddb.h | 3 - world/worlddb.cpp | 5 +- zone/aa.cpp | 7 +- zone/bot.h | 4 +- zone/client.cpp | 52 +++--- zone/client.h | 7 +- zone/client_packet.cpp | 254 ++++++++++++++--------------- zone/command.cpp | 6 +- zone/common.h | 4 + zone/mob.h | 2 +- zone/spells.cpp | 8 +- zone/zone.cpp | 164 ++++++------------- zone/zone.h | 3 - zone/zonedb.cpp | 250 ++++++++++++++++++---------- zone/zonedb.h | 16 +- zone/zonedbasync.cpp | 24 --- 22 files changed, 423 insertions(+), 521 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 429da0e42..fcfab3c52 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -758,17 +758,18 @@ void Database::GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID void Database::GetCharName(uint32 char_id, char* name) { - std::string query = StringFormat("SELECT name FROM character_ WHERE id='%i'", char_id); + std::string query = StringFormat("SELECT `name` FROM `character_data` WHERE id='%i'", char_id); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in GetCharName query '" << query << "' " << results.ErrorMessage() << std::endl; - return; + return; } auto row = results.begin(); - strcpy(name, row[0]); + for (auto row = results.begin(); row != results.end(); ++row) { + strcpy(name, row[0]); + } } static inline void loadbar(unsigned int x, unsigned int n, unsigned int w = 50) { @@ -849,7 +850,7 @@ bool Database::CheckDatabaseConversions() { " `level2` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " " `anon` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " " `gm` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - " `intoxication` int(11) UNSIGNED NOT NULL, " + " `intoxication` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `hair_color` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " " `beard_color` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " " `eye_color_1` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " @@ -885,9 +886,9 @@ bool Database::CheckDatabaseConversions() { " `drakkin_heritage` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `drakkin_tattoo` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `drakkin_details` int(11) UNSIGNED NOT NULL DEFAULT 0, " - " `toxicity` int(11) UNSIGNED NOT NULL DEFAULT 0, " - " `hunger_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " - " `thirst_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `toxicity` int(11) NOT NULL DEFAULT 0, " + " `hunger_level` int(11) NOT NULL DEFAULT 0, " + " `thirst_level` int(11) NOT NULL DEFAULT 0, " " `ability_up` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `zone_id` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `zone_instance` int(11) UNSIGNED NOT NULL DEFAULT 0, " @@ -1092,7 +1093,7 @@ bool Database::CheckDatabaseConversions() { // querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); int char_iter_count = 0; - querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` >= 61238 LIMIT 1"); + querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` >= 61238 LIMIT 100"); if (RunQuery(query, querylen, errbuf, &result)) { safe_delete_array(query); while (row = mysql_fetch_row(result)) { @@ -1147,6 +1148,8 @@ bool Database::CheckDatabaseConversions() { ); auto results = QueryDatabase(rquery); + if (pp->tribute_time_remaining < 0 || pp->tribute_time_remaining == 4294967295){ pp->tribute_time_remaining = 0; } + /* Run Character Data Convert */ rquery = StringFormat( "REPLACE INTO `character_data` (" diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index d2fbcbb76..d6ffd119b 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -801,8 +801,8 @@ struct SuspendedMinion_Struct ** OpCode: 0x006a */ static const uint32 MAX_PP_LANGUAGE = 28; -static const uint32 MAX_PP_SPELLBOOK = 480; // Increased to 480 to support SoF -static const uint32 MAX_PP_MEMSPELL = 9; +static const uint32 MAX_PP_SPELLBOOK = 720; // Increased to 480 to support SoF +static const uint32 MAX_PP_MEMSPELL = 12; static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 240; static const uint32 MAX_GROUP_MEMBERS = 6; diff --git a/common/guild_base.cpp b/common/guild_base.cpp index 8f6144270..99e54934e 100644 --- a/common/guild_base.cpp +++ b/common/guild_base.cpp @@ -751,7 +751,7 @@ bool BaseGuildManager::DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank) { std::string query; if(guild_id != GUILD_NONE) { - query = StringFormat("REPLACE INTO guild_members (char_id,guild_id,rank) VALUES(%d,%d,%d)", charid, guild_id, rank); + query = StringFormat("REPLACE INTO guild_members (char_id,guild_id,rank,public_note) VALUES(%d,%d,%d,'')", charid, guild_id, rank); auto results = m_db->QueryDatabase(query); if (!results.Success()) { diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index bdac3c72b..1eab127d5 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -479,7 +479,6 @@ ENCODE(OP_PlayerProfile) { uint32 r; eq->available_slots=0xffffffff; - memset(eq->unknown06284, 0xff, sizeof(eq->unknown06284)); memset(eq->unknown07284, 0xff, sizeof(eq->unknown07284)); // OUT(checksum); @@ -546,7 +545,7 @@ ENCODE(OP_PlayerProfile) { OUT(gold); OUT(silver); OUT(copper); - OUT(platinum_cursor); + OUT(platinum_cursor); OUT(gold_cursor); OUT(silver_cursor); OUT(copper_cursor); diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index ee7286dab..860fdd34a 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -766,7 +766,7 @@ struct BindStruct { ** OpCode: 0x006a */ static const uint32 MAX_PP_LANGUAGE = 25; // -static const uint32 MAX_PP_SPELLBOOK = 480; // Confirmed 60 pages on Underfoot now +static const uint32 MAX_PP_SPELLBOOK = 720; // Confirmed 60 pages on Underfoot now static const uint32 MAX_PP_MEMSPELL = 10; //was 9 now 10 on Underfoot static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 300; //was 299 @@ -879,7 +879,6 @@ struct PlayerProfile_Struct /*04216*/ uint8 face; // Player face - Actually uint32? /*04217*/ uint8 unknown04217[147]; // was [175] /*04364*/ uint32 spell_book[MAX_PP_SPELLBOOK]; // List of the Spells in spellbook 720 = 90 pages [2880] was [1920] -/*06284*/ uint8 unknown06284[960]; // Spacer for the end of the book for now (pages 60 to 90) /*07244*/ uint32 mem_spells[MAX_PP_MEMSPELL]; // List of spells memorized /*07284*/ uint8 unknown07284[28]; //#### uint8 unknown04396[32]; in Titanium ####[28] /*07312*/ uint32 platinum; // Platinum Pieces on player diff --git a/common/shareddb.cpp b/common/shareddb.cpp index c10e87856..e4336b02f 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1209,106 +1209,6 @@ bool SharedDatabase::LoadNPCFactionLists() { return true; } -// Get the player profile and inventory for the given account "account_id" and -// character name "name". Return true if the character was found, otherwise false. -// False will also be returned if there is a database error. -bool SharedDatabase::GetPlayerProfile(uint32 account_id, char* name, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, char* current_zone, uint32 *current_instance) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - bool ret = false; - - unsigned long* lengths; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT profile,zonename,x,y,z,extprofile,instanceid FROM character_ WHERE account_id=%i AND name='%s'", account_id, name), errbuf, &result)) { - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - lengths = mysql_fetch_lengths(result); - if (lengths[0] == sizeof(PlayerProfile_Struct)) { - memcpy(pp, row[0], sizeof(PlayerProfile_Struct)); - - if (current_zone) - strcpy(current_zone, row[1]); - pp->zone_id = GetZoneID(row[1]); - pp->x = atof(row[2]); - pp->y = atof(row[3]); - pp->z = atof(row[4]); - pp->zoneInstance = atoi(row[6]); - if (pp->x == -1 && pp->y == -1 && pp->z == -1) - GetSafePoints(pp->zone_id, GetInstanceVersion(pp->zoneInstance), &pp->x, &pp->y, &pp->z); - - if(current_instance) - *current_instance = pp->zoneInstance; - - if(ext) { - //SetExtendedProfile handles any conversion - SetExtendedProfile(ext, row[5], lengths[5]); - } - - // Retrieve character inventory - ret = GetInventory(account_id, name, inv); - } - else { - LogFile->write(EQEMuLog::Error, "Player profile length mismatch in GetPlayerProfile. Found: %i, Expected: %i", - lengths[0], sizeof(PlayerProfile_Struct)); - } - } - - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "GetPlayerProfile query '%s' %s", query, errbuf); - } - - safe_delete_array(query); - return ret; -} - -bool SharedDatabase::SetPlayerProfile(uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 affected_rows = 0; - bool ret = false; - - clock_t t = std::clock(); /* Function timer start */ - if (RunQuery(query, SetPlayerProfile_MQ(&query, account_id, charid, pp, inv, ext, current_zone, current_instance, MaxXTargets), errbuf, 0, &affected_rows)) { - ret = (affected_rows != 0); - } - LogFile->write(EQEMuLog::Status, "SharedDatabase::SetPlayerProfile SetPlayerProfile_MQ done... Took %f seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC); - - if (!ret) { - LogFile->write(EQEMuLog::Error, "SetPlayerProfile query '%s' %s", query, errbuf); - } - - safe_delete_array(query); - return ret; -} - -// Generate SQL for updating player profile -uint32 SharedDatabase::SetPlayerProfile_MQ(char** query, uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { - *query = new char[396 + sizeof(PlayerProfile_Struct)*2 + sizeof(ExtendedProfile_Struct)*2 + 4]; - char* end = *query; - if (!current_zone) - current_zone = pp->zone_id; - - if (!current_instance) - current_instance = pp->zoneInstance; - - if(strlen(pp->name) == 0) /* Sanity check in case pp never loaded */ - return false; - - end += sprintf(end, "UPDATE character_ SET timelaston=unix_timestamp(now()),name=\'%s\', zonename=\'%s\', zoneid=%u, instanceid=%u, x = %f, y = %f, z = %f, profile=\'", pp->name, GetZoneName(current_zone), current_zone, current_instance, pp->x, pp->y, pp->z); - end += DoEscapeString(end, (char*)pp, sizeof(PlayerProfile_Struct)); - end += sprintf(end,"\', extprofile=\'"); - end += DoEscapeString(end, (char*)ext, sizeof(ExtendedProfile_Struct)); - end += sprintf(end,"\',class=%d,level=%d,xtargets=%u WHERE id=%u", pp->class_, pp->level, MaxXTargets, charid); - - return (uint32) (end - (*query)); -} - - - // Create appropriate ItemInst class ItemInst* SharedDatabase::CreateItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5) { diff --git a/common/shareddb.h b/common/shareddb.h index 0fd72426c..5401bfa0a 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -40,9 +40,6 @@ public: bool SetGMSpeed(uint32 account_id, uint8 gmspeed); uint8 GetGMSpeed(uint32 account_id); bool SetHideMe(uint32 account_id, uint8 hideme); - bool GetPlayerProfile(uint32 account_id, char* name, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, char* current_zone = 0, uint32 *current_instance = 0); - bool SetPlayerProfile(uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets); - uint32 SetPlayerProfile_MQ(char** query, uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets); int32 DeleteStalePlayerCorpses(); int32 DeleteStalePlayerBackups(); void GetPlayerInspectMessage(char* playername, InspectMessage_Struct* message); diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 7a07c6f6f..02dfa4290 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -41,11 +41,12 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* MYSQL_ROW row; Inventory *inv; + /* Initialize Variables */ for (int i=0; i<10; i++) { strcpy(cs->name[i], ""); cs->zone[i] = 0; cs->level[i] = 0; - cs->tutorial[i] = 0; + cs->tutorial[i] = 0; cs->gohome[i] = 0; } @@ -165,7 +166,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* if(result2) { row2 = mysql_fetch_row(result2); ExtendedProfile_Struct* ext = (ExtendedProfile_Struct*)row2[0]; - SetPlayerProfile(account_id,char_id,pp,inv,ext, 0, 0, 5); + // SetPlayerProfile(account_id,char_id,pp,inv,ext, 0, 0, 5); } mysql_free_result(result2); } diff --git a/zone/aa.cpp b/zone/aa.cpp index 390174cf5..2af81a619 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -300,7 +300,7 @@ void Client::ActivateAA(aaID activate){ return; } } else { - if(!CastSpell(caa->spell_id, target_id, 10, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) { + if (!CastSpell(caa->spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) { //Reset on failed cast SendAATimer(AATimerID, 0, 0xFFFFFF); Message_StringID(15,ABILITY_FAILED); @@ -525,7 +525,7 @@ void Client::HandleAAAction(aaID activate) { //cast the spell, if we have one if(IsValidSpell(spell_id)) { int aatid = GetAATimerID(activate); - if(!CastSpell(spell_id, target_id , 10, -1, -1, 0, -1, pTimerAAStart + aatid , CalcAAReuseTimer(caa), 1)) { + if (!CastSpell(spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, pTimerAAStart + aatid, CalcAAReuseTimer(caa), 1)) { SendAATimer(aatid, 0, 0xFFFFFF); Message_StringID(15,ABILITY_FAILED); p_timers.Clear(&database, pTimerAAStart + aatid); @@ -1822,8 +1822,7 @@ void ZoneDatabase::LoadAAs(SendAA_Struct **load){ } AALevelCost_Struct aalcs; - for (auto row = results.begin(); row != results.end(); ++row) - { + for (auto row = results.begin(); row != results.end(); ++row) { aalcs.Level = atoi(row[1]); aalcs.Cost = atoi(row[2]); AARequiredLevelAndCost[atoi(row[0])] = aalcs; diff --git a/zone/bot.h b/zone/bot.h index 9c1ec9cb2..7601dd00c 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -311,11 +311,11 @@ public: virtual float GetAOERange(uint16 spell_id); virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100); virtual void DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster = 0); - virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr); + virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction); - virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF); + virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF); // Bot Action Command Methods bool MesmerizeTarget(Mob* target); diff --git a/zone/client.cpp b/zone/client.cpp index 0065d31c5..975e61dc1 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -545,7 +545,7 @@ bool Client::Save(uint8 iCommitNow) { database.SaveCharacterCurrency(this->CharacterID(), &m_pp); /* Save Character AA */ - SaveAA(); + // SaveAA(); /* Save Character Buffs */ database.SaveBuffs(this); @@ -591,31 +591,31 @@ bool Client::Save(uint8 iCommitNow) { /* Save Character Data */ database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp); - if (iCommitNow <= 1) { - char* query = 0; - uint32_breakdown workpt; - workpt.b4() = DBA_b4_Entity; - workpt.w2_3() = GetID(); - workpt.b1() = DBA_b1_Entity_Client_Save; - DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Write, 0xFFFFFFFF); - dbaw->AddQuery(iCommitNow == 0 ? true : false, &query, database.SetPlayerProfile_MQ(&query, account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets), false); - if (iCommitNow == 0){ - pQueuedSaveWorkID = dbasync->AddWork(&dbaw, 2500); - } - else { - dbasync->AddWork(&dbaw, 0); - SaveBackup(); - } - safe_delete_array(query); - return true; - } - else if (database.SetPlayerProfile(account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets)) { - SaveBackup(); - } - else { - std::cerr << "Failed to update player profile" << std::endl; - return false; - } + // if (iCommitNow <= 1) { + // char* query = 0; + // uint32_breakdown workpt; + // workpt.b4() = DBA_b4_Entity; + // workpt.w2_3() = GetID(); + // workpt.b1() = DBA_b1_Entity_Client_Save; + // DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Write, 0xFFFFFFFF); + // dbaw->AddQuery(iCommitNow == 0 ? true : false, &query, database.SetPlayerProfile_MQ(&query, account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets), false); + // if (iCommitNow == 0){ + // pQueuedSaveWorkID = dbasync->AddWork(&dbaw, 2500); + // } + // else { + // dbasync->AddWork(&dbaw, 0); + // SaveBackup(); + // } + // safe_delete_array(query); + // return true; + // } + // else if (database.SetPlayerProfile(account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets)) { + // SaveBackup(); + // } + // else { + // std::cerr << "Failed to update player profile" << std::endl; + // return false; + // } /* Mirror Character Data */ database.StoreCharacterLookup(this->CharacterID()); diff --git a/zone/client.h b/zone/client.h index a490d73c4..bab48e8ea 100644 --- a/zone/client.h +++ b/zone/client.h @@ -37,6 +37,7 @@ class Client; #include "../common/item_struct.h" #include "../common/clientversions.h" +#include "common.h" #include "zonedb.h" #include "errno.h" #include "mob.h" @@ -102,11 +103,6 @@ enum { //scribing argument to MemorizeSpell memSpellSpellbar = 3 }; -#define USE_ITEM_SPELL_SLOT 10 -#define POTION_BELT_SPELL_SLOT 11 -#define DISCIPLINE_SPELL_SLOT 10 -#define ABILITY_SPELL_SLOT 9 - //Modes for the zoning state of the client. typedef enum { ZoneToSafeCoords, // Always send ZonePlayerToBind_Struct to client: Succor/Evac @@ -239,7 +235,6 @@ public: void KeyRingList(); virtual bool IsClient() const { return true; } virtual void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw); - bool FinishConnState2(DBAsyncWork* dbaw); void CompleteConnect(); bool TryStacking(ItemInst* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true); void SendTraderPacket(Client* trader, uint32 Unknown72 = 51); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 9b2af6c45..9921298fe 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -506,19 +506,18 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if(strlen(cze->char_name) > 63) return; - conn_state = ReceivedZoneEntry; - + conn_state = ReceivedZoneEntry; ClientVersion = Connection()->ClientVersion(); ClientVersionBit = 1 << (ClientVersion - 1); - // Antighost code - // tmp var is so the search doesnt find this object + /* Antighost code + tmp var is so the search doesnt find this object + */ Client* client = entity_list.GetClientByName(cze->char_name); if (!zone->GetAuth(ip, cze->char_name, &WID, &account_id, &character_id, &admin, lskey, &tellsoff)) { LogFile->write(EQEMuLog::Error, "GetAuth() returned false kicking client"); - if (client != 0) - { + if (client != 0) { client->Save(); client->Kick(); } @@ -527,7 +526,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) return; } + strcpy(name, cze->char_name); + /* Check for Client Spoofing */ if (client != 0) { struct in_addr ghost_addr; ghost_addr.s_addr = eqs->GetRemoteIP(); @@ -554,8 +555,10 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) uint32 cid = CharacterID(); character_id = cid; + /* Flush and reload factions */ database.RemoveTempFactions(this); database.LoadCharacterFactionValues(cid, factionvalues); + /* Load Character Account Data: Temp until I move */ query = StringFormat("SELECT `status`, `name`, `lsaccount_id`, `gmspeed`, `revoked`, `hideme` FROM `account` WHERE `id` = %i", this->AccountID()); auto results = database.QueryDatabase(query); @@ -565,74 +568,75 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (lsaccountid && atoi(row[2]) > 0){ lsaccountid = atoi(row[2]); } else{ lsaccountid = 0; } if (gmspeed){ gmspeed = atoi(row[3]); } - if (revoked){ revoked = atoi(row[4]); } + if (revoked){ revoked = atoi(row[4]); } if (gmhideme){ gmhideme = atoi(row[5]); } if (account_creation){ account_creation = atoul(row[6]); } } + /* Load Character Legacy Data: Temp until I move */ - query = StringFormat("id,profile,zonename,x,y,z,guild_id,rank,extprofile,class,level,lfp,lfg,instanceid,xtargets,firstlogon FROM character_ LEFT JOIN guild_members ON id=char_id WHERE id=%i", cid); + query = StringFormat("SELECT id,profile,zonename,x,y,z,guild_id,rank,extprofile,class,level,lfp,lfg,instanceid,xtargets,firstlogon FROM character_ LEFT JOIN guild_members ON id=char_id WHERE id=%i", cid); results = database.QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { m_pp.lastlogin = time(nullptr); - + if (row[6]){ + guild_id = atoi(row[6]); + if (guildrank) { + if (row[7] != nullptr){ guildrank = atoi(row[7]); } + else{ guildrank = GUILD_RANK_NONE; } + } + } if (RuleB(Character, SharedBankPlat)) m_pp.platinum_shared = database.GetSharedPlatinum(database.GetAccountIDByChar(cid)); - if (guildrank) { - if (row[7] != nullptr) - guildrank = atoi(row[7]); - else - guildrank = GUILD_RANK_NONE; - } // if (ext) { SetExtendedProfile(ext, row[8], lengths[8]); } if (level){ level = atoi(row[10]); } if (LFP){ LFP = atoi(row[11]); } if (LFG){ LFG = atoi(row[12]); } if (firstlogon){ firstlogon = atoi(row[15]); } } - /* Load Character Inventory */ - loaditems = database.GetInventory(cid, &m_inv); - /* Load Character Currency into PP */ - database.LoadCharacterCurrency(cid, &m_pp); - /* Load Character Data from DB into PP */ - database.LoadCharacterData(cid, &m_pp); - /* Move to another method when can, this is pointless... */ - database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); - /* Load Character Currency */ - database.LoadCharacterCurrency(cid, &m_pp); - /* Load Character Skills */ - database.LoadCharacterSkills(cid, &m_pp); - /* Load Character Disciplines */ - database.LoadCharacterDisciplines(cid, &m_pp); + + loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ + database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ + database.LoadCharacterCurrency(cid, &m_pp); /* Load Character Currency into PP */ + database.LoadCharacterData(cid, &m_pp); /* Load Character Data from DB into PP */ + database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); /* Move to another method when can, this is pointless... */ + database.LoadCharacterCurrency(cid, &m_pp); /* Load Character Currency */ + database.LoadCharacterSkills(cid, &m_pp); /* Load Character Skills */ + database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ + database.LoadCharacterSpellBook(cid, &m_pp); /* Load Character Spell Book */ + database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */ + database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */ + /* If GM, not trackable */ if (gmhideme) { trackable = false; } - + /* Set Con State for Reporting */ conn_state = PlayerProfileLoaded; - - /* Set Current zone */ + // m_pp.zone_id = zone->GetZoneID(); // m_pp.zoneInstance = zone->GetInstanceID(); + /* Set Total Seconds Played */ TotalSecondsPlayed = m_pp.timePlayedMin * 60; - + /* Set Max AA XP */ max_AAXP = RuleI(AA, ExpPerPoint); - + /* If we can maintain intoxication across zones, check for it */ if (!RuleB(Character, MaintainIntoxicationAcrossZones)) - m_pp.intoxication = 0; - + m_pp.intoxication = 0; + strcpy(name, m_pp.name); + strcpy(lastname, m_pp.last_name); + /* If PP is set to wierd coordinates */ if ((m_pp.x == -1 && m_pp.y == -1 && m_pp.z == -1) || (m_pp.x == -2 && m_pp.y == -2 && m_pp.z == -2)) { m_pp.x = zone->safe_x(); m_pp.y = zone->safe_y(); m_pp.z = zone->safe_z(); - } - + } /* If too far below ground, then fix */ float ground_z = GetGroundZ(m_pp.x, m_pp.y, m_pp.z); if (m_pp.z < (ground_z - 500)) m_pp.z = ground_z; - class_ = m_pp.class_; - + /* Set Mob variables for spawn */ + class_ = m_pp.class_; level = m_pp.level; x_pos = m_pp.x; y_pos = m_pp.y; @@ -642,7 +646,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) base_race = m_pp.race; gender = m_pp.gender; base_gender = m_pp.gender; - deity = m_pp.deity; //FYI: DEITY_AGNOSTIC = 396; still valid? + deity = m_pp.deity; haircolor = m_pp.haircolor; beardcolor = m_pp.beardcolor; eyecolor1 = m_pp.eyecolor1; @@ -654,42 +658,41 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) drakkin_tattoo = m_pp.drakkin_tattoo; drakkin_details = m_pp.drakkin_details; + /* If GM not set in DB, and does not meet min status to be GM, reset */ if (m_pp.gm && admin < minStatusToBeGM) m_pp.gm = 0; /* Load Guild */ if (!IsInAGuild()) { m_pp.guild_id = GUILD_NONE; } else { - m_pp.guild_id = GuildID(); - + m_pp.guild_id = GuildID(); if (zone->GetZoneID() == RuleI(World, GuildBankZoneID)) GuildBanker = (guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || guild_mgr.GetBankerFlag(CharacterID())); } - m_pp.guildbanker = GuildBanker; switch (race) { - case OGRE: - size = 9; break; - case TROLL: - size = 8; break; - case VAHSHIR: case BARBARIAN: - size = 7; break; - case HUMAN: case HIGH_ELF: case ERUDITE: case IKSAR: case DRAKKIN: - size = 6; break; - case HALF_ELF: - size = 5.5; break; - case WOOD_ELF: case DARK_ELF: case FROGLOK: - size = 5; break; - case DWARF: - size = 4; break; - case HALFLING: - size = 3.5; break; - case GNOME: - size = 3; break; - default: - size = 0; + case OGRE: + size = 9; break; + case TROLL: + size = 8; break; + case VAHSHIR: case BARBARIAN: + size = 7; break; + case HUMAN: case HIGH_ELF: case ERUDITE: case IKSAR: case DRAKKIN: + size = 6; break; + case HALF_ELF: + size = 5.5; break; + case WOOD_ELF: case DARK_ELF: case FROGLOK: + size = 5; break; + case DWARF: + size = 4; break; + case HALFLING: + size = 3.5; break; + case GNOME: + size = 3; break; + default: + size = 0; } /* Check for Invalid points */ @@ -700,6 +703,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (m_pp.ldon_points_tak < 0 || m_pp.ldon_points_tak > 2000000000){ m_pp.ldon_points_tak = 0; } if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000){ m_pp.ldon_points_available = 0; } + /* Set Swimming Skill 100 by default if under 100 */ if (GetSkill(SkillSwimming) < 100) SetSkill(SkillSwimming, 100); @@ -859,29 +863,20 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) Mob::SetMana(m_pp.mana); SetEndurance(m_pp.endurance); - if (IsLFP()) { - // Update LFP in case any (or all) of our group disbanded while we were zoning. - UpdateLFP(); - } - - if (m_pp.z <= zone->newzone_data.underworld) { - m_pp.x = zone->newzone_data.safe_x; - m_pp.y = zone->newzone_data.safe_y; - m_pp.z = zone->newzone_data.safe_z; - } + /* Update LFP in case any (or all) of our group disbanded while we were zoning. */ + if (IsLFP()) { UpdateLFP(); } /* Get Expansions from variables table and ship via PP */ char val[20] = { 0 }; - if (database.GetVariable("Expansions", val, 20)) - m_pp.expansions = atoi(val); - else - m_pp.expansions = 0x3FF; + if (database.GetVariable("Expansions", val, 20)){ m_pp.expansions = atoi(val); } + else{ m_pp.expansions = 0x3FF; } p_timers.SetCharID(CharacterID()); if (!p_timers.Load(&database)) { LogFile->write(EQEMuLog::Error, "Unable to load ability timers from the database for %s (%i)!", GetCleanName(), CharacterID()); } + /* Load Spell Slot Refresh from Currently Memoried Spells */ for (unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) if (IsValidSpell(m_pp.mem_spells[i])) m_pp.spellSlotRefresh[i] = p_timers.GetRemainingTime(pTimerSpellStart + m_pp.mem_spells[i]) * 1000; @@ -904,14 +899,12 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_inv.dumpEntireInventory(); #endif - //lost in current PP - // strcpy(m_pp.servername,"eqemulator"); - - m_pp.air_remaining = 60; //Reset to max so they dont drown on zone in if its underwater - + /* Reset to max so they dont drown on zone in if its underwater */ + m_pp.air_remaining = 60; + /* Check for PVP Zone status*/ if (zone->IsPVPZone()) m_pp.pvp = 1; - + /* Time entitled on Account: Move to account */ m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; /* Reset rest timer if the durations have been lowered in the database */ @@ -934,9 +927,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) database.LoadPetInfo(this); /* - This was moved before the spawn packets are sent - in hopes that it adds more consistency... - Remake pet + This was moved before the spawn packets are sent + in hopes that it adds more consistency... + Remake pet */ if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) { @@ -985,16 +978,15 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) } /* - Character Inventory Packet - this is not quite where live sends inventory, they do it after tribute + Character Inventory Packet + this is not quite where live sends inventory, they do it after tribute */ - if (loaditems) { //dont load if a length error occurs + if (loaditems) { /* Dont load if a length error occurs */ BulkSendInventoryItems(); - - // Send stuff on the cursor which isnt sent in bulk + /* Send stuff on the cursor which isnt sent in bulk */ iter_queue it; for (it = m_inv.cursor_begin(); it != m_inv.cursor_end(); ++it) { - // First item cursor is sent in bulk inventory packet + /* First item cursor is sent in bulk inventory packet */ if (it == m_inv.cursor_begin()) continue; const ItemInst *inst = *it; @@ -1005,15 +997,13 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /* Task Packets */ LoadClientTaskState(); - if (GetClientVersion() >= EQClientRoF) - { + if (GetClientVersion() >= EQClientRoF) { outapp = new EQApplicationPacket(OP_ReqNewZone, 0); Handle_Connect_OP_ReqNewZone(outapp); safe_delete(outapp); } - if (ClientVersionBit & BIT_UnderfootAndLater) - { + if (ClientVersionBit & BIT_UnderfootAndLater) { outapp = new EQApplicationPacket(OP_XTargetResponse, 8); outapp->WriteUInt32(GetMaxXTargets()); outapp->WriteUInt32(0); @@ -1021,17 +1011,16 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) } /* - Weather Packet - This shouldent be moved, this seems to be what the client - uses to advance to the next state (sending ReqNewZone) + Weather Packet + This shouldent be moved, this seems to be what the client + uses to advance to the next state (sending ReqNewZone) */ outapp = new EQApplicationPacket(OP_Weather, 12); Weather_Struct *ws = (Weather_Struct *)outapp->pBuffer; ws->val1 = 0x000000FF; if (zone->zone_weather == 1) ws->type = 0x31; // Rain - if (zone->zone_weather == 2) - { + if (zone->zone_weather == 2) { outapp->pBuffer[8] = 0x01; ws->type = 0x02; } @@ -2585,7 +2574,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } if(i == 0) { - CastSpell(item->Click.Effect, target_id, 10, item->CastTime, 0, 0, slot_id); + CastSpell(item->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, item->CastTime, 0, 0, slot_id); } } else @@ -2612,7 +2601,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } if(i == 0) { - CastSpell(augitem->Click.Effect, target_id, 10, augitem->CastTime, 0, 0, slot_id); + CastSpell(augitem->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, augitem->CastTime, 0, 0, slot_id); } } else @@ -4947,6 +4936,7 @@ void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app) void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) { + if (app->size != sizeof(SwapSpell_Struct)) { std::cout << "Wrong size on OP_SwapSpell. Got: " << app->size << ", Expected: " << sizeof(SwapSpell_Struct) << std::endl; return; @@ -4958,8 +4948,13 @@ void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) return; swapspelltemp = m_pp.spell_book[swapspell->from_slot]; + if (swapspelltemp < 0){ + return; + } m_pp.spell_book[swapspell->from_slot] = m_pp.spell_book[swapspell->to_slot]; m_pp.spell_book[swapspell->to_slot] = swapspelltemp; + + database.SaveCharacterSpellSwap(this->CharacterID(), swapspelltemp, swapspell->from_slot, swapspell->to_slot); QueuePacket(app); return; @@ -4991,7 +4986,27 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) #endif LogFile->write(EQEMuLog::Debug, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot); - if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast + std::cout << "OP_CastSpell " << castspell->slot << " spell " << castspell->spell_id << " inventory slot " << castspell->inventoryslot << "\n" << std::endl; + + /* Memorized Spell */ + if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id){ + uint16 spell_to_cast = 0; + if (castspell->slot < MAX_PP_MEMSPELL) { + spell_to_cast = m_pp.mem_spells[castspell->slot]; + if (spell_to_cast != castspell->spell_id) { + InterruptSpell(castspell->spell_id); //CHEATER!!! + return; + } + } + else if (castspell->slot >= MAX_PP_MEMSPELL) { + InterruptSpell(); + return; + } + + CastSpell(spell_to_cast, castspell->target_id, castspell->slot); + } + /* Spell Slot or Potion Belt Slot */ + else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast { //discipline, using the item spell slot if (castspell->inventoryslot == 0xFFFFFFFF) { @@ -5070,14 +5085,16 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) InterruptSpell(castspell->spell_id); } } - else if (castspell->slot == DISCIPLINE_SPELL_SLOT) { // DISCIPLINE cast + /* Discipline */ + else if (castspell->slot == DISCIPLINE_SPELL_SLOT) { if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { printf("Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); InterruptSpell(castspell->spell_id); return; } } - else if (castspell->slot == ABILITY_SPELL_SLOT) { // ABILITY cast (LoH and Harm Touch) + /* ABILITY cast (LoH and Harm Touch) */ + else if (castspell->slot == ABILITY_SPELL_SLOT) { uint16 spell_to_cast = 0; if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) { @@ -5109,26 +5126,6 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) if (spell_to_cast > 0) // if we've matched LoH or HT, cast now CastSpell(spell_to_cast, castspell->target_id, castspell->slot); } - else // MEMORIZED SPELL (first confirm that it's a valid memmed spell slot, then validate that the spell is currently memorized) - { - uint16 spell_to_cast = 0; - - if(castspell->slot < MAX_PP_MEMSPELL) - { - spell_to_cast = m_pp.mem_spells[castspell->slot]; - if(spell_to_cast != castspell->spell_id) - { - InterruptSpell(castspell->spell_id); //CHEATER!!! - return; - } - } - else if (castspell->slot >= MAX_PP_MEMSPELL) { - InterruptSpell(); - return; - } - - CastSpell(spell_to_cast, castspell->target_id, castspell->slot); - } return; } @@ -9224,8 +9221,7 @@ void Client::CompleteConnect() { SendAppearancePacket(AT_GuildID, GuildID(), false); SendAppearancePacket(AT_GuildRank, GuildRank(), false); } - for (uint32 spellInt = 0; spellInt < MAX_PP_SPELLBOOK; spellInt++) - { + for (uint32 spellInt = 0; spellInt < MAX_PP_SPELLBOOK; spellInt++) { if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000) m_pp.spell_book[spellInt] = 0xFFFFFFFF; } diff --git a/zone/command.cpp b/zone/command.cpp index 011cb0f09..b1d195bf4 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2491,14 +2491,14 @@ void command_castspell(Client *c, const Seperator *sep) else if (c->GetTarget() == 0) if(c->Admin() >= commandInstacast) - c->SpellFinished(spellid, 0, 10, 0, -1, spells[spellid].ResistDiff); + c->SpellFinished(spellid, 0, USE_ITEM_SPELL_SLOT, 0, -1, spells[spellid].ResistDiff); else - c->CastSpell(spellid, 0, 10, 0); + c->CastSpell(spellid, 0, USE_ITEM_SPELL_SLOT, 0); else if(c->Admin() >= commandInstacast) c->SpellFinished(spellid, c->GetTarget(), 10, 0, -1, spells[spellid].ResistDiff); else - c->CastSpell(spellid, c->GetTarget()->GetID(), 10, 0); + c->CastSpell(spellid, c->GetTarget()->GetID(), USE_ITEM_SPELL_SLOT, 0); } } diff --git a/zone/common.h b/zone/common.h index 1401af860..0dbb68e0b 100644 --- a/zone/common.h +++ b/zone/common.h @@ -17,6 +17,10 @@ #define _NPCPET(x) (x && x->IsNPC() && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsNPC()) #define _BECOMENPCPET(x) (x && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsClient() && x->CastToMob()->GetOwner()->CastToClient()->IsBecomeNPC()) +#define USE_ITEM_SPELL_SLOT 10 +#define POTION_BELT_SPELL_SLOT 11 +#define DISCIPLINE_SPELL_SLOT 10 +#define ABILITY_SPELL_SLOT 9 //LOS Parameters: #define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from diff --git a/zone/mob.h b/zone/mob.h index 55a57892b..51439cfee 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -205,7 +205,7 @@ public: void SendSpellBarEnable(uint16 spellid); void ZeroCastingVars(); virtual void SpellProcess(); - virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1, + virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, uint32 type = 0, int16 *resist_adjust = nullptr); virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1, diff --git a/zone/spells.cpp b/zone/spells.cpp index 357fafbd4..94921c685 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4843,6 +4843,8 @@ void Client::MemSpell(uint16 spell_id, int slot, bool update_client) m_pp.mem_spells[slot] = spell_id; mlog(CLIENT__SPELLS, "Spell %d memorized into slot %d", spell_id, slot); + database.SaveCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot); + if(update_client) { MemorizeSpell(slot, spell_id, memSpellMemorize); @@ -4857,6 +4859,8 @@ void Client::UnmemSpell(int slot, bool update_client) mlog(CLIENT__SPELLS, "Spell %d forgotten from slot %d", m_pp.mem_spells[slot], slot); m_pp.mem_spells[slot] = 0xFFFFFFFF; + database.DeleteCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot); + if(update_client) { MemorizeSpell(slot, m_pp.mem_spells[slot], memSpellForget); @@ -4884,6 +4888,7 @@ void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client) } m_pp.spell_book[slot] = spell_id; + database.SaveCharacterSpell(this->CharacterID(), spell_id, slot); mlog(CLIENT__SPELLS, "Spell %d scribed into spell book slot %d", spell_id, slot); if(update_client) @@ -4899,7 +4904,8 @@ void Client::UnscribeSpell(int slot, bool update_client) mlog(CLIENT__SPELLS, "Spell %d erased from spell book slot %d", m_pp.spell_book[slot], slot); m_pp.spell_book[slot] = 0xFFFFFFFF; - + + database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[slot], slot); if(update_client) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct)); diff --git a/zone/zone.cpp b/zone/zone.cpp index 8f4b211d8..a9c63b22f 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -401,40 +401,33 @@ uint32 Zone::GetTempMerchantQuantity(uint32 NPCID, uint32 Slot) { void Zone::LoadTempMerchantData(){ LogFile->write(EQEMuLog::Status, "Loading Temporary Merchant Lists..."); - - char* query = 0; - uint32_breakdown workpt; - workpt.b4() = DBA_b4_Zone; - workpt.w2_3() = 0; - workpt.b1() = DBA_b1_Zone_MerchantListsTemp; - DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Read); - dbaw->AddQuery(1, &query, MakeAnyLenString(&query, - "select ml.npcid,ml.slot,ml.itemid,ml.charges " - "from " - " merchantlist_temp ml, " - " spawnentry se, " - " spawn2 s2 " - "where " - " ml.npcid=se.npcid " - " and se.spawngroupid=s2.spawngroupid " - " and s2.zone='%s' and s2.version=%u", GetShortName(), GetInstanceVersion())); - if (!(pQueuedMerchantsWorkID = dbasync->AddWork(&dbaw))) { - safe_delete(dbaw); - LogFile->write(EQEMuLog::Error, "dbasync->AddWork() failed adding merchant list query"); + std::string query = StringFormat( + "SELECT " + "ml.npcid, " + "ml.slot, " + "ml.charges, " + "ml.itemid " + "FROM " + "merchantlist_temp ml, " + "spawnentry se, " + "spawn2 s2 " + "WHERE " + "ml.npcid = se.npcid " + "AND se.spawngroupid = s2.spawngroupid " + "AND s2.zone = '%s' AND s2.version = %i", GetShortName(), GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadTempMerchantData query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); return; } -} - -void Zone::LoadTempMerchantData_result(MYSQL_RES* result) { - MYSQL_ROW row; - std::map >::iterator cur; + std::map >::iterator cur; uint32 npcid = 0; - while((row = mysql_fetch_row(result))) { + for (auto row = results.begin(); row != results.end(); ++row) { TempMerchantList ml; ml.npcid = atoul(row[0]); - if(npcid != ml.npcid){ + if (npcid != ml.npcid){ cur = tmpmerchanttable.find(ml.npcid); - if(cur == tmpmerchanttable.end()) { + if (cur == tmpmerchanttable.end()) { std::list empty; tmpmerchanttable[ml.npcid] = empty; cur = tmpmerchanttable.find(ml.npcid); @@ -447,9 +440,9 @@ void Zone::LoadTempMerchantData_result(MYSQL_RES* result) { ml.origslot = ml.slot; cur->second.push_back(ml); } + pQueuedMerchantsWorkID = 0; } -//there should prolly be a temp counterpart of this... void Zone::LoadNewMerchantData(uint32 merchantid){ std::list merlist; @@ -476,16 +469,35 @@ void Zone::LoadNewMerchantData(uint32 merchantid){ merchanttable[merchantid] = merlist; } -void Zone::LoadMerchantData_result(MYSQL_RES* result) { - MYSQL_ROW row; - std::map >::iterator cur; +void Zone::GetMerchantDataForZoneLoad(){ + LogFile->write(EQEMuLog::Status, "Loading Merchant Lists..."); + std::string query = StringFormat( + "SELECT " + "ml.merchantid, " + "ml.slot, " + "ml.item, " + "ml.faction_required, " + "ml.level_required, " + "ml.alt_currency_cost, " + "ml.classes_required, " + "ml.probability " + "FROM " + "merchantlist AS ml, " + "npc_types AS nt, " + "spawnentry AS se, " + "spawn2 AS s2 " + "WHERE nt.merchant_id = ml.merchantid AND nt.id = se.npcid " + "AND se.spawngroupid = s2.spawngroupid AND s2.zone = '%s' AND s2.version = %i ", GetShortName(), GetInstanceVersion()); + auto results = database.QueryDatabase(query); + std::map >::iterator cur; uint32 npcid = 0; - while((row = mysql_fetch_row(result))) { + if (results.RowCount() == 0){ LogFile->write(EQEMuLog::Error, "Error in loading Merchant Data for zone"); return; } + for (auto row = results.begin(); row != results.end(); ++row) { MerchantList ml; ml.id = atoul(row[0]); - if(npcid != ml.id){ + if (npcid != ml.id){ cur = merchanttable.find(ml.id); - if(cur == merchanttable.end()) { + if (cur == merchanttable.end()) { std::list empty; merchanttable[ml.id] = empty; cur = merchanttable.find(ml.id); @@ -495,15 +507,15 @@ void Zone::LoadMerchantData_result(MYSQL_RES* result) { std::list::iterator iter = cur->second.begin(); bool found = false; - while(iter != cur->second.end()) { - if((*iter).item == ml.id) { + while (iter != cur->second.end()) { + if ((*iter).item == ml.id) { found = true; break; } ++iter; } - if(found) { + if (found) { continue; } @@ -516,28 +528,7 @@ void Zone::LoadMerchantData_result(MYSQL_RES* result) { ml.probability = atoul(row[7]); cur->second.push_back(ml); } -} -void Zone::GetMerchantDataForZoneLoad(){ - LogFile->write(EQEMuLog::Status, "Loading Merchant Lists..."); - char* query = 0; - uint32_breakdown workpt; - workpt.b4() = DBA_b4_Zone; - workpt.w2_3() = 0; - workpt.b1() = DBA_b1_Zone_MerchantLists; - DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Read); - dbaw->AddQuery(1, &query, MakeAnyLenString(&query, - "select ml.merchantid,ml.slot,ml.item,ml.faction_required,ml.level_required,ml.alt_currency_cost,ml.classes_required,ml.probability " - "from merchantlist ml, npc_types nt, spawnentry se, spawn2 s2 " - "where nt.merchant_id=ml.merchantid and nt.id=se.npcid " - "and se.spawngroupid=s2.spawngroupid and s2.zone='%s' and s2.version=%u " - //"group by ml.merchantid,slot order by merchantid,slot asc" //this made the query use a temp table/filesort (very slow)... so we handle unsorted data on our end. - , GetShortName(), GetInstanceVersion())); - if (!(pQueuedMerchantsWorkID = dbasync->AddWork(&dbaw))) { - safe_delete(dbaw); - LogFile->write(EQEMuLog::Error,"dbasync->AddWork() failed adding merchant list query"); - return; - } } void Zone::LoadMercTemplates(){ @@ -663,61 +654,6 @@ void Zone::LoadMercSpells(){ } -void Zone::DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { -// LogFile->write(EQEMuLog::Debug, "Zone work complete..."); - switch (workpt_b1) { - case DBA_b1_Zone_MerchantLists: { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* result = 0; - DBAsyncQuery* dbaq = dbaw->PopAnswer(); - if(dbaq == nullptr) { - LogFile->write(EQEMuLog::Error, "nullptr answer provided for async merchant list load."); - break; - } - if(!dbaq->GetAnswer(errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Unable to get results for merchant lists"); - break; - } - if(dbaq->QPT() != 1) { - LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Invalid query part for merchant lists"); - break; - } - - LoadMerchantData_result(result); - - pQueuedMerchantsWorkID = 0; - break; - } - case DBA_b1_Zone_MerchantListsTemp: { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* result = 0; - DBAsyncQuery* dbaq = dbaw->PopAnswer(); - if(dbaq == nullptr) { - LogFile->write(EQEMuLog::Error, "nullptr answer provided for async temp merchant list load."); - break; - } - if(!dbaq->GetAnswer(errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Unable to get results for temp merchant lists"); - break; - } - if(dbaq->QPT() != 1) { - LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Invalid query part for temp merchant lists"); - break; - } - - LoadTempMerchantData_result(result); - - pQueuedMerchantsWorkID = 0; - break; - } - - default: { - LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Unknown workpt_b1"); - break; - } - } -} - bool Zone::IsLoaded() { return ZoneLoaded; } diff --git a/zone/zone.h b/zone/zone.h index 77132124e..9ad692fa0 100644 --- a/zone/zone.h +++ b/zone/zone.h @@ -166,7 +166,6 @@ public: void SetStaticZone(bool sz) { staticzone = sz; } inline bool IsStaticZone() { return staticzone; } inline void GotCurTime(bool time) { gottime = time; } - void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw); void SpawnConditionChanged(const SpawnCondition &c, int16 old_value); @@ -174,8 +173,6 @@ public: void LoadNewMerchantData(uint32 merchantid); void LoadTempMerchantData(); uint32 GetTempMerchantQuantity(uint32 NPCID, uint32 Slot); - void LoadTempMerchantData_result(MYSQL_RES* result); - void LoadMerchantData_result(MYSQL_RES* result); int SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold=false); void LoadMercTemplates(); void LoadMercSpells(); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 4eb0e64ab..09aa26bc8 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1022,6 +1022,45 @@ bool ZoneDatabase::LoadCharacterFactionValues(uint32 character_id, faction_map & return true; } +bool ZoneDatabase::LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "slot_id, " + "`spell_id` " + "FROM " + "`character_memmed_spells` " + "WHERE `id` = %u ORDER BY `slot_id`", character_id); + auto results = database.QueryDatabase(query); int i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); pp->mem_spells[i] = atoi(row[1]); } + return true; +} + +bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "slot_id, " + "`spell_id` " + "FROM " + "`character_spells` " + "WHERE `id` = %u ORDER BY `slot_id`", character_id); + auto results = database.QueryDatabase(query); int i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); pp->spell_book[i] = atoi(row[1]); } + return true; +} + +bool ZoneDatabase::LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "lang_id, " + "`value` " + "FROM " + "`character_languages` " + "WHERE `id` = %u ORDER BY `language_id`", character_id); + auto results = database.QueryDatabase(query); int i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); pp->languages[i] = atoi(row[1]); } + return true; +} + bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat( "SELECT " @@ -1092,8 +1131,23 @@ bool ZoneDatabase::LoadCharacterCurrency(uint32 character_id, PlayerProfile_Stru return true; } +bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT id, zone_id, instance_id, x, y, z, heading FROM character_bind_home WHERE `id` = %u", character_id); + auto results = database.QueryDatabase(query); int i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + pp->binds[4].zoneId = atoi(row[i]); i++; + i++; /* Instance ID can go here eventually */ + pp->binds[4].x = atoi(row[i]); i++; + pp->binds[4].y = atoi(row[i]); i++; + pp->binds[4].z = atoi(row[i]); i++; + pp->binds[4].heading = atoi(row[i]); i++; + } + return true; +} + bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp){ clock_t t = std::clock(); /* Function timer start */ + if (pp->tribute_time_remaining < 0 || pp->tribute_time_remaining == 4294967295){ pp->tribute_time_remaining = 0; } std::string query = StringFormat( "REPLACE INTO `character_data` (" " id, " @@ -1187,96 +1241,96 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla " guild_auto_consent, " " RestTimer) " "VALUES (" - "%i," // id " id, " - "%i," // account_id " account_id, " + "%u," // id " id, " + "%u," // account_id " account_id, " "'%s'," // `name` pp->name, " `name`, " "'%s'," // last_name pp->last_name, " last_name, " - "%i," // gender pp->gender, " gender, " - "%i," // race pp->race, " race, " - "%i," // class pp->class_, " class, " - "%i," // `level` pp->level, " `level`, " - "%i," // deity pp->deity, " deity, " - "%i," // birthday pp->birthday, " birthday, " - "%i," // last_login pp->lastlogin, " last_login, " - "%i," // time_played pp->timePlayedMin, " time_played, " - "%i," // pvp_status pp->pvp, " pvp_status, " - "%i," // level2 pp->level2, " level2, " - "%i," // anon pp->anon, " anon, " - "%i," // gm pp->gm, " gm, " - "%i," // intoxication pp->intoxication, " intoxication, " - "%i," // hair_color pp->haircolor, " hair_color, " - "%i," // beard_color pp->beardcolor, " beard_color, " - "%i," // eye_color_1 pp->eyecolor1, " eye_color_1, " - "%i," // eye_color_2 pp->eyecolor2, " eye_color_2, " - "%i," // hair_style pp->hairstyle, " hair_style, " - "%i," // beard pp->beard, " beard, " - "%i," // ability_time_seconds pp->ability_time_seconds, " ability_time_seconds, " - "%i," // ability_number pp->ability_number, " ability_number, " - "%i," // ability_time_minutes pp->ability_time_minutes, " ability_time_minutes, " - "%i," // ability_time_hours pp->ability_time_hours, " ability_time_hours, " + "%u," // gender pp->gender, " gender, " + "%u," // race pp->race, " race, " + "%u," // class pp->class_, " class, " + "%u," // `level` pp->level, " `level`, " + "%u," // deity pp->deity, " deity, " + "%u," // birthday pp->birthday, " birthday, " + "%u," // last_login pp->lastlogin, " last_login, " + "%u," // time_played pp->timePlayedMin, " time_played, " + "%u," // pvp_status pp->pvp, " pvp_status, " + "%u," // level2 pp->level2, " level2, " + "%u," // anon pp->anon, " anon, " + "%u," // gm pp->gm, " gm, " + "%u," // intoxication pp->intoxication, " intoxication, " + "%u," // hair_color pp->haircolor, " hair_color, " + "%u," // beard_color pp->beardcolor, " beard_color, " + "%u," // eye_color_1 pp->eyecolor1, " eye_color_1, " + "%u," // eye_color_2 pp->eyecolor2, " eye_color_2, " + "%u," // hair_style pp->hairstyle, " hair_style, " + "%u," // beard pp->beard, " beard, " + "%u," // ability_time_seconds pp->ability_time_seconds, " ability_time_seconds, " + "%u," // ability_number pp->ability_number, " ability_number, " + "%u," // ability_time_minutes pp->ability_time_minutes, " ability_time_minutes, " + "%u," // ability_time_hours pp->ability_time_hours, " ability_time_hours, " "'%s'," // title pp->title, " title, " " "'%s'," // suffix pp->suffix, " suffix, " - "%i," // exp pp->exp, " exp, " - "%i," // points pp->points, " points, " - "%i," // mana pp->mana, " mana, " - "%i," // cur_hp pp->cur_hp, " cur_hp, " - "%i," // str pp->STR, " str, " - "%i," // sta pp->STA, " sta, " - "%i," // cha pp->CHA, " cha, " - "%i," // dex pp->DEX, " dex, " - "%i," // `int` pp->INT, " `int`, " - "%i," // agi pp->AGI, " agi, " - "%i," // wis pp->WIS, " wis, " - "%i," // face pp->face, " face, " + "%u," // exp pp->exp, " exp, " + "%u," // points pp->points, " points, " + "%u," // mana pp->mana, " mana, " + "%u," // cur_hp pp->cur_hp, " cur_hp, " + "%u," // str pp->STR, " str, " + "%u," // sta pp->STA, " sta, " + "%u," // cha pp->CHA, " cha, " + "%u," // dex pp->DEX, " dex, " + "%u," // `int` pp->INT, " `int`, " + "%u," // agi pp->AGI, " agi, " + "%u," // wis pp->WIS, " wis, " + "%u," // face pp->face, " face, " "%f," // y pp->y, " y, " "%f," // x pp->x, " x, " "%f," // z pp->z, " z, " "%f," // heading pp->heading, " heading, " - "%i," // pvp2 pp->pvp2, " pvp2, " - "%i," // pvp_type pp->pvptype, " pvp_type, " - "%i," // autosplit_enabled pp->autosplit, " autosplit_enabled, " - "%i," // zone_change_count pp->zone_change_count, " zone_change_count, " - "%i," // drakkin_heritage pp->drakkin_heritage, " drakkin_heritage, " - "%i," // drakkin_tattoo pp->drakkin_tattoo, " drakkin_tattoo, " - "%i," // drakkin_details pp->drakkin_details, " drakkin_details, " + "%u," // pvp2 pp->pvp2, " pvp2, " + "%u," // pvp_type pp->pvptype, " pvp_type, " + "%u," // autosplit_enabled pp->autosplit, " autosplit_enabled, " + "%u," // zone_change_count pp->zone_change_count, " zone_change_count, " + "%u," // drakkin_heritage pp->drakkin_heritage, " drakkin_heritage, " + "%u," // drakkin_tattoo pp->drakkin_tattoo, " drakkin_tattoo, " + "%u," // drakkin_details pp->drakkin_details, " drakkin_details, " "%i," // toxicity pp->toxicity, " toxicity, " "%i," // hunger_level pp->hunger_level, " hunger_level, " "%i," // thirst_level pp->thirst_level, " thirst_level, " - "%i," // ability_up pp->ability_up, " ability_up, " - "%i," // zone_id pp->zone_id, " zone_id, " - "%i," // zone_instance pp->zoneInstance, " zone_instance, " - "%i," // leadership_exp_on pp->leadAAActive, " leadership_exp_on, " - "%i," // ldon_points_guk pp->ldon_points_guk, " ldon_points_guk, " - "%i," // ldon_points_mir pp->ldon_points_mir, " ldon_points_mir, " - "%i," // ldon_points_mmc pp->ldon_points_mmc, " ldon_points_mmc, " - "%i," // ldon_points_ruj pp->ldon_points_ruj, " ldon_points_ruj, " - "%i," // ldon_points_tak pp->ldon_points_tak, " ldon_points_tak, " - "%i," // ldon_points_available pp->ldon_points_available, " ldon_points_available, " - "%i," // tribute_time_remaining pp->tribute_time_remaining, " tribute_time_remaining, " - "%i," // show_helm pp->showhelm, " show_helm, " - "%i," // career_tribute_points pp->career_tribute_points, " career_tribute_points, " - "%i," // tribute_points pp->tribute_points, " tribute_points, " - "%i," // tribute_active pp->tribute_active, " tribute_active, " - "%i," // endurance pp->endurance, " endurance, " - "%i," // group_leadership_exp pp->group_leadership_exp, " group_leadership_exp, " - "%i," // raid_leadership_exp pp->raid_leadership_exp, " raid_leadership_exp, " - "%i," // group_leadership_points pp->group_leadership_points, " group_leadership_points, " - "%i," // raid_leadership_points pp->raid_leadership_points, " raid_leadership_points, " - "%i," // air_remaining pp->air_remaining, " air_remaining, " - "%i," // pvp_kills pp->PVPKills, " pvp_kills, " - "%i," // pvp_deaths pp->PVPDeaths, " pvp_deaths, " - "%i," // pvp_current_points pp->PVPCurrentPoints, " pvp_current_points, " - "%i," // pvp_career_points pp->PVPCareerPoints, " pvp_career_points, " - "%i," // pvp_best_kill_streak pp->PVPBestKillStreak, " pvp_best_kill_streak, " - "%i," // pvp_worst_death_streak pp->PVPWorstDeathStreak, " pvp_worst_death_streak, " - "%i," // pvp_current_kill_streak pp->PVPCurrentKillStreak, " pvp_current_kill_streak, " - "%i," // aa_points_spent pp->aapoints_spent, " aa_points_spent, " - "%i," // aa_exp pp->expAA, " aa_exp, " - "%i," // aa_points pp->aapoints, " aa_points, " - "%i," // group_auto_consent pp->groupAutoconsent, " group_auto_consent, " - "%i," // raid_auto_consent pp->raidAutoconsent, " raid_auto_consent, " - "%i," // guild_auto_consent pp->guildAutoconsent, " guild_auto_consent, " - "%i" // RestTimer pp->RestTimer, " RestTimer) " + "%u," // ability_up pp->ability_up, " ability_up, " + "%u," // zone_id pp->zone_id, " zone_id, " + "%u," // zone_instance pp->zoneInstance, " zone_instance, " + "%u," // leadership_exp_on pp->leadAAActive, " leadership_exp_on, " + "%u," // ldon_points_guk pp->ldon_points_guk, " ldon_points_guk, " + "%u," // ldon_points_mir pp->ldon_points_mir, " ldon_points_mir, " + "%u," // ldon_points_mmc pp->ldon_points_mmc, " ldon_points_mmc, " + "%u," // ldon_points_ruj pp->ldon_points_ruj, " ldon_points_ruj, " + "%u," // ldon_points_tak pp->ldon_points_tak, " ldon_points_tak, " + "%u," // ldon_points_available pp->ldon_points_available, " ldon_points_available, " + "%u," // tribute_time_remaining pp->tribute_time_remaining, " tribute_time_remaining, " + "%u," // show_helm pp->showhelm, " show_helm, " + "%u," // career_tribute_points pp->career_tribute_points, " career_tribute_points, " + "%u," // tribute_points pp->tribute_points, " tribute_points, " + "%u," // tribute_active pp->tribute_active, " tribute_active, " + "%u," // endurance pp->endurance, " endurance, " + "%u," // group_leadership_exp pp->group_leadership_exp, " group_leadership_exp, " + "%u," // raid_leadership_exp pp->raid_leadership_exp, " raid_leadership_exp, " + "%u," // group_leadership_points pp->group_leadership_points, " group_leadership_points, " + "%u," // raid_leadership_points pp->raid_leadership_points, " raid_leadership_points, " + "%u," // air_remaining pp->air_remaining, " air_remaining, " + "%u," // pvp_kills pp->PVPKills, " pvp_kills, " + "%u," // pvp_deaths pp->PVPDeaths, " pvp_deaths, " + "%u," // pvp_current_points pp->PVPCurrentPoints, " pvp_current_points, " + "%u," // pvp_career_points pp->PVPCareerPoints, " pvp_career_points, " + "%u," // pvp_best_kill_streak pp->PVPBestKillStreak, " pvp_best_kill_streak, " + "%u," // pvp_worst_death_streak pp->PVPWorstDeathStreak, " pvp_worst_death_streak, " + "%u," // pvp_current_kill_streak pp->PVPCurrentKillStreak, " pvp_current_kill_streak, " + "%u," // aa_points_spent pp->aapoints_spent, " aa_points_spent, " + "%u," // aa_exp pp->expAA, " aa_exp, " + "%u," // aa_points pp->aapoints, " aa_points, " + "%u," // group_auto_consent pp->groupAutoconsent, " group_auto_consent, " + "%u," // raid_auto_consent pp->raidAutoconsent, " raid_auto_consent, " + "%u," // guild_auto_consent pp->guildAutoconsent, " guild_auto_consent, " + "%u" // RestTimer pp->RestTimer, " RestTimer) " ")", character_id, account_id, @@ -1370,7 +1424,12 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla pp->RestTimer ); auto results = database.QueryDatabase(query); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + //if (results.RowsAffected() != 2) { + // LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::SaveCharacterData Error! Query: %s \n", query); return false; + //} + //else{ + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + //} return true; } @@ -1407,7 +1466,7 @@ bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Stru pp->currentEbonCrystals, pp->careerEbonCrystals); auto results = database.QueryDatabase(query); - LogFile->write(EQEMuLog::Status, "Saving Currency for character ID: %i, done", character_id); + LogFile->write(EQEMuLog::Status, "Saving Currency for character ID: %i, done", character_id); return true; } @@ -1416,10 +1475,35 @@ bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 cur " VALUES (%u, %u, %u)", character_id, aa_id, current_level); auto results = QueryDatabase(rquery); - LogFile->write(EQEMuLog::Status, "Saving AA for character ID: %i, aa_id: %u current_level: %i", character_id, aa_id, current_level); + LogFile->write(EQEMuLog::Status, "Saving AA for character ID: %u, aa_id: %u current_level: %u", character_id, aa_id, current_level); return true; } +bool ZoneDatabase::SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot){ + std::string rquery = StringFormat("UPDATE `character_spells` SET `slot_id` = %u WHERE `slot_id` = %u AND `id` = %u", + to_slot, from_slot, character_id); + clock_t t = std::clock(); /* Function timer start */ + auto results = QueryDatabase(rquery); + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterSpellSwap for character ID: %u, from_slot: %u to_slot: %u spell: %u time: %f seconds", character_id, from_slot, to_slot, spell_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + return true; +} + +bool ZoneDatabase::SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ + std::string query = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); QueryDatabase(query); return true; +} + +bool ZoneDatabase::DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ + std::string query = StringFormat("DELETE FROM `character_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); QueryDatabase(query); return true; +} + +bool ZoneDatabase::SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ + std::string query = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); QueryDatabase(query); return true; +} + +bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ + std::string query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); QueryDatabase(query); return true; +} + bool ZoneDatabase::NoRentExpired(const char* name){ char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; @@ -1429,7 +1513,7 @@ bool ZoneDatabase::NoRentExpired(const char* name){ safe_delete_array(query); if (mysql_num_rows(result) == 1) { row = mysql_fetch_row(result); - uint32 seconds = atoi(row[0]); + uint32 seconds = atoi(row[0]); mysql_free_result(result); return (seconds>1800); } diff --git a/zone/zonedb.h b/zone/zonedb.h index ce32e919f..87aefd4cf 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -256,18 +256,28 @@ public: void SavePetInfo(Client *c); void RemoveTempFactions(Client *c); - /* Player Profile Loaders */ + /* Character Data Loaders */ bool LoadCharacterFactionValues(uint32 character_id, faction_map & val_list); + bool LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp); - /* Player Profile Saves */ - + /* Character Data Saves */ bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp); bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); + bool SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot); + bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + + /* Character Data Deletes */ + bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); /* * Character Inventory diff --git a/zone/zonedbasync.cpp b/zone/zonedbasync.cpp index 579517d55..454252ab6 100644 --- a/zone/zonedbasync.cpp +++ b/zone/zonedbasync.cpp @@ -12,30 +12,6 @@ void DispatchFinishedDBAsync(DBAsyncWork* dbaw) { uint32_breakdown workpt; workpt = dbaw->WPT(); switch (workpt.b4()) { -/* case DBA_b4_Main: { - switch (workpt.i24_1()) { - case DBA_i24_1_Main_LoadVariables: { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* result; - DBAsyncQuery* dbaq = dbaw->PopAnswer(); - if (dbaq->GetAnswer(errbuf, result)) - database.LoadVariables_result(result); - else - std::cout << "Async DB.LoadVariables() failed: '" << errbuf << "'" << std::endl; - break; - } - default: { - std::cout << "Error: DispatchFinishedDBAsync(): Unknown workpt.b4" << std::endl; - break; - } - } - }*/ - case DBA_b4_Zone: { - if(zone == nullptr) - break; - zone->DBAWComplete(workpt.b1(), dbaw); - break; - } case DBA_b4_Entity: { Entity* entity = entity_list.GetID(workpt.w2_3()); if (!entity) From 6497bdf45a9ff991b9bb916af063526bc0309637 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 31 Aug 2014 21:31:44 -0500 Subject: [PATCH 007/368] More stuff --- common/database.cpp | 39 ++++- world/worlddb.cpp | 348 +++++++++++++++++++++-------------------- zone/client.cpp | 3 + zone/client_packet.cpp | 1 + zone/inventory.cpp | 6 +- zone/zonedb.cpp | 22 +++ zone/zonedb.h | 2 + 7 files changed, 243 insertions(+), 178 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index fcfab3c52..1058cc5e1 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1086,6 +1086,28 @@ bool Database::CheckDatabaseConversions() { QueryDatabase(rquery); printf(" done...\n"); } + /* Check for table `character_material` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_material'"); + results = QueryDatabase(rquery); + // blue, green, red, use_tint, + if (results.RowCount() == 0){ + printf("Table: `character_material` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_material` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT," + "`slot` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," + "`blue` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," + "`green` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," + "`red` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," + "`use_tint` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," + "`color` int(11) UNSIGNED NOT NULL DEFAULT '0'," + "PRIMARY KEY(`id`, `slot`)," + "KEY `id` (`id`)" + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ); + QueryDatabase(rquery); + printf(" done...\n"); + } /* Done */ printf("Starting conversion...\n\n"); @@ -1093,7 +1115,7 @@ bool Database::CheckDatabaseConversions() { // querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); int char_iter_count = 0; - querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` >= 61238 LIMIT 100"); + querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` >= 61238 LIMIT 10"); if (RunQuery(query, querylen, errbuf, &result)) { safe_delete_array(query); while (row = mysql_fetch_row(result)) { @@ -1105,14 +1127,15 @@ bool Database::CheckDatabaseConversions() { pp = (PlayerProfile_Struct*)row2[1]; character_id = atoi(row[0]); account_id = atoi(row2[4]); + /* Verify PP Integrity */ lengths = mysql_fetch_lengths(result2); - if (lengths[1] == sizeof(PlayerProfile_Struct)) { + if (lengths[1] > 0) { memcpy(pp, row2[1], sizeof(PlayerProfile_Struct)); - // printf("FINE: Player profile '%s' %i length mismatch Expected: %i, Got: %i \n", row2[2], atoi(row2[3]), sizeof(PlayerProfile_Struct), lengths[1]); + // printf("FINE: Player profile '%s' %i length Expected: %i, Got: %i \n", row2[2], atoi(row2[3]), sizeof(PlayerProfile_Struct), lengths[1]); } /* Continue of PP Size does not match (Usually a created character never logged in) */ - else { + else { // printf("NO PP: Player profile '%s' %i length mismatch Expected: %i, Got: %i \n", row2[2], atoi(row2[3]), sizeof(PlayerProfile_Struct), lengths[1]); continue; } @@ -1483,6 +1506,14 @@ bool Database::CheckDatabaseConversions() { QueryDatabase(rquery); } } + /* Run Material Color Convert */ + for (i = 0; i < _MaterialCount; i++){ + if (pp->item_tint[i].color > 0){ + if (pp->item_tint[i].rgb.use_tint > 0){ pp->item_tint[i].rgb.use_tint = 1; } + rquery = StringFormat("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); + QueryDatabase(rquery); + } + } } /* Print out the entire Player Profile for testing */ diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 02dfa4290..b07480031 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -35,12 +35,10 @@ extern std::vector character_create_race_class_combos; // solar: the current stuff is at the bottom of this function void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - Inventory *inv; - + /* Initialize Player Profile for the small time it is being used for item material */ + PlayerProfile_Struct pp; + memset(&pp, 0, sizeof(PlayerProfile_Struct)); + /* Initialize Variables */ for (int i=0; i<10; i++) { strcpy(cs->name[i], ""); @@ -50,188 +48,194 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs->gohome[i] = 0; } - int char_num = 0; - unsigned long* lengths; + /* Get Character Info */ + std::string cquery = StringFormat( + "SELECT " + "`id`, " // 0 + "name, " // 1 + "gender, " // 2 + "race, " // 3 + "class, " // 4 + "`level`, " // 5 + "deity, " // 6 + "last_login, " // 7 + "time_played, " // 8 + "hair_color, " // 9 + "beard_color, " // 10 + "eye_color_1, " // 11 + "eye_color_2, " // 12 + "hair_style, " // 13 + "beard, " // 14 + "face, " // 15 + "drakkin_heritage, " // 16 + "drakkin_tattoo, " // 17 + "drakkin_details, " // 18 + "zone_id " // 19 + "FROM " + "character_data " + "WHERE `account_id` = %i LIMIT 10 ", account_id); + auto results = database.QueryDatabase(cquery); uint16 char_num = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + printf("id is %i \n", atoi(row[0])); + uint32 character_id = atoi(row[0]); + strcpy(cs->name[char_num], row[1]); + uint8 lvl = atoi(row[5]); + cs->level[char_num] = lvl; + cs->class_[char_num] = atoi(row[4]); + cs->race[char_num] = atoi(row[3]); + cs->gender[char_num] = atoi(row[2]); + cs->deity[char_num] = atoi(row[6]); + cs->zone[char_num] = atoi(row[19]); + cs->face[char_num] = atoi(row[15]); + cs->haircolor[char_num] = atoi(row[9]); + cs->beardcolor[char_num] = atoi(row[10]); + cs->eyecolor2[char_num] = atoi(row[12]); + cs->eyecolor1[char_num] = atoi(row[11]); + cs->hairstyle[char_num] = atoi(row[13]); + cs->beard[char_num] = atoi(row[14]); + cs->drakkin_heritage[char_num] = atoi(row[16]); + cs->drakkin_tattoo[char_num] = atoi(row[17]); + cs->drakkin_details[char_num] = atoi(row[18]); - // Populate character info - if (RunQuery(query, MakeAnyLenString(&query, "SELECT name,profile,zonename,class,level FROM character_ WHERE account_id=%i order by name limit 10", account_id), errbuf, &result)) { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - lengths = mysql_fetch_lengths(result); - //////////// - //////////// This is the current one, the other are for converting - //////////// - if ((lengths[1] == sizeof(PlayerProfile_Struct))) { - strcpy(cs->name[char_num], row[0]); - PlayerProfile_Struct* pp = (PlayerProfile_Struct*)row[1]; - uint8 clas = atoi(row[3]); - uint8 lvl = atoi(row[4]); + if (RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial))) + cs->tutorial[char_num] = 1; - // Character information - if(lvl == 0) - cs->level[char_num] = pp->level; //no level in DB, trust PP - else - cs->level[char_num] = lvl; - if(clas == 0) - cs->class_[char_num] = pp->class_; //no class in DB, trust PP - else - cs->class_[char_num] = clas; - cs->race[char_num] = pp->race; - cs->gender[char_num] = pp->gender; - cs->deity[char_num] = pp->deity; - cs->zone[char_num] = GetZoneID(row[2]); - cs->face[char_num] = pp->face; - cs->haircolor[char_num] = pp->haircolor; - cs->beardcolor[char_num] = pp->beardcolor; - cs->eyecolor2[char_num] = pp->eyecolor2; - cs->eyecolor1[char_num] = pp->eyecolor1; - cs->hairstyle[char_num] = pp->hairstyle; - cs->beard[char_num] = pp->beard; - cs->drakkin_heritage[char_num] = pp->drakkin_heritage; - cs->drakkin_tattoo[char_num] = pp->drakkin_tattoo; - cs->drakkin_details[char_num] = pp->drakkin_details; + if (RuleB(World, EnableReturnHomeButton)) { + int now = time(nullptr); + if ((now - atoi(row[8])) >= RuleI(World, MinOfflineTimeToReturnHome)) + cs->gohome[char_num] = 1; + } - if(RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial))) - cs->tutorial[char_num] = 1; + /* + This part creates home city entries for characters created before the home bind point was tracked. + Do it here because the player profile is already loaded and it's as good a spot as any. This whole block should + probably be removed at some point, when most accounts are safely converted. + */ - if(RuleB(World, EnableReturnHomeButton)) { - int now = time(nullptr); - if((now - pp->lastlogin) >= RuleI(World, MinOfflineTimeToReturnHome)) - cs->gohome[char_num] = 1; - } + /* Load Character Bind Data */ + cquery = StringFormat("SELECT zone_id, instance_id, x, y, z, heading FROM character_bind_home WHERE `id` = %i LIMIT 1", character_id); + auto results_bind = database.QueryDatabase(cquery); int r = 0; + for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { + uint8 bind_zone_id = atoi(row_b[r]); r++; + uint8 bind_instance_id = atoi(row_b[r]); r++; + uint8 bind_x_id = atoi(row_b[r]); r++; + uint8 bind_y_id = atoi(row_b[r]); r++; + uint8 bind_z_id = atoi(row_b[r]); r++; + uint8 bind_heading_id = atoi(row_b[r]); r++; + } + // if (pp->binds[4].zoneId == 0) { + // bool altered = false; + // MYSQL_RES *result2; + // MYSQL_ROW row2; + // char startzone[50] = { 0 }; + // + // // check for start zone variable (I didn't even know any variables were still being used...) + // if (database.GetVariable("startzone", startzone, 50)) { + // uint32 zoneid = database.GetZoneID(startzone); + // if (zoneid) { + // pp->binds[4].zoneId = zoneid; + // GetSafePoints(zoneid, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z); + // altered = true; + // } + // } + // else { + // RunQuery(query, + // MakeAnyLenString(&query, + // "SELECT zone_id,bind_id,x,y,z FROM start_zones " + // "WHERE player_class=%i AND player_deity=%i AND player_race=%i", + // pp->class_, + // pp->deity, + // pp->race + // ), + // errbuf, + // &result2 + // ); + // safe_delete_array(query); + // + // // if there is only one possible start city, set it + // if (mysql_num_rows(result2) == 1) { + // row2 = mysql_fetch_row(result2); + // if (atoi(row2[1]) != 0) { // if a bind_id is specified, make them start there + // pp->binds[4].zoneId = (uint32)atoi(row2[1]); + // GetSafePoints(pp->binds[4].zoneId, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z); + // } + // else { // otherwise, use the zone and coordinates given + // pp->binds[4].zoneId = (uint32)atoi(row2[0]); + // float x = atof(row2[2]); + // float y = atof(row2[3]); + // float z = atof(row2[4]); + // if (x == 0 && y == 0 && z == 0) + // GetSafePoints(pp->binds[4].zoneId, 0, &x, &y, &z); + // + // pp->binds[4].x = x; + // pp->binds[4].y = y; + // pp->binds[4].z = z; + // } + // altered = true; + // } + // + // mysql_free_result(result2); + // } + // } - // This part creates home city entries for characters created before the home bind point was tracked. - // Do it here because the player profile is already loaded and it's as good a spot as any. This whole block should - // probably be removed at some point, when most accounts are safely converted. - if(pp->binds[4].zoneId == 0) { - bool altered = false; - MYSQL_RES *result2; - MYSQL_ROW row2; - char startzone[50] = {0}; + /* + Character's equipped items + @merth: Haven't done bracer01/bracer02 yet. + Also: this needs a second look after items are a little more solid + NOTE: items don't have a color, players MAY have a tint, if the + use_tint part is set. otherwise use the regular color + */ - // check for start zone variable (I didn't even know any variables were still being used...) - if(database.GetVariable("startzone", startzone, 50)) { - uint32 zoneid = database.GetZoneID(startzone); - if(zoneid) { - pp->binds[4].zoneId = zoneid; - GetSafePoints(zoneid, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z); - altered = true; - } - } - else { - RunQuery(query, - MakeAnyLenString(&query, - "SELECT zone_id,bind_id,x,y,z FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - pp->class_, - pp->deity, - pp->race - ), - errbuf, - &result2 - ); - safe_delete_array(query); + /* Load Character Material Data for Char Select */ + cquery = StringFormat("SELECT slot, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); + auto results_b = database.QueryDatabase(cquery); uint8 slot = 0; + for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { + slot = atoi(row_b[0]); + if (atoi(row_b[1]) == 1){ pp.item_tint[slot].rgb.use_tint = 0xFF; } + pp.item_tint[slot].color = atoul(row_b[2]); + printf("charid: %u \n", character_id); + printf("slot: %u \n", slot); + printf("use_tint: %u item_tint: %u \n", pp.item_tint[slot].rgb.use_tint, atoul(row_b[2])); + cs->cs_colors[char_num][slot].color = atoul(row_b[2]); + } - // if there is only one possible start city, set it - if(mysql_num_rows(result2) == 1) { - row2 = mysql_fetch_row(result2); - if(atoi(row2[1]) != 0) { // if a bind_id is specified, make them start there - pp->binds[4].zoneId = (uint32)atoi(row2[1]); - GetSafePoints(pp->binds[4].zoneId, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z); - } - else { // otherwise, use the zone and coordinates given - pp->binds[4].zoneId = (uint32)atoi(row2[0]); - float x = atof(row2[2]); - float y = atof(row2[3]); - float z = atof(row2[4]); - if(x == 0 && y == 0 && z == 0) - GetSafePoints(pp->binds[4].zoneId, 0, &x, &y, &z); + /* Load Inventory */ + Inventory *inv; + inv = new Inventory; + if (GetInventory(account_id, cs->name[char_num], inv)) { + for (uint8 material = 0; material <= 8; material++) { + uint32 color = 0; + ItemInst *item = inv->GetItem(Inventory::CalcSlotFromMaterial(material)); + if (item == 0) + continue; - pp->binds[4].x = x; - pp->binds[4].y = y; - pp->binds[4].z = z; - } - altered = true; - } + cs->equip[char_num][material] = item->GetItem()->Material; - mysql_free_result(result2); - } + color = pp.item_tint[material].color; + // if (pp.item_tint[material].rgb.use_tint){ color = pp.item_tint[material].color; } + // else{ color = item->GetItem()->Color; } - // update the player profile - if(altered) { - uint32 char_id = GetCharacterID(cs->name[char_num]); - RunQuery(query,MakeAnyLenString(&query,"SELECT extprofile FROM character_ WHERE id=%i",char_id), errbuf, &result2); - safe_delete_array(query); - if(result2) { - row2 = mysql_fetch_row(result2); - ExtendedProfile_Struct* ext = (ExtendedProfile_Struct*)row2[0]; - // SetPlayerProfile(account_id,char_id,pp,inv,ext, 0, 0, 5); - } - mysql_free_result(result2); - } - } // end of "set start zone" block + cs->cs_colors[char_num][material].color = color; - - // Character's equipped items - // @merth: Haven't done bracer01/bracer02 yet. - // Also: this needs a second look after items are a little more solid - // NOTE: items don't have a color, players MAY have a tint, if the - // use_tint part is set. otherwise use the regular color - inv = new Inventory; - if(GetInventory(account_id, cs->name[char_num], inv)) - { - for (uint8 material = 0; material <= 8; material++) - { - uint32 color; - ItemInst *item = inv->GetItem(Inventory::CalcSlotFromMaterial(material)); - if(item == 0) - continue; - - cs->equip[char_num][material] = item->GetItem()->Material; - - if(pp->item_tint[material].rgb.use_tint) // they have a tint (LoY dye) - color = pp->item_tint[material].color; - else // no tint, use regular item color - color = item->GetItem()->Color; - - cs->cs_colors[char_num][material].color = color; - - // the weapons are kept elsewhere - if ((material==MaterialPrimary) || (material==MaterialSecondary)) - { - if(strlen(item->GetItem()->IDFile) > 2) { - uint32 idfile=atoi(&item->GetItem()->IDFile[2]); - if (material==MaterialPrimary) - cs->primary[char_num]=idfile; - else - cs->secondary[char_num]=idfile; - } - } + // the weapons are kept elsewhere + if ((material == MaterialPrimary) || (material == MaterialSecondary)) { + if (strlen(item->GetItem()->IDFile) > 2) { + uint32 idfile = atoi(&item->GetItem()->IDFile[2]); + if (material == MaterialPrimary) + cs->primary[char_num] = idfile; + else + cs->secondary[char_num] = idfile; } } - else - { - printf("Error loading inventory for %s\n", cs->name[char_num]); - } - safe_delete(inv); - if (++char_num > 10) - break; - } - else - { - std::cout << "Got a bogus character (" << row[0] << ") Ignoring!!!" << std::endl; - std::cout << "PP length ="<name[char_num]); + } + safe_delete(inv); + if (++char_num > 10) + break; } return; diff --git a/zone/client.cpp b/zone/client.cpp index 975e61dc1..6d2a784a4 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3120,6 +3120,7 @@ void Client::SetTint(int16 in_slot, uint32 color) { Color_Struct new_color; new_color.color = color; SetTint(in_slot, new_color); + database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color); } // Still need to reconcile bracer01 versus bracer02 @@ -3147,6 +3148,8 @@ void Client::SetTint(int16 in_slot, Color_Struct& color) { m_pp.item_tint[MaterialLegs].color=color.color; else if (in_slot==MainFeet) m_pp.item_tint[MaterialFeet].color=color.color; + + database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.color); } void Client::SetHideMe(bool flag) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 9921298fe..bdd08b002 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -597,6 +597,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ + database.LoadCharacterMaterial(cid, &m_pp); /* Load Character Material */ database.LoadCharacterCurrency(cid, &m_pp); /* Load Character Currency into PP */ database.LoadCharacterData(cid, &m_pp); /* Load Character Data from DB into PP */ database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); /* Move to another method when can, this is pointless... */ diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 197841c76..50b48fff9 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1877,7 +1877,9 @@ void Client::DyeArmor(DyeStruct* dye){ uint8 slot2=SlotConvert(i); ItemInst* inst = this->m_inv.GetItem(slot2); if(inst){ - inst->SetColor((dye->dye[i].rgb.red*65536)+(dye->dye[i].rgb.green*256)+(dye->dye[i].rgb.blue)); + uint32 armor_color = (dye->dye[i].rgb.red * 65536) + (dye->dye[i].rgb.green * 256) + (dye->dye[i].rgb.blue); + inst->SetColor(armor_color); + database.SaveCharacterMaterialColor(this->CharacterID(), slot2, armor_color); database.SaveInventory(CharacterID(),inst,slot2); if(dye->dye[i].rgb.use_tint) m_pp.item_tint[i].rgb.use_tint = 0xFF; @@ -1898,7 +1900,7 @@ void Client::DyeArmor(DyeStruct* dye){ EQApplicationPacket* outapp=new EQApplicationPacket(OP_Dye,0); QueuePacket(outapp); safe_delete(outapp); - Save(); + } /*bool Client::DecreaseByItemType(uint32 type, uint8 amt) { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 09aa26bc8..b998733c3 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1145,6 +1145,28 @@ bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Str return true; } +bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color){ + std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, color) VALUES (%u, %u, %u)", character_id, slot_id, color); QueryDatabase(query); + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterMaterialColor for character ID: %i, slot_id: %u color: %u done", character_id, slot_id, color); + return true; +} + +bool ZoneDatabase::LoadCharacterMaterial(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT slot, blue, green, red, use_tint, color FROM `character_material` WHERE `id` = %u LIMIT 9", character_id); + auto results = database.QueryDatabase(query); int i = 0; int r = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + r = 0; + i = atoi(row[r]); /* Slot */ r++; + pp->item_tint[i].rgb.blue = atoi(row[r]); r++; + pp->item_tint[i].rgb.green = atoi(row[r]); r++; + pp->item_tint[i].rgb.red = atoi(row[r]); r++; + if (row[r] && atoi(row[r]) > 0){ pp->item_tint[i].rgb.use_tint = 0xFF; } r++; + pp->item_tint[i].color = atoi(row[r]); r++; + printf("Loading color: %u tint: %u \n", pp->item_tint[i].color, pp->item_tint[i].rgb.use_tint); + } + return true; +} + bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp){ clock_t t = std::clock(); /* Function timer start */ if (pp->tribute_time_remaining < 0 || pp->tribute_time_remaining == 4294967295){ pp->tribute_time_remaining = 0; } diff --git a/zone/zonedb.h b/zone/zonedb.h index 87aefd4cf..d98749f5e 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -266,6 +266,7 @@ public: bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterMaterial(uint32 character_id, PlayerProfile_Struct* pp); /* Character Data Saves */ bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); @@ -274,6 +275,7 @@ public: bool SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot); bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); /* Character Data Deletes */ bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); From e0a99730e5ac1fafe3b4aee13c186ef83662bcdb Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 31 Aug 2014 21:58:04 -0500 Subject: [PATCH 008/368] pp revert --- common/database.cpp | 2 +- common/eq_packet_structs.h | 4 +-- world/worlddb.cpp | 64 ++------------------------------------ zone/client_packet.cpp | 2 +- zone/inventory.cpp | 4 +-- zone/zonedb.cpp | 4 +-- zone/zonedb.h | 2 +- 7 files changed, 11 insertions(+), 71 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 1058cc5e1..cd5693e89 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1130,7 +1130,7 @@ bool Database::CheckDatabaseConversions() { /* Verify PP Integrity */ lengths = mysql_fetch_lengths(result2); - if (lengths[1] > 0) { + if (lengths[1] == sizeof(PlayerProfile_Struct)) { memcpy(pp, row2[1], sizeof(PlayerProfile_Struct)); // printf("FINE: Player profile '%s' %i length Expected: %i, Got: %i \n", row2[2], atoi(row2[3]), sizeof(PlayerProfile_Struct), lengths[1]); } diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index d6ffd119b..c561e3a22 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -801,8 +801,8 @@ struct SuspendedMinion_Struct ** OpCode: 0x006a */ static const uint32 MAX_PP_LANGUAGE = 28; -static const uint32 MAX_PP_SPELLBOOK = 720; // Increased to 480 to support SoF -static const uint32 MAX_PP_MEMSPELL = 12; +static const uint32 MAX_PP_SPELLBOOK = 480; // Increased to 480 to support SoF +static const uint32 MAX_PP_MEMSPELL = 9; static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 240; static const uint32 MAX_GROUP_MEMBERS = 6; diff --git a/world/worlddb.cpp b/world/worlddb.cpp index b07480031..81c99d022 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -124,61 +124,6 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* uint8 bind_heading_id = atoi(row_b[r]); r++; } - // if (pp->binds[4].zoneId == 0) { - // bool altered = false; - // MYSQL_RES *result2; - // MYSQL_ROW row2; - // char startzone[50] = { 0 }; - // - // // check for start zone variable (I didn't even know any variables were still being used...) - // if (database.GetVariable("startzone", startzone, 50)) { - // uint32 zoneid = database.GetZoneID(startzone); - // if (zoneid) { - // pp->binds[4].zoneId = zoneid; - // GetSafePoints(zoneid, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z); - // altered = true; - // } - // } - // else { - // RunQuery(query, - // MakeAnyLenString(&query, - // "SELECT zone_id,bind_id,x,y,z FROM start_zones " - // "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - // pp->class_, - // pp->deity, - // pp->race - // ), - // errbuf, - // &result2 - // ); - // safe_delete_array(query); - // - // // if there is only one possible start city, set it - // if (mysql_num_rows(result2) == 1) { - // row2 = mysql_fetch_row(result2); - // if (atoi(row2[1]) != 0) { // if a bind_id is specified, make them start there - // pp->binds[4].zoneId = (uint32)atoi(row2[1]); - // GetSafePoints(pp->binds[4].zoneId, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z); - // } - // else { // otherwise, use the zone and coordinates given - // pp->binds[4].zoneId = (uint32)atoi(row2[0]); - // float x = atof(row2[2]); - // float y = atof(row2[3]); - // float z = atof(row2[4]); - // if (x == 0 && y == 0 && z == 0) - // GetSafePoints(pp->binds[4].zoneId, 0, &x, &y, &z); - // - // pp->binds[4].x = x; - // pp->binds[4].y = y; - // pp->binds[4].z = z; - // } - // altered = true; - // } - // - // mysql_free_result(result2); - // } - // } - /* Character's equipped items @merth: Haven't done bracer01/bracer02 yet. @@ -194,10 +139,6 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* slot = atoi(row_b[0]); if (atoi(row_b[1]) == 1){ pp.item_tint[slot].rgb.use_tint = 0xFF; } pp.item_tint[slot].color = atoul(row_b[2]); - printf("charid: %u \n", character_id); - printf("slot: %u \n", slot); - printf("use_tint: %u item_tint: %u \n", pp.item_tint[slot].rgb.use_tint, atoul(row_b[2])); - cs->cs_colors[char_num][slot].color = atoul(row_b[2]); } /* Load Inventory */ @@ -212,9 +153,8 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs->equip[char_num][material] = item->GetItem()->Material; - color = pp.item_tint[material].color; - // if (pp.item_tint[material].rgb.use_tint){ color = pp.item_tint[material].color; } - // else{ color = item->GetItem()->Color; } + if (pp.item_tint[material].rgb.use_tint){ color = pp.item_tint[material].color; } + else{ color = item->GetItem()->Color; } cs->cs_colors[char_num][material].color = color; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index bdd08b002..88425fade 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -597,7 +597,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ - database.LoadCharacterMaterial(cid, &m_pp); /* Load Character Material */ + database.LoadCharacterMaterialColor(cid, &m_pp); /* Load Character Material */ database.LoadCharacterCurrency(cid, &m_pp); /* Load Character Currency into PP */ database.LoadCharacterData(cid, &m_pp); /* Load Character Data from DB into PP */ database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); /* Move to another method when can, this is pointless... */ diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 50b48fff9..02cf6e568 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1878,8 +1878,8 @@ void Client::DyeArmor(DyeStruct* dye){ ItemInst* inst = this->m_inv.GetItem(slot2); if(inst){ uint32 armor_color = (dye->dye[i].rgb.red * 65536) + (dye->dye[i].rgb.green * 256) + (dye->dye[i].rgb.blue); - inst->SetColor(armor_color); - database.SaveCharacterMaterialColor(this->CharacterID(), slot2, armor_color); + inst->SetColor(armor_color); + database.SaveCharacterMaterialColor(this->CharacterID(), i, armor_color); database.SaveInventory(CharacterID(),inst,slot2); if(dye->dye[i].rgb.use_tint) m_pp.item_tint[i].rgb.use_tint = 0xFF; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index b998733c3..50a74c892 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1151,9 +1151,9 @@ bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_i return true; } -bool ZoneDatabase::LoadCharacterMaterial(uint32 character_id, PlayerProfile_Struct* pp){ +bool ZoneDatabase::LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat("SELECT slot, blue, green, red, use_tint, color FROM `character_material` WHERE `id` = %u LIMIT 9", character_id); - auto results = database.QueryDatabase(query); int i = 0; int r = 0; + auto results = database.QueryDatabase(query); int i = 0; int r = 0; for (auto row = results.begin(); row != results.end(); ++row) { r = 0; i = atoi(row[r]); /* Slot */ r++; diff --git a/zone/zonedb.h b/zone/zonedb.h index d98749f5e..705377a39 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -266,7 +266,7 @@ public: bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterMaterial(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp); /* Character Data Saves */ bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); From e0db3c0b60aa049ee8e74e2c08916eca21cd8b2c Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 1 Sep 2014 22:17:06 -0500 Subject: [PATCH 009/368] Fixed Character select to be loaded from new character data tables --- common/database.cpp | 250 ++++++++++++++++++++--------------------- common/shareddb.cpp | 40 +++++++ common/shareddb.h | 3 + world/worlddb.cpp | 25 +++-- zone/client.cpp | 52 ++++----- zone/client_packet.cpp | 15 ++- zone/zonedb.cpp | 17 +-- 7 files changed, 218 insertions(+), 184 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index cd5693e89..d926cb950 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -798,7 +798,7 @@ bool Database::CheckDatabaseConversions() { int character_id = 0; int account_id = 0; int number_of_characters = 0; - int printppdebug = 0; + int printppdebug = 0; /* Prints Player Profile */ int runconvert = 0; /* Check For Legacy Storage Method */ @@ -842,7 +842,7 @@ bool Database::CheckDatabaseConversions() { " `race` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " " `class` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " " `level` int(11) UNSIGNED NOT NULL DEFAULT 0, " - " `deity` tinyint(11) UNSIGNED NOT NULL, " + " `deity` int(11) UNSIGNED NOT NULL, " " `birthday` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `last_login` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `time_played` int(11) UNSIGNED NOT NULL DEFAULT 0, " @@ -886,9 +886,9 @@ bool Database::CheckDatabaseConversions() { " `drakkin_heritage` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `drakkin_tattoo` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `drakkin_details` int(11) UNSIGNED NOT NULL DEFAULT 0, " - " `toxicity` int(11) NOT NULL DEFAULT 0, " - " `hunger_level` int(11) NOT NULL DEFAULT 0, " - " `thirst_level` int(11) NOT NULL DEFAULT 0, " + " `toxicity` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `hunger_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `thirst_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `ability_up` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `zone_id` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `zone_instance` int(11) UNSIGNED NOT NULL DEFAULT 0, " @@ -1113,9 +1113,9 @@ bool Database::CheckDatabaseConversions() { printf("Starting conversion...\n\n"); } - // querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); + // querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); // WHERE `account_id` = 11001 int char_iter_count = 0; - querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` >= 61238 LIMIT 10"); + querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `account_id` = 11001"); if (RunQuery(query, querylen, errbuf, &result)) { safe_delete_array(query); while (row = mysql_fetch_row(result)) { @@ -1176,96 +1176,96 @@ bool Database::CheckDatabaseConversions() { /* Run Character Data Convert */ rquery = StringFormat( "REPLACE INTO `character_data` (" - " id, " - " account_id, " - " `name`, " - " last_name, " - " gender, " - " race, " - " class, " - " `level`, " - " deity, " - " birthday, " - " last_login, " - " time_played, " - " pvp_status, " - " level2, " - " anon, " - " gm, " - " intoxication, " - " hair_color, " - " beard_color, " - " eye_color_1, " - " eye_color_2, " - " hair_style, " - " beard, " - " ability_time_seconds, " - " ability_number, " - " ability_time_minutes, " - " ability_time_hours, " - " title, " - " suffix, " - " exp, " - " points, " - " mana, " - " cur_hp, " - " str, " - " sta, " - " cha, " - " dex, " - " `int`, " - " agi, " - " wis, " - " face, " - " y, " - " x, " - " z, " - " heading, " - " pvp2, " - " pvp_type, " - " autosplit_enabled, " - " zone_change_count, " - " drakkin_heritage, " - " drakkin_tattoo, " - " drakkin_details, " - " toxicity, " - " hunger_level, " - " thirst_level, " - " ability_up, " - " zone_id, " - " zone_instance, " - " leadership_exp_on, " - " ldon_points_guk, " - " ldon_points_mir, " - " ldon_points_mmc, " - " ldon_points_ruj, " - " ldon_points_tak, " - " ldon_points_available, " - " tribute_time_remaining, " - " show_helm, " - " career_tribute_points, " - " tribute_points, " - " tribute_active, " - " endurance, " - " group_leadership_exp, " - " raid_leadership_exp, " - " group_leadership_points, " - " raid_leadership_points, " - " air_remaining, " - " pvp_kills, " - " pvp_deaths, " - " pvp_current_points, " - " pvp_career_points, " - " pvp_best_kill_streak, " - " pvp_worst_death_streak, " - " pvp_current_kill_streak, " - " aa_points_spent, " - " aa_exp, " - " aa_points, " - " group_auto_consent, " - " raid_auto_consent, " - " guild_auto_consent, " - " RestTimer) " + "id," + "account_id," + "`name`," + "last_name," + "gender," + "race," + "class," + "`level`," + "deity," + "birthday," + "last_login," + "time_played," + "pvp_status," + "level2," + "anon," + "gm," + "intoxication," + "hair_color," + "beard_color," + "eye_color_1," + "eye_color_2," + "hair_style," + "beard," + "ability_time_seconds," + "ability_number," + "ability_time_minutes," + "ability_time_hours," + "title," + "suffix," + "exp," + "points," + "mana," + "cur_hp," + "str," + "sta," + "cha," + "dex," + "`int`," + "agi," + "wis," + "face," + "y," + "x," + "z," + "heading," + "pvp2," + "pvp_type," + "autosplit_enabled," + "zone_change_count," + "drakkin_heritage," + "drakkin_tattoo," + "drakkin_details," + "toxicity," + "hunger_level," + "thirst_level," + "ability_up," + "zone_id," + "zone_instance," + "leadership_exp_on," + "ldon_points_guk," + "ldon_points_mir," + "ldon_points_mmc," + "ldon_points_ruj," + "ldon_points_tak," + "ldon_points_available," + "tribute_time_remaining," + "show_helm," + "career_tribute_points," + "tribute_points," + "tribute_active," + "endurance," + "group_leadership_exp," + "raid_leadership_exp," + "group_leadership_points," + "raid_leadership_points," + "air_remaining," + "pvp_kills," + "pvp_deaths," + "pvp_current_points," + "pvp_career_points," + "pvp_best_kill_streak," + "pvp_worst_death_streak," + "pvp_current_kill_streak," + "aa_points_spent," + "aa_exp," + "aa_points," + "group_auto_consent," + "raid_auto_consent," + "guild_auto_consent," + "RestTimer)" "VALUES (" "%u," // id "%u," // account_id @@ -1450,6 +1450,7 @@ bool Database::CheckDatabaseConversions() { pp->RestTimer ); results = QueryDatabase(rquery); + if (!results.RowsAffected()){ std::cout << "ERROR PP Data Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run AA Convert */ for (i = 0; i < MAX_PP_AA_ARRAY; i++){ @@ -1457,21 +1458,21 @@ bool Database::CheckDatabaseConversions() { rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" " VALUES (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); - QueryDatabase(rquery); + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR AA Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } /* Run Bind Home Convert */ rquery = StringFormat("REPLACE INTO `character_bind_home` (id, zone_id, instance_id, x, y, z, heading)" " VALUES (%u, %u, %u, %f, %f, %f, %f)", - character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); - QueryDatabase(rquery); + character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Language Convert */ for (i = 0; i < MAX_PP_LANGUAGE; i++){ if (pp->languages[i] > 0){ rquery = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, i, pp->languages[i]); - QueryDatabase(rquery); + QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Language Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } @@ -1479,7 +1480,7 @@ bool Database::CheckDatabaseConversions() { for (i = 0; i < MAX_PP_SKILL; i++){ if (pp->skills[i] > 0){ rquery = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, i, pp->skills[i]); - QueryDatabase(rquery); + QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Skill Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } @@ -1487,7 +1488,7 @@ bool Database::CheckDatabaseConversions() { for (i = 0; i < MAX_PP_SPELLBOOK; i++){ if (pp->spell_book[i] > 0 && pp->spell_book[i] != 4294967295){ rquery = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->spell_book[i]); - QueryDatabase(rquery); + QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } @@ -1495,7 +1496,7 @@ bool Database::CheckDatabaseConversions() { for (i = 0; i < MAX_PP_MEMSPELL; i++){ if (pp->mem_spells[i] > 0){ rquery = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->mem_spells[i]); - QueryDatabase(rquery); + QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Memmed Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } @@ -1503,16 +1504,16 @@ bool Database::CheckDatabaseConversions() { for (i = 0; i < MAX_PP_DISCIPLINES; i++){ if (pp->disciplines.values[i] > 0){ rquery = StringFormat("REPLACE INTO `character_disciplines` (id, disc_id) VALUES (%u, %u)", character_id, pp->disciplines.values[i]); - QueryDatabase(rquery); + QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Discipline Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } /* Run Material Color Convert */ for (i = 0; i < _MaterialCount; i++){ if (pp->item_tint[i].color > 0){ - if (pp->item_tint[i].rgb.use_tint > 0){ pp->item_tint[i].rgb.use_tint = 1; } rquery = StringFormat("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); - QueryDatabase(rquery); - } + // printf("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u);\n", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); + QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Color Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + } } } @@ -2568,13 +2569,13 @@ uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 return base_cap; } -uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZoneID, uint32* oInstanceID, float* oX, float* oY, float* oZ) { - - std::string query = StringFormat("SELECT id, account_id, zonename, instanceid, x, y, z FROM character_ WHERE name='%s'", iName); +uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZoneID, uint32* oInstanceID, float* oX, float* oY, float* oZ) { + std::string query = StringFormat("SELECT `id`, `account_id`, `zone_id`, `zone_instance`, `x`, `y`, `z` FROM `character_data` WHERE `name` = '%s'", iName); auto results = QueryDatabase(query); - if (!results.Success()) - { + printf("Calling Database::GetCharacterInfo: ACCID: %u ZONEID: %u INSTANCEID: %u \n", oAccID, oZoneID, oInstanceID); + + if (!results.Success()) { std::cerr << "Error in GetCharacterInfo query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -2582,21 +2583,16 @@ uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZo if (results.RowCount() != 1) return 0; - auto row = results.begin(); - + auto row = results.begin(); uint32 charid = atoi(row[0]); - if (oAccID) - *oAccID = atoi(row[1]); - if (oZoneID) - *oZoneID = GetZoneID(row[2]); - if(oInstanceID) - *oInstanceID = atoi(row[3]); - if (oX) - *oX = atof(row[4]); - if (oY) - *oY = atof(row[5]); - if (oZ) - *oZ = atof(row[6]); + if (oAccID){ *oAccID = atoi(row[1]); } + if (oZoneID){ *oZoneID = atoi(row[2]); } + if (oInstanceID){ *oInstanceID = atoi(row[3]); } + if (oX){ *oX = atof(row[4]); } + if (oY){ *oY = atof(row[5]); } + if (oZ){ *oZ = atof(row[6]); } + + printf("Calling Database::GetCharacterInfo: ACCID: %u ZONEID: %u INSTANCEID: %u \n", oAccID, oZoneID, oInstanceID); return charid; } diff --git a/common/shareddb.cpp b/common/shareddb.cpp index e4336b02f..9f234b4ae 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -61,6 +61,46 @@ bool SharedDatabase::SetHideMe(uint32 account_id, uint8 hideme) return true; } +bool SharedDatabase::SetPlayerProfile(uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + uint32 affected_rows = 0; + bool ret = false; + + if (RunQuery(query, SetPlayerProfile_MQ(&query, account_id, charid, pp, inv, ext, current_zone, current_instance, MaxXTargets), errbuf, 0, &affected_rows)) { + ret = (affected_rows != 0); + } + + if (!ret) { + LogFile->write(EQEMuLog::Error, "SetPlayerProfile query '%s' %s", query, errbuf); + } + + safe_delete_array(query); + return ret; +} + +// Generate SQL for updating player profile +uint32 SharedDatabase::SetPlayerProfile_MQ(char** query, uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { + *query = new char[396 + sizeof(PlayerProfile_Struct)* 2 + sizeof(ExtendedProfile_Struct)* 2 + 4]; + char* end = *query; + if (!current_zone) + current_zone = pp->zone_id; + + if (!current_instance) + current_instance = pp->zoneInstance; + + if (strlen(pp->name) == 0) // Sanity check in case pp never loaded + return false; + + end += sprintf(end, "UPDATE character_ SET timelaston=unix_timestamp(now()),name=\'%s\', zonename=\'%s\', zoneid=%u, instanceid=%u, x = %f, y = %f, z = %f, ", pp->name, "halas", 29, current_instance, pp->x, pp->y, pp->z); + // end += DoEscapeString(end, (char*)pp, sizeof(PlayerProfile_Struct)); + end += sprintf(end, " extprofile=\'"); + end += DoEscapeString(end, (char*)ext, sizeof(ExtendedProfile_Struct)); + end += sprintf(end, "\',class=%d,level=%d,xtargets=%u WHERE id=%u", pp->class_, pp->level, MaxXTargets, charid); + + return (uint32)(end - (*query)); +} + uint8 SharedDatabase::GetGMSpeed(uint32 account_id) { diff --git a/common/shareddb.h b/common/shareddb.h index 5401bfa0a..c55163465 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -34,6 +34,9 @@ public: SharedDatabase(const char* host, const char* user, const char* passwd, const char* database,uint32 port); virtual ~SharedDatabase(); + /* Temp */ + uint32 SetPlayerProfile_MQ(char** query, uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets); + bool SetPlayerProfile(uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets); /* * General Character Related Stuff */ diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 81c99d022..deccc9207 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -35,9 +35,7 @@ extern std::vector character_create_race_class_combos; // solar: the current stuff is at the bottom of this function void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs) { - /* Initialize Player Profile for the small time it is being used for item material */ - PlayerProfile_Struct pp; - memset(&pp, 0, sizeof(PlayerProfile_Struct)); + Inventory *inv; /* Initialize Variables */ for (int i=0; i<10; i++) { @@ -73,10 +71,12 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* "zone_id " // 19 "FROM " "character_data " - "WHERE `account_id` = %i LIMIT 10 ", account_id); - auto results = database.QueryDatabase(cquery); uint16 char_num = 0; + "WHERE `account_id` = %i ORDER BY `name` LIMIT 10 ", account_id); + auto results = database.QueryDatabase(cquery); int char_num = 0; for (auto row = results.begin(); row != results.end(); ++row) { - printf("id is %i \n", atoi(row[0])); + PlayerProfile_Struct pp; + memset(&pp, 0, sizeof(PlayerProfile_Struct)); + uint32 character_id = atoi(row[0]); strcpy(cs->name[char_num], row[1]); uint8 lvl = atoi(row[5]); @@ -133,16 +133,17 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* */ /* Load Character Material Data for Char Select */ - cquery = StringFormat("SELECT slot, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); + cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); auto results_b = database.QueryDatabase(cquery); uint8 slot = 0; for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { slot = atoi(row_b[0]); - if (atoi(row_b[1]) == 1){ pp.item_tint[slot].rgb.use_tint = 0xFF; } - pp.item_tint[slot].color = atoul(row_b[2]); + pp.item_tint[slot].rgb.red = atoi(row_b[1]); + pp.item_tint[slot].rgb.green = atoi(row_b[2]); + pp.item_tint[slot].rgb.blue = atoi(row_b[3]); + pp.item_tint[slot].rgb.use_tint = atoi(row_b[4]); } /* Load Inventory */ - Inventory *inv; inv = new Inventory; if (GetInventory(account_id, cs->name[char_num], inv)) { for (uint8 material = 0; material <= 8; material++) { @@ -156,9 +157,9 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* if (pp.item_tint[material].rgb.use_tint){ color = pp.item_tint[material].color; } else{ color = item->GetItem()->Color; } - cs->cs_colors[char_num][material].color = color; + cs->cs_colors[char_num][material].color = color; - // the weapons are kept elsewhere + /* Weapons are handled a bit differently */ if ((material == MaterialPrimary) || (material == MaterialSecondary)) { if (strlen(item->GetItem()->IDFile) > 2) { uint32 idfile = atoi(&item->GetItem()->IDFile[2]); diff --git a/zone/client.cpp b/zone/client.cpp index 6d2a784a4..2213c0a26 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -588,35 +588,35 @@ bool Client::Save(uint8 iCommitNow) { /* Save Character Task */ SaveTaskState(); + if (iCommitNow <= 1) { + // char* query = 0; + // uint32_breakdown workpt; + // workpt.b4() = DBA_b4_Entity; + // workpt.w2_3() = GetID(); + // workpt.b1() = DBA_b1_Entity_Client_Save; + // DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Write, 0xFFFFFFFF); + // dbaw->AddQuery(iCommitNow == 0 ? true : false, &query, database.SetPlayerProfile_MQ(&query, account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets), false); + // if (iCommitNow == 0){ + // pQueuedSaveWorkID = dbasync->AddWork(&dbaw, 2500); + // } + // else { + // dbasync->AddWork(&dbaw, 0); + // SaveBackup(); + // } + // safe_delete_array(query); + // return true; + } + else if (database.SetPlayerProfile(account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets)) { + SaveBackup(); + } + else { + std::cerr << "Failed to update player profile" << std::endl; + return false; + } + /* Save Character Data */ database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp); - // if (iCommitNow <= 1) { - // char* query = 0; - // uint32_breakdown workpt; - // workpt.b4() = DBA_b4_Entity; - // workpt.w2_3() = GetID(); - // workpt.b1() = DBA_b1_Entity_Client_Save; - // DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Write, 0xFFFFFFFF); - // dbaw->AddQuery(iCommitNow == 0 ? true : false, &query, database.SetPlayerProfile_MQ(&query, account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets), false); - // if (iCommitNow == 0){ - // pQueuedSaveWorkID = dbasync->AddWork(&dbaw, 2500); - // } - // else { - // dbasync->AddWork(&dbaw, 0); - // SaveBackup(); - // } - // safe_delete_array(query); - // return true; - // } - // else if (database.SetPlayerProfile(account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets)) { - // SaveBackup(); - // } - // else { - // std::cerr << "Failed to update player profile" << std::endl; - // return false; - // } - /* Mirror Character Data */ database.StoreCharacterLookup(this->CharacterID()); LogFile->write(EQEMuLog::Status, "Client::Save %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 88425fade..87d884eb7 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -553,7 +553,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_pp.item_tint[i].rgb.use_tint = 0xFF; uint32 cid = CharacterID(); - character_id = cid; + character_id = cid; /* Global character_id reference */ /* Flush and reload factions */ database.RemoveTempFactions(this); @@ -588,8 +588,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (RuleB(Character, SharedBankPlat)) m_pp.platinum_shared = database.GetSharedPlatinum(database.GetAccountIDByChar(cid)); - // if (ext) { SetExtendedProfile(ext, row[8], lengths[8]); } - if (level){ level = atoi(row[10]); } if (LFP){ LFP = atoi(row[11]); } if (LFG){ LFG = atoi(row[12]); } if (firstlogon){ firstlogon = atoi(row[15]); } @@ -608,6 +606,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */ database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */ + if (level){ level = m_pp.level; } + /* If GM, not trackable */ if (gmhideme) { trackable = false; } /* Set Con State for Reporting */ @@ -632,9 +632,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_pp.z = zone->safe_z(); } /* If too far below ground, then fix */ - float ground_z = GetGroundZ(m_pp.x, m_pp.y, m_pp.z); - if (m_pp.z < (ground_z - 500)) - m_pp.z = ground_z; + // float ground_z = GetGroundZ(m_pp.x, m_pp.y, m_pp.z); + // if (m_pp.z < (ground_z - 500)) + // m_pp.z = ground_z; /* Set Mob variables for spawn */ class_ = m_pp.class_; @@ -1019,8 +1019,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) outapp = new EQApplicationPacket(OP_Weather, 12); Weather_Struct *ws = (Weather_Struct *)outapp->pBuffer; ws->val1 = 0x000000FF; - if (zone->zone_weather == 1) - ws->type = 0x31; // Rain + if (zone->zone_weather == 1){ ws->type = 0x31; } // Rain if (zone->zone_weather == 2) { outapp->pBuffer[8] = 0x01; ws->type = 0x02; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 50a74c892..794b1240f 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1146,7 +1146,7 @@ bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Str } bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color){ - std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, color) VALUES (%u, %u, %u)", character_id, slot_id, color); QueryDatabase(query); + std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, color, use_tint) VALUES (%u, %u, %u, 255)", character_id, slot_id, color); QueryDatabase(query); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterMaterialColor for character ID: %i, slot_id: %u color: %u done", character_id, slot_id, color); return true; } @@ -1160,9 +1160,8 @@ bool ZoneDatabase::LoadCharacterMaterialColor(uint32 character_id, PlayerProfile pp->item_tint[i].rgb.blue = atoi(row[r]); r++; pp->item_tint[i].rgb.green = atoi(row[r]); r++; pp->item_tint[i].rgb.red = atoi(row[r]); r++; - if (row[r] && atoi(row[r]) > 0){ pp->item_tint[i].rgb.use_tint = 0xFF; } r++; - pp->item_tint[i].color = atoi(row[r]); r++; - printf("Loading color: %u tint: %u \n", pp->item_tint[i].color, pp->item_tint[i].rgb.use_tint); + pp->item_tint[i].rgb.use_tint = atoi(row[r]); + printf("Material Load: %u %u %u %u\n", pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint); } return true; } @@ -1445,13 +1444,9 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla pp->guildAutoconsent, pp->RestTimer ); - auto results = database.QueryDatabase(query); - //if (results.RowsAffected() != 2) { - // LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::SaveCharacterData Error! Query: %s \n", query); return false; - //} - //else{ - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); - //} + auto results = database.QueryDatabase(query); + if (!results.RowsAffected()){ std::cout << "ERROR ZoneDatabase:SaveCharacterData: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; } From 0a9222e1ee39791f5de1d3b3c5db8b33201cdee5 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 1 Sep 2014 23:54:15 -0500 Subject: [PATCH 010/368] - --- common/shareddb.cpp | 2 +- world/zoneserver.cpp | 3 +++ zone/client.cpp | 50 ++++++++++++++++++++++---------------------- zone/mob.cpp | 1 + zone/worldserver.cpp | 2 +- zone/zoning.cpp | 22 +++++++++++++++++++ 6 files changed, 53 insertions(+), 27 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 9f234b4ae..6124d37f1 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -92,7 +92,7 @@ uint32 SharedDatabase::SetPlayerProfile_MQ(char** query, uint32 account_id, uint if (strlen(pp->name) == 0) // Sanity check in case pp never loaded return false; - end += sprintf(end, "UPDATE character_ SET timelaston=unix_timestamp(now()),name=\'%s\', zonename=\'%s\', zoneid=%u, instanceid=%u, x = %f, y = %f, z = %f, ", pp->name, "halas", 29, current_instance, pp->x, pp->y, pp->z); + end += sprintf(end, "UPDATE character_ SET timelaston=unix_timestamp(now()),name=\'%s\', zonename=\'%s\', zoneid=%u, instanceid=%u, x = %f, y = %f, z = %f, ", pp->name, GetZoneName(current_zone), current_zone, current_instance, pp->x, pp->y, pp->z); // end += DoEscapeString(end, (char*)pp, sizeof(PlayerProfile_Struct)); end += sprintf(end, " extprofile=\'"); end += DoEscapeString(end, (char*)ext, sizeof(ExtendedProfile_Struct)); diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 7c4f4f963..af0752a95 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -675,6 +675,9 @@ bool ZoneServer::Process() { zlog(WORLD__ZONE,"ZoneToZone request for %s current zone %d req zone %d\n", ztz->name, ztz->current_zone_id, ztz->requested_zone_id); + printf("\n\n ZoneToZone request for %s current zone %d req zone %d\n\n", + ztz->name, ztz->current_zone_id, ztz->requested_zone_id); + if(GetZoneID() == ztz->current_zone_id && GetInstanceID() == ztz->current_instance_id) // this is a request from the egress zone { zlog(WORLD__ZONE,"Processing ZTZ for egress from zone for client %s\n", ztz->name); diff --git a/zone/client.cpp b/zone/client.cpp index 2213c0a26..c56d47b4d 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -588,31 +588,31 @@ bool Client::Save(uint8 iCommitNow) { /* Save Character Task */ SaveTaskState(); - if (iCommitNow <= 1) { - // char* query = 0; - // uint32_breakdown workpt; - // workpt.b4() = DBA_b4_Entity; - // workpt.w2_3() = GetID(); - // workpt.b1() = DBA_b1_Entity_Client_Save; - // DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Write, 0xFFFFFFFF); - // dbaw->AddQuery(iCommitNow == 0 ? true : false, &query, database.SetPlayerProfile_MQ(&query, account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets), false); - // if (iCommitNow == 0){ - // pQueuedSaveWorkID = dbasync->AddWork(&dbaw, 2500); - // } - // else { - // dbasync->AddWork(&dbaw, 0); - // SaveBackup(); - // } - // safe_delete_array(query); - // return true; - } - else if (database.SetPlayerProfile(account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets)) { - SaveBackup(); - } - else { - std::cerr << "Failed to update player profile" << std::endl; - return false; - } + // if (iCommitNow <= 1) { + // // char* query = 0; + // // uint32_breakdown workpt; + // // workpt.b4() = DBA_b4_Entity; + // // workpt.w2_3() = GetID(); + // // workpt.b1() = DBA_b1_Entity_Client_Save; + // // DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Write, 0xFFFFFFFF); + // // dbaw->AddQuery(iCommitNow == 0 ? true : false, &query, database.SetPlayerProfile_MQ(&query, account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets), false); + // // if (iCommitNow == 0){ + // // pQueuedSaveWorkID = dbasync->AddWork(&dbaw, 2500); + // // } + // // else { + // // dbasync->AddWork(&dbaw, 0); + // // SaveBackup(); + // // } + // // safe_delete_array(query); + // // return true; + // } + // else if (database.SetPlayerProfile(account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets)) { + // SaveBackup(); + // } + // else { + // std::cerr << "Failed to update player profile" << std::endl; + // return false; + // } /* Save Character Data */ database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp); diff --git a/zone/mob.cpp b/zone/mob.cpp index 0d7002253..9b4e3f53e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1931,6 +1931,7 @@ void Mob::SetZone(uint32 zone_id, uint32 instance_id) { CastToClient()->GetPP().zone_id = zone_id; CastToClient()->GetPP().zoneInstance = instance_id; + CastToClient()->Save(); } Save(); } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 791a34ee4..152ed4f0d 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -322,7 +322,7 @@ void WorldServer::Process() { entity->CastToMob()->SetZone(ztz->requested_zone_id, ztz->requested_instance_id); - if(ztz->ignorerestrictions == 3) + if(ztz->ignorerestrictions == 3) entity->CastToClient()->GoToSafeCoords(ztz->requested_zone_id, ztz->requested_instance_id); } diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 1b64fef3c..88351ee3e 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -50,6 +50,10 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { #endif ZoneChange_Struct* zc=(ZoneChange_Struct*)app->pBuffer; + printf("INCOMING CLIENT\n\n"); + printf("%s\n", zc->char_name); + printf("%u\n", zc->zoneID); + uint16 target_zone_id = 0; uint16 target_instance_id = zc->instanceID; ZonePoint* zone_point = nullptr; @@ -368,6 +372,8 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc outapp->priority = 6; FastQueuePacket(&outapp); + printf("INTERZONE PROCESS\n"); + zone->StartShutdownTimer(AUTHENTICATION_TIMEOUT * 1000); } else { // vesuvias - zoneing to another zone so we need to the let the world server @@ -384,6 +390,17 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc strcpy(ztz->name, GetName()); ztz->guild_id = GuildID(); worldserver.SendPacket(pack); + + printf("ZONING REQUEST TO WORLD\n"); + printf("ztz->response %u \n", ztz->response); + printf("ztz->current_zone_id %u \n", ztz->current_zone_id); + printf("ztz->current_instance_id %u \n", ztz->current_instance_id); + printf("ztz->requested_zone_id %u \n", ztz->requested_zone_id); + printf("ztz->requested_instance_id %u \n", ztz->requested_instance_id); + printf("ztz->admin %u \n", ztz->admin); + printf("ztz->ignorerestrictions %u \n", ztz->ignorerestrictions); + printf("ztz->guild_id %u \n", ztz->guild_id); + safe_delete(pack); } @@ -551,6 +568,7 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z if(ReadyToZone) { zone_mode = zm; + printf("\n\n ZONE MODE %u \n\n", zm); if(zm == ZoneToBindPoint) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + iZoneNameLength); ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer; @@ -584,6 +602,10 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z gmg->instance_id = instance_id; gmg->type = 0x01; //an observed value, not sure of meaning + printf("gmg->zone_id %u \n", gmg->zone_id); + printf("gmg->x %u \n", gmg->x); + printf("gmg->y %u \n", gmg->y); + outapp->priority = 6; FastQueuePacket(&outapp); safe_delete(outapp); From 118c2a9db97731ecb7603917991b77d287b36ec3 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 13:49:04 -0700 Subject: [PATCH 011/368] LoadAAs converted to QueryDatabase --- zone/bot.cpp | 224 ++++++++++++++++++++++++--------------------------- 1 file changed, 107 insertions(+), 117 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index e782e6db8..3032f5af5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1466,85 +1466,75 @@ void Bot::GenerateAABonuses(StatBonuses* newbon) { } void Bot::LoadAAs() { - std::string errorMessage; - char* Query = 0; - int length = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - int maxAAExpansion = RuleI(Bots, BotAAExpansion); //get expansion to get AAs up to botAAs.clear(); //start fresh + std::string query; + if(GetClass() == BERSERKER) - length = MakeAnyLenString(&Query, "SELECT skill_id FROM altadv_vars WHERE berserker = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetLevel(), maxAAExpansion); + query = StringFormat("SELECT skill_id FROM altadv_vars WHERE berserker = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetLevel(), maxAAExpansion); else - length = MakeAnyLenString(&Query, "SELECT skill_id FROM altadv_vars WHERE ((classes & ( 1 << %i )) >> %i) = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetClass(), GetClass(), GetLevel(), maxAAExpansion); + query = StringFormat("SELECT skill_id FROM altadv_vars WHERE ((classes & ( 1 << %i )) >> %i) = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetClass(), GetClass(), GetLevel(), maxAAExpansion); - if(!database.RunQuery(Query, length, TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int totalAAs = database.CountAAs(); + auto results = database.QueryDatabase(query); - while(DataRow = mysql_fetch_row(DatasetResult)) { - uint32 skill_id = 0; - skill_id = atoi(DataRow[0]); - - if(skill_id > 0 && skill_id < totalAAs) { - SendAA_Struct *sendAA = zone->FindAA(skill_id); - - if(sendAA) { - for(int i=0; imax_level; i++) { - //Get AA info & add to list - uint32 aaid = sendAA->id + i; - uint8 total_levels = 0; - uint8 req_level; - std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(aaid); - - //Get level required for AA - if(RequiredLevel != AARequiredLevelAndCost.end()) - req_level = RequiredLevel->second.Level; - else - req_level = (sendAA->class_type + i * sendAA->level_inc); - - if(req_level > GetLevel()) - break; - - //Bot is high enough level for AA - std::map::iterator foundAA = botAAs.find(aaid); - - // AA is not already in list - if(foundAA == botAAs.end()) { - if(sendAA->id == aaid) { - BotAA newAA; - - newAA.total_levels = 0; - newAA.aa_id = aaid; - newAA.req_level = req_level; - newAA.total_levels += 1; - - botAAs[aaid] = newAA; //add to list - } - else { - //update master AA record with number of levels a bot has in AA, based on level. - botAAs[sendAA->id].total_levels+=1; - } - } - } - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { + if(!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in Bot::LoadAAs()"); + return; } + + int totalAAs = database.CountAAs(); + + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 skill_id = 0; + skill_id = atoi(row[0]); + + if(skill_id <= 0 || skill_id >= totalAAs) + continue; + + SendAA_Struct *sendAA = zone->FindAA(skill_id); + + if(!sendAA) + continue; + + for(int i=0; imax_level; i++) { + //Get AA info & add to list + uint32 aaid = sendAA->id + i; + uint8 total_levels = 0; + uint8 req_level; + std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(aaid); + + //Get level required for AA + if(RequiredLevel != AARequiredLevelAndCost.end()) + req_level = RequiredLevel->second.Level; + else + req_level = (sendAA->class_type + i * sendAA->level_inc); + + if(req_level > GetLevel()) + break; + + //Bot is high enough level for AA + std::map::iterator foundAA = botAAs.find(aaid); + + // AA is already in list + if(foundAA != botAAs.end()) + continue; + + if(sendAA->id == aaid) { + BotAA newAA; + + newAA.total_levels = 0; + newAA.aa_id = aaid; + newAA.req_level = req_level; + newAA.total_levels += 1; + + botAAs[aaid] = newAA; //add to list + } + else //update master AA record with number of levels a bot has in AA, based on level. + botAAs[sendAA->id].total_levels+=1; + } + } + } uint32 Bot::GetAA(uint32 aa_id) { @@ -2485,10 +2475,10 @@ void Bot::SaveBuffs() { CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune, buffs[BuffCount].dot_rune, - buffs[BuffCount].caston_x, - IsPersistent, + buffs[BuffCount].caston_x, + IsPersistent, buffs[BuffCount].caston_y, - buffs[BuffCount].caston_z, + buffs[BuffCount].caston_z, buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) { errorMessage = std::string(TempErrorMessageBuffer); safe_delete(Query); @@ -3398,7 +3388,7 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes if (CanSkillProc && HasSkillProcs()) TrySkillProc(other, skillinuse, ReuseTime); - + if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) TrySkillProc(other, skillinuse, ReuseTime, true); } @@ -8109,7 +8099,7 @@ void Bot::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, if (HasSkillProcs()) TrySkillProc(who, skill, ReuseTime*1000); - + if (max_damage > 0 && HasSkillProcSuccess()) TrySkillProc(who, skill, ReuseTime*1000, true); @@ -9113,7 +9103,7 @@ void Bot::SetAttackTimer() { } int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { - + if (spells[spell_id].targettype == ST_Self) return value; @@ -9125,22 +9115,22 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40. if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40) value -= (GetLevel() - 40) * 20; - + //This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch. if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch - + int chance = RuleI(Spells, BaseCritChance); chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; - + if (chance > 0){ - + int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. //Improved Harm Touch is a guaranteed crit if you have at least one level of SCF. if (spell_id == SPELL_IMP_HARM_TOUCH && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0)) chance = 100; - + if (MakeRandomInt(1,100) <= chance){ Critical = true; ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; @@ -9153,23 +9143,23 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } ratio += RuleI(Spells, WizCritRatio); //Default is zero - + if (Critical){ - value = value_BaseEffect*ratio/100; + value = value_BaseEffect*ratio/100; - value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; + value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; value += int(value_BaseEffect*GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id)/100)*ratio/100; if (target) { - value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; - value -= target->GetFcDamageAmtIncoming(this, spell_id); + value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; + value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id)*ratio/100; + value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id)*ratio/100; - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; @@ -9181,28 +9171,28 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } value = value_BaseEffect; - - value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; - + + value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; + value += value_BaseEffect*GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id)/100; if (target) { value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; - value -= target->GetFcDamageAmtIncoming(this, spell_id); + value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); + value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); + + value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); - if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); + value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); return value; } int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - + if (target == nullptr) target = this; @@ -9210,37 +9200,37 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int16 chance = 0; int8 modifier = 1; bool Critical = false; - - value_BaseEffect = value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id)/100); - + + value_BaseEffect = value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id)/100); + value = value_BaseEffect; - value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id)/100); - + value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id)/100); + // Instant Heals if(spells[spell_id].buffduration < 1) { - chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + + chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - if (spellbonuses.CriticalHealDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); - + chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); + if(chance && (MakeRandomInt(0,99) < chance)) { Critical = true; modifier = 2; //At present time no critical heal amount modifier SPA exists. } - + value *= modifier; - value += GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier; - value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id); - value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); + value += GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier; + value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id); + value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; - value += value*target->GetHealRate(spell_id, this)/100; + value += value*target->GetHealRate(spell_id, this)/100; if (Critical) entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), value); @@ -9250,14 +9240,14 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] else { - - chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - + chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; + + chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); + if (spellbonuses.CriticalRegenDecay) chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); - + if(chance && (MakeRandomInt(0,99) < chance)) return (value * 2); } From 9eb3907d4565ea96654e08d9184ffa86832c6fa4 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 13:59:05 -0700 Subject: [PATCH 012/368] IsBotNameValid converted to QueryDatabase --- zone/bot.cpp | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 3032f5af5..6fd06736f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2339,35 +2339,29 @@ bool Bot::IsValidName() { } bool Bot::IsBotNameAvailable(std::string* errorMessage) { - bool Result = false; - if(this->GetCleanName()) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(!this->GetCleanName()) + return false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM vwBotCharacterMobs WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - uint32 ExistingNameCount = 0; + std::string query = StringFormat("SELECT COUNT(id) FROM vwBotCharacterMobs " + "WHERE name LIKE '%s'", this->GetCleanName()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return false; + } - while(DataRow = mysql_fetch_row(DatasetResult)) { - ExistingNameCount = atoi(DataRow[0]); - break; - } + uint32 existingNameCount = 0; - if(ExistingNameCount == 0) - Result = true; + for (auto row = results.begin(); row != results.end(); ++row) { + existingNameCount = atoi(row[0]); + break; + } - mysql_free_result(DatasetResult); - } + if(existingNameCount != 0) + return false; - safe_delete(Query); - } - - return Result; + return true; } bool Bot::Save() { From 0178f3c9bbfcc9dbffa4eb403a3bce31514da2fd Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 14:16:21 -0700 Subject: [PATCH 013/368] Save converted to QueryDatabase --- zone/bot.cpp | 101 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 6fd06736f..71139a0f0 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2365,53 +2365,80 @@ bool Bot::IsBotNameAvailable(std::string* errorMessage) { } bool Bot::Save() { - bool Result = false; - std::string errorMessage; - - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - uint32 affectedRows = 0; if(this->GetBotID() == 0) { // New bot record - uint32 TempNewBotID = 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO bots (BotOwnerCharacterID, BotSpellsID, Name, LastName, BotLevel, Race, Class, Gender, Size, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, MR, CR, DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, LastSpawnDate, TotalPlayTime, LastZoneId) VALUES('%u', '%u', '%s', '%s', '%u', '%i', '%i', '%i', '%f', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', NOW(), 0, %i)", this->_botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), this->lastname, this->GetLevel(), GetRace(), GetClass(), GetGender(), GetSize(), this->GetLuclinFace(), this->GetHairStyle(), GetHairColor(), this->GetEyeColor1(), this->GetEyeColor2(), this->GetBeardColor(), this->GetBeard(), this->GetDrakkinHeritage(), this->GetDrakkinTattoo(), this->GetDrakkinDetails(), GetHP(), GetMana(), GetMR(), GetCR(), GetDR(), GetFR(), GetPR(), GetCorrup(), GetAC(), GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA(), GetATK(), _lastZoneId), TempErrorMessageBuffer, 0, &affectedRows, &TempNewBotID)) { - errorMessage = std::string(TempErrorMessageBuffer); + std::string query = StringFormat("INSERT INTO bots (BotOwnerCharacterID, BotSpellsID, Name, LastName, " + "BotLevel, Race, Class, Gender, Size, Face, LuclinHairStyle, " + "LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, " + "LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, " + "MR, CR, DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, " + "LastSpawnDate, TotalPlayTime, LastZoneId) " + "VALUES('%u', '%u', '%s', '%s', '%u', '%i', '%i', '%i', '%f', '%i', '%i', " + "'%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', " + "'%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', " + "'%i', NOW(), 0, %i)", + this->_botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), + this->lastname, this->GetLevel(), GetRace(), GetClass(), GetGender(), + GetSize(), this->GetLuclinFace(), this->GetHairStyle(), GetHairColor(), + this->GetEyeColor1(), this->GetEyeColor2(), this->GetBeardColor(), + this->GetBeard(), this->GetDrakkinHeritage(), this->GetDrakkinTattoo(), + this->GetDrakkinDetails(), GetHP(), GetMana(), GetMR(), GetCR(), GetDR(), + GetFR(), GetPR(), GetCorrup(), GetAC(), GetSTR(), GetSTA(), GetDEX(), + GetAGI(), GetINT(), GetWIS(), GetCHA(), GetATK(), _lastZoneId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + auto botOwner = GetBotOwner(); + if (botOwner) + botOwner->Message(13, results.ErrorMessage().c_str()); + return false; } - else { - SetBotID(TempNewBotID); - Result = true; - } - } - else { - // Update existing bot record - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE bots SET BotOwnerCharacterID = '%u', BotSpellsID = '%u', Name = '%s', LastName = '%s', BotLevel = '%u', Race = '%i', Class = '%i', Gender = '%i', Size = '%f', Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', DrakkinDetails = '%i', HP = '%i', Mana = '%i', MR = '%i', CR = '%i', DR = '%i', FR = '%i', PR = '%i', Corrup = '%i', AC = '%i', STR = '%i', STA = '%i', DEX = '%i', AGI = '%i', _INT = '%i', WIS = '%i', CHA = '%i', ATK = '%i', LastSpawnDate = NOW(), TotalPlayTime = '%u', LastZoneId = %i WHERE BotID = '%u'", _botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), this->lastname, this->GetLevel(), _baseRace, this->GetClass(), _baseGender, GetSize(), this->GetLuclinFace(), this->GetHairStyle(), GetHairColor(), this->GetEyeColor1(), this->GetEyeColor2(), this->GetBeardColor(), this->GetBeard(), this->GetDrakkinHeritage(), GetDrakkinTattoo(), GetDrakkinDetails(), GetHP(), GetMana(), _baseMR, _baseCR, _baseDR, _baseFR, _basePR, _baseCorrup, _baseAC, _baseSTR, _baseSTA, _baseDEX, _baseAGI, _baseINT, _baseWIS, _baseCHA, _baseATK, GetTotalPlayTime(), _lastZoneId, GetBotID()), TempErrorMessageBuffer, 0, &affectedRows)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - Result = true; - time(&_startTotalPlayTime); - } - } - safe_delete(Query); - - if(!errorMessage.empty() || (Result && affectedRows != 1)) { - if(GetBotOwner() && !errorMessage.empty()) - GetBotOwner()->Message(13, errorMessage.c_str()); - else if(GetBotOwner()) - GetBotOwner()->Message(13, std::string("Unable to save bot to the database.").c_str()); - - Result = false; - } - else { - SaveBuffs(); + SetBotID(results.LastInsertedID()); + SaveBuffs(); SavePet(); SaveStance(); SaveTimers(); + return true; } - return Result; + + // Update existing bot record + std::string query = StringFormat("UPDATE bots SET BotOwnerCharacterID = '%u', BotSpellsID = '%u', " + "Name = '%s', LastName = '%s', BotLevel = '%u', Race = '%i', " + "Class = '%i', Gender = '%i', Size = '%f', Face = '%i', " + "LuclinHairStyle = '%i', LuclinHairColor = '%i', " + "LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', " + "LuclinBeardColor = '%i', LuclinBeard = '%i', DrakkinHeritage = '%i', " + "DrakkinTattoo = '%i', DrakkinDetails = '%i', HP = '%i', Mana = '%i', " + "MR = '%i', CR = '%i', DR = '%i', FR = '%i', PR = '%i', " + "Corrup = '%i', AC = '%i', STR = '%i', STA = '%i', DEX = '%i', " + "AGI = '%i', _INT = '%i', WIS = '%i', CHA = '%i', ATK = '%i', " + "LastSpawnDate = NOW(), TotalPlayTime = '%u', LastZoneId = %i " + "WHERE BotID = '%u'", + _botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), + this->lastname, this->GetLevel(), _baseRace, this->GetClass(), + _baseGender, GetSize(), this->GetLuclinFace(), this->GetHairStyle(), + GetHairColor(), this->GetEyeColor1(), this->GetEyeColor2(), + this->GetBeardColor(), this->GetBeard(), this->GetDrakkinHeritage(), + GetDrakkinTattoo(), GetDrakkinDetails(), GetHP(), GetMana(), + _baseMR, _baseCR, _baseDR, _baseFR, _basePR, _baseCorrup, _baseAC, + _baseSTR, _baseSTA, _baseDEX, _baseAGI, _baseINT, _baseWIS, _baseCHA, + _baseATK, GetTotalPlayTime(), _lastZoneId, GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + auto botOwner = GetBotOwner(); + if (botOwner) + botOwner->Message(13, results.ErrorMessage().c_str()); + return false; + } + + SaveBuffs(); + SavePet(); + SaveStance(); + SaveTimers(); + + return true; } // Returns the current total play time for the bot From 0574a3db868e0f11e9e97f5999a28a09ad5a2c41 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 14:57:39 -0700 Subject: [PATCH 014/368] SaveBuffs converted to QueryDatabase --- zone/bot.cpp | 86 ++++++++++++++++++---------------------------------- 1 file changed, 30 insertions(+), 56 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 71139a0f0..42eae5db1 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2402,7 +2402,6 @@ bool Bot::Save() { return true; } - // Update existing bot record std::string query = StringFormat("UPDATE bots SET BotOwnerCharacterID = '%u', BotSpellsID = '%u', " "Name = '%s', LastName = '%s', BotLevel = '%u', Race = '%i', " @@ -2459,66 +2458,41 @@ uint32 Bot::GetTotalPlayTime() { } void Bot::SaveBuffs() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int BuffCount = 0; - int InsertCount = 0; - uint32 buff_count = GetMaxTotalSlots(); - while(BuffCount < BUFF_COUNT) { - if(buffs[BuffCount].spellid > 0 && buffs[BuffCount].spellid != SPELL_UNKNOWN) { - if(InsertCount == 0) { - // Remove any existing buff saves - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - } + // Remove any existing buff saves + std::string query = StringFormat("DELETE FROM botbuffs WHERE BotId = %u", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - int IsPersistent = 0; + for (int buffIndex = 0; buffIndex < BUFF_COUNT; buffIndex++) { + if (buffs[buffIndex].spellid <= 0 || buffs[buffIndex].spellid == SPELL_UNKNOWN) + continue; - if(buffs[BuffCount].persistant_buff) - IsPersistent = 1; - else - IsPersistent = 0; + int isPersistent = buffs[buffIndex].persistant_buff? 1: 0; + query = StringFormat("INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, " + "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, " + "CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, " + "caston_x, Persistent, caston_y, caston_z, ExtraDIChance) " + "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, " + "%i, %i, %i);", + GetBotID(), buffs[buffIndex].spellid, buffs[buffIndex].casterlevel, + spells[buffs[buffIndex].spellid].buffdurationformula, + buffs[buffIndex].ticsremaining, + CalculatePoisonCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, + CalculateDiseaseCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, + CalculateCurseCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, + CalculateCorruptionCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, + buffs[buffIndex].numhits, buffs[buffIndex].melee_rune, + buffs[buffIndex].magic_rune, buffs[buffIndex].dot_rune, + buffs[buffIndex].caston_x, isPersistent, buffs[buffIndex].caston_y, + buffs[buffIndex].caston_z, buffs[buffIndex].ExtraDIChance); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, " - "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, " - "dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);", - GetBotID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula, - buffs[BuffCount].ticsremaining, - CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateDiseaseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateCurseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune, - buffs[BuffCount].dot_rune, - buffs[BuffCount].caston_x, - IsPersistent, - buffs[BuffCount].caston_y, - buffs[BuffCount].caston_z, - buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - else { - safe_delete(Query); - Query = 0; - InsertCount++; - } - } + } - BuffCount++; - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } } void Bot::LoadBuffs() { From 87f1f78b67eb395f1bb42bfb84af6b1b4d649454 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 15:11:33 -0700 Subject: [PATCH 015/368] LoadBuffs converted to QueryDatabase --- zone/bot.cpp | 97 ++++++++++++++++++++-------------------------------- 1 file changed, 37 insertions(+), 60 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 42eae5db1..3dd5c8bc4 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2496,77 +2496,54 @@ void Bot::SaveBuffs() { } void Bot::LoadBuffs() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - bool BuffsLoaded = false; + std::string query = StringFormat("SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, " + "PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, " + "HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, " + "caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", + GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int BuffCount = 0; + int buffCount = 0; - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(BuffCount == BUFF_COUNT) - break; + for (auto row = results.begin(); row != results.end(); ++row) { + if(buffCount == BUFF_COUNT) + break; - buffs[BuffCount].spellid = atoi(DataRow[0]); - buffs[BuffCount].casterlevel = atoi(DataRow[1]); - buffs[BuffCount].ticsremaining = atoi(DataRow[3]); + buffs[buffCount].spellid = atoi(row[0]); + buffs[buffCount].casterlevel = atoi(row[1]); + buffs[buffCount].ticsremaining = atoi(row[3]); - if(CalculatePoisonCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[4]); - } else if(CalculateDiseaseCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[5]); - } else if(CalculateCurseCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[6]); - } else if(CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[7]); - } - buffs[BuffCount].numhits = atoi(DataRow[8]); - buffs[BuffCount].melee_rune = atoi(DataRow[9]); - buffs[BuffCount].magic_rune = atoi(DataRow[10]); - buffs[BuffCount].dot_rune = atoi(DataRow[11]); - buffs[BuffCount].caston_x = atoi(DataRow[12]); - buffs[BuffCount].casterid = 0; + if(CalculatePoisonCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[4]); + else if(CalculateDiseaseCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[5]); + else if(CalculateCurseCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[6]); + else if(CalculateCorruptionCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[7]); - bool IsPersistent = false; + buffs[buffCount].numhits = atoi(row[8]); + buffs[buffCount].melee_rune = atoi(row[9]); + buffs[buffCount].magic_rune = atoi(row[10]); + buffs[buffCount].dot_rune = atoi(row[11]); + buffs[buffCount].caston_x = atoi(row[12]); + buffs[buffCount].casterid = 0; - if(atoi(DataRow[13])) - IsPersistent = true; + buffs[buffCount].persistant_buff = atoi(row[13])? true: false; - buffs[BuffCount].caston_y = atoi(DataRow[14]); - buffs[BuffCount].caston_z = atoi(DataRow[15]); - buffs[BuffCount].ExtraDIChance = atoi(DataRow[16]); + buffs[buffCount].caston_y = atoi(row[14]); + buffs[buffCount].caston_z = atoi(row[15]); + buffs[buffCount].ExtraDIChance = atoi(row[16]); - buffs[BuffCount].persistant_buff = IsPersistent; + buffCount++; + } - BuffCount++; - } + query = StringFormat("DELETE FROM botbuffs WHERE BotId = %u", GetBotID()); + results = database.QueryDatabase(query); - mysql_free_result(DatasetResult); - - BuffsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(errorMessage.empty() && BuffsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } } uint32 Bot::GetPetSaveId() { From 93aa690a6d7cfe0ed84c5d2621520b725de11743 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 15:14:21 -0700 Subject: [PATCH 016/368] GetPetSaveId converted to QueryDatabase --- zone/bot.cpp | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 3dd5c8bc4..e7339f888 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2547,32 +2547,18 @@ void Bot::LoadBuffs() { } uint32 Bot::GetPetSaveId() { - uint32 Result = 0; - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotPetsId from botpets where BotId = %u;", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - Result = atoi(DataRow[0]); - break; - } + std::string query = StringFormat("SELECT BotPetsId FROM botpets WHERE BotId = %u;", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return 0; - mysql_free_result(DatasetResult); - } + if (results.RowCount() == 0) + return 0; - safe_delete(Query); + auto row = results.begin(); - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - - return Result; + return atoi(row[0]); } void Bot::LoadPet() { From c8e7d9e005e91d44550f9a546690a139f91ed878 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 15:19:44 -0700 Subject: [PATCH 017/368] LoadPetStats converted to QueryDatabase --- zone/bot.cpp | 44 +++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index e7339f888..687019979 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2594,39 +2594,25 @@ void Bot::LoadPet() { } void Bot::LoadPetStats(std::string* petName, uint16* petMana, uint16* petHitPoints, uint32* botPetId, uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botPetSaveId == 0) + return; - bool statsLoaded = false; + std::string query = StringFormat("SELECT PetId, Name, Mana, HitPoints " + "FROM botpets WHERE BotPetsId = %u;", + botPetSaveId); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select PetId, Name, Mana, HitPoints from botpets where BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - *botPetId = atoi(DataRow[0]); - *petName = std::string(DataRow[1]); - *petMana = atoi(DataRow[2]); - *petHitPoints = atoi(DataRow[3]); - break; - } + if (results.RowCount() == 0) + return; - mysql_free_result(DatasetResult); + auto row = results.begin(); - statsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } + *botPetId = atoi(row[0]); + *petName = std::string(row[1]); + *petMana = atoi(row[2]); + *petHitPoints = atoi(row[3]); } void Bot::LoadPetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { From a486db5e957b1618eaa40dfe146078a7fa032159 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 15:28:13 -0700 Subject: [PATCH 018/368] LoadPetBuffs converted to QueryDatabase --- zone/bot.cpp | 58 +++++++++++++++++----------------------------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 687019979..c5374bef7 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2616,52 +2616,32 @@ void Bot::LoadPetStats(std::string* petName, uint16* petMana, uint16* petHitPoin } void Bot::LoadPetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { - if(petBuffs && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(!petBuffs || botPetSaveId == 0) + return; - bool BuffsLoaded = false; + std::string query = StringFormat("SELECT SpellId, CasterLevel, Duration " + "FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, Duration FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int BuffCount = 0; - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(BuffCount == BUFF_COUNT) - break; + int buffIndex = 0; - petBuffs[BuffCount].spellid = atoi(DataRow[0]); - petBuffs[BuffCount].level = atoi(DataRow[1]); - petBuffs[BuffCount].duration = atoi(DataRow[2]); + for (auto row = results.begin();row != results.end(); ++row) { + if(buffIndex == BUFF_COUNT) + break; - BuffCount++; - } + petBuffs[buffIndex].spellid = atoi(row[0]); + petBuffs[buffIndex].level = atoi(row[1]); + petBuffs[buffIndex].duration = atoi(row[2]); - mysql_free_result(DatasetResult); - - BuffsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(errorMessage.empty() && BuffsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } + buffIndex++; } + + query = StringFormat("DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + results = database.QueryDatabase(query); + } void Bot::LoadPetItems(uint32* petItems, uint32 botPetSaveId) { From 364a51b11905a518b03ccf42edf881ab50349a8a Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 15:38:15 -0700 Subject: [PATCH 019/368] LoadPetItems converted to QueryDatabase --- zone/bot.cpp | 53 ++++++++++++++++------------------------------------ 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c5374bef7..ce8239e24 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2645,50 +2645,29 @@ void Bot::LoadPetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { } void Bot::LoadPetItems(uint32* petItems, uint32 botPetSaveId) { - if(petItems && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(!petItems || botPetSaveId == 0) + return; - bool itemsLoaded = false; + std::string query = StringFormat("SELECT ItemId FROM botpetinventory " + "WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT ItemId FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int ItemCount = 0; + int itemIndex = 0; - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(ItemCount == EmuConstants::EQUIPMENT_SIZE) - break; + for(auto row = results.begin(); row != results.end(); ++row) { + if(itemIndex == EmuConstants::EQUIPMENT_SIZE) + break; - petItems[ItemCount] = atoi(DataRow[0]); + petItems[itemIndex] = atoi(row[0]); - ItemCount++; - } + itemIndex++; + } - mysql_free_result(DatasetResult); + query = StringFormat("DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId); + results = database.QueryDatabase(query); - itemsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(errorMessage.empty() && itemsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } } void Bot::SavePet() { From ab76783f8bef2b0f13341a3a07ce55ac73ecc2e1 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 15:42:32 -0700 Subject: [PATCH 020/368] SavePetStats converted to QueryDatabase --- zone/bot.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ce8239e24..a617e57b7 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2707,20 +2707,13 @@ void Bot::SavePet() { } uint32 Bot::SavePetStats(std::string petName, uint16 petMana, uint16 petHitPoints, uint32 botPetId) { - uint32 Result = 0; - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + std::string query = StringFormat("REPLACE INTO botpets SET PetId = %u, BotId = %u, Name = '%s', " + "Mana = %u, HitPoints = %u;", botPetId, GetBotID(), petName.c_str(), + petMana, petHitPoints); + auto results = database.QueryDatabase(query); - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "REPLACE INTO botpets SET PetId = %u, BotId = %u, Name = '%s', Mana = %u, HitPoints = %u;", botPetId, GetBotID(), petName.c_str(), petMana, petHitPoints), TempErrorMessageBuffer, 0, 0, &Result)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - - safe_delete(Query); - Query = 0; - - return Result; + return 0; } void Bot::SavePetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { From fe753f05df966fc0507fd10f429cfce0e399c4c3 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 15:47:14 -0700 Subject: [PATCH 021/368] SavePetBuffs converted to QueryDatabase --- zone/bot.cpp | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a617e57b7..5ceb52464 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2717,33 +2717,28 @@ uint32 Bot::SavePetStats(std::string petName, uint16 petMana, uint16 petHitPoint } void Bot::SavePetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { - if(petBuffs && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int BuffCount = 0; + if(!petBuffs || botPetSaveId == 0) + return; - while(BuffCount < BUFF_COUNT) { - if(petBuffs[BuffCount].spellid > 0 && petBuffs[BuffCount].spellid != SPELL_UNKNOWN) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botpetbuffs (BotPetsId, SpellId, CasterLevel, Duration) VALUES(%u, %u, %u, %u);", botPetSaveId, petBuffs[BuffCount].spellid, petBuffs[BuffCount].level, petBuffs[BuffCount].duration), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - else { - safe_delete(Query); - Query = 0; - } - } + int buffIndex = 0; - BuffCount++; - } + while(buffIndex < BUFF_COUNT) { + if(petBuffs[buffIndex].spellid > 0 && petBuffs[buffIndex].spellid != SPELL_UNKNOWN) { + + std::string query = StringFormat("INSERT INTO botpetbuffs " + "(BotPetsId, SpellId, CasterLevel, Duration) " + "VALUES(%u, %u, %u, %u);", + botPetSaveId, petBuffs[buffIndex].spellid, + petBuffs[buffIndex].level, petBuffs[buffIndex].duration); + auto results = database.QueryDatabase(query); + if(!results.Success()) + break; + + } + + buffIndex++; + } - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } } void Bot::SavePetItems(uint32* petItems, uint32 botPetSaveId) { From 54b2c50109e15bc3c003afbe3ae41432d2646a15 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 15:53:32 -0700 Subject: [PATCH 022/368] SavePetItems converted to QueryDatabase --- zone/bot.cpp | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 5ceb52464..719f546b8 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2742,34 +2742,21 @@ void Bot::SavePetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { } void Bot::SavePetItems(uint32* petItems, uint32 botPetSaveId) { - if(petItems && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int ItemCount = 0; + if(!petItems || botPetSaveId == 0) + return; - while (ItemCount < EmuConstants::EQUIPMENT_SIZE) { - if(petItems[ItemCount] > 0) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botpetinventory (BotPetsId, ItemId) VALUES(%u, %u);", botPetSaveId, petItems[ItemCount]), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - else { - safe_delete(Query); - Query = 0; - ItemCount++; - } - } + for (int itemIndex = 0;itemIndex < EmuConstants::EQUIPMENT_SIZE; itemIndex++) { + if(petItems[itemIndex] == 0) + continue; - ItemCount++; - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } + std::string query = StringFormat("INSERT INTO botpetinventory " + "(BotPetsId, ItemId) VALUES(%u, %u);", + botPetSaveId, petItems[itemIndex]); + auto results = database.QueryDatabase(query); + if(!results.Success()) + break; } + } void Bot::DeletePetBuffs(uint32 botPetSaveId) { From 510a51e56470a3ed96ae32c70e3e96bf64c139d9 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 15:55:05 -0700 Subject: [PATCH 023/368] DeletePetBuffs converted to QueryDatabase --- zone/bot.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 719f546b8..778ebc6fb 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2760,18 +2760,12 @@ void Bot::SavePetItems(uint32* petItems, uint32 botPetSaveId) { } void Bot::DeletePetBuffs(uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(botPetSaveId == 0) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } + std::string query = StringFormat("DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); - safe_delete(Query); - Query = 0; - } } void Bot::DeletePetItems(uint32 botPetSaveId) { From 04045dfc27d25eda692d2b05defac30d8dacaa32 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 15:56:19 -0700 Subject: [PATCH 024/368] DeletePetItems converted to QueryDatabase --- zone/bot.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 778ebc6fb..774f29b5b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2769,18 +2769,12 @@ void Bot::DeletePetBuffs(uint32 botPetSaveId) { } void Bot::DeletePetItems(uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(botPetSaveId == 0) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } + std::string query = StringFormat("DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); - safe_delete(Query); - Query = 0; - } } void Bot::DeletePetStats(uint32 botPetSaveId) { From a67255475c67b3c999e60335f0d5f54a33d090c1 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 16:02:02 -0700 Subject: [PATCH 025/368] DeletePetStats converted to QueryDatabase --- zone/bot.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 774f29b5b..185dcedc5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2778,18 +2778,12 @@ void Bot::DeletePetItems(uint32 botPetSaveId) { } void Bot::DeletePetStats(uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(botPetSaveId == 0) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE from botpets where BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } + std::string query = StringFormat("DELETE from botpets where BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); - safe_delete(Query); - Query = 0; - } } void Bot::LoadStance() { From 14c642a3f786ea00cd8075e49b06080c62665b94 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 16:07:10 -0700 Subject: [PATCH 026/368] LoadStance converted to QueryDatabase --- zone/bot.cpp | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 185dcedc5..443fd5e59 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2787,38 +2787,18 @@ void Bot::DeletePetStats(uint32 botPetSaveId) { } void Bot::LoadStance() { - int Result = 0; - bool loaded = false; - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select StanceID from botstances where BotID = %u;", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - Result = atoi(DataRow[0]); - loaded = true; - break; - } - - mysql_free_result(DatasetResult); - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { + std::string query = StringFormat("SELECT StanceID FROM botstances WHERE BotID = %u;", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success() || results.RowCount() == 0) { LogFile->write(EQEMuLog::Error, "Error in Bot::LoadStance()"); + SetDefaultBotStance(); + return; } - if(loaded) - SetBotStance((BotStanceType)Result); - else - SetDefaultBotStance(); + auto row = results.begin(); + + SetBotStance((BotStanceType)atoi(row[0])); } void Bot::SaveStance() { From 7d8e128b5fd11da2aaecfde60d40cf059496f935 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 16:11:13 -0700 Subject: [PATCH 027/368] SaveStance converted to QueryDatabase --- zone/bot.cpp | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 443fd5e59..f76dd2d75 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2802,25 +2802,15 @@ void Bot::LoadStance() { } void Bot::SaveStance() { - if(_baseBotStance != _botStance) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(_baseBotStance == _botStance) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "REPLACE INTO botstances (BotID, StanceId) VALUES(%u, %u);", GetBotID(), GetBotStance()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - else { - safe_delete(Query); - Query = 0; - } + std::string query = StringFormat("REPLACE INTO botstances (BotID, StanceId) " + "VALUES(%u, %u);", GetBotID(), GetBotStance()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in Bot::SaveStance()"); - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error in Bot::SaveStance()"); - } - } } void Bot::LoadTimers() { From 89f34246f0bf947e864af27ded810d1836dfe4fe Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 16:16:04 -0700 Subject: [PATCH 028/368] LoadTimers converted to QueryDatabase --- zone/bot.cpp | 54 +++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index f76dd2d75..a343f4584 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2814,39 +2814,33 @@ void Bot::SaveStance() { } void Bot::LoadTimers() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT IfNull(bt.TimerID, 0) As TimerID, IfNull(bt.Value, 0) As Value, IfNull(MAX(sn.recast_time), 0) AS MaxTimer FROM bottimers bt, spells_new sn WHERE bt.BotID = %u AND sn.EndurTimerIndex = (SELECT case WHEN TimerID > %i THEN TimerID - %i ELSE TimerID END AS TimerID FROM bottimers WHERE TimerID = bt.TimerID AND BotID = bt.BotID ) AND sn.classes%i <= %i;", GetBotID(), DisciplineReuseStart-1, DisciplineReuseStart-1, GetClass(), GetLevel()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int TimerID = 0; - uint32 Value = 0; - uint32 MaxValue = 0; - - while(DataRow = mysql_fetch_row(DatasetResult)) { - TimerID = atoi(DataRow[0]) - 1; - Value = atoi(DataRow[1]); - MaxValue = atoi(DataRow[2]); - - if(TimerID >= 0 && TimerID < MaxTimer && Value < (Timer::GetCurrentTime() + MaxValue)) { - timers[TimerID] = Value; - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { + std::string query = StringFormat("SELECT IfNull(bt.TimerID, 0) As TimerID, IfNull(bt.Value, 0) As Value, " + "IfNull(MAX(sn.recast_time), 0) AS MaxTimer FROM bottimers bt, spells_new sn " + "WHERE bt.BotID = %u AND sn.EndurTimerIndex = " + "(SELECT case WHEN TimerID > %i THEN TimerID - %i ELSE TimerID END AS TimerID " + "FROM bottimers WHERE TimerID = bt.TimerID AND BotID = bt.BotID ) " + "AND sn.classes%i <= %i;", + GetBotID(), DisciplineReuseStart-1, DisciplineReuseStart-1, GetClass(), GetLevel()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in Bot::LoadTimers()"); + return; } + + int timerID = 0; + uint32 value = 0; + uint32 maxValue = 0; + + for (auto row = results.begin(); row != results.end(); ++row) { + timerID = atoi(row[0]) - 1; + value = atoi(row[1]); + maxValue = atoi(row[2]); + + if(timerID >= 0 && timerID < MaxTimer && value < (Timer::GetCurrentTime() + maxValue)) + timers[timerID] = value; + } + } void Bot::SaveTimers() { From 06d1bd632bb8017dcd051bfd69e337e1307d0253 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 16:22:45 -0700 Subject: [PATCH 029/368] SaveTimers converted to QueryDatabase --- zone/bot.cpp | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a343f4584..c7f1ef9e2 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2844,30 +2844,28 @@ void Bot::LoadTimers() { } void Bot::SaveTimers() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + bool hadError = false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM bottimers WHERE BotID = %u;", GetBotID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; + std::string query = StringFormat("DELETE FROM bottimers WHERE BotID = %u;", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + hadError = true; + + for(int timerIndex = 0; timerIndex < MaxTimer; timerIndex++) { + if(timers[timerIndex] <= Timer::GetCurrentTime()) + continue; + + query = StringFormat("REPLACE INTO bottimers (BotID, TimerID, Value) VALUES(%u, %u, %u);", + GetBotID(), timerIndex+1, timers[timerIndex]); + results = database.QueryDatabase(query); + + if(!results.Success()) + hadError = true; } - for(int i = 0; i < MaxTimer; i++) { - if(timers[i] > Timer::GetCurrentTime()) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "REPLACE INTO bottimers (BotID, TimerID, Value) VALUES(%u, %u, %u);", GetBotID(), i+1, timers[i]), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { + if(hadError) LogFile->write(EQEMuLog::Error, "Error in Bot::SaveTimers()"); - } + } bool Bot::Process() From 515fe8d9e58efa343cbf7f0e7f04392bb56cf48e Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 16:29:47 -0700 Subject: [PATCH 030/368] DeleteBot converted to QueryDatabase --- zone/bot.cpp | 61 +++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c7f1ef9e2..72aae3089 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4067,44 +4067,41 @@ void Bot::Depop() { } bool Bot::DeleteBot(std::string* errorMessage) { - bool Result = false; - int TempCounter = 0; + bool hadError = false; - if(this->GetBotID() > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(this->GetBotID() == 0) + return false; - // TODO: These queries need to be ran together as a transaction.. ie, if one or more fail then they all will fail to commit to the database. + // TODO: These queries need to be ran together as a transaction.. ie, if one or more fail then they all will fail to commit to the database. + std::string query = StringFormat("DELETE FROM botinventory WHERE botid = '%u'", this->GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botinventory WHERE botid = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; + query = StringFormat("DELETE FROM botbuffs WHERE botid = '%u'", this->GetBotID()); + results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botbuffs WHERE botid = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; + query = StringFormat("DELETE FROM botstances WHERE BotID = '%u'", this->GetBotID()); + results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botstances WHERE BotID = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; + query = StringFormat("DELETE FROM bots WHERE BotID = '%u'", this->GetBotID()); + results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM bots WHERE BotID = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; - - if(TempCounter == 4) - Result = true; - } - - return Result; + return !hadError; } void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { From cf0c773002337401a0b5c631f1291f767eb3ac67 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 16:40:31 -0700 Subject: [PATCH 031/368] SetBotItemInSlot converted to QueryDatabase --- zone/bot.cpp | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 72aae3089..fe9ffa499 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4156,30 +4156,29 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { // Saves the specified item as an inventory record in the database for this bot. void Bot::SetBotItemInSlot(uint32 slotID, uint32 itemID, const ItemInst* inst, std::string *errorMessage) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; - if (this->GetBotID() > 0 && slotID >= EmuConstants::EQUIPMENT_BEGIN && itemID > NO_ITEM) { - if (inst && inst->IsType(ItemClassCommon)) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; ++i) { - ItemInst* auginst = inst->GetItem(i); - augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } - } - if(!database.RunQuery(query, MakeAnyLenString(&query, - "REPLACE INTO botinventory " - " (botid,slotid,itemid,charges,instnodrop,color," - " augslot1,augslot2,augslot3,augslot4,augslot5)" - " VALUES(%lu,%lu,%lu,%lu,%lu,%lu," - " %lu,%lu,%lu,%lu,%lu)", - (unsigned long)this->GetBotID(), (unsigned long)slotID, (unsigned long)itemID, (unsigned long)inst->GetCharges(), (unsigned long)(inst->IsInstNoDrop() ? 1:0),(unsigned long)inst->GetColor(), - (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4]), errbuf)) { - *errorMessage = std::string(errbuf); - } + if (this->GetBotID() == 0 || slotID < EmuConstants::EQUIPMENT_BEGIN || itemID <= NO_ITEM) + return; - safe_delete_array(query); - } + if (inst && inst->IsType(ItemClassCommon)) { + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; ++i) { + ItemInst* auginst = inst->GetItem(i); + augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; + } + } + + std::string query = StringFormat("REPLACE INTO botinventory (botid, slotid, itemid, charges, instnodrop, color, " + "augslot1, augslot2, augslot3, augslot4, augslot5) " + "VALUES(%lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu)", + (unsigned long)this->GetBotID(), (unsigned long)slotID, (unsigned long)itemID, + (unsigned long)inst->GetCharges(), (unsigned long)(inst->IsInstNoDrop()? 1: 0), + (unsigned long)inst->GetColor(), (unsigned long)augslot[0], (unsigned long)augslot[1], + (unsigned long)augslot[2], (unsigned long)augslot[3], (unsigned long)augslot[4]); + auto results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); } // Deletes the inventory record for the specified item from the database for this bot. From 53572b4d132383a368ad4f5b5f501157f4cd5792 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 16:43:59 -0700 Subject: [PATCH 032/368] RemoveBotItemBySlot converted to QueryDatabase --- zone/bot.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index fe9ffa499..3d6821617 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4183,16 +4183,18 @@ void Bot::SetBotItemInSlot(uint32 slotID, uint32 itemID, const ItemInst* inst, s // Deletes the inventory record for the specified item from the database for this bot. void Bot::RemoveBotItemBySlot(uint32 slotID, std::string *errorMessage) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(this->GetBotID() > 0 && slotID >= 0) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botinventory WHERE botid=%i AND slotid=%i", this->GetBotID(), slotID), errbuf)){ - *errorMessage = std::string(errbuf); - } - safe_delete_array(query); - m_inv.DeleteItem(slotID); - } + if(this->GetBotID() == 0) + return; + + std::string query = StringFormat("DELETE FROM botinventory " + "WHERE botid = %i AND slotid = %i", + this->GetBotID(), slotID); + auto results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); + + m_inv.DeleteItem(slotID); } // Retrieves all the inventory records from the database for this bot. From b8caa5dc31dd61ad988a7314138d4f2fc9d01aac Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 16:52:18 -0700 Subject: [PATCH 033/368] GetBotItems converted to QueryDatabase --- zone/bot.cpp | 107 +++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 55 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 3d6821617..cfdd0c7ff 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4200,65 +4200,62 @@ void Bot::RemoveBotItemBySlot(uint32 slotID, std::string *errorMessage) { // Retrieves all the inventory records from the database for this bot. void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { - if(this->GetBotID() > 0) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(this->GetBotID() == 0) + return; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5,instnodrop FROM botinventory WHERE botid=%i order by slotid", this->GetBotID()), errbuf, &DatasetResult)) { - while(DataRow = mysql_fetch_row(DatasetResult)) { - int16 slot_id = atoi(DataRow[0]); - uint32 item_id = atoi(DataRow[1]); - uint16 charges = atoi(DataRow[2]); - uint32 color = atoul(DataRow[3]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoul(DataRow[4]); - aug[1] = (uint32)atoul(DataRow[5]); - aug[2] = (uint32)atoul(DataRow[6]); - aug[3] = (uint32)atoul(DataRow[7]); - aug[4] = (uint32)atoul(DataRow[8]); - bool instnodrop = (DataRow[9] && (uint16)atoi(DataRow[9])) ? true : false; + std::string query = StringFormat("SELECT slotid, itemid, charges, color, " + "augslot1, augslot2, augslot3, augslot4, " + "augslot5, instnodrop FROM botinventory " + "WHERE botid = %i ORDER BY slotid", this->GetBotID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return; + } - ItemInst* inst = database.CreateItem(item_id, charges, aug[0], aug[1], aug[2], aug[3], aug[4]); - if(inst) { - int16 put_slot_id = INVALID_INDEX; - if(instnodrop || ((slot_id >= EmuConstants::EQUIPMENT_BEGIN) && (slot_id <= EmuConstants::EQUIPMENT_END) && inst->GetItem()->Attuneable)) - inst->SetInstNoDrop(true); - if(color > 0) - inst->SetColor(color); - if(charges==255) - inst->SetCharges(-1); - else - inst->SetCharges(charges); - if((slot_id >= 8000) && (slot_id <= 8999)) { - // do nothing - } - else { - put_slot_id = inv.PutItem(slot_id, *inst); - } - safe_delete(inst); + for (auto row = results.begin(); row != results.end(); ++row) { + int16 slot_id = atoi(row[0]); + uint32 item_id = atoi(row[1]); + uint16 charges = atoi(row[2]); + uint32 color = atoul(row[3]); + uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; + aug[0] = (uint32)atoul(row[4]); + aug[1] = (uint32)atoul(row[5]); + aug[2] = (uint32)atoul(row[6]); + aug[3] = (uint32)atoul(row[7]); + aug[4] = (uint32)atoul(row[8]); + bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in inventory: botid=%i, item_id=%i, slot_id=%i", - this->GetBotID(), item_id, slot_id); - } - } - else { - LogFile->write(EQEMuLog::Error, - "Warning: botid %i has an invalid item_id %i in inventory slot %i", - this->GetBotID(), item_id, slot_id); - } - } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(errbuf); + ItemInst* inst = database.CreateItem(item_id, charges, aug[0], aug[1], aug[2], aug[3], aug[4]); + if (!inst) { + LogFile->write(EQEMuLog::Error, "Warning: botid %i has an invalid item_id %i in inventory slot %i", this->GetBotID(), item_id, slot_id); + continue; + } + + int16 put_slot_id = INVALID_INDEX; + + if (instnodrop || ((slot_id >= EmuConstants::EQUIPMENT_BEGIN) && (slot_id <= EmuConstants::EQUIPMENT_END) && inst->GetItem()->Attuneable)) + inst->SetInstNoDrop(true); + + if (color > 0) + inst->SetColor(color); + + if (charges==255) + inst->SetCharges(-1); + else + inst->SetCharges(charges); + + if (slot_id < 8000 || slot_id > 8999) + put_slot_id = inv.PutItem(slot_id, *inst); + + safe_delete(inst); + + // Save ptr to item in inventory + if (put_slot_id == INVALID_INDEX) + LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in inventory: botid=%i, item_id=%i, slot_id=%i",this->GetBotID(), item_id, slot_id); + + } - safe_delete_array(query); - } } // Returns the inventory record for this bot from the database for the specified equipment slot. From cd1b45f0d6fe9f8c22f456073dccc11027bda2f2 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:04:21 -0700 Subject: [PATCH 034/368] GetBotItemBySlot converted to QueryDatabase --- zone/bot.cpp | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index cfdd0c7ff..4cb979997 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4260,27 +4260,21 @@ void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { // Returns the inventory record for this bot from the database for the specified equipment slot. uint32 Bot::GetBotItemBySlot(uint32 slotID) { - uint32 Result = 0; - if(this->GetBotID() > 0 && slotID >= EmuConstants::EQUIPMENT_BEGIN) { - char* query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(this->GetBotID() == 0 || slotID < EmuConstants::EQUIPMENT_BEGIN) + return 0; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT itemid FROM botinventory WHERE botid=%i AND slotid=%i", GetBotID(), slotID), 0, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT itemid FROM botinventory WHERE botid=%i AND slotid = %i", GetBotID(), slotID); + auto results = database.QueryDatabase(query); + if(results.Success()) + return 0; - mysql_free_result(DatasetResult); - } + if(results.RowCount() != 1) + return 0; - safe_delete_array(query); - } + auto row = results.begin(); - return Result; + return atoi(row[0]); } // Returns the number of inventory records the bot has in the database. From 390dcc9a88f382437ef141bcc8661970b7ffd313 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:07:11 -0700 Subject: [PATCH 035/368] GetBotItemsCount converted to QueryDatabase --- zone/bot.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 4cb979997..567986005 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4279,30 +4279,22 @@ uint32 Bot::GetBotItemBySlot(uint32 slotID) { // Returns the number of inventory records the bot has in the database. uint32 Bot::GetBotItemsCount(std::string *errorMessage) { - uint32 Result = 0; - if(this->GetBotID() > 0) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(this->GetBotID() == 0) + return 0; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT COUNT(*) FROM botinventory WHERE botid=%i", this->GetBotID()), errbuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT COUNT(*) FROM botinventory WHERE botid = %i", this->GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(errbuf); + if(results.RowCount() != 1) + return 0; - safe_delete_array(query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } bool Bot::MesmerizeTarget(Mob* target) { From 8312a8cf3b20683de07f40a6c7e558adb4d7e1b6 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:11:06 -0700 Subject: [PATCH 036/368] GetBotIDByBotName converted to QueryDatabase --- zone/bot.cpp | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 567986005..9882a565a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4487,35 +4487,19 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { } uint32 Bot::GetBotIDByBotName(std::string botName) { - uint32 Result = 0; + if(botName.empty()) + return 0; - if(!botName.empty()) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - std::string errorMessage; + std::string query = StringFormat("SELECT BotID FROM bots WHERE Name = '%s'", botName.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotID FROM bots WHERE Name = '%s'", botName.c_str()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - Result = atoi(DataRow[0]); - break; - } + if (results.RowCount() == 0) + return 0; - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - - if(!errorMessage.empty()) { - // TODO: Log this error to zone error log - } - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } Bot* Bot::LoadBot(uint32 botID, std::string* errorMessage) { From ba612f91c739b8b33f1085d2f942f66dcf6b109a Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:18:50 -0700 Subject: [PATCH 037/368] LoadBot converted to QueryDatabase --- zone/bot.cpp | 55 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 9882a565a..691e69601 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4503,32 +4503,45 @@ uint32 Bot::GetBotIDByBotName(std::string botName) { } Bot* Bot::LoadBot(uint32 botID, std::string* errorMessage) { - Bot* Result = 0; + Bot* loadedBot = nullptr; - if(botID > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botID == 0) + return nullptr; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotOwnerCharacterID, BotSpellsID, Name, LastName, BotLevel, Race, Class, Gender, Size, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, MR, CR, DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, BotCreateDate, LastSpawnDate, TotalPlayTime, LastZoneId FROM bots WHERE BotID = '%u'", botID), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - NPCType DefaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(DataRow[2]), std::string(DataRow[3]), atoi(DataRow[4]), atoi(DataRow[5]), atoi(DataRow[6]), atoi(DataRow[7])); - NPCType TempNPCStruct = FillNPCTypeStruct(atoi(DataRow[1]), std::string(DataRow[2]), std::string(DataRow[3]), atoi(DataRow[4]), atoi(DataRow[5]), atoi(DataRow[6]), atoi(DataRow[7]), atof(DataRow[8]), atoi(DataRow[9]), atoi(DataRow[10]), atoi(DataRow[11]), atoi(DataRow[12]), atoi(DataRow[13]), atoi(DataRow[14]), atoi(DataRow[15]), atoi(DataRow[16]), atoi(DataRow[17]), atoi(DataRow[18]), atoi(DataRow[19]), atoi(DataRow[20]), DefaultNPCTypeStruct.MR, DefaultNPCTypeStruct.CR, DefaultNPCTypeStruct.DR, DefaultNPCTypeStruct.FR, DefaultNPCTypeStruct.PR, DefaultNPCTypeStruct.Corrup, DefaultNPCTypeStruct.AC, DefaultNPCTypeStruct.STR, DefaultNPCTypeStruct.STA, DefaultNPCTypeStruct.DEX, DefaultNPCTypeStruct.AGI, DefaultNPCTypeStruct.INT, DefaultNPCTypeStruct.WIS, DefaultNPCTypeStruct.CHA, DefaultNPCTypeStruct.ATK); - Result = new Bot(botID, atoi(DataRow[0]), atoi(DataRow[1]), atof(DataRow[38]), atoi(DataRow[39]), TempNPCStruct); - break; - } + std::string query = StringFormat("SELECT BotOwnerCharacterID, BotSpellsID, Name, LastName, BotLevel, " + "Race, Class, Gender, Size, Face, LuclinHairStyle, LuclinHairColor, " + "LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " + "DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, MR, CR, " + "DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, " + "BotCreateDate, LastSpawnDate, TotalPlayTime, LastZoneId " + "FROM bots WHERE BotID = '%u'", botID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return nullptr; + } - mysql_free_result(DatasetResult); - } + if (results.RowCount() == 0) + return nullptr; - safe_delete_array(Query); - } + auto row = results.begin(); + NPCType defaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), + atoi(row[4]), atoi(row[5]), atoi(row[6]), atoi(row[7])); - return Result; + NPCType tempNPCStruct = FillNPCTypeStruct(atoi(row[1]), std::string(row[2]), std::string(row[3]), atoi(row[4]), + atoi(row[5]), atoi(row[6]), atoi(row[7]), atof(row[8]), atoi(row[9]), + atoi(row[10]), atoi(row[11]), atoi(row[12]), atoi(row[13]), atoi(row[14]), + atoi(row[15]), atoi(row[16]), atoi(row[17]), atoi(row[18]), atoi(row[19]), + atoi(row[20]), defaultNPCTypeStruct.MR, defaultNPCTypeStruct.CR, + defaultNPCTypeStruct.DR, defaultNPCTypeStruct.FR, defaultNPCTypeStruct.PR, + defaultNPCTypeStruct.Corrup, defaultNPCTypeStruct.AC, defaultNPCTypeStruct.STR, + defaultNPCTypeStruct.STA, defaultNPCTypeStruct.DEX, defaultNPCTypeStruct.AGI, + defaultNPCTypeStruct.INT, defaultNPCTypeStruct.WIS, defaultNPCTypeStruct.CHA, + defaultNPCTypeStruct.ATK); + + loadedBot = new Bot(botID, atoi(row[0]), atoi(row[1]), atof(row[38]), atoi(row[39]), tempNPCStruct); + + return loadedBot; } std::list Bot::GetGroupedBotsByGroupId(uint32 groupId, std::string* errorMessage) { From d213e3b1060bdc9ebc667dee4bfc1a365c0903e7 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:26:51 -0700 Subject: [PATCH 038/368] GetGroupedBotsByGroupID converted to QueryDatabase --- zone/bot.cpp | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 691e69601..a94f123aa 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4545,31 +4545,24 @@ Bot* Bot::LoadBot(uint32 botID, std::string* errorMessage) { } std::list Bot::GetGroupedBotsByGroupId(uint32 groupId, std::string* errorMessage) { - std::list Result; + std::list groupedBots; - if(groupId > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(groupId == 0) + return groupedBots; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select g.mobid as BotID from vwGroups as g join bots as b on g.mobid = b.BotId and g.mobtype = 'B' where g.groupid = %u", groupId), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(DataRow) { - Result.push_back(atoi(DataRow[0])); - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); + std::string query = StringFormat("SELECT g.mobid AS BotID FROM vwGroups AS g " + "JOIN bots AS b ON g.mobid = b.BotId AND g.mobtype = 'B' " + "WHERE g.groupid = %u", groupId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return groupedBots; } - return Result; + for (auto row = results.begin(); row != results.end(); ++row) + groupedBots.push_back(atoi(row[0])); + + return groupedBots; } // Load and spawn all zoned bots by bot owner character From 7ae14fffd021df5cee3727cad5f1e628b246025d Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:32:55 -0700 Subject: [PATCH 039/368] GetBotList converted to QueryDatabase --- zone/bot.cpp | 49 +++++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a94f123aa..29a0ca3c8 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4628,38 +4628,31 @@ bool Bot::GroupHasBot(Group* group) { } std::list Bot::GetBotList(uint32 botOwnerCharacterID, std::string* errorMessage) { - std::list Result; + std::list ownersBots; - if(botOwnerCharacterID > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterID == 0) + return ownersBots; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotID, Name, Class, BotLevel, Race FROM bots WHERE BotOwnerCharacterID = '%u'", botOwnerCharacterID), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(DataRow) { - BotsAvailableList TempAvailableBot; - TempAvailableBot.BotID = atoi(DataRow[0]); - strcpy(TempAvailableBot.BotName, DataRow[1]); - TempAvailableBot.BotClass = atoi(DataRow[2]); - TempAvailableBot.BotLevel = atoi(DataRow[3]); - TempAvailableBot.BotRace = atoi(DataRow[4]); - - Result.push_back(TempAvailableBot); - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); + std::string query = StringFormat("SELECT BotID, Name, Class, BotLevel, Race " + "FROM bots WHERE BotOwnerCharacterID = '%u'", botOwnerCharacterID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return ownersBots; } - return Result; + for (auto row = results.begin(); row != results.end(); ++row) { + BotsAvailableList availableBot; + availableBot.BotID = atoi(row[0]); + strcpy(availableBot.BotName, row[1]); + availableBot.BotClass = atoi(row[2]); + availableBot.BotLevel = atoi(row[3]); + availableBot.BotRace = atoi(row[4]); + + ownersBots.push_back(availableBot); + } + + return ownersBots; } std::list Bot::ListSpawnedBots(uint32 characterID, std::string* errorMessage) { From 6410f52c9c2c1ca5d91dc8eee94df5be6b8b80c4 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:36:59 -0700 Subject: [PATCH 040/368] ListSpawnedBots converted to QueryDatabase --- zone/bot.cpp | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 29a0ca3c8..2641701a5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4656,38 +4656,28 @@ std::list Bot::GetBotList(uint32 botOwnerCharacterID, std::st } std::list Bot::ListSpawnedBots(uint32 characterID, std::string* errorMessage) { - std::list Result; - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + std::list spawnedBots; - if(characterID > 0) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT bot_name, zone_name FROM botleader WHERE leaderid=%i", characterID), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + if(characterID == 0) + return spawnedBots; - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); - SpawnedBotsList TempSpawnedBotsList; - TempSpawnedBotsList.BotLeaderCharID = characterID; - strcpy(TempSpawnedBotsList.BotName, DataRow[0]); - strcpy(TempSpawnedBotsList.ZoneName, DataRow[1]); + std::string query = StringFormat("SELECT bot_name, zone_name FROM botleader WHERE leaderid=%i", characterID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return spawnedBots; + } - Result.push_back(TempSpawnedBotsList); - } - } + for(auto row = results.begin(); row != results.end(); ++row) { + SpawnedBotsList spawnedBotsList; + spawnedBotsList.BotLeaderCharID = characterID; + strcpy(spawnedBotsList.BotName, row[0]); + strcpy(spawnedBotsList.ZoneName, row[1]); - mysql_free_result(DatasetResult); - } + spawnedBots.push_back(spawnedBotsList); + } - safe_delete_array(Query); - } - - return Result; + return spawnedBots; } void Bot::SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* errorMessage) { From 36325226eb731d2c1da88d539f25f68e1fd95b9c Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:43:18 -0700 Subject: [PATCH 041/368] SaveBotGroup converted to QueryDatabase --- zone/bot.cpp | 56 +++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 2641701a5..9c8dd85ec 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4681,41 +4681,43 @@ std::list Bot::ListSpawnedBots(uint32 characterID, std::string* } void Bot::SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* errorMessage) { - if(botGroup && !botGroupName.empty()) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + if(!botGroup || botGroupName.empty()) + return; - Mob* tempGroupLeader = botGroup->GetLeader(); + Mob* tempGroupLeader = botGroup->GetLeader(); - if(tempGroupLeader->IsBot()) { - uint32 botGroupId = 0; + if(!tempGroupLeader->IsBot()) + return; - uint32 botGroupLeaderBotId = tempGroupLeader->CastToBot()->GetBotID(); + uint32 botGroupId = 0; + uint32 botGroupLeaderBotId = tempGroupLeader->CastToBot()->GetBotID(); - if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT into botgroup (BotGroupLeaderBotId, BotGroupName) values (%u, '%s')", botGroupLeaderBotId, botGroupName.c_str()), errbuf, 0, 0, &botGroupId)) { - *errorMessage = std::string(errbuf); - } - else { - if(botGroupId > 0) { - for(int counter = 0; counter < botGroup->GroupCount(); counter++) { - Mob* tempBot = botGroup->members[counter]; + std::string query = StringFormat("INSERT INTO botgroup (BotGroupLeaderBotId, BotGroupName) " + "VALUES (%u, '%s')", botGroupLeaderBotId, botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return; + } - if(tempBot && tempBot->IsBot()) { - uint32 botGroupMemberBotId = tempBot->CastToBot()->GetBotID(); + if(botGroupId == 0) + return; - safe_delete_array(query); + for(int groupMemberIndex = 0; groupMemberIndex < botGroup->GroupCount(); groupMemberIndex++) { + Mob* tempBot = botGroup->members[groupMemberIndex]; - if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT into botgroupmembers (BotGroupId, BotId) values (%u, %u)", botGroupId, botGroupMemberBotId), errbuf)) { - *errorMessage = std::string(errbuf); - } - } - } - } - } + if(!tempBot || !tempBot->IsBot()) + continue; + + uint32 botGroupMemberBotId = tempBot->CastToBot()->GetBotID(); + + query = StringFormat("INSERT INTO botgroupmembers (BotGroupId, BotId) " + "VALUES (%u, %u)", botGroupId, botGroupMemberBotId); + results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); + } - safe_delete_array(query); - } - } } void Bot::DeleteBotGroup(std::string botGroupName, std::string* errorMessage) { From 96cf3d967f6905ddd0ef97d178486e84a45e1b21 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:47:38 -0700 Subject: [PATCH 042/368] DeleteBotGroup converted to QueryDatabase --- zone/bot.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 9c8dd85ec..6aa14682b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4721,27 +4721,26 @@ void Bot::SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* e } void Bot::DeleteBotGroup(std::string botGroupName, std::string* errorMessage) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(!botGroupName.empty()) { - uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); + if(botGroupName.empty()) + return; - if(errorMessage->empty() && botGroupId > 0) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botgroupmembers WHERE BotGroupId = %u", botGroupId), errbuf)) { - *errorMessage = std::string(errbuf); - } - else { - safe_delete_array(query); + uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botgroup WHERE BotGroupId = %u", botGroupId), errbuf)) { - *errorMessage = std::string(errbuf); - } - } + if(!errorMessage->empty() || botGroupId== 0) + return; - safe_delete_array(query); - } - } + std::string query = StringFormat("DELETE FROM botgroupmembers WHERE BotGroupId = %u", botGroupId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return; + } + + query = StringFormat("DELETE FROM botgroup WHERE BotGroupId = %u", botGroupId); + results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); } std::list Bot::LoadBotGroup(std::string botGroupName, std::string* errorMessage) { From 38d04931bab0593a2c259614595f38defc62fdb3 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:52:06 -0700 Subject: [PATCH 043/368] LoadBotGroup converted to QueryDatabase --- zone/bot.cpp | 50 +++++++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 6aa14682b..b33b74b64 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4744,44 +4744,32 @@ void Bot::DeleteBotGroup(std::string botGroupName, std::string* errorMessage) { } std::list Bot::LoadBotGroup(std::string botGroupName, std::string* errorMessage) { - std::list Result; - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + std::list botGroup; - if(!botGroupName.empty()) { - uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); + if(botGroupName.empty()) + return botGroup; - if(botGroupId > 0) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotId from botgroupmembers where BotGroupId = %u", botGroupId), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + if(botGroupId == 0) + return botGroup; - if(DataRow) { - BotGroup tempBotGroup; - tempBotGroup.BotGroupID = botGroupId; - tempBotGroup.BotID = atoi(DataRow[0]); + std::string query = StringFormat("SELECT BotId FROM botgroupmembers WHERE BotGroupId = %u", botGroupId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return botGroup; + } - Result.push_back(tempBotGroup); - } - } - } + for(auto row = results.begin(); row != results.end(); ++row) { + BotGroup tempBotGroup; + tempBotGroup.BotGroupID = botGroupId; + tempBotGroup.BotID = atoi(row[0]); - mysql_free_result(DatasetResult); - } + botGroup.push_back(tempBotGroup); + } - safe_delete_array(Query); - } - } - - return Result; + return botGroup; } std::list Bot::GetBotGroupListByBotOwnerCharacterId(uint32 botOwnerCharacterId, std::string* errorMessage) { From 26569ac51d1007f46f00e4850c0fd831cde4ec79 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 17:57:10 -0700 Subject: [PATCH 044/368] GetBotGroupListByBotOwnerCharacterId converted to QueryDatabase --- zone/bot.cpp | 47 +++++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index b33b74b64..0225450fa 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4773,41 +4773,28 @@ std::list Bot::LoadBotGroup(std::string botGroupName, std::string* err } std::list Bot::GetBotGroupListByBotOwnerCharacterId(uint32 botOwnerCharacterId, std::string* errorMessage) { - std::list result; + std::list botGroups; - if(botOwnerCharacterId > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterId == 0) + return botGroups; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupName, BotGroupLeaderName from vwBotGroups where BotOwnerCharacterId = %u", botOwnerCharacterId), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupName, BotGroupLeaderName FROM vwBotGroups " + "WHERE BotOwnerCharacterId = %u", botOwnerCharacterId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return botGroups; + } - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + for(auto row = results.begin(); row != results.end(); ++row) { + BotGroupList botGroupList; + botGroupList.BotGroupName = std::string(row[0]); + botGroupList.BotGroupLeaderName = std::string(row[1]); - if(DataRow) { - BotGroupList botGroupList; - botGroupList.BotGroupName = std::string(DataRow[0]); - botGroupList.BotGroupLeaderName = std::string(DataRow[1]); + botGroups.push_back(botGroupList); + } - result.push_back(botGroupList); - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + return botGroups; } bool Bot::DoesBotGroupNameExist(std::string botGroupName) { From fe6e289606852342c5789eb7527847d80496b6e0 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:01:32 -0700 Subject: [PATCH 045/368] DoesBotGroupNameExist converted to QueryDatabase --- zone/bot.cpp | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0225450fa..98507e2bf 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4798,39 +4798,25 @@ std::list Bot::GetBotGroupListByBotOwnerCharacterId(uint32 botOwne } bool Bot::DoesBotGroupNameExist(std::string botGroupName) { - bool result = false; - if(!botGroupName.empty()) { - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botGroupName.empty()) + return false; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), 0, &DatasetResult)) { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupId FROM vwBotGroups " + "WHERE BotGroupName = '%s'", botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return false; - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + for(auto row = results.begin(); row != results.end(); ++row) { + uint32 tempBotGroupId = atoi(row[0]); + std::string tempBotGroupName = std::string(row[1]); - if(DataRow) { - uint32 tempBotGroupId = atoi(DataRow[0]); - std::string tempBotGroupName = std::string(DataRow[1]); + if (botGroupName == tempBotGroupName && tempBotGroupId != 0) + return true; + } - if(botGroupName == tempBotGroupName) { - result = tempBotGroupId; - break; - } - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + return false; } uint32 Bot::CanLoadBotGroup(uint32 botOwnerCharacterId, std::string botGroupName, std::string* errorMessage) { From 52d64d03a693c05fba51baccee4fe03df2b325a9 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:06:05 -0700 Subject: [PATCH 046/368] CanLoadBotGroup converted to QueryDatabase --- zone/bot.cpp | 48 ++++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 98507e2bf..283214355 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4820,43 +4820,31 @@ bool Bot::DoesBotGroupNameExist(std::string botGroupName) { } uint32 Bot::CanLoadBotGroup(uint32 botOwnerCharacterId, std::string botGroupName, std::string* errorMessage) { - uint32 result = 0; - if(botOwnerCharacterId > 0 && !botGroupName.empty()) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterId == 0 || botGroupName.empty()) + return 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupId, BotGroupName from vwBotGroups where BotOwnerCharacterId = %u", botOwnerCharacterId), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupId, BotGroupName FROM vwBotGroups " + "WHERE BotOwnerCharacterId = %u", botOwnerCharacterId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + if(results.RowCount() == 0) + return 0; - if(DataRow) { - uint32 tempBotGroupId = atoi(DataRow[0]); - std::string tempBotGroupName = std::string(DataRow[1]); + for(auto row = results.begin(); row != results.end(); ++row) { - if(botGroupName == tempBotGroupName) { - result = tempBotGroupId; - break; - } - } - } - } + uint32 tempBotGroupId = atoi(row[0]); + std::string tempBotGroupName = std::string(row[1]); - mysql_free_result(DatasetResult); - } + if(botGroupName == tempBotGroupName) + return tempBotGroupId; + } - safe_delete_array(Query); - } - - return result; + return 0; } uint32 Bot::GetBotGroupIdByBotGroupName(std::string botGroupName, std::string* errorMessage) { From 6d33a13e23543ecceac70031402013092955f77e Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:09:53 -0700 Subject: [PATCH 047/368] GetBotGroupIdByBotGroupName converted to QueryDatabase --- zone/bot.cpp | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 283214355..8b4d8d072 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4848,38 +4848,23 @@ uint32 Bot::CanLoadBotGroup(uint32 botOwnerCharacterId, std::string botGroupName } uint32 Bot::GetBotGroupIdByBotGroupName(std::string botGroupName, std::string* errorMessage) { - uint32 result = 0; - if(!botGroupName.empty()) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botGroupName.empty()) + return 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupId FROM vwBotGroups " + "WHERE BotGroupName = '%s'", botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + if (results.RowCount() == 0) + return 0; - if(DataRow) { - result = atoi(DataRow[0]); - break; - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::GetBotGroupLeaderIdByBotGroupName(std::string botGroupName) { From 45320fd8ec21a330c87d0e22e4cf40300bf9d8c9 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:13:15 -0700 Subject: [PATCH 048/368] GetBotGroupLeaderIdByBotGroupName converted to QueryDatabase --- zone/bot.cpp | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8b4d8d072..6817bf540 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4868,34 +4868,17 @@ uint32 Bot::GetBotGroupIdByBotGroupName(std::string botGroupName, std::string* e } uint32 Bot::GetBotGroupLeaderIdByBotGroupName(std::string botGroupName) { - uint32 result = 0; - if(!botGroupName.empty()) { - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botGroupName.empty()) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupLeaderBotId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), 0, &DatasetResult)) { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupLeaderBotId FROM vwBotGroups WHERE BotGroupName = '%s'", botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return 0; - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); - - if(DataRow) { - result = atoi(DataRow[0]); - break; - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage) { From 0ac238d762bbc8989f7ce933effc1de6cd46957c Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:16:22 -0700 Subject: [PATCH 049/368] AllowedBotSpawns converted to QueryDatabase --- zone/bot.cpp | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 6817bf540..b38a74f79 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4882,30 +4882,24 @@ uint32 Bot::GetBotGroupLeaderIdByBotGroupName(std::string botGroupName) { } uint32 Bot::AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage) { - uint32 Result = 0; - if(botOwnerCharacterID > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterID == 0) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT value FROM quest_globals WHERE name='bot_spawn_limit' and charid=%i", botOwnerCharacterID), ErrBuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT value FROM quest_globals " + "WHERE name = 'bot_spawn_limit' AND charid = %i", + botOwnerCharacterID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(ErrBuf); + if (results.RowCount() != 1) + return 0; - safe_delete_array(Query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { From 2429980fd5eae59abf4846f348d2d18b9e850c25 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:21:17 -0700 Subject: [PATCH 050/368] CreatedBotCount converted to QueryDatabase --- zone/bot.cpp | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index b38a74f79..f782090d7 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4915,30 +4915,23 @@ uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessag } uint32 Bot::CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { - uint32 Result = 0; - if(botOwnerCharacterID > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterID == 0) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(BotID) FROM bots WHERE BotOwnerCharacterID=%i", botOwnerCharacterID), ErrBuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT COUNT(BotID) FROM bots " + "WHERE BotOwnerCharacterID=%i", botOwnerCharacterID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(ErrBuf); + if (results.RowCount() != 1) + return 0; - safe_delete_array(Query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage) { From 081192d29eecc71f9bfe370bab8187f875854122 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:24:30 -0700 Subject: [PATCH 051/368] GetBotOwnerCharacterID converted to QueryDatabase --- zone/bot.cpp | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index f782090d7..ff6c2c0b5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4935,29 +4935,22 @@ uint32 Bot::CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessag } uint32 Bot::GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage) { - uint32 Result = 0; - if(botID > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botID == 0) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotOwnerCharacterID FROM bots WHERE BotID = %u", botID), ErrBuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - if(DataRow = mysql_fetch_row(DatasetResult)) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT BotOwnerCharacterID FROM bots WHERE BotID = %u", botID); + auto results = database.QueryDatabase(query); + if (results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(ErrBuf); + if (results.RowCount() != 1) + return 0; - safe_delete_array(Query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) { From 2095380ba469473434fab5bf35465340eadb849c Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:31:14 -0700 Subject: [PATCH 052/368] SetBotGuildMembership converted to QueryDatabase --- zone/bot.cpp | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ff6c2c0b5..c8e93d735 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -8557,28 +8557,19 @@ bool Bot::ProcessGuildRemoval(Client* guildOfficer, std::string botName) { } void Bot::SetBotGuildMembership(uint32 botId, uint32 guildid, uint8 rank) { - if(botId > 0) { - std::string errorMessage; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + if(botId == 0) + return; - if(guildid > 0) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "REPLACE INTO botguildmembers SET char_id = %u, guild_id = %u, rank = %u;", botId, guildid, rank), errbuf)) { - errorMessage = std::string(errbuf); - } - } - else { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botguildmembers WHERE char_id = %u;", botId), errbuf)) { - errorMessage = std::string(errbuf); - } - } + if(guildid > 0) { + std::string query = StringFormat("REPLACE INTO botguildmembers " + "SET char_id = %u, guild_id = %u, rank = %u;", + botId, guildid, rank); + auto results = database.QueryDatabase(query); + return; + } - safe_delete_array(query); - - if(!errorMessage.empty()) { - // TODO: Log this error message to the zone error log - } - } + std::string query = StringFormat("DELETE FROM botguildmembers WHERE char_id = %u;", botId); + auto results = database.QueryDatabase(query); } void Bot::LoadGuildMembership(uint32* guildId, uint8* guildRank, std::string* guildName) { From 049a0bf787e4f98b8a3a31e4a047940ef4a05b01 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:41:21 -0700 Subject: [PATCH 053/368] LoadGuildMembership converted to QueryDatabase --- zone/bot.cpp | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c8e93d735..3a475c5ce 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -8573,33 +8573,23 @@ void Bot::SetBotGuildMembership(uint32 botId, uint32 guildid, uint8 rank) { } void Bot::LoadGuildMembership(uint32* guildId, uint8* guildRank, std::string* guildName) { - if(guildId && guildRank && guildName) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT gm.guild_id, gm.rank, g.name FROM vwGuildMembers AS gm JOIN guilds AS g ON gm.guild_id = g.id WHERE gm.char_id = %u AND gm.mobtype = 'B';", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - *guildId = atoi(DataRow[0]); - *guildRank = atoi(DataRow[1]); - *guildName = std::string(DataRow[2]); - break; - } + if(guildId == nullptr || guildRank == nullptr || guildName == nullptr) + return; - mysql_free_result(DatasetResult); - } + std::string query = StringFormat("SELECT gm.guild_id, gm.rank, g.name " + "FROM vwGuildMembers AS gm JOIN guilds AS g " + "ON gm.guild_id = g.id " + "WHERE gm.char_id = %u AND gm.mobtype = 'B';", + GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success() || results.RowCount() == 0) + return; - safe_delete(Query); - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } + auto row = results.begin(); + *guildId = atoi(row[0]); + *guildRank = atoi(row[1]); + *guildName = std::string(row[2]); } int32 Bot::CalcMaxMana() { From 73c8d3d09da9b749b793a567fcf6220f91100da6 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:46:41 -0700 Subject: [PATCH 054/368] ProcessBoyCommands converted to QueryDatabase --- zone/bot.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 3a475c5ce..28a82cdc7 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11349,18 +11349,22 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 botid = c->GetTarget()->CastToBot()->GetBotID(); std::string errorMessage; - char* Query = 0; + int setslot = atoi(sep->arg[2]); uint8 red = atoi(sep->arg[3]); uint8 green = atoi(sep->arg[4]); uint8 blue = atoi(sep->arg[5]); uint32 setcolor = (red << 16) | (green << 8) | blue; + std::string query = StringFormat("UPDATE botinventory SET color = %u " + "WHERE slotID = %i AND botID = %u", + setcolor, setslot, botid); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE botinventory SET color = %u WHERE slotID = %i AND botID = %u",setcolor, setslot, botid))){ - int slotmaterial = Inventory::CalcMaterialFromSlot(setslot); - c->GetTarget()->CastToBot()->SendWearChange(slotmaterial); - } + int slotmaterial = Inventory::CalcMaterialFromSlot(setslot); + c->GetTarget()->CastToBot()->SendWearChange(slotmaterial); } else { c->Message(15, "You must target a bot you own to do this."); @@ -11368,11 +11372,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } // Help for coloring bot armor - if(!strcasecmp(sep->arg[1], "help") && !strcasecmp(sep->arg[2], "armorcolor") ){ + if(!strcasecmp(sep->arg[1], "help") && !strcasecmp(sep->arg[2], "armorcolor") ){ //read from db - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; c->Message(0, "-----------------#bot armorcolor help-----------------------------"); c->Message(0, "Armor: 17(Chest/Robe), 7(Arms), 9(Bracer), 12(Hands), 18(Legs), 19(Boots), 2(Helm)"); From 95dc0c5fc8c64b551155e5f57a1d383abaebc449 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 18:49:47 -0700 Subject: [PATCH 055/368] GetEquipmentColor converted to QueryDatabase --- zone/bot.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 28a82cdc7..a635fc959 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -16125,27 +16125,20 @@ uint32 Bot::GetEquipmentColor(uint8 material_slot) const { //Bot tints uint32 slotid = 0; - uint32 returncolor = 0; uint32 botid = this->GetBotID(); //Translate code slot # to DB slot # slotid = Inventory::CalcSlotFromMaterial(material_slot); //read from db - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + std::string query = StringFormat("SELECT color FROM botinventory " + "WHERE BotID = %u AND SlotID = %u", botid, slotid); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() != 1) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT color FROM botinventory WHERE BotID = %u AND SlotID = %u", botid, slotid), 0, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - returncolor = atoul(DataRow[0]); - } - mysql_free_result(DatasetResult); - safe_delete_array(Query); - } - return returncolor; + auto row = results.begin(); + return atoul(row[0]); } int Bot::GetRawACNoShield(int &shield_ac) From 091c8ea5f14ec826b6c7f97c8bd60dd264f5ec67 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 19:33:20 -0700 Subject: [PATCH 056/368] BazaarAuditTrail converted to QueryDatabase --- zone/trading.cpp | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/zone/trading.cpp b/zone/trading.cpp index c89af4010..1b431cd2a 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -133,7 +133,7 @@ void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) { client->Kick(); return; } - + SendItemData(inst, trade_slot_id); _log(TRADING__HOLDER, "%s added item '%s' to trade slot %i", owner->GetName(), inst->GetItem()->Name, trade_slot_id); @@ -451,13 +451,13 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st mlog(TRADING__CLIENT, "Finishing trade with client %s", other->GetName()); this->AddMoneyToPP(other->trade->cp, other->trade->sp, other->trade->gp, other->trade->pp, true); - + // step 0: pre-processing // QS code if (RuleB(QueryServ, PlayerLogTrades) && event_entry && event_details) { qs_audit = (QSPlayerLogTrade_Struct*)event_entry; qs_log = true; - + if (finalizer) { qs_audit->char2_id = this->character_id; @@ -506,7 +506,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st detail->aug_5 = inst->GetAugmentItemID(5); event_details->push_back(detail); - + if (finalizer) qs_audit->char2_count += detail->charges; else @@ -886,7 +886,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if (baginst) { const Item_Struct* bagitem = baginst->GetItem(); if (bagitem && (GetGM() || (bagitem->NoDrop != 0 && baginst->IsInstNoDrop() == false))) { - tradingWith->CastToNPC()->AddLootDrop(bagitem, &tradingWith->CastToNPC()->itemlist, + tradingWith->CastToNPC()->AddLootDrop(bagitem, &tradingWith->CastToNPC()->itemlist, baginst->GetCharges(), 1, 127, true, true); } else if (RuleB(NPC, ReturnNonQuestNoDropItems)) { @@ -895,8 +895,8 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st } } } - - tradingWith->CastToNPC()->AddLootDrop(item, &tradingWith->CastToNPC()->itemlist, + + tradingWith->CastToNPC()->AddLootDrop(item, &tradingWith->CastToNPC()->itemlist, inst->GetCharges(), 1, 127, true, true); } // Return NO DROP and Attuned items being handed into a non-quest NPC if the rule is true @@ -943,7 +943,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if(tradingWith->GetAppearance() != eaDead) { tradingWith->FaceTarget(this); } - + ItemInst *insts[4] = { 0 }; for(int i = EmuConstants::TRADE_BEGIN; i <= EmuConstants::TRADE_NPC_END; ++i) { insts[i - EmuConstants::TRADE_BEGIN] = m_inv.PopItem(i); @@ -1475,18 +1475,16 @@ void Client::TradeRequestFailed(const EQApplicationPacket* app) { } -static void BazaarAuditTrail(const char *Seller, const char *Buyer, const char *ItemName, int Quantity, int TotalCost, int TranType) { +static void BazaarAuditTrail(const char *seller, const char *buyer, const char *itemName, int quantity, int totalCost, int tranType) { - const char *AuditQuery="INSERT INTO `trader_audit` (`time`, `seller`, `buyer`, `itemname`, `quantity`, `totalcost`, `trantype`) " - "VALUES (NOW(), '%s', '%s', '%s', %i, %i, %i)"; + std::string query = StringFormat("INSERT INTO `trader_audit` " + "(`time`, `seller`, `buyer`, `itemname`, `quantity`, `totalcost`, `trantype`) " + "VALUES (NOW(), '%s', '%s', '%s', %i, %i, %i)", + seller, buyer, itemName, quantity, totalCost, tranType); + auto results = database.QueryDatabase(query); + if(!results.Success()) + _log(TRADING__CLIENT, "Audit write error: %s : %s", query.c_str(), results.ErrorMessage().c_str()); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - if(!database.RunQuery(query, MakeAnyLenString(&query, AuditQuery, Seller, Buyer, ItemName, Quantity, TotalCost, TranType), errbuf)) - _log(TRADING__CLIENT, "Audit write error: %s : %s", query, errbuf); - - safe_delete_array(query); } From 6eba6720132aa8dab5758b855cff97e55aeb1ed0 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 19:39:50 -0700 Subject: [PATCH 057/368] SendBazaarWelcome converted to QueryDatabase --- zone/trading.cpp | 55 ++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/zone/trading.cpp b/zone/trading.cpp index 1b431cd2a..67fbd004d 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1624,50 +1624,35 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat void Client::SendBazaarWelcome(){ - char errbuf[MYSQL_ERRMSG_SIZE]; + const std::string query = "SELECT COUNT(DISTINCT char_id), count(char_id) FROM trader"; + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() == 1){ + auto row = results.begin(); - char* query = 0; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); - MYSQL_RES *result; + memset(outapp->pBuffer,0,outapp->size); - MYSQL_ROW row; + BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer; - if (database.RunQuery(query,MakeAnyLenString(&query, "select count(distinct char_id),count(char_id) from trader"),errbuf,&result)){ - if(mysql_num_rows(result)==1){ + bws->Beginning.Action = BazaarWelcome; - row = mysql_fetch_row(result); + bws->Traders = atoi(row[0]); + bws->Items = atoi(row[1]); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); + QueuePacket(outapp); - memset(outapp->pBuffer,0,outapp->size); + safe_delete(outapp); + } - BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer; + const std::string buyerCountQuery = "SELECT COUNT(DISTINCT charid) FROM buyer"; + results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() != 1) + return; - bws->Beginning.Action = BazaarWelcome; - - bws->Items = atoi(row[1]); - - bws->Traders = atoi(row[0]); - - QueuePacket(outapp); - - safe_delete(outapp); - } - mysql_free_result(result); - } - safe_delete_array(query); - - if (database.RunQuery(query,MakeAnyLenString(&query, "select count(distinct charid) from buyer"),errbuf,&result)){ - if(mysql_num_rows(result)==1) { - - row = mysql_fetch_row(result); - - Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them," - " or use /buyer to set up your own Buy Lines.", atoi(row[0])); - } - mysql_free_result(result); - } - safe_delete_array(query); + auto row = results.begin(); + Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them, " + "or use /buyer to set up your own Buy Lines.", atoi(row[0])); } void Client::SendBazaarResults(uint32 TraderID, uint32 Class_, uint32 Race, uint32 ItemStat, uint32 Slot, uint32 Type, From 36c1d88eac8a041e9d66f8f78de7c1df1110a24c Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 20:18:07 -0700 Subject: [PATCH 058/368] SendBazaarResults converted to QueryDatabase --- zone/trading.cpp | 398 ++++++++++++++++++++++------------------------- 1 file changed, 187 insertions(+), 211 deletions(-) diff --git a/zone/trading.cpp b/zone/trading.cpp index 67fbd004d..2df409b22 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1658,314 +1658,290 @@ void Client::SendBazaarWelcome(){ void Client::SendBazaarResults(uint32 TraderID, uint32 Class_, uint32 Race, uint32 ItemStat, uint32 Slot, uint32 Type, char Name[64], uint32 MinPrice, uint32 MaxPrice) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - std::string Search, Values; - MYSQL_RES *Result; - MYSQL_ROW Row; - char Tmp[100] = {0}; + std::string searchValues = " COUNT(item_id), trader.*, items.name "; + std::string searchCriteria = " WHERE trader.item_id = items.id "; - Values.append("count(item_id),trader.*,items.name"); + if(TraderID > 0) { + Client* trader = entity_list.GetClientByID(TraderID); - Search.append("where trader.item_id=items.id"); + if(trader) + searchCriteria.append(StringFormat(" AND trader.char_id = %i", trader->CharacterID())); + } - if(TraderID > 0){ - Client* Trader = entity_list.GetClientByID(TraderID); + if(MinPrice != 0) + searchCriteria.append(StringFormat(" AND trader.item_cost >= %i", MinPrice)); - if(Trader){ - sprintf(Tmp," and trader.char_id=%i",Trader->CharacterID()); - Search.append(Tmp); - } + if(MaxPrice != 0) + searchCriteria.append(StringFormat(" AND trader.item_cost <= %i", MaxPrice)); + if(strlen(Name) > 0) { + char *safeName = RemoveApostrophes(Name); + searchCriteria.append(StringFormat(" AND items.name LIKE '%%%s%%'", safeName)); + safe_delete_array(safeName); } - std::string SearchrResults; - if(MinPrice != 0){ - sprintf(Tmp, " and trader.item_cost>=%i", MinPrice); - Search.append(Tmp); - } - if(MaxPrice != 0){ - sprintf(Tmp, " and trader.item_cost<=%i", MaxPrice); - Search.append(Tmp); - } - if(strlen(Name) > 0){ - char *SafeName = RemoveApostrophes(Name); - sprintf(Tmp, " and items.name like '%%%s%%'", SafeName); - safe_delete_array(SafeName); - Search.append(Tmp); - } - if(Class_ != 0xFFFFFFFF){ - sprintf(Tmp, " and mid(reverse(bin(items.classes)),%i,1)=1", Class_); - Search.append(Tmp); - } - if(Race!=0xFFFFFFFF){ - sprintf(Tmp, " and mid(reverse(bin(items.races)),%i,1)=1", Race); - Search.append(Tmp); - } - if(Slot!=0xFFFFFFFF){ - sprintf(Tmp, " and mid(reverse(bin(items.slots)),%i,1)=1", Slot + 1); - Search.append(Tmp); - } - if(Type!=0xFFFFFFFF){ + if(Class_ != 0xFFFFFFFF) + searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.classes)), %i, 1) = 1", Class_)); - switch(Type){ + if(Race != 0xFFFFFFFF) + searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.races)), %i, 1) = 1", Race)); - case 0: - // 1H Slashing - Search.append(" and items.itemtype=0 and damage>0"); - break; - case 31: - Search.append(" and items.itemclass=2"); - break; - case 46: - Search.append(" and items.spellid>0 and items.spellid<65000"); - break; - case 47: - Search.append(" and items.spellid=998"); - break; - case 48: - Search.append(" and items.spellid>=1298 and items.spellid<=1307"); - break; - case 49: - Search.append(" and items.focuseffect>0"); - break; - default: - sprintf(Tmp, " and items.itemtype=%i", Type); - Search.append(Tmp); - } - } + if(Slot != 0xFFFFFFFF) + searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.slots)), %i, 1) = 1", Slot + 1)); + + switch(Type){ + case 0xFFFFFFFF: + break; + case 0: + // 1H Slashing + searchCriteria.append(" AND items.itemtype = 0 AND damage > 0"); + break; + case 31: + searchCriteria.append(" AND items.itemclass = 2"); + break; + case 46: + searchCriteria.append(" AND items.spellid > 0 AND items.spellid < 65000"); + break; + case 47: + searchCriteria.append(" AND items.spellid = 998"); + break; + case 48: + searchCriteria.append(" AND items.spellid >= 1298 AND items.spellid <= 1307"); + break; + case 49: + searchCriteria.append(" AND items.focuseffect > 0"); + break; + + default: + searchCriteria.append(StringFormat(" AND items.itemtype = %i", Type)); + } switch(ItemStat) { case STAT_AC: - Search.append(" and items.ac>0"); - Values.append(",items.ac"); + searchCriteria.append(" AND items.ac > 0"); + searchValues.append(", items.ac"); break; case STAT_AGI: - Search.append(" and items.aagi>0"); - Values.append(",items.aagi"); + searchCriteria.append(" AND items.aagi > 0"); + searchValues.append(", items.aagi"); break; case STAT_CHA: - Search.append(" and items.acha>0"); - Values.append(",items.acha"); + searchCriteria.append(" AND items.acha > 0"); + searchValues.append(", items.acha"); break; case STAT_DEX: - Search.append(" and items.adex>0"); - Values.append(",items.adex"); + searchCriteria.append(" AND items.adex > 0"); + searchValues.append(", items.adex"); break; case STAT_INT: - Search.append(" and items.aint>0"); - Values.append(",items.aint"); + searchCriteria.append(" AND items.aint > 0"); + searchValues.append(", items.aint"); break; case STAT_STA: - Search.append(" and items.asta>0"); - Values.append(",items.asta"); + searchCriteria.append(" AND items.asta > 0"); + searchValues.append(", items.asta"); break; case STAT_STR: - Search.append(" and items.astr>0"); - Values.append(",items.astr"); + searchCriteria.append(" AND items.astr > 0"); + searchValues.append(", items.astr"); break; case STAT_WIS: - Search.append(" and items.awis>0"); - Values.append(",items.awis"); + searchCriteria.append(" AND items.awis > 0"); + searchValues.append(", items.awis"); break; case STAT_COLD: - Search.append(" and items.cr>0"); - Values.append(",items.cr"); + searchCriteria.append(" AND items.cr > 0"); + searchValues.append(", items.cr"); break; case STAT_DISEASE: - Search.append(" and items.dr>0"); - Values.append(",items.dr"); + searchCriteria.append(" AND items.dr > 0"); + searchValues.append(", items.dr"); break; case STAT_FIRE: - Search.append(" and items.fr>0"); - Values.append(",items.fr"); + searchCriteria.append(" AND items.fr > 0"); + searchValues.append(", items.fr"); break; case STAT_MAGIC: - Values.append(",items.mr"); - Search.append(" and items.mr>0"); + searchCriteria.append(" AND items.mr > 0"); + searchValues.append(", items.mr"); break; case STAT_POISON: - Search.append(" and items.pr>0"); - Values.append(",items.pr"); + searchCriteria.append(" AND items.pr > 0"); + searchValues.append(", items.pr"); break; case STAT_HP: - Search.append(" and items.hp>0"); - Values.append(",items.hp"); + searchCriteria.append(" AND items.hp > 0"); + searchValues.append(", items.hp"); break; case STAT_MANA: - Search.append(" and items.mana>0"); - Values.append(",items.mana"); + searchCriteria.append(" AND items.mana > 0"); + searchValues.append(", items.mana"); break; case STAT_ENDURANCE: - Search.append(" and items.endur>0"); - Values.append(",items.endur"); + searchCriteria.append(" AND items.endur > 0"); + searchValues.append(", items.endur"); break; case STAT_ATTACK: - Search.append(" and items.attack>0"); - Values.append(",items.attack"); + searchCriteria.append(" AND items.attack > 0"); + searchValues.append(", items.attack"); break; case STAT_HP_REGEN: - Search.append(" and items.regen>0"); - Values.append(",items.regen"); + searchCriteria.append(" AND items.regen > 0"); + searchValues.append(", items.regen"); break; case STAT_MANA_REGEN: - Search.append(" and items.manaregen>0"); - Values.append(",items.manaregen"); + searchCriteria.append(" AND items.manaregen > 0"); + searchValues.append(", items.manaregen"); break; case STAT_HASTE: - Search.append(" and items.haste>0"); - Values.append(",items.haste"); + searchCriteria.append(" AND items.haste > 0"); + searchValues.append(", items.haste"); break; case STAT_DAMAGE_SHIELD: - Search.append(" and items.damageshield>00"); - Values.append(",items.damageshield"); + searchCriteria.append(" AND items.damageshield > 0"); + searchValues.append(", items.damageshield"); break; default: - Values.append(",0"); + searchValues.append(", 0"); break; } - Values.append(",sum(charges), items.stackable "); + std::string query = StringFormat("SELECT %s, SUM(charges), items.stackable " + "FROM trader, items %s GROUP BY items.id, charges, char_id LIMIT %i", + searchValues.c_str(), searchCriteria.c_str(), RuleI(Bazaar, MaxSearchResults)); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + _log(TRADING__CLIENT, "Failed to retrieve Bazaar Search!! %s %s\n", query.c_str(), results.ErrorMessage().c_str()); + return; + } - if (database.RunQuery(Query,MakeAnyLenString(&Query, "select %s from trader,items %s group by items.id,charges,char_id limit %i", - Values.c_str(),Search.c_str(), RuleI(Bazaar, MaxSearchResults)),errbuf,&Result)){ + _log(TRADING__CLIENT, "SRCH: %s", query.c_str()); - _log(TRADING__CLIENT, "SRCH: %s", Query); - safe_delete_array(Query); + int Size = 0; + uint32 ID = 0; - int Size = 0; - uint32 ID = 0; - - if(mysql_num_rows(Result) == static_cast(RuleI(Bazaar, MaxSearchResults))) + if (results.RowCount() == static_cast(RuleI(Bazaar, MaxSearchResults))) Message(15, "Your search reached the limit of %i results. Please narrow your search down by selecting more options.", RuleI(Bazaar, MaxSearchResults)); - if(mysql_num_rows(Result) == 0){ - EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct)); - BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer; - brds->TraderID = ID; - brds->Type = BazaarSearchDone; - brds->Unknown008 = 0xFFFFFFFF; - brds->Unknown012 = 0xFFFFFFFF; - brds->Unknown016 = 0xFFFFFFFF; - this->QueuePacket(outapp2); - _pkt(TRADING__PACKETS,outapp2); - safe_delete(outapp2); - mysql_free_result(Result); - return; - } - Size = mysql_num_rows(Result) * sizeof(BazaarSearchResults_Struct); - uchar *buffer = new uchar[Size]; - uchar *bufptr = buffer; - memset(buffer, 0, Size); - - int Action = BazaarSearchResults; - uint32 Cost = 0; - int32 SerialNumber = 0; - char Name[64] = {0}; - int Count = 0; - uint32 StatValue=0; - - while ((Row = mysql_fetch_row(Result))) { - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Action); - Count = atoi(Row[0]); - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Count); - SerialNumber = atoi(Row[3]); - VARSTRUCT_ENCODE_TYPE(int32, bufptr, SerialNumber); - Client* Trader2=entity_list.GetClientByCharID(atoi(Row[1])); - if(Trader2){ - ID = Trader2->GetID(); - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, ID); - } - else{ - _log(TRADING__CLIENT, "Unable to find trader: %i\n",atoi(Row[1])); - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0); - } - Cost = atoi(Row[5]); - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Cost); - StatValue = atoi(Row[8]); - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, StatValue); - bool Stackable = atoi(Row[10]); - if(Stackable) { - int Charges = atoi(Row[9]); - sprintf(Name, "%s(%i)", Row[7], Charges); - } - else - sprintf(Name,"%s(%i)",Row[7], Count); - - memcpy(bufptr,&Name, strlen(Name)); - - bufptr += 64; - - // Extra fields for SoD+ - // - if(Trader2) - sprintf(Name, "%s", Trader2->GetName()); - else - sprintf(Name, "Unknown"); - - memcpy(bufptr,&Name, strlen(Name)); - - bufptr += 64; - - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, atoi(Row[1])); // ItemID - } - mysql_free_result(Result); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, Size); - - memcpy(outapp->pBuffer, buffer, Size); - - this->QueuePacket(outapp); - - _pkt(TRADING__PACKETS,outapp); - - safe_delete(outapp); - safe_delete_array(buffer); - + if(results.RowCount() == 0) { EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct)); BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer; - brds->TraderID = ID; brds->Type = BazaarSearchDone; - brds->Unknown008 = 0xFFFFFFFF; brds->Unknown012 = 0xFFFFFFFF; brds->Unknown016 = 0xFFFFFFFF; - this->QueuePacket(outapp2); - _pkt(TRADING__PACKETS,outapp2); safe_delete(outapp2); - - } - else{ - _log(TRADING__CLIENT, "Failed to retrieve Bazaar Search!! %s %s\n", Query, errbuf); - safe_delete_array(Query); return; } + + Size = results.RowCount() * sizeof(BazaarSearchResults_Struct); + uchar *buffer = new uchar[Size]; + uchar *bufptr = buffer; + memset(buffer, 0, Size); + + int Action = BazaarSearchResults; + uint32 Cost = 0; + int32 SerialNumber = 0; + char Name[64] = {0}; + int Count = 0; + uint32 StatValue=0; + + for (auto row = results.begin(); row != results.end(); ++row) { + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Action); + Count = atoi(row[0]); + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Count); + SerialNumber = atoi(row[3]); + VARSTRUCT_ENCODE_TYPE(int32, bufptr, SerialNumber); + Client* Trader2=entity_list.GetClientByCharID(atoi(row[1])); + if(Trader2){ + ID = Trader2->GetID(); + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, ID); + } + else{ + _log(TRADING__CLIENT, "Unable to find trader: %i\n",atoi(row[1])); + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0); + } + Cost = atoi(row[5]); + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Cost); + StatValue = atoi(row[8]); + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, StatValue); + bool Stackable = atoi(row[10]); + if(Stackable) { + int Charges = atoi(row[9]); + sprintf(Name, "%s(%i)", row[7], Charges); + } + else + sprintf(Name,"%s(%i)",row[7], Count); + + memcpy(bufptr,&Name, strlen(Name)); + + bufptr += 64; + + // Extra fields for SoD+ + // + if(Trader2) + sprintf(Name, "%s", Trader2->GetName()); + else + sprintf(Name, "Unknown"); + + memcpy(bufptr,&Name, strlen(Name)); + + bufptr += 64; + + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, atoi(row[1])); // ItemID + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, Size); + + memcpy(outapp->pBuffer, buffer, Size); + + this->QueuePacket(outapp); + + _pkt(TRADING__PACKETS,outapp); + + safe_delete(outapp); + safe_delete_array(buffer); + + EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct)); + BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer; + + brds->TraderID = ID; + brds->Type = BazaarSearchDone; + + brds->Unknown008 = 0xFFFFFFFF; + brds->Unknown012 = 0xFFFFFFFF; + brds->Unknown016 = 0xFFFFFFFF; + + this->QueuePacket(outapp2); + + _pkt(TRADING__PACKETS,outapp2); + safe_delete(outapp2); } static void UpdateTraderCustomerItemsAdded(uint32 CustomerID, TraderCharges_Struct* gis, uint32 ItemID) { From a8b8f7109294bb0e5fa8a24b55d9ae332e39a288 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 20:24:30 -0700 Subject: [PATCH 059/368] ShowBuyLines converted to QueryDatabase --- zone/trading.cpp | 78 +++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/zone/trading.cpp b/zone/trading.cpp index 2df409b22..10cf33e69 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -2402,60 +2402,44 @@ void Client::ShowBuyLines(const EQApplicationPacket *app) { safe_delete(outapp); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - char ItemName[64]; - std::string Search, Values; - MYSQL_RES *Result; - MYSQL_ROW Row; + std::string query = StringFormat("SELECT * FROM buyer WHERE charid = %i", Buyer->CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return; - if (database.RunQuery(Query,MakeAnyLenString(&Query, "select * from buyer where charid = %i", - Buyer->CharacterID()),errbuf,&Result)){ + for (auto row = results.begin(); row != results.end(); ++row) { + char ItemName[64]; + uint32 BuySlot = atoi(row[1]); + uint32 ItemID = atoi(row[2]); + strcpy(ItemName, row[3]); + uint32 Quantity = atoi(row[4]); + uint32 Price = atoi(row[5]); - if(mysql_num_rows(Result) == 0) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 936); - safe_delete_array(Query); + char *Buf = (char *)outapp->pBuffer; - mysql_free_result(Result); + const Item_Struct* item = database.GetItem(ItemID); - return; - } + if(!item) + continue; - while ((Row = mysql_fetch_row(Result))) { + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerInspectWindow); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot); + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag + VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID); + VARSTRUCT_ENCODE_STRING(Buf, ItemName); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity); + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Buyer->GetID()); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); + VARSTRUCT_ENCODE_STRING(Buf, Buyer->GetName()); - uint32 BuySlot = atoi(Row[1]); - uint32 ItemID = atoi(Row[2]); - strcpy(ItemName, Row[3]); - uint32 Quantity = atoi(Row[4]); - uint32 Price = atoi(Row[5]); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 936); - - char *Buf = (char *)outapp->pBuffer; - - const Item_Struct* item = database.GetItem(ItemID); - - if(!item) continue; - - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerInspectWindow); - VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot); - VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag - VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID); - VARSTRUCT_ENCODE_STRING(Buf, ItemName); - VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon); - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity); - VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price); - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Buyer->GetID()); - VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); - VARSTRUCT_ENCODE_STRING(Buf, Buyer->GetName()); - - _pkt(TRADING__BARTER, outapp); - QueuePacket(outapp); - } - mysql_free_result(Result); - } - safe_delete_array(Query); + _pkt(TRADING__BARTER, outapp); + QueuePacket(outapp); + } } void Client::SellToBuyer(const EQApplicationPacket *app) { From 081905dbc31627ef6d94bf37423fd30ba1f04d56 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 20:34:07 -0700 Subject: [PATCH 060/368] SendBuyerResults converted to QueryDatabase --- zone/trading.cpp | 155 ++++++++++++++++++++++------------------------- 1 file changed, 72 insertions(+), 83 deletions(-) diff --git a/zone/trading.cpp b/zone/trading.cpp index 10cf33e69..98b3c8a5a 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -2263,103 +2263,92 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { } -void Client::SendBuyerResults(char* SearchString, uint32 SearchID) { +void Client::SendBuyerResults(char* searchString, uint32 searchID) { // This method is called when a potential seller in the /barter window searches for matching buyers // - _log(TRADING__BARTER, "Client::SendBuyerResults %s\n", SearchString); + _log(TRADING__BARTER, "Client::SendBuyerResults %s\n", searchString); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - char ItemName[64]; - std::string Search, Values; - MYSQL_RES *Result; - MYSQL_ROW Row; + char* escSearchString = new char[strlen(searchString) * 2 + 1]; + database.DoEscapeString(escSearchString, searchString, strlen(searchString)); - char*EscSearchString = new char[strlen(SearchString) * 2 + 1]; - database.DoEscapeString(EscSearchString, SearchString, strlen(SearchString)); + std::string query = StringFormat("SELECT * FROM buyer WHERE itemname LIKE '%%%s%%' ORDER BY charid LIMIT %i", + escSearchString, RuleI(Bazaar, MaxBarterSearchResults)); + safe_delete_array(escSearchString); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + _log(TRADING__CLIENT, "Failed to retrieve Barter Search!! %s %s\n", query.c_str(), results.ErrorMessage().c_str()); + return; + } - if (database.RunQuery(Query,MakeAnyLenString(&Query, "select * from buyer where itemname like '%%%s%%' order by charid limit %i", - EscSearchString, RuleI(Bazaar, MaxBarterSearchResults)), errbuf, &Result)) { + int numberOfRows = results.RowCount(); - int NumberOfRows = mysql_num_rows(Result); + if(numberOfRows == RuleI(Bazaar, MaxBarterSearchResults)) + Message(15, "Your search found too many results; some are not displayed."); + else if(strlen(searchString) == 0) + Message(10, "There are %i Buy Lines.", numberOfRows); + else + Message(10, "There are %i Buy Lines that match the search string '%s'.", numberOfRows, searchString); - if(NumberOfRows == RuleI(Bazaar, MaxBarterSearchResults)) - Message(15, "Your search found too many results; some are not displayed."); - else { - if(strlen(SearchString) == 0) - Message(10, "There are %i Buy Lines.", NumberOfRows); - else - Message(10, "There are %i Buy Lines that match the search string '%s'.", - NumberOfRows, SearchString); + if(numberOfRows == 0) + return; + + uint32 lastCharID = 0; + Client *buyer = nullptr; + + for (auto row = results.begin(); row != results.end(); ++row) { + char itemName[64]; + + uint32 charID = atoi(row[0]); + uint32 buySlot = atoi(row[1]); + uint32 itemID = atoi(row[2]); + strcpy(itemName, row[3]); + uint32 quantity = atoi(row[4]); + uint32 price = atoi(row[5]); + + // Each item in the search results is sent as a single fixed length packet, although the position of + // the fields varies due to the use of variable length strings. The reason the packet is so big, is + // to allow item compensation, e.g. a buyer could offer to buy a Blade Of Carnage for 10000pp plus + // other items in exchange. Item compensation is not currently supported in EQEmu. + // + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 940); + + char *buf = (char *)outapp->pBuffer; + + const Item_Struct* item = database.GetItem(itemID); + + if(!item) + continue; + + // Save having to scan the client list when dealing with multiple buylines for the same Character. + if(charID != lastCharID) { + buyer = entity_list.GetClientByCharID(charID); + lastCharID = charID; } - if(NumberOfRows == 0) { - mysql_free_result(Result); - safe_delete_array(Query); - return; - } + if(!buyer) + continue; - uint32 LastCharID = 0; - Client *Buyer = nullptr; + VARSTRUCT_ENCODE_TYPE(uint32, buf, Barter_BuyerSearchResults); // Command + VARSTRUCT_ENCODE_TYPE(uint32, buf, searchID); // Match up results with the request + VARSTRUCT_ENCODE_TYPE(uint32, buf, buySlot); // Slot in this Buyer's list + VARSTRUCT_ENCODE_TYPE(uint8, buf, 0x01); // Unknown - probably a flag field + VARSTRUCT_ENCODE_TYPE(uint32, buf, itemID); // ItemID + VARSTRUCT_ENCODE_STRING(buf, itemName); // Itemname + VARSTRUCT_ENCODE_TYPE(uint32, buf, item->Icon); // Icon + VARSTRUCT_ENCODE_TYPE(uint32, buf, quantity); // Quantity + VARSTRUCT_ENCODE_TYPE(uint8, buf, 0x01); // Unknown - probably a flag field + VARSTRUCT_ENCODE_TYPE(uint32, buf, price); // Price + VARSTRUCT_ENCODE_TYPE(uint32, buf, buyer->GetID()); // Entity ID + VARSTRUCT_ENCODE_TYPE(uint32, buf, 0); // Flag for + Items , probably ItemCount + VARSTRUCT_ENCODE_STRING(buf, buyer->GetName()); // Seller Name - while ((Row = mysql_fetch_row(Result))) { + _pkt(TRADING__BARTER, outapp); - uint32 CharID = atoi(Row[0]); - uint32 BuySlot = atoi(Row[1]); - uint32 ItemID = atoi(Row[2]); - strcpy(ItemName, Row[3]); - uint32 Quantity = atoi(Row[4]); - uint32 Price = atoi(Row[5]); + QueuePacket(outapp); + safe_delete(outapp); + } - // Each item in the search results is sent as a single fixed length packet, although the position of - // the fields varies due to the use of variable length strings. The reason the packet is so big, is - // to allow item compensation, e.g. a buyer could offer to buy a Blade Of Carnage for 10000pp plus - // other items in exchange. Item compensation is not currently supported in EQEmu. - // - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 940); - - char *Buf = (char *)outapp->pBuffer; - - const Item_Struct* item = database.GetItem(ItemID); - - if(!item) continue; - - // Save having to scan the client list when dealing with multiple buylines for the same Character. - if(CharID != LastCharID) { - Buyer = entity_list.GetClientByCharID(CharID); - LastCharID = CharID; - } - - if(!Buyer) continue; - - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerSearchResults); // Command - VARSTRUCT_ENCODE_TYPE(uint32, Buf, SearchID); // Match up results with the request - VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot); // Slot in this Buyer's list - VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0x01); // Unknown - probably a flag field - VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID); // ItemID - VARSTRUCT_ENCODE_STRING(Buf, ItemName); // Itemname - VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon); // Icon - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity); // Quantity - VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0x01); // Unknown - probably a flag field - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price); // Price - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Buyer->GetID()); // Entity ID - VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); // Flag for + Items , probably ItemCount - VARSTRUCT_ENCODE_STRING(Buf, Buyer->GetName()); // Seller Name - - _pkt(TRADING__BARTER, outapp); - - QueuePacket(outapp); - safe_delete(outapp); - } - - mysql_free_result(Result); - } - else{ - _log(TRADING__CLIENT, "Failed to retrieve Barter Search!! %s %s\n", Query, errbuf); - } - safe_delete_array(Query); - safe_delete_array(EscSearchString); } void Client::ShowBuyLines(const EQApplicationPacket *app) { From 58343480ff7da00c2ea18f8da023d14827ec9320 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 23:07:28 -0700 Subject: [PATCH 061/368] InsertQuestGlobal converted to QueryDatabase --- zone/questmgr.cpp | 128 +++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 70 deletions(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index fc31178ae..2ace6c0dd 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -172,7 +172,7 @@ void QuestManager::EndQuest() { cur = QTimerList.erase(cur); else ++cur; - } + } run.owner->Depop(); } quests_running_.pop(); @@ -447,7 +447,7 @@ void QuestManager::settimer(const char *timer_name, int seconds) { end = QTimerList.end(); while (cur != end) { - if(cur->mob && cur->mob == owner && cur->name == timer_name) + if(cur->mob && cur->mob == owner && cur->name == timer_name) { cur->Timer_.Enable(); cur->Timer_.Start(seconds * 1000, false); @@ -471,7 +471,7 @@ void QuestManager::settimerMS(const char *timer_name, int milliseconds) { end = QTimerList.end(); while (cur != end) { - if(cur->mob && cur->mob == owner && cur->name == timer_name) + if(cur->mob && cur->mob == owner && cur->name == timer_name) { cur->Timer_.Enable(); cur->Timer_.Start(milliseconds, false); @@ -1308,7 +1308,7 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti if (initiator && initiator->IsClient()){ // some events like waypoint and spawn don't have a player involved qgCharid=initiator->CharacterID(); - } + } else { qgCharid=-qgNpcid; // make char id negative npc id as a fudge } @@ -1335,81 +1335,69 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti /* Inserts global variable into quest_globals table */ int QuestManager::InsertQuestGlobal(int charid, int npcid, int zoneid, const char *varname, const char *varvalue, int duration) { - char *query = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; // Make duration string either "unix_timestamp(now()) + xxx" or "NULL" - std::stringstream duration_ss; - if (duration == INT_MAX) { - duration_ss << "NULL"; - } - else { - duration_ss << "unix_timestamp(now()) + " << duration; - } + std::string durationText = (duration == INT_MAX)? "NULL": StringFormat("unix_timestamp(now()) + %i", duration); /* NOTE: this should be escaping the contents of arglist npcwise a malicious script can arbitrarily alter the DB */ - uint32 last_id = 0; - if (!database.RunQuery(query, MakeAnyLenString(&query, - "REPLACE INTO quest_globals (charid, npcid, zoneid, name, value, expdate)" - "VALUES (%i, %i, %i, '%s', '%s', %s)", - charid, npcid, zoneid, varname, varvalue, duration_ss.str().c_str() - ), errbuf, nullptr, nullptr, &last_id)) - { - std::cerr << "setglobal error inserting " << varname << " : " << errbuf << std::endl; - } - safe_delete_array(query); - if(zone) { - /* Delete existing qglobal data and update zone processes */ - ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); - ServerQGlobalDelete_Struct *qgd = (ServerQGlobalDelete_Struct*)pack->pBuffer; - qgd->npc_id = npcid; - qgd->char_id = charid; - qgd->zone_id = zoneid; - qgd->from_zone_id = zone->GetZoneID(); - qgd->from_instance_id = zone->GetInstanceID(); - strcpy(qgd->name, varname); + std::string query = StringFormat("REPLACE INTO quest_globals " + "(charid, npcid, zoneid, name, value, expdate)" + "VALUES (%i, %i, %i, '%s', '%s', %s)", + charid, npcid, zoneid, varname, varvalue, durationText.c_str()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + std::cerr << "setglobal error inserting " << varname << " : " << results.ErrorMessage() << std::endl; - entity_list.DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id); - zone->DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id); + if(!zone) + return 0; - worldserver.SendPacket(pack); - safe_delete(pack); + /* Delete existing qglobal data and update zone processes */ + ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); + ServerQGlobalDelete_Struct *qgd = (ServerQGlobalDelete_Struct*)pack->pBuffer; + qgd->npc_id = npcid; + qgd->char_id = charid; + qgd->zone_id = zoneid; + qgd->from_zone_id = zone->GetZoneID(); + qgd->from_instance_id = zone->GetInstanceID(); + strcpy(qgd->name, varname); - /* Create new qglobal data and update zone processes */ - pack = new ServerPacket(ServerOP_QGlobalUpdate, sizeof(ServerQGlobalUpdate_Struct)); - ServerQGlobalUpdate_Struct *qgu = (ServerQGlobalUpdate_Struct*)pack->pBuffer; - qgu->npc_id = npcid; - qgu->char_id = charid; - qgu->zone_id = zoneid; - if(duration == INT_MAX) { - qgu->expdate = 0xFFFFFFFF; - } - else { - qgu->expdate = Timer::GetTimeSeconds() + duration; - } - strcpy((char*)qgu->name, varname); - strn0cpy((char*)qgu->value, varvalue, 128); - qgu->id = last_id; - qgu->from_zone_id = zone->GetZoneID(); - qgu->from_instance_id = zone->GetInstanceID(); + entity_list.DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id); + zone->DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id); - QGlobal temp; - temp.npc_id = npcid; - temp.char_id = charid; - temp.zone_id = zoneid; - temp.expdate = qgu->expdate; - temp.name.assign(qgu->name); - temp.value.assign(qgu->value); - entity_list.UpdateQGlobal(qgu->id, temp); - zone->UpdateQGlobal(qgu->id, temp); + worldserver.SendPacket(pack); + safe_delete(pack); - worldserver.SendPacket(pack); - safe_delete(pack); - } + /* Create new qglobal data and update zone processes */ + pack = new ServerPacket(ServerOP_QGlobalUpdate, sizeof(ServerQGlobalUpdate_Struct)); + ServerQGlobalUpdate_Struct *qgu = (ServerQGlobalUpdate_Struct*)pack->pBuffer; + qgu->npc_id = npcid; + qgu->char_id = charid; + qgu->zone_id = zoneid; + + qgu->expdate = (duration == INT_MAX)? 0xFFFFFFFF: Timer::GetTimeSeconds() + duration; + + strcpy((char*)qgu->name, varname); + strn0cpy((char*)qgu->value, varvalue, 128); + qgu->id = results.LastInsertedID(); + qgu->from_zone_id = zone->GetZoneID(); + qgu->from_instance_id = zone->GetInstanceID(); + + QGlobal temp; + temp.npc_id = npcid; + temp.char_id = charid; + temp.zone_id = zoneid; + temp.expdate = qgu->expdate; + temp.name.assign(qgu->name); + temp.value.assign(qgu->value); + entity_list.UpdateQGlobal(qgu->id, temp); + zone->UpdateQGlobal(qgu->id, temp); + + worldserver.SendPacket(pack); + safe_delete(pack); return 0; } @@ -1425,9 +1413,9 @@ void QuestManager::delglobal(const char *varname) { int qgZoneid=zone->GetZoneID(); int qgCharid=0; int qgNpcid=owner->GetNPCTypeID(); - - - + + + if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved { qgCharid=initiator->CharacterID(); @@ -1709,7 +1697,7 @@ void QuestManager::showgrid(int grid) { pt.z = atof(row[2]); pts.push_back(pt); } - mysql_free_result(result); + mysql_free_result(result); initiator->SendPathPacket(pts); } else // DB query error! From 1bb5c4e0d8a4c0cda834924842d84254caed5767 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 23:10:42 -0700 Subject: [PATCH 062/368] delglobal converted to QueryDatabase --- zone/questmgr.cpp | 53 ++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 2ace6c0dd..e951f0fee 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1408,22 +1408,14 @@ void QuestManager::targlobal(const char *varname, const char *value, const char void QuestManager::delglobal(const char *varname) { QuestManagerCurrentQuestVars(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; int qgZoneid=zone->GetZoneID(); int qgCharid=0; int qgNpcid=owner->GetNPCTypeID(); - - if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved - { qgCharid=initiator->CharacterID(); - } - - else { + else qgCharid=-qgNpcid; // make char id negative npc id as a fudge - } /* QS: PlayerLogQGlobalUpdate */ if (RuleB(QueryServ, PlayerLogQGlobalUpdate) && qgCharid && qgCharid > 0 && initiator && initiator->IsClient()){ @@ -1431,31 +1423,32 @@ void QuestManager::delglobal(const char *varname) { QServ->PlayerLogEvent(Player_Log_QGlobal_Update, qgCharid, event_desc); } - if (!database.RunQuery(query, - MakeAnyLenString(&query, - "DELETE FROM quest_globals WHERE name='%s'" - " && (npcid=0 || npcid=%i) && (charid=0 || charid=%i) && (zoneid=%i || zoneid=0)", - varname,qgNpcid,qgCharid,qgZoneid),errbuf)) - { - std::cerr << "delglobal error deleting " << varname << " : " << errbuf << std::endl; - } - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM quest_globals " + "WHERE name = '%s' " + "&& (npcid=0 || npcid=%i) " + "&& (charid=0 || charid=%i) " + "&& (zoneid=%i || zoneid=0)", + varname, qgNpcid, qgCharid, qgZoneid); + auto results = database.QueryDatabase(query); + if (!results.Success()) + std::cerr << "delglobal error deleting " << varname << " : " << results.ErrorMessage() << std::endl; - if(zone) { - ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); - ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer; + if(!zone) + return; - qgu->npc_id = qgNpcid; - qgu->char_id = qgCharid; - qgu->zone_id = qgZoneid; - strcpy(qgu->name, varname); + ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); + ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer; - entity_list.DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id); - zone->DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id); + qgu->npc_id = qgNpcid; + qgu->char_id = qgCharid; + qgu->zone_id = qgZoneid; + strcpy(qgu->name, varname); - worldserver.SendPacket(pack); - safe_delete(pack); - } + entity_list.DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id); + zone->DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id); + + worldserver.SendPacket(pack); + safe_delete(pack); } // Converts duration string to duration value (in seconds) From 882521836129c261e94564cf3a5a4d5e2cd9cedc Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 23:14:16 -0700 Subject: [PATCH 063/368] showgrid converted to QueryDatabase --- zone/questmgr.cpp | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index e951f0fee..333aaf7be 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1669,11 +1669,6 @@ void QuestManager::showgrid(int grid) { if(initiator == nullptr) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - FindPerson_Point pt; std::vector pts; @@ -1683,22 +1678,25 @@ void QuestManager::showgrid(int grid) { pts.push_back(pt); // Retrieve all waypoints for this grid - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `x`,`y`,`z` FROM grid_entries WHERE `gridid`=%i AND `zoneid`=%i ORDER BY `number`",grid,zone->GetZoneID()),errbuf,&result)) { - while((row = mysql_fetch_row(result))) { - pt.x = atof(row[0]); - pt.y = atof(row[1]); - pt.z = atof(row[2]); - pts.push_back(pt); - } - mysql_free_result(result); - initiator->SendPathPacket(pts); - } - else // DB query error! - { - LogFile->write(EQEMuLog::Quest, "Error loading grid %d for showgrid(): %s", grid, errbuf); + std::string query = StringFormat("SELECT `x`,`y`,`z` FROM grid_entries " + "WHERE `gridid` = %i AND `zoneid` = %i " + "ORDER BY `number`", grid, zone->GetZoneID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Quest, "Error loading grid %d for showgrid(): %s", grid, results.ErrorMessage().c_str()); return; - } - safe_delete_array(query); + } + + for(auto row = results.begin(); row != results.end(); ++row) { + pt.x = atof(row[0]); + pt.y = atof(row[1]); + pt.z = atof(row[2]); + + pts.push_back(pt); + } + + initiator->SendPathPacket(pts); + } //change the value of a spawn condition From b710c41c345254ab382c7dd78becb8604878e820 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 23:17:20 -0700 Subject: [PATCH 064/368] clearspawntimers converted to QueryDatabase --- zone/questmgr.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 333aaf7be..db7d53e47 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2272,19 +2272,19 @@ bool QuestManager::istaskappropriate(int task) { } void QuestManager::clearspawntimers() { - if(zone) { - //TODO: Dec 19, 2008, replace with code updated for current spawn timers. - LinkedListIterator iterator(zone->spawn2_list); - iterator.Reset(); - while (iterator.MoreElements()) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM respawn_times WHERE id=%lu AND " - "instance_id=%lu",(unsigned long)iterator.GetData()->GetID(), (unsigned long)zone->GetInstanceID()), errbuf); - safe_delete_array(query); - iterator.Advance(); - } + if(!zone) + return; + + //TODO: Dec 19, 2008, replace with code updated for current spawn timers. + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + while (iterator.MoreElements()) { + std::string query = StringFormat("DELETE FROM respawn_times " + "WHERE id = %lu AND instance_id = %lu", + (unsigned long)iterator.GetData()->GetID(), + (unsigned long)zone->GetInstanceID()); + auto results = database.QueryDatabase(query); + iterator.Advance(); } } From 221c667a232cd2131a18e51750bdcb89bd0dc4ac Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 3 Sep 2014 23:26:09 -0700 Subject: [PATCH 065/368] saylink converted to QueryDatabase --- zone/questmgr.cpp | 59 +++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 43 deletions(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index db7d53e47..51e2e568d 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2661,11 +2661,6 @@ void QuestManager::FlagInstanceByRaidLeader(uint32 zone, int16 version) const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkName) { QuestManagerCurrentQuestVars(); - const char *ERR_MYSQLERROR = "Error in saylink phrase queries"; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; int sayid = 0; int sz = strlen(Phrase); @@ -2673,42 +2668,26 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam database.DoEscapeString(escaped_string, Phrase, sz); // Query for an existing phrase and id in the saylink table - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string),errbuf,&result)) + std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string); + auto results = database.QueryDatabase(query); + if(results.Success()) { - if (mysql_num_rows(result) >= 1) - { - while((row = mysql_fetch_row(result))) - { + if (results.RowCount() >= 1) + for (auto row = results.begin();row != results.end(); ++row) sayid = atoi(row[0]); - } - mysql_free_result(result); - } else // Add a new saylink entry to the database and query it again for the new sayid number { - safe_delete_array(query); + query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string); + results = database.QueryDatabase(query); - database.RunQuery(query,MakeAnyLenString(&query,"INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string),errbuf); - safe_delete_array(query); - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `id` FROM saylink WHERE `phrase` = '%s'", escaped_string),errbuf,&result)) - { - if (mysql_num_rows(result) >= 1) - { - while((row = mysql_fetch_row(result))) - { + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str()); + else if (results.RowCount() >= 1) + for(auto row = results.begin(); row != results.end(); ++row) sayid = atoi(row[0]); - } - mysql_free_result(result); - } - } - else - { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } - safe_delete_array(query); + } } - safe_delete_array(query); safe_delete_array(escaped_string); if(silent) @@ -2716,27 +2695,21 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam else sayid = sayid + 500000; - //Create the say link as an item link hash - char linktext[250]; + //Create the say link as an item link hash + char linktext[250]; if(initiator) { if (initiator->GetClientVersion() >= EQClientRoF) - { sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12); - } else if (initiator->GetClientVersion() >= EQClientSoF) - { sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"00000000000000000000000000000000000000000000",LinkName,0x12); - } else - { sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"000000000000000000000000000000000000000",LinkName,0x12); - } } - else { // If no initiator, create an RoF saylink, since older clients handle RoF ones better than RoF handles older ones. + else // If no initiator, create an RoF saylink, since older clients handle RoF ones better than RoF handles older ones. sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12); - } + strcpy(Phrase,linktext); return Phrase; From 4432c070815873489923c234cab131b01acd825f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Thu, 4 Sep 2014 07:24:17 -0500 Subject: [PATCH 066/368] State of Commit: Testable if you ask me (Akkadius) what you need to do - Need to convert a list of functions and columns and should be ready to start intensive testing phase - All preliminary tests show things working great - All of player profile is saved and loaded from the database - DBAsync has been completely removed from all code - Removed zone/dbasync.cpp/.h - Removed common/dbasync.cpp/.h - Removed dbasync from cmake commmon and zone - Cleaned up a ton of functions - Added several tables to world CheckDatabaseConversions script: - `character_skills` - `character_languages` - `character_bind` - `character_alternate_abilities` - `character_currency` - `character_data` - `character_spells` - `character_memmed_spells` - `character_disciplines` - `character_material` - `character_tribute` - `character_bandolier` - `character_potionbelt` - Character select now loads from `character_data` - Character creation now creates to `character_data` - Updated function Database::UpdateName to use `character_data` - Updated function Database::CheckUsedName to use `character_data` - Updated function Database::MoveCharacterToZone to use `character_data` - Updated function Database::SetLoginFlags to use `character_data` - Updated function Database::SetFirstLogon to use `character_data` - Updated function Database::SetLFG to use `character_data` - Removed CopyCharacter functions and commands, to be recreated later since it never worked to begin with - Removed SharedDatabase::SetPlayerProfile - Trimmed down redundant case switch statements for World sendpackets to QueryServ - Added Character Methods to Database class: Loads: bool LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp); Saves: bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home); bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp); bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); bool SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot); bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); bool SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value); bool SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value); bool SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id); bool SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name); bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon); Deletes: bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool DeleteCharacterDisc(uint32 character_id, uint32 slot_id); bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id); --- common/CMakeLists.txt | 2 - common/database.cpp | 1354 ++++++++++++++++--------------- common/database.h | 15 +- common/dbasync.cpp | 669 --------------- common/dbasync.h | 176 ---- common/eq_packet_structs.h | 15 +- common/mysql_request_result.cpp | 1 + common/mysql_request_result.h | 2 +- common/shareddb.cpp | 107 +-- common/shareddb.h | 3 - world/client.cpp | 161 ++-- world/net.cpp | 3 - world/worlddb.cpp | 17 +- world/zoneserver.cpp | 109 +-- zone/CMakeLists.txt | 2 - zone/client.cpp | 90 +- zone/client.h | 10 +- zone/client_packet.cpp | 61 +- zone/client_process.cpp | 3 +- zone/command.cpp | 32 +- zone/command.h | 1 - zone/effects.cpp | 1 + zone/entity.cpp | 5 +- zone/entity.h | 4 +- zone/exp.cpp | 9 +- zone/inventory.cpp | 24 +- zone/net.cpp | 10 - zone/questmgr.cpp | 12 +- zone/spells.cpp | 3 +- zone/trading.cpp | 11 +- zone/tribute.cpp | 10 +- zone/zone.cpp | 5 - zone/zonedb.cpp | 395 ++++++--- zone/zonedb.h | 148 ++-- zone/zonedbasync.cpp | 103 --- zone/zonedbasync.h | 22 - zone/zoning.cpp | 10 +- 37 files changed, 1235 insertions(+), 2370 deletions(-) delete mode 100644 common/dbasync.cpp delete mode 100644 common/dbasync.h delete mode 100644 zone/zonedbasync.cpp delete mode 100644 zone/zonedbasync.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index f02212161..55e8a8a72 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -8,7 +8,6 @@ SET(common_sources crc16.cpp crc32.cpp database.cpp - dbasync.cpp dbcore.cpp debug.cpp emu_opcodes.cpp @@ -103,7 +102,6 @@ SET(common_headers crc16.h crc32.h database.h - dbasync.h dbcore.h debug.h deity.h diff --git a/common/database.cpp b/common/database.cpp index d926cb950..5de92ba08 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -71,55 +71,25 @@ Database::Database(const char* host, const char* user, const char* passwd, const Connect(host, user, passwd, database, port); } -bool Database::Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port) -{ +bool Database::Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port) { uint32 errnum= 0; char errbuf[MYSQL_ERRMSG_SIZE]; - if (!Open(host, user, passwd, database, port, &errnum, errbuf)) - { + if (!Open(host, user, passwd, database, port, &errnum, errbuf)) { LogFile->write(EQEMuLog::Error, "Failed to connect to database: Error: %s", errbuf); - HandleMysqlError(errnum); return false; } - else - { + else { LogFile->write(EQEMuLog::Status, "Using database '%s' at %s:%d",database,host,port); return true; } } -void Database::DBInitVars() { - +void Database::DBInitVars() { varcache_array = 0; varcache_max = 0; varcache_lastupdate = 0; } - -void Database::HandleMysqlError(uint32 errnum) { -/* switch(errnum) { - case 0: - break; - case 1045: // Access Denied - case 2001: { - AddEQEMuError(EQEMuError_Mysql_1405, true); - break; - } - case 2003: { // Unable to connect - AddEQEMuError(EQEMuError_Mysql_2003, true); - break; - } - case 2005: { // Unable to connect - AddEQEMuError(EQEMuError_Mysql_2005, true); - break; - } - case 2007: { // Unable to connect - AddEQEMuError(EQEMuError_Mysql_2007, true); - break; - } - }*/ -} - /* Close the connection to the database @@ -191,24 +161,18 @@ bool Database::CheckBannedIPs(const char* loginIP) return false; } -bool Database::AddBannedIP(char* bannedIP, const char* notes) -{ - std::string query = StringFormat("INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, notes); - - auto results = QueryDatabase(query); - - if (!results.Success()) - { - std::cerr << "Error in ReserveName query '" << query << "' " << results.ErrorMessage() << std::endl; +bool Database::AddBannedIP(char* bannedIP, const char* notes) { + std::string query = StringFormat("INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, notes); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in Database::AddBannedIP query '" << query << "' " << results.ErrorMessage() << std::endl; return false; - } - + } return true; } bool Database::CheckGMIPs(const char* ip_address, uint32 account_id) { - std::string query = StringFormat("SELECT * FROM `gm_ips` WHERE `ip_address` = '%s' AND `account_id` = %i", ip_address, account_id); - + std::string query = StringFormat("SELECT * FROM `gm_ips` WHERE `ip_address` = '%s' AND `account_id` = %i", ip_address, account_id); auto results = QueryDatabase(query); if (!results.Success()) @@ -221,32 +185,24 @@ bool Database::AddBannedIP(char* bannedIP, const char* notes) } bool Database::AddGMIP(char* ip_address, char* name) { - std::string query = StringFormat("INSERT into `gm_ips` SET `ip_address` = '%s', `name` = '%s'", ip_address, name); - - auto results = QueryDatabase(query); - + std::string query = StringFormat("INSERT into `gm_ips` SET `ip_address` = '%s', `name` = '%s'", ip_address, name); + auto results = QueryDatabase(query); return results.Success(); } -void Database::LoginIP(uint32 AccountID, const char* LoginIP) -{ - std::string query = StringFormat("INSERT INTO account_ip SET accid=%i, ip='%s' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()", AccountID, LoginIP); - - auto results = QueryDatabase(query); - +void Database::LoginIP(uint32 AccountID, const char* LoginIP) { + std::string query = StringFormat("INSERT INTO account_ip SET accid=%i, ip='%s' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()", AccountID, LoginIP); + auto results = QueryDatabase(query); if (!results.Success()) std::cerr << "Error in Log IP query '" << query << "' " << results.ErrorMessage() << std::endl; } -int16 Database::CheckStatus(uint32 account_id) -{ +int16 Database::CheckStatus(uint32 account_id) { std::string query = StringFormat("SELECT `status`, UNIX_TIMESTAMP(`suspendeduntil`) as `suspendeduntil`, UNIX_TIMESTAMP() as `current`" " FROM `account` WHERE `id` = %i", account_id); - auto results = QueryDatabase(query); - - if (!results.Success()) - { + auto results = QueryDatabase(query); + if (!results.Success()) { std::cerr << "Error in CheckStatus query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -254,10 +210,8 @@ int16 Database::CheckStatus(uint32 account_id) if (results.RowCount() != 1) return 0; - auto row = results.begin(); - - int16 status = atoi(row[0]); - + auto row = results.begin(); + int16 status = atoi(row[0]); int32 suspendeduntil = 0; // MariaDB initalizes with NULL if unix_timestamp() is out of range @@ -281,12 +235,10 @@ uint32 Database::CreateAccount(const char* name, const char* password, int16 sta else query = StringFormat("INSERT INTO account SET name='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name, status, lsaccount_id); - std::cerr << "Account Attempting to be created:" << name << " " << (int16) status << std::endl; - + std::cerr << "Account Attempting to be created:" << name << " " << (int16) status << std::endl; auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in CreateAccount query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -301,8 +253,7 @@ uint32 Database::CreateAccount(const char* name, const char* password, int16 sta } bool Database::DeleteAccount(const char* name) { - std::string query = StringFormat("DELETE FROM account WHERE name='%s';",name); - + std::string query = StringFormat("DELETE FROM account WHERE name='%s';",name); std::cout << "Account Attempting to be deleted:" << name << std::endl; auto results = QueryDatabase(query); @@ -317,7 +268,7 @@ bool Database::DeleteAccount(const char* name) { } bool Database::SetLocalPassword(uint32 accid, const char* password) { - std::string query = StringFormat("UPDATE account SET password=MD5('%s') where id=%i;", password, accid); + std::string query = StringFormat("UPDATE `account` SET `password` = MD5('%s') where id=%i;", password, accid); auto results = QueryDatabase(query); @@ -342,275 +293,394 @@ bool Database::SetAccountStatus(const char* name, int16 status) { if (results.RowsAffected() == 0) { std::cout << "Account: " << name << " does not exist, therefore it cannot be flagged\n"; - return false; + return false; } return true; } -bool Database::ReserveName(uint32 account_id, char* name) -{ - std::string query = StringFormat("INSERT into character_ SET account_id=%i, name='%s', profile=NULL", account_id, name); - - auto results = QueryDatabase(query); - - if (!results.Success()) - { - std::cerr << "Error in ReserveName query '" << query << "' " << results.ErrorMessage() << std::endl; - return false; - } - +/* This initially creates the character during character create */ +bool Database::ReserveName(uint32 account_id, char* name) { + std::string query = StringFormat("INSERT INTO `character_data` SET `account_id` = %i, `name` = '%s'", account_id, name); + auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::ReserveName", query); return true; } +bool Database::ThrowDBError(std::string ErrorMessage, std::string query_title, std::string query){ + if (ErrorMessage != ""){ std::cout << "\nERROR " << query_title << ": " << ErrorMessage << "\n\n" << query << "\n" << std::endl; return true; } + return false; +} + /* -Delete the character with the name "name" -returns false on failure, true otherwise + Delete the character with the name "name" + returns false on failure, true otherwise */ -bool Database::DeleteCharacter(char *name) -{ - std::string query=StringFormat("SELECT id from character_ WHERE name='%s'", name); - int charid; - - if(!name || !strlen(name)) - { +bool Database::DeleteCharacter(char *name) { + uint32 charid = 0; + printf("Database::DeleteCharacter name : %s \n", name); + if(!name || !strlen(name)) { std::cerr << "DeleteCharacter: request to delete without a name (empty char slot)" << std::endl; return false; } -// get id from character_ before deleting record so we can clean up inventory and qglobal - -#if DEBUG >= 5 - std::cout << "DeleteCharacter: Attempting to delete '" << name << "'" << std::endl; -#endif - + /* Get id from character_data before deleting record so we can clean up the rest of the tables */ + std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", name); auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { charid = atoi(row[0]); } + if (charid <= 0){ std::cerr << "Database::DeleteCharacter :: Character not found, stopping delete...\n"; return false; } - if(results.RowCount() != 1) - { - std::cerr << "DeleteCharacter error: got " << results.RowCount() << " rows matching '" << name << "'" << std::endl; - return false; - } - - auto row = results.begin(); - charid = atoi(row[0]); - -#if DEBUG >= 5 - std::cout << "DeleteCharacter: found '" << name << "' with char id: " << charid << std::endl; - std::cout << "DeleteCharacter: deleting << '" << name << "' (id " << charid << "): " << std::endl; - std::cout << " quest_globals"; -#endif - - query = StringFormat("DELETE from quest_globals WHERE charid='%d'", charid); + query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); +#ifdef BOTS + query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", charid); +#else + query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", charid); +#endif QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " character_tasks"; -#endif - - query = StringFormat("DELETE from character_tasks WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " character_activities"; -#endif - - query = StringFormat("DELETE from character_activities WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " character_enabledtasks"; -#endif - - query = StringFormat("DELETE from character_enabledtasks WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " completed_tasks"; -#endif - - query = StringFormat("DELETE from completed_tasks WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " friends"; -#endif - - query = StringFormat("DELETE from friends WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " mail"; -#endif - - query = StringFormat( "DELETE from mail WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " ptimers"; -#endif - - query = StringFormat("DELETE from timers WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " inventory"; -#endif - - query = StringFormat("DELETE from inventory WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " guild_members"; -#endif - -#ifdef BOTS - query = StringFormat("DELETE FROM guild_members WHERE char_id='%d' AND GetMobTypeById(%i) = 'C'", charid); -#else - query = StringFormat("DELETE FROM guild_members WHERE char_id='%d'", charid); -#endif - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " recipes"; -#endif - - query = StringFormat("DELETE FROM char_recipe_list WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " adventure_stats"; -#endif - - query = StringFormat("DELETE FROM adventure_stats WHERE player_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " zone_flags"; -#endif - - query = StringFormat("DELETE FROM zone_flags WHERE charID='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " titles"; -#endif - - query = StringFormat("DELETE FROM titles WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " titlesets"; -#endif - - query = StringFormat("DELETE FROM player_titlesets WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " keyring"; -#endif - - query = StringFormat("DELETE FROM keyring WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " factions"; -#endif - - query = StringFormat("DELETE FROM faction_values WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " instances"; -#endif - - query = StringFormat("DELETE FROM instance_list_player WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " _character"; -#endif - - query = StringFormat("DELETE from character_ WHERE id='%d'", charid); - results = QueryDatabase(query); - - if(results.RowsAffected() != 1) // here we have to have a match or it's an error - { - LogFile->write(EQEMuLog::Error, "DeleteCharacter: error: delete operation affected %d rows\n", results.RowsAffected()); - return false; - } - -#if DEBUG >= 5 - std::cout << " alternate currency"; -#endif - - query = StringFormat("DELETE FROM character_alt_currency WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << std::endl; -#endif - std::cout << "DeleteCharacter: successfully deleted '" << name << "' (id " << charid << ")" << std::endl; - + return true; } -/* This only for new Character creation storing */ -bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext) -{ - char query[256+sizeof(PlayerProfile_Struct)*2+sizeof(ExtendedProfile_Struct)*2+5]; - char* end = query; - uint32 charid = 0; - char zone[50]; - float x, y, z; +bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "REPLACE INTO `character_data` (" + "id," + "account_id," + "`name`," + "last_name," + "gender," + "race," + "class," + "`level`," + "deity," + "birthday," + "last_login," + "time_played," + "pvp_status," + "level2," + "anon," + "gm," + "intoxication," + "hair_color," + "beard_color," + "eye_color_1," + "eye_color_2," + "hair_style," + "beard," + "ability_time_seconds," + "ability_number," + "ability_time_minutes," + "ability_time_hours," + "title," + "suffix," + "exp," + "points," + "mana," + "cur_hp," + "str," + "sta," + "cha," + "dex," + "`int`," + "agi," + "wis," + "face," + "y," + "x," + "z," + "heading," + "pvp2," + "pvp_type," + "autosplit_enabled," + "zone_change_count," + "drakkin_heritage," + "drakkin_tattoo," + "drakkin_details," + "toxicity," + "hunger_level," + "thirst_level," + "ability_up," + "zone_id," + "zone_instance," + "leadership_exp_on," + "ldon_points_guk," + "ldon_points_mir," + "ldon_points_mmc," + "ldon_points_ruj," + "ldon_points_tak," + "ldon_points_available," + "tribute_time_remaining," + "show_helm," + "career_tribute_points," + "tribute_points," + "tribute_active," + "endurance," + "group_leadership_exp," + "raid_leadership_exp," + "group_leadership_points," + "raid_leadership_points," + "air_remaining," + "pvp_kills," + "pvp_deaths," + "pvp_current_points," + "pvp_career_points," + "pvp_best_kill_streak," + "pvp_worst_death_streak," + "pvp_current_kill_streak," + "aa_points_spent," + "aa_exp," + "aa_points," + "group_auto_consent," + "raid_auto_consent," + "guild_auto_consent," + "RestTimer) " + "VALUES (" + "%u," // id + "%u," // account_id + "'%s'," // `name` + "'%s'," // last_name + "%u," // gender + "%u," // race + "%u," // class + "%u," // `level` + "%u," // deity + "%u," // birthday + "%u," // last_login + "%u," // time_played + "%u," // pvp_status + "%u," // level2 + "%u," // anon + "%u," // gm + "%u," // intoxication + "%u," // hair_color + "%u," // beard_color + "%u," // eye_color_1 + "%u," // eye_color_2 + "%u," // hair_style + "%u," // beard + "%u," // ability_time_seconds + "%u," // ability_number + "%u," // ability_time_minutes + "%u," // ability_time_hours + "'%s'," // title + "'%s'," // suffix + "%u," // exp + "%u," // points + "%u," // mana + "%u," // cur_hp + "%u," // str + "%u," // sta + "%u," // cha + "%u," // dex + "%u," // `int` + "%u," // agi + "%u," // wis + "%u," // face + "%f," // y + "%f," // x + "%f," // z + "%f," // heading + "%u," // pvp2 + "%u," // pvp_type + "%u," // autosplit_enabled + "%u," // zone_change_count + "%u," // drakkin_heritage + "%u," // drakkin_tattoo + "%u," // drakkin_details + "%i," // toxicity + "%i," // hunger_level + "%i," // thirst_level + "%u," // ability_up + "%u," // zone_id + "%u," // zone_instance + "%u," // leadership_exp_on + "%u," // ldon_points_guk + "%u," // ldon_points_mir + "%u," // ldon_points_mmc + "%u," // ldon_points_ruj + "%u," // ldon_points_tak + "%u," // ldon_points_available + "%u," // tribute_time_remaining + "%u," // show_helm + "%u," // career_tribute_points + "%u," // tribute_points + "%u," // tribute_active + "%u," // endurance + "%u," // group_leadership_exp + "%u," // raid_leadership_exp + "%u," // group_leadership_point + "%u," // raid_leadership_points + "%u," // air_remaining + "%u," // pvp_kills + "%u," // pvp_deaths + "%u," // pvp_current_points + "%u," // pvp_career_points + "%u," // pvp_best_kill_streak + "%u," // pvp_worst_death_streak + "%u," // pvp_current_kill_strea + "%u," // aa_points_spent + "%u," // aa_exp + "%u," // aa_points + "%u," // group_auto_consent + "%u," // raid_auto_consent + "%u," // guild_auto_consent + "%u" // RestTimer + ")", + character_id, // " id, " + account_id, // " account_id, " + pp->name, // " `name`, " + pp->last_name, // " last_name, " + pp->gender, // " gender, " + pp->race, // " race, " + pp->class_, // " class, " + pp->level, // " `level`, " + pp->deity, // " deity, " + pp->birthday, // " birthday, " + pp->lastlogin, // " last_login, " + pp->timePlayedMin, // " time_played, " + pp->pvp, // " pvp_status, " + pp->level2, // " level2, " + pp->anon, // " anon, " + pp->gm, // " gm, " + pp->intoxication, // " intoxication, " + pp->haircolor, // " hair_color, " + pp->beardcolor, // " beard_color, " + pp->eyecolor1, // " eye_color_1, " + pp->eyecolor2, // " eye_color_2, " + pp->hairstyle, // " hair_style, " + pp->beard, // " beard, " + pp->ability_time_seconds, // " ability_time_seconds, " + pp->ability_number, // " ability_number, " + pp->ability_time_minutes, // " ability_time_minutes, " + pp->ability_time_hours, // " ability_time_hours, " + pp->title, // " title, " + pp->suffix, // " suffix, " + pp->exp, // " exp, " + pp->points, // " points, " + pp->mana, // " mana, " + pp->cur_hp, // " cur_hp, " + pp->STR, // " str, " + pp->STA, // " sta, " + pp->CHA, // " cha, " + pp->DEX, // " dex, " + pp->INT, // " `int`, " + pp->AGI, // " agi, " + pp->WIS, // " wis, " + pp->face, // " face, " + pp->y, // " y, " + pp->x, // " x, " + pp->z, // " z, " + pp->heading, // " heading, " + pp->pvp2, // " pvp2, " + pp->pvptype, // " pvp_type, " + pp->autosplit, // " autosplit_enabled, " + pp->zone_change_count, // " zone_change_count, " + pp->drakkin_heritage, // " drakkin_heritage, " + pp->drakkin_tattoo, // " drakkin_tattoo, " + pp->drakkin_details, // " drakkin_details, " + pp->toxicity, // " toxicity, " + pp->hunger_level, // " hunger_level, " + pp->thirst_level, // " thirst_level, " + pp->ability_up, // " ability_up, " + pp->zone_id, // " zone_id, " + pp->zoneInstance, // " zone_instance, " + pp->leadAAActive, // " leadership_exp_on, " + pp->ldon_points_guk, // " ldon_points_guk, " + pp->ldon_points_mir, // " ldon_points_mir, " + pp->ldon_points_mmc, // " ldon_points_mmc, " + pp->ldon_points_ruj, // " ldon_points_ruj, " + pp->ldon_points_tak, // " ldon_points_tak, " + pp->ldon_points_available, // " ldon_points_available, " + pp->tribute_time_remaining, // " tribute_time_remaining, " + pp->showhelm, // " show_helm, " + pp->career_tribute_points, // " career_tribute_points, " + pp->tribute_points, // " tribute_points, " + pp->tribute_active, // " tribute_active, " + pp->endurance, // " endurance, " + pp->group_leadership_exp, // " group_leadership_exp, " + pp->raid_leadership_exp, // " raid_leadership_exp, " + pp->group_leadership_points, // " group_leadership_points, " + pp->raid_leadership_points, // " raid_leadership_points, " + pp->air_remaining, // " air_remaining, " + pp->PVPKills, // " pvp_kills, " + pp->PVPDeaths, // " pvp_deaths, " + pp->PVPCurrentPoints, // " pvp_current_points, " + pp->PVPCareerPoints, // " pvp_career_points, " + pp->PVPBestKillStreak, // " pvp_best_kill_streak, " + pp->PVPWorstDeathStreak, // " pvp_worst_death_streak, " + pp->PVPCurrentKillStreak, // " pvp_current_kill_streak, " + pp->aapoints_spent, // " aa_points_spent, " + pp->expAA, // " aa_exp, " + pp->aapoints, // " aa_points, " + pp->groupAutoconsent, // " group_auto_consent, " + pp->raidAutoconsent, // " raid_auto_consent, " + pp->guildAutoconsent, // " guild_auto_consent, " + pp->RestTimer // " RestTimer) " + ); + auto results = QueryDatabase(query); + return true; +} + +/* This only for new Character creation storing */ +bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv) { + uint32 charid = 0; char zone[50]; float x, y, z; charid = GetCharacterID(pp->name); - if(!charid) - { + if(!charid) { LogFile->write(EQEMuLog::Error, "StoreCharacter: no character id"); return false; } const char *zname = GetZoneName(pp->zone_id); if(zname == nullptr) { - //zone not in the DB, something to prevent crash... + /* Zone not in the DB, something to prevent crash... */ strn0cpy(zone, "qeynos", 49); pp->zone_id = 1; - } else - strn0cpy(zone, zname, 49); - - x=pp->x; - y=pp->y; - z=pp->z; - - // construct the character_ query - end += sprintf(end, - "UPDATE character_ SET timelaston=0, " - "zonename=\'%s\', x=%f, y=%f, z=%f, profile=\'", - zone, x, y, z - ); - end += DoEscapeString(end, (char*)pp, sizeof(PlayerProfile_Struct)); - end += sprintf(end, "\', extprofile=\'"); - end += DoEscapeString(end, (char*)ext, sizeof(ExtendedProfile_Struct)); - end += sprintf(end, "\' WHERE account_id=%d AND name='%s'",account_id, pp->name); - - auto results = QueryDatabase(query, (uint32) (end - query)); - // stack assigned query, no need to delete it. - - if(!results.RowsAffected()) - { - LogFile->write(EQEMuLog::Error, "StoreCharacter query '%s' %s", query, results.ErrorMessage().c_str()); - return false; } + else{ strn0cpy(zone, zname, 49); } - // now the inventory + x = pp->x; + y = pp->y; + z = pp->z; + + /* Saves Player Profile Data to `character_data` */ + SaveCharacterCreate(charid, account_id, pp); + + /* Insert starting inventory... */ std::string invquery; - for (int16 i=EmuConstants::EQUIPMENT_BEGIN; i<=EmuConstants::BANK_BAGS_END;) - { + for (int16 i=EmuConstants::EQUIPMENT_BEGIN; i<=EmuConstants::BANK_BAGS_END;) { const ItemInst* newinv = inv->GetItem(i); - if (newinv) - { + if (newinv) { invquery = StringFormat("INSERT INTO `inventory` (charid, slotid, itemid, charges, color) VALUES (%u, %i, %u, %i, %u)", charid, i, newinv->GetItem()->ID, newinv->GetCharges(), newinv->GetColor()); @@ -624,69 +694,41 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inven #endif } - if (i == MainCursor) { - i = EmuConstants::GENERAL_BAGS_BEGIN; - continue; - } - else if (i == EmuConstants::CURSOR_BAG_END) { - i = EmuConstants::BANK_BEGIN; - continue; - } - else if (i == EmuConstants::BANK_END) { - i = EmuConstants::BANK_BAGS_BEGIN; - continue; - } - + if (i == MainCursor) { i = EmuConstants::GENERAL_BAGS_BEGIN; continue; } + else if (i == EmuConstants::CURSOR_BAG_END) { i = EmuConstants::BANK_BEGIN; continue; } + else if (i == EmuConstants::BANK_END) { i = EmuConstants::BANK_BAGS_BEGIN; continue; } i++; } return true; } -//0=failure, otherwise returns the char ID for the given char name. uint32 Database::GetCharacterID(const char *name) { - uint32 cid = 0; - if(GetAccountIDByChar(name, &cid) == 0) - return(0); - return(cid); + std::string query = StringFormat("SELECT `id` FROM `character_data` WHERE `name` = '%s'", name); + auto results = QueryDatabase(query); + auto row = results.begin(); + if (row[0]){ return atoi(row[0]); } + return 0; } /* -This function returns the account_id that owns the character with -the name "name" or zero if no character with that name was found -Zero will also be returned if there is a database error. + This function returns the account_id that owns the character with + the name "name" or zero if no character with that name was found + Zero will also be returned if there is a database error. */ -uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { - std::string query = StringFormat("SELECT account_id, id FROM character_ WHERE name='%s'", charname); - - auto results = QueryDatabase(query); - - if (!results.Success()) - { - std::cerr << "Error in GetAccountIDByChar query '" << query << "' " << results.ErrorMessage() << std::endl; - return 0; - } - - if (results.RowCount() != 1) - return 0; - +uint32 Database::GetAccountIDByChar(const char* charname) { + uint32 accountId = 0; + std::string query = StringFormat("SELECT `account_id`, `id` FROM `character_data` WHERE `name` = '%s' LIMIT 1", charname); + auto results = QueryDatabase(query); auto row = results.begin(); - - uint32 accountId = atoi(row[0]); - - if (oCharID) - *oCharID = atoi(row[1]); - + if (row[0]){ accountId = atoi(row[0]); } return accountId; } // Retrieve account_id for a given char_id uint32 Database::GetAccountIDByChar(uint32 char_id) { - std::string query = StringFormat("SELECT account_id FROM character_ WHERE id=%i", char_id); - - auto results = QueryDatabase(query); - - if (!results.Success()) - { + std::string query = StringFormat("SELECT `account_id` FROM `character_` WHERE `id` = %i LIMIT 1", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in GetAccountIDByChar query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return 0; } @@ -694,8 +736,7 @@ uint32 Database::GetAccountIDByChar(uint32 char_id) { if (results.RowCount() != 1) return 0; - auto row = results.begin(); - + auto row = results.begin(); return atoi(row[0]); } @@ -703,11 +744,10 @@ uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* if (!isAlphaNumeric(accname)) return 0; - std::string query = StringFormat("SELECT id, status, lsaccount_id FROM account WHERE name='%s'", accname); + std::string query = StringFormat("SELECT `id`, `status`, `lsaccount_id` FROM `account` WHERE `name` = '%s' LIMIT 1", accname); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in GetAccountIDByAcc query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -722,8 +762,7 @@ uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* if (status) *status = atoi(row[1]); - if (lsid) - { + if (lsid) { if (row[2]) *lsid = atoi(row[2]); else @@ -734,12 +773,10 @@ uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* } void Database::GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID) { - std::string query = StringFormat("SELECT name, lsaccount_id FROM account WHERE id='%i'", accountid); - + std::string query = StringFormat("SELECT `name`, `lsaccount_id` FROM `account` WHERE `id` = '%i'", accountid); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in GetAccountName query '" << query << "' " << results.ErrorMessage() << std::endl; return; } @@ -773,11 +810,9 @@ void Database::GetCharName(uint32 char_id, char* name) { } static inline void loadbar(unsigned int x, unsigned int n, unsigned int w = 50) { - if ((x != n) && (x % (n / 100 + 1) != 0)) return; - + if ((x != n) && (x % (n / 100 + 1) != 0)) return; float ratio = x / (float)n; - int c = ratio * w; - + int c = ratio * w; std::cout << std::setw(3) << (int)(ratio * 100) << "% ["; for (int x = 0; xcareerRadCrystals, pp->currentEbonCrystals, pp->careerEbonCrystals - ); + ); auto results = QueryDatabase(rquery); if (pp->tribute_time_remaining < 0 || pp->tribute_time_remaining == 4294967295){ pp->tribute_time_remaining = 0; } @@ -1228,7 +1322,7 @@ bool Database::CheckDatabaseConversions() { "drakkin_heritage," "drakkin_tattoo," "drakkin_details," - "toxicity," + "toxicity," "hunger_level," "thirst_level," "ability_up," @@ -1319,7 +1413,7 @@ bool Database::CheckDatabaseConversions() { "%u," // drakkin_heritage "%u," // drakkin_tattoo "%u," // drakkin_details - "%u," // toxicity + "%i," // toxicity "%u," // hunger_level "%u," // thirst_level "%u," // ability_up @@ -1463,35 +1557,38 @@ bool Database::CheckDatabaseConversions() { } /* Run Bind Home Convert */ - rquery = StringFormat("REPLACE INTO `character_bind_home` (id, zone_id, instance_id, x, y, z, heading)" - " VALUES (%u, %u, %u, %f, %f, %f, %f)", + rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, 1)", character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + + /* Run Bind Convert */ + rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, 0)", + character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Language Convert */ for (i = 0; i < MAX_PP_LANGUAGE; i++){ if (pp->languages[i] > 0){ rquery = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, i, pp->languages[i]); - QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Language Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Language Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } - /* Run Skill Convert */ for (i = 0; i < MAX_PP_SKILL; i++){ - if (pp->skills[i] > 0){ + if (pp->skills[i] > 0){ rquery = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, i, pp->skills[i]); - QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Skill Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Skill Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } - /* Run Spell Convert */ for (i = 0; i < MAX_PP_SPELLBOOK; i++){ - if (pp->spell_book[i] > 0 && pp->spell_book[i] != 4294967295){ + if (pp->spell_book[i] > 0 && pp->spell_book[i] != 4294967295 && pp->spell_book[i] < 40000 && pp->spell_book[i] != 1){ rquery = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->spell_book[i]); - QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } - /* Run Max Memmed Spell Convert */ for (i = 0; i < MAX_PP_MEMSPELL; i++){ if (pp->mem_spells[i] > 0){ @@ -1499,12 +1596,11 @@ bool Database::CheckDatabaseConversions() { QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Memmed Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } - /* Run Discipline Convert */ for (i = 0; i < MAX_PP_DISCIPLINES; i++){ if (pp->disciplines.values[i] > 0){ - rquery = StringFormat("REPLACE INTO `character_disciplines` (id, disc_id) VALUES (%u, %u)", character_id, pp->disciplines.values[i]); - QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Discipline Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + rquery = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Discipline Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } /* Run Material Color Convert */ @@ -1512,9 +1608,32 @@ bool Database::CheckDatabaseConversions() { if (pp->item_tint[i].color > 0){ rquery = StringFormat("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); // printf("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u);\n", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); - QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Color Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Color Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } } + /* Run Tribute Convert */ + for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ + if (pp->tributes[i].tribute > 0){ + rquery = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Tribute Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + } + } + /* Run Bandolier Convert */ + for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ + for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ + if (pp->bandoliers[i].items[si].item_id > 0){ + rquery = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + } + } + } + /* Run Potion Belt Convert */ + for (i = 0; i <= EmuConstants::POTION_BELT_SIZE; i++){ + if (pp->potionbelt.items[i].item_id > 0){ + rquery = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Potion Belt Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + } + } } /* Print out the entire Player Profile for testing */ @@ -2283,7 +2402,7 @@ bool Database::AddToNameFilter(const char* name) { } uint32 Database::GetAccountIDFromLSID(uint32 iLSID, char* oAccountName, int16* oStatus) { - + uint32 account_id = 0; std::string query = StringFormat("SELECT id, name, status FROM account WHERE lsaccount_id=%i", iLSID); auto results = QueryDatabase(query); @@ -2296,14 +2415,14 @@ uint32 Database::GetAccountIDFromLSID(uint32 iLSID, char* oAccountName, int16* o if (results.RowCount() != 1) return 0; - auto row = results.begin(); + for (auto row = results.begin(); row != results.end(); ++row) { + account_id = atoi(row[0]); - uint32 account_id = atoi(row[0]); - - if (oAccountName) - strcpy(oAccountName, row[1]); - if (oStatus) - *oStatus = atoi(row[2]); + if (oAccountName) + strcpy(oAccountName, row[1]); + if (oStatus) + *oStatus = atoi(row[2]); + } return account_id; } @@ -2339,11 +2458,9 @@ void Database::ClearMerchantTemp(){ std::cerr << "Error in ClearMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl; } -bool Database::UpdateName(const char* oldname, const char* newname) { - - std::cout << "Renaming " << oldname << " to " << newname << "..." << std::endl; - - std::string query = StringFormat("UPDATE character_ SET name='%s' WHERE name='%s';", newname, oldname); +bool Database::UpdateName(const char* oldname, const char* newname) { + std::cout << "Renaming " << oldname << " to " << newname << "..." << std::endl; + std::string query = StringFormat("UPDATE `character_data` SET `name` = '%s' WHERE `name` = '%s';", newname, oldname); auto results = QueryDatabase(query); if (!results.Success()) @@ -2356,13 +2473,10 @@ bool Database::UpdateName(const char* oldname, const char* newname) { } // If the name is used or an error occurs, it returns false, otherwise it returns true -bool Database::CheckUsedName(const char* name) -{ - std::string query = StringFormat("SELECT id FROM character_ where name='%s'", name); - auto results = QueryDatabase(query); - - if (!results.Success()) - { +bool Database::CheckUsedName(const char* name) { + std::string query = StringFormat("SELECT `id` FROM `character_data` WHERE `name` = '%s'", name); + auto results = QueryDatabase(query); + if (!results.Success()) { std::cerr << "Error in CheckUsedName query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } @@ -2373,13 +2487,10 @@ bool Database::CheckUsedName(const char* name) return true; } -uint8 Database::GetServerType() -{ - std::string query("SELECT value FROM variables WHERE varname='ServerType'"); - auto results = QueryDatabase(query); - - if (!results.Success()) - { +uint8 Database::GetServerType() { + std::string query("SELECT `value` FROM `variables` WHERE `varname` = 'ServerType' LIMIT 1"); + auto results = QueryDatabase(query); + if (!results.Success()) { std::cerr << "Error in GetServerType query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -2391,15 +2502,14 @@ uint8 Database::GetServerType() return atoi(row[0]); } -bool Database::MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid) { +bool Database::MoveCharacterToZone(const char* charname, const char* zonename, uint32 zoneid) { if(zonename == nullptr || strlen(zonename) == 0) return false; - std::string query = StringFormat("UPDATE character_ SET zonename = '%s',zoneid=%i,x=-1, y=-1, z=-1 WHERE name='%s'", zonename,zoneid, charname); + std::string query = StringFormat("UPDATE `character_data` SET `zoneid` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `name` = '%s'", zoneid, charname); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in MoveCharacterToZone(name) query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } @@ -2414,13 +2524,11 @@ bool Database::MoveCharacterToZone(const char* charname, const char* zonename) { return MoveCharacterToZone(charname, zonename, GetZoneID(zonename)); } -bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { - - std::string query = StringFormat("UPDATE character_ SET zonename = '%s', zoneid=%i, x=-1, y=-1, z=-1 WHERE id=%i", iZonename, GetZoneID(iZonename), iCharID); +bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { + std::string query = StringFormat("UPDATE `character_data` SET `zoneid` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `id` = %i", iZonename, GetZoneID(iZonename), iCharID); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in MoveCharacterToZone(id) query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } @@ -2428,57 +2536,11 @@ bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { return results.RowsAffected() != 0; } -uint8 Database::CopyCharacter(const char* oldname, const char* newname, uint32 acctid) { - - PlayerProfile_Struct* pp; - ExtendedProfile_Struct* ext; - - std::string query = StringFormat("SELECT profile, extprofile FROM character_ WHERE name='%s'", oldname); +bool Database::SetHackerFlag(const char* accountname, const char* charactername, const char* hacked) { + std::string query = StringFormat("INSERT INTO `hackers` (account, name, hacked) values('%s','%s','%s')", accountname, charactername, hacked); auto results = QueryDatabase(query); - if (!results.Success()) - { - std::cerr << "Error in CopyCharacter read query '" << query << "' " << results.ErrorMessage() << std::endl; - return 0; - } - - auto row = results.begin(); - - pp = (PlayerProfile_Struct*)row[0]; - strcpy(pp->name, newname); - - ext = (ExtendedProfile_Struct*)row[1]; - - char query2[276 + sizeof(PlayerProfile_Struct)*2 + sizeof(ExtendedProfile_Struct)*2 + 1]; - char* end=query2; - - end += sprintf(end, "INSERT INTO character_ SET zonename=\'%s\', x = %f, y = %f, z = %f, profile=\'", GetZoneName(pp->zone_id), pp->x, pp->y, pp->z); - end += DoEscapeString(end, (char*) pp, sizeof(PlayerProfile_Struct)); - end += sprintf(end,"\', extprofile=\'"); - end += DoEscapeString(end, (char*) ext, sizeof(ExtendedProfile_Struct)); - end += sprintf(end, "\', account_id=%d, name='%s'", acctid, newname); - - results = QueryDatabase(query2, (uint32) (end - query2)); - - if (!results.Success()) - { - std::cerr << "Error in CopyCharacter query '" << query2 << "' " << results.ErrorMessage() << std::endl; - return 0; - } - - if (results.RowsAffected() == 0) - return 0; - - return 1; -} - -bool Database::SetHackerFlag(const char* accountname, const char* charactername, const char* hacked) { - - std::string query = StringFormat("INSERT INTO hackers(account,name,hacked) values('%s','%s','%s')", accountname, charactername, hacked); - auto results = QueryDatabase(query); - - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in SetHackerFlag query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } @@ -2486,8 +2548,7 @@ bool Database::SetHackerFlag(const char* accountname, const char* charactername, return results.RowsAffected() != 0; } -bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) { - +bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) { //Utilize the "hacker" table, but also give zone information. std::string query = StringFormat("INSERT INTO hackers(account,name,hacked,zone) values('%s','%s','%s','%s')", accountname, charactername, hacked, zone); auto results = QueryDatabase(query); @@ -2573,8 +2634,6 @@ uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZo std::string query = StringFormat("SELECT `id`, `account_id`, `zone_id`, `zone_instance`, `x`, `y`, `z` FROM `character_data` WHERE `name` = '%s'", iName); auto results = QueryDatabase(query); - printf("Calling Database::GetCharacterInfo: ACCID: %u ZONEID: %u INSTANCEID: %u \n", oAccID, oZoneID, oInstanceID); - if (!results.Success()) { std::cerr << "Error in GetCharacterInfo query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; @@ -2583,7 +2642,7 @@ uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZo if (results.RowCount() != 1) return 0; - auto row = results.begin(); + auto row = results.begin(); uint32 charid = atoi(row[0]); if (oAccID){ *oAccID = atoi(row[1]); } if (oZoneID){ *oZoneID = atoi(row[2]); } @@ -2592,8 +2651,6 @@ uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZo if (oY){ *oY = atof(row[5]); } if (oZ){ *oZ = atof(row[6]); } - printf("Calling Database::GetCharacterInfo: ACCID: %u ZONEID: %u INSTANCEID: %u \n", oAccID, oZoneID, oInstanceID); - return charid; } @@ -2631,45 +2688,35 @@ bool Database::GetLiveChar(uint32 account_id, char* cname) { return true; } -void Database::SetLFP(uint32 CharID, bool LFP) { - - std::string query = StringFormat("update character_ set lfp=%i where id=%i",LFP, CharID); - auto results = QueryDatabase(query); - +void Database::SetLFP(uint32 CharID, bool LFP) { + std::string query = StringFormat("UPDATE `character_data` SET `lfp` = %i WHERE `id` = %i",LFP, CharID); + auto results = QueryDatabase(query); if (!results.Success()) LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, results.ErrorMessage().c_str()); } -void Database::SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon) { - - std::string query = StringFormat("update character_ set lfp=%i, lfg=%i, firstlogon=%i where id=%i",LFP, LFG, firstlogon, CharID); - auto results = QueryDatabase(query); - +void Database::SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon) { + std::string query = StringFormat("update `character_data` SET `lfp` = %i, `lfg` = %i, `firstlogon` = %i WHERE `id` = %i",LFP, LFG, firstlogon, CharID); + auto results = QueryDatabase(query); if (!results.Success()) LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, results.ErrorMessage().c_str()); } -void Database::SetLFG(uint32 CharID, bool LFG) { - - std::string query = StringFormat("update character_ set lfg=%i where id=%i",LFG, CharID); - auto results = QueryDatabase(query); - +void Database::SetLFG(uint32 CharID, bool LFG) { + std::string query = StringFormat("update `character_data` SET `lfg` = %i WHERE `id` = %i",LFG, CharID); + auto results = QueryDatabase(query); if (!results.Success()) LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, results.ErrorMessage().c_str()); } -void Database::SetFirstLogon(uint32 CharID, uint8 firstlogon) { - - std::string query = StringFormat( "update character_ set firstlogon=%i where id=%i",firstlogon, CharID); - auto results = QueryDatabase(query); - +void Database::SetFirstLogon(uint32 CharID, uint8 firstlogon) { + std::string query = StringFormat( "UPDATE `character_data` SET `firstlogon` = %i WHERE `id` = %i",firstlogon, CharID); + auto results = QueryDatabase(query); if (!results.Success()) LogFile->write(EQEMuLog::Error, "Error updating firstlogon for character %i : %s", CharID, results.ErrorMessage().c_str()); } -void Database::AddReport(std::string who, std::string against, std::string lines) -{ - +void Database::AddReport(std::string who, std::string against, std::string lines) { char *escape_str = new char[lines.size()*2+1]; DoEscapeString(escape_str, lines.c_str(), lines.size()); @@ -2681,11 +2728,9 @@ void Database::AddReport(std::string who, std::string against, std::string lines LogFile->write(EQEMuLog::Error, "Error adding a report for %s: %s", who.c_str(), results.ErrorMessage().c_str()); } -void Database::SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc){ - +void Database::SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc) { std::string query; - if (id == 0) - { + if (id == 0) { // removing from group query = StringFormat("delete from group_id where charid=%i and name='%s' and ismerc=%i",charid, name, ismerc); auto results = QueryDatabase(query); @@ -2696,8 +2741,8 @@ void Database::SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ism return; } - // adding to group - query = StringFormat("replace into group_id set charid=%i, groupid=%i, name='%s', ismerc='%i'",charid, id, name, ismerc); + /* Add to the Group */ + query = StringFormat("REPLACE INTO `group_id` SET `charid` = %i, `groupid` = %i, `name` = '%s', `ismerc` = '%i'", charid, id, name, ismerc); auto results = QueryDatabase(query); if (!results.Success()) @@ -2755,46 +2800,34 @@ uint32 Database::GetGroupID(const char* name){ return atoi(row[0]); } -char* Database::GetGroupLeaderForLogin(const char* name,char* leaderbuf){ - - PlayerProfile_Struct pp; - - std::string query = StringFormat("SELECT profile from character_ where name='%s'", name); +/* Is this really getting used properly... A half implementation ? Akkadius */ +char* Database::GetGroupLeaderForLogin(const char* name, char* leaderbuf){ + std::string query = StringFormat("SELECT `groupid` FROM `group_id` WHERE `name = '%s'", name); auto results = QueryDatabase(query); + auto row = results.begin(); uint32 group_id = 0; + if (row[0]){ group_id = atoi(row[0]); } - if (!results.Success()) - { - std::cout << "Unable to get leader name: " << results.ErrorMessage() << std::endl; - return leaderbuf; - } - - if (results.LengthOfColumn(0) != sizeof(PlayerProfile_Struct)) - return leaderbuf; - - auto row = results.begin(); - - memcpy(&pp, row[0], sizeof(PlayerProfile_Struct)); - strcpy(leaderbuf,pp.groupMembers[0]); + query = StringFormat("SELECT `name` FROM `group_id` WHERE `name` != '%s' AND `groupid` = %u", name, group_id); + results = QueryDatabase(query); + row = results.begin(); + if (row[0]){ strcpy(leaderbuf, row[0]); } return leaderbuf; } -void Database::SetGroupLeaderName(uint32 gid, const char* name) { - - std::string query = StringFormat("Replace into group_leaders set gid=%lu, leadername='%s'",(unsigned long)gid,name); +void Database::SetGroupLeaderName(uint32 gid, const char* name) { + std::string query = StringFormat("REPLACE INTO `group_leaders` SET `gid` = %lu, `leadername` = '%s'",(unsigned long)gid,name); auto results = QueryDatabase(query); if (!results.Success()) std::cout << "Unable to set group leader: " << results.ErrorMessage() << std::endl; } -char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, GroupLeadershipAA_Struct* GLAA){ - - std::string query = StringFormat("SELECT leadername, maintank, assist, puller, marknpc, leadershipaa FROM group_leaders WHERE gid=%lu",(unsigned long)gid); +char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, GroupLeadershipAA_Struct* GLAA){ + std::string query = StringFormat("SELECT `leadername`, `maintank`, `assist`, `puller`, `marknpc`, `leadershipaa` FROM `group_leaders` WHERE `gid` = %lu",(unsigned long)gid); auto results = QueryDatabase(query); - if (!results.Success() || results.RowCount() == 0) - { + if (!results.Success() || results.RowCount() == 0) { if(leaderbuf) strcpy(leaderbuf, "UNKNOWN"); @@ -2837,8 +2870,7 @@ char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* mainta } // Clearing all group leaders -void Database::ClearAllGroupLeaders(void) -{ +void Database::ClearAllGroupLeaders(void) { std::string query("DELETE from group_leaders"); auto results = QueryDatabase(query); @@ -2863,8 +2895,7 @@ void Database::ClearGroupLeader(uint32 gid) { std::cout << "Unable to clear group leader: " << results.ErrorMessage() << std::endl; } -uint8 Database::GetAgreementFlag(uint32 acctid) -{ +uint8 Database::GetAgreementFlag(uint32 acctid) { std::string query = StringFormat("SELECT rulesflag FROM account WHERE id=%i",acctid); auto results = QueryDatabase(query); @@ -2880,14 +2911,12 @@ uint8 Database::GetAgreementFlag(uint32 acctid) return atoi(row[0]); } -void Database::SetAgreementFlag(uint32 acctid) -{ +void Database::SetAgreementFlag(uint32 acctid) { std::string query = StringFormat("UPDATE account SET rulesflag=1 where id=%i", acctid); QueryDatabase(query); } void Database::ClearRaid(uint32 rid) { - if(rid == 0) { //clear all raids @@ -2903,8 +2932,7 @@ void Database::ClearRaid(uint32 rid) { std::cout << "Unable to clear raids: " << results.ErrorMessage() << std::endl; } -void Database::ClearAllRaids(void) -{ +void Database::ClearAllRaids(void) { std::string query("delete from raid_members"); auto results = QueryDatabase(query); diff --git a/common/database.h b/common/database.h index a22e6d406..c70464094 100644 --- a/common/database.h +++ b/common/database.h @@ -105,10 +105,16 @@ public: Database(const char* host, const char* user, const char* passwd, const char* database,uint32 port); bool Connect(const char* host, const char* user, const char* passwd, const char* database,uint32 port); ~Database(); + bool ThrowDBError(std::string ErrorMessage, std::string query_title, std::string query); + /* * General Character Related Stuff */ + + /* Character Creation */ + bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp); + bool MoveCharacterToZone(const char* charname, const char* zonename); bool MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid); bool MoveCharacterToZone(uint32 iCharID, const char* iZonename); @@ -118,16 +124,15 @@ public: bool AddToNameFilter(const char* name); bool ReserveName(uint32 account_id, char* name); bool CreateCharacter(uint32 account_id, char* name, uint16 gender, uint16 race, uint16 class_, uint8 str, uint8 sta, uint8 cha, uint8 dex, uint8 int_, uint8 agi, uint8 wis, uint8 face); - bool StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext); + bool StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv); bool DeleteCharacter(char* name); - uint8 CopyCharacter(const char* oldname, const char* newname, uint32 acctid); /* * General Information Getting Queries */ bool CheckNameFilter(const char* name, bool surname = false); bool CheckUsedName(const char* name); - uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0); + uint32 GetAccountIDByChar(const char* charname); uint32 GetAccountIDByChar(uint32 char_id); uint32 GetAccountIDByName(const char* accname, int16* status = 0, uint32* lsid = 0); uint32 GetGuildIDByCharID(uint32 char_id); @@ -252,10 +257,6 @@ public: void SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon); void AddReport(std::string who, std::string against, std::string lines); - -protected: - void HandleMysqlError(uint32 errnum); - private: void DBInitVars(); diff --git a/common/dbasync.cpp b/common/dbasync.cpp deleted file mode 100644 index 00714c2a0..000000000 --- a/common/dbasync.cpp +++ /dev/null @@ -1,669 +0,0 @@ -#include "debug.h" -#ifdef _WINDOWS - #include - #include - #include -#endif -#include -#include "dbasync.h" -#include "database.h" -#include -#include -#include -#include "dbcore.h" -#include -//#include "../common/misc_functions.h" -#include "string_util.h" -#define ASYNC_LOOP_GRANULARITY 4 //# of ms between checking our work - -bool DBAsyncCB_LoadVariables(DBAsyncWork* iWork) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* result = 0; - DBAsyncQuery* dbaq = iWork->PopAnswer(); - if (dbaq->GetAnswer(errbuf, &result)) - iWork->GetDB()->LoadVariables_result(result); - else - std::cout << "Error: DBAsyncCB_LoadVariables failed: !GetAnswer: '" << errbuf << "'" << std::endl; - return true; -} - -void AsyncLoadVariables(DBAsync *dba, Database *db) { - char* query = 0; - DBAsyncWork* dbaw = new DBAsyncWork(db, &DBAsyncCB_LoadVariables, 0, DBAsync::Read); - dbaw->AddQuery(0, &query, db->LoadVariables_MQ(&query)); - dba->AddWork(&dbaw); -} - - -//we only need to do anything when somebody puts work on the queue -//so instead of checking all the time, we will wait on a condition -//which will get signaled when somebody puts something on the queue -ThreadReturnType DBAsyncLoop(void* tmp) { - DBAsync* dba = (DBAsync*) tmp; - -#ifndef WIN32 - _log(COMMON__THREADS, "Starting DBAsyncLoop with thread ID %d", pthread_self()); -#endif - - dba->MLoopRunning.lock(); - while (dba->RunLoop()) { - //wait before working so we check the loop condition - //as soon as were done working - dba->CInList.Wait(); - //we could check dba->RunLoop() again to see if we - //got turned off while we were waiting - { - dba->Process(); - } - } - dba->MLoopRunning.unlock(); - -#ifndef WIN32 - _log(COMMON__THREADS, "Ending DBAsyncLoop with thread ID %d", pthread_self()); -#endif - - THREAD_RETURN(nullptr); -} - -DBAsync::DBAsync(DBcore* iDBC) -: Timeoutable(10000) -{ - pDBC = iDBC; - pRunLoop = true; - pNextID = 1; -#ifdef _WINDOWS - _beginthread(DBAsyncLoop, 0, this); -#else - pthread_t thread; - pthread_create(&thread, nullptr, DBAsyncLoop, this); -#endif -} - -DBAsync::~DBAsync() { - StopThread(); -} - -bool DBAsync::StopThread() { - bool ret; - MRunLoop.lock(); - ret = pRunLoop; - pRunLoop = false; - MRunLoop.unlock(); - - //signal the condition so we exit the loop if were waiting - CInList.Signal(); - - //this effectively waits for the processing thread to finish - MLoopRunning.lock(); - MLoopRunning.unlock(); - - return ret; -} - -uint32 DBAsync::AddWork(DBAsyncWork** iWork, uint32 iDelay) { - MInList.lock(); - uint32 ret = GetNextID(); - if (!(*iWork)->SetWorkID(ret)) { - MInList.unlock(); - return 0; - } - InList.Append(*iWork); - (*iWork)->SetStatus(Queued); - if (iDelay) - (*iWork)->pExecuteAfter = Timer::GetCurrentTime() + iDelay; -#if DEBUG_MYSQL_QUERIES >= 2 - std::cout << "Adding AsyncWork #" << (*iWork)->GetWorkID() << std::endl; - std::cout << "ExecuteAfter = " << (*iWork)->pExecuteAfter << " (" << Timer::GetCurrentTime() << " + " << iDelay << ")" << std::endl; -#endif - *iWork = 0; - MInList.unlock(); - - //wake up the processing thread and tell it to get to work. - CInList.Signal(); - - return ret; -} - -bool DBAsync::CancelWork(uint32 iWorkID) { - if (iWorkID == 0) - return false; -#if DEBUG_MYSQL_QUERIES >= 2 - std::cout << "DBAsync::CancelWork: " << iWorkID << std::endl; -#endif - MCurrentWork.lock(); - if (CurrentWork && CurrentWork->GetWorkID() == iWorkID) { - CurrentWork->Cancel(); - MCurrentWork.unlock(); - return true; - } - MCurrentWork.unlock(); - MInList.lock(); - LinkedListIterator iterator(InList); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->GetWorkID() == iWorkID) { - iterator.RemoveCurrent(true); - MInList.unlock(); - return true; - } - iterator.Advance(); - } - MInList.unlock(); - return false; -} - -bool DBAsync::RunLoop() { - bool ret; - MRunLoop.lock(); - ret = pRunLoop; - MRunLoop.unlock(); - return ret; -} - -DBAsyncWork* DBAsync::InListPop() { - DBAsyncWork* ret = 0; - MInList.lock(); - LinkedListIterator iterator(InList); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->pExecuteAfter <= Timer::GetCurrentTime()) { - ret = iterator.GetData(); -#if DEBUG_MYSQL_QUERIES >= 2 - std::cout << "Poping AsyncWork #" << ret->GetWorkID() << std::endl; - std::cout << ret->pExecuteAfter << " <= " << Timer::GetCurrentTime() << std::endl; -#endif - iterator.RemoveCurrent(false); - break; - } - iterator.Advance(); - } - MInList.unlock(); - return ret; -} - -DBAsyncWork* DBAsync::InListPopWrite() { - MInList.lock(); - LinkedListIterator iterator(InList); - - DBAsyncWork* ret = 0; - DBAsync::Type tmpType; - iterator.Reset(); - while (iterator.MoreElements()) { - tmpType = iterator.GetData()->Type(); - if (tmpType == Write || tmpType == Both) { - ret = iterator.GetData(); - iterator.RemoveCurrent(false); - break; - } - iterator.Advance(); - } - MInList.unlock(); - return ret; -} - -void DBAsync::AddFQ(DBAsyncFinishedQueue* iDBAFQ) { - MFQList.lock(); - DBAsyncFinishedQueue** tmp = new DBAsyncFinishedQueue*; - *tmp = iDBAFQ; - FQList.Append(tmp); - MFQList.unlock(); -} - -void DBAsync::Process() { - DBAsyncWork* tmpWork; - MCurrentWork.lock(); - while ((CurrentWork = InListPop())) { - MCurrentWork.unlock(); - //move from queued to executing - Status tmpStatus = CurrentWork->SetStatus(Executing); - if (tmpStatus == Queued) { - //execute the work - ProcessWork(CurrentWork); - tmpWork = CurrentWork; - MCurrentWork.lock(); - CurrentWork = 0; - MCurrentWork.unlock(); - //move from executing to finished - tmpStatus = tmpWork->SetStatus(DBAsync::Finished); - if (tmpStatus != Executing) { - if (tmpStatus != Canceled) { - std::cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::Process #1" << std::endl; - } - MCurrentWork.lock(); - safe_delete(tmpWork); - } - else { - //call callbacks or put results on finished queue - DispatchWork(tmpWork); - Sleep(25); - MCurrentWork.lock(); - } - } - else { - if (tmpStatus != Canceled) { - std::cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::Process #2" << std::endl; - } - MCurrentWork.lock(); - safe_delete(CurrentWork); - } - } - MCurrentWork.unlock(); -} - -void DBAsync::CheckTimeout() { - try{ - MFQList.lock(); - LinkedListIterator iterator(FQList); - - iterator.Reset(); - while (iterator.MoreElements()) { - (*iterator.GetData())->CheckTimeouts(); - iterator.Advance(); - } - MFQList.unlock(); - } - catch(...){ - - } -} - -void DBAsync::CommitWrites() { -#if DEBUG_MYSQL_QUERIES >= 2 - std::cout << "DBAsync::CommitWrites() called." << std::endl; -#endif - DBAsyncWork* tmpWork; - while ((tmpWork = InListPopWrite())) { - Status tmpStatus = tmpWork->SetStatus(Executing); - if (tmpStatus == Queued) { - ProcessWork(tmpWork); - tmpStatus = tmpWork->SetStatus(DBAsync::Finished); - if (tmpStatus != Executing) { - if (tmpStatus != Canceled) { - std::cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::CommitWrites #1" << std::endl; - } - safe_delete(tmpWork); - } - else { - DispatchWork(tmpWork); - } - } - else { - if (tmpStatus != Canceled) { - std::cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::CommitWrites #2" << std::endl; - } - safe_delete(tmpWork); - } - } -} - -void DBAsync::ProcessWork(DBAsyncWork* iWork, bool iSleep) { - DBAsyncQuery* CurrentQuery; - while ((CurrentQuery = iWork->PopQuery())) { - CurrentQuery->Process(pDBC); - iWork->PushAnswer(CurrentQuery); - if (iSleep) - Sleep(1); - } -} - -void DBAsync::DispatchWork(DBAsyncWork* iWork) { - //if this work has a callback, call it - //otherwise, stick the work on the finish queue - if (iWork->pCB) { - if (iWork->pCB(iWork)) - safe_delete(iWork); - } - else { - if (!iWork->pDBAFQ->Push(iWork)) - safe_delete(iWork); - } -} - - - -DBAsyncFinishedQueue::DBAsyncFinishedQueue(uint32 iTimeout) { - pTimeout = iTimeout; -} - -DBAsyncFinishedQueue::~DBAsyncFinishedQueue() { -} - -void DBAsyncFinishedQueue::CheckTimeouts() { - if (pTimeout == 0xFFFFFFFF) - return; - MLock.lock(); - LinkedListIterator iterator(list); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->CheckTimeout(pTimeout)) - iterator.RemoveCurrent(true); - iterator.Advance(); - } - MLock.unlock(); -} - -DBAsyncWork* DBAsyncFinishedQueue::Pop() { - DBAsyncWork* ret = 0; - MLock.lock(); - ret = list.Pop(); - MLock.unlock(); - return ret; -} - -DBAsyncWork* DBAsyncFinishedQueue::Find(uint32 iWorkID) { - DBAsyncWork* ret = 0; - MLock.lock(); - LinkedListIterator iterator(list); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->GetWorkID() == iWorkID) { - ret = iterator.GetData(); - iterator.RemoveCurrent(false); - break; - } - iterator.Advance(); - } - MLock.unlock(); - return ret; -} - -DBAsyncWork* DBAsyncFinishedQueue::PopByWPT(uint32 iWPT) { - DBAsyncWork* ret = 0; - MLock.lock(); - LinkedListIterator iterator(list); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->WPT() == iWPT) { - ret = iterator.GetData(); - iterator.RemoveCurrent(false); - break; - } - iterator.Advance(); - } - MLock.unlock(); - return ret; -} - -bool DBAsyncFinishedQueue::Push(DBAsyncWork* iDBAW) { - if (!this) - return false; - MLock.lock(); - list.Append(iDBAW); - MLock.unlock(); - return true; -} - - - -DBAsyncWork::DBAsyncWork(Database *db, DBAsyncFinishedQueue* iDBAFQ, uint32 iWPT, DBAsync::Type iType, uint32 iTimeout) -: m_db(db) -{ - pstatus = DBAsync::AddingWork; - pType = iType; - pExecuteAfter = 0; - pWorkID = 0; - pDBAFQ = iDBAFQ; - pCB = 0; - pWPT = iWPT; - pQuestionCount = 0; - pAnswerCount = 0; - pTimeout = iTimeout; - pTSFinish = 0; -} - -DBAsyncWork::DBAsyncWork(Database *db, DBWorkCompleteCallBack iCB, uint32 iWPT, DBAsync::Type iType, uint32 iTimeout) -: m_db(db) -{ - pstatus = DBAsync::AddingWork; - pType = iType; - pExecuteAfter = 0; - pWorkID = 0; - pDBAFQ = 0; - pCB = iCB; - pWPT = iWPT; - pQuestionCount = 0; - pAnswerCount = 0; - pTimeout = iTimeout; - pTSFinish = 0; -} - -DBAsyncWork::~DBAsyncWork() { - DBAsyncQuery* dbaq = 0; - while ((dbaq = todo.pop())) - safe_delete(dbaq); - while ((dbaq = done.pop())) - safe_delete(dbaq); - while ((dbaq = todel.pop())) - safe_delete(dbaq); -} - -bool DBAsyncWork::AddQuery(DBAsyncQuery** iDBAQ) { - bool ret; - MLock.lock(); - if (pstatus != DBAsync::AddingWork) - ret = false; - else { - ret = true; - pQuestionCount++; - todo.push(*iDBAQ); - (*iDBAQ)->pstatus = DBAsync::Queued; - *iDBAQ = 0; - } - MLock.unlock(); - return ret; -} - -bool DBAsyncWork::AddQuery(uint32 iQPT, char** iQuery, uint32 iQueryLen, bool iGetResultSet, bool iGetErrbuf) { - DBAsyncQuery* DBAQ = new DBAsyncQuery(iQPT, iQuery, iQueryLen, iGetResultSet, iGetErrbuf); - if (AddQuery(&DBAQ)) - return true; - else { - safe_delete(DBAQ); - return false; - } -} - -bool DBAsyncWork::SetWorkID(uint32 iWorkID) { - bool ret = true; - MLock.lock(); - if (pWorkID) - ret = false; - else - pWorkID = iWorkID; - MLock.unlock(); - return ret; -} - -uint32 DBAsyncWork::GetWorkID() { - uint32 ret; - MLock.lock(); - ret = pWorkID; - MLock.unlock(); - return ret; -} - -uint32 DBAsyncWork::WPT() { - uint32 ret; - MLock.lock(); - ret = pWPT; - MLock.unlock(); - return ret; -} - -DBAsync::Type DBAsyncWork::Type() { - DBAsync::Type ret; - MLock.lock(); - ret = pType; - MLock.unlock(); - return ret; -} - -DBAsyncQuery* DBAsyncWork::PopAnswer() { - DBAsyncQuery* ret; - MLock.lock(); - ret = done.pop(); - if (ret) - pAnswerCount--; - todel.push(ret); - MLock.unlock(); - return ret; -} - -bool DBAsyncWork::CheckTimeout(uint32 iFQTimeout) { - if (pTimeout == 0xFFFFFFFF) - return false; - bool ret = false; - MLock.lock(); - if (pTimeout > iFQTimeout) - iFQTimeout = pTimeout; - if (Timer::GetCurrentTime() > (pTSFinish + iFQTimeout)) - ret = true; - MLock.unlock(); - return ret; -} - -//sets the work's status to the supplied value and returns -//the revious status -DBAsync::Status DBAsyncWork::SetStatus(DBAsync::Status iStatus) { - DBAsync::Status ret; - MLock.lock(); - if (iStatus == DBAsync::Finished) - pTSFinish = Timer::GetCurrentTime(); - ret = pstatus; - pstatus = iStatus; - MLock.unlock(); - return ret; -} - -bool DBAsyncWork::Cancel() { - bool ret; - MLock.lock(); - if (pstatus != DBAsync::Finished) { - pstatus = DBAsync::Canceled; - ret = true; - } - else - ret = false; - MLock.unlock(); - return ret; -} - -bool DBAsyncWork::IsCancled() { - bool ret; - MLock.lock(); - ret = (bool) (pstatus == DBAsync::Canceled); - MLock.unlock(); - return ret; -} - -DBAsyncQuery* DBAsyncWork::PopQuery() { - DBAsyncQuery* ret = 0; - MLock.lock(); - ret = todo.pop(); - if (ret) - pQuestionCount--; - MLock.unlock(); - return ret; -} - -void DBAsyncWork::PushAnswer(DBAsyncQuery* iDBAQ) { - MLock.lock(); - done.push(iDBAQ); - pAnswerCount++; - MLock.unlock(); -} - - -DBAsyncQuery::DBAsyncQuery(uint32 iQPT, char** iQuery, uint32 iQueryLen, bool iGetResultSet, bool iGetErrbuf) { - if (iQueryLen == 0xFFFFFFFF) - pQueryLen = strlen(*iQuery); - else - pQueryLen = iQueryLen; - pQuery = *iQuery; - *iQuery = 0; - Init(iQPT, iGetResultSet, iGetErrbuf); -} - -DBAsyncQuery::DBAsyncQuery(uint32 iQPT, const char* iQuery, uint32 iQueryLen, bool iGetResultSet, bool iGetErrbuf) { - if (iQueryLen == 0xFFFFFFFF) - pQueryLen = strlen(iQuery); - else - pQueryLen = iQueryLen; - pQuery = strn0cpy(new char[pQueryLen+1], iQuery, pQueryLen+1); - Init(iQPT, iGetResultSet, iGetErrbuf); -} - -void DBAsyncQuery::Init(uint32 iQPT, bool iGetResultSet, bool iGetErrbuf) { - pstatus = DBAsync::AddingWork; - pQPT = iQPT; - pGetResultSet = iGetResultSet; - pGetErrbuf = iGetErrbuf; - - pmysqlsuccess = false; - perrbuf = 0; - perrnum = 0; - presult = 0; - paffected_rows = 0; - plast_insert_id = 0; -} - -DBAsyncQuery::~DBAsyncQuery() { - safe_delete_array(perrbuf); - safe_delete_array(pQuery); - if (presult) - mysql_free_result(presult); -} - -bool DBAsyncQuery::GetAnswer(char* errbuf, MYSQL_RES** result, uint32* affected_rows, uint32* last_insert_id, uint32* errnum) { - if (pstatus != DBAsync::Finished) { - if (errbuf) - snprintf(errbuf, MYSQL_ERRMSG_SIZE, "Error: Query not finished."); - if (errnum) - *errnum = UINT_MAX; - return false; - } - if (errbuf) { - if (pGetErrbuf) { - if (perrbuf) - strn0cpy(errbuf, perrbuf, MYSQL_ERRMSG_SIZE); - else - snprintf(errbuf, MYSQL_ERRMSG_SIZE, "Error message should've been saved, but hasnt. errno: %u", perrnum); - } - else - snprintf(errbuf, MYSQL_ERRMSG_SIZE, "Error message not saved. errno: %u", perrnum); - } - if (errnum) - *errnum = perrnum; - if (affected_rows) - *affected_rows = paffected_rows; - if (last_insert_id) - *last_insert_id = plast_insert_id; - if (result) - *result = presult; - return pmysqlsuccess; -} - -void DBAsyncQuery::Process(DBcore* iDBC) { - pstatus = DBAsync::Executing; - if (pGetErrbuf) - perrbuf = new char[MYSQL_ERRMSG_SIZE]; - MYSQL_RES** resultPP = 0; - if (pGetResultSet) - resultPP = &presult; - pmysqlsuccess = iDBC->RunQuery(pQuery, pQueryLen, perrbuf, resultPP, &paffected_rows, &plast_insert_id, &perrnum); - pstatus = DBAsync::Finished; -} - - - - - - - - - diff --git a/common/dbasync.h b/common/dbasync.h deleted file mode 100644 index f65f9cb58..000000000 --- a/common/dbasync.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef DBASYNC_H -#define DBASYNC_H -#include "../common/dbcore.h" -#include "../common/timeoutmgr.h" - - -class DBAsyncFinishedQueue; -class DBAsyncWork; -class DBAsyncQuery; -class Database; - -// Big daddy that owns the threads and does the work -class DBAsync : private Timeoutable { -public: - enum Status { AddingWork, Queued, Executing, Finished, Canceled }; - enum Type { Read, Write, Both }; - - DBAsync(DBcore* iDBC); - ~DBAsync(); - bool StopThread(); - - uint32 AddWork(DBAsyncWork** iWork, uint32 iDelay = 0); - bool CancelWork(uint32 iWorkID); - void CommitWrites(); - - void AddFQ(DBAsyncFinishedQueue* iDBAFQ); -protected: - //things related to the processing thread: - friend ThreadReturnType DBAsyncLoop(void* tmp); - Mutex MLoopRunning; - Condition CInList; - bool RunLoop(); - void Process(); - -private: - virtual void CheckTimeout(); - - void ProcessWork(DBAsyncWork* iWork, bool iSleep = true); - void DispatchWork(DBAsyncWork* iWork); - inline uint32 GetNextID() { return pNextID++; } - DBAsyncWork* InListPop(); - DBAsyncWork* InListPopWrite(); // Ignores delay - void OutListPush(DBAsyncWork* iDBAW); - - Mutex MRunLoop; - bool pRunLoop; - - DBcore* pDBC; - uint32 pNextID; - Mutex MInList; - LinkedList InList; - - Mutex MFQList; - LinkedList FQList; - - // Mutex for outside access to current work & when current work is being changed. - // NOT locked when CurrentWork is being accessed by the DBAsync thread. - // Never change pointer from outside DBAsync thread! - // Only here for access to thread-safe DBAsyncWork functions. - Mutex MCurrentWork; - DBAsyncWork* CurrentWork; - -}; - -/* - DB Work Complete Callback: - This will be called under the DBAsync thread! Never access any non-threadsafe - data/functions/classes. (ie: zone, entitylist, client, etc are not threadsafe) - Function prototype: - return value: true if we should delete the data, false if we should keep it -*/ -typedef bool(*DBWorkCompleteCallBack)(DBAsyncWork*); - -class DBAsyncFinishedQueue { -public: - DBAsyncFinishedQueue(uint32 iTimeout = 90000); - ~DBAsyncFinishedQueue(); - - DBAsyncWork* Pop(); - DBAsyncWork* PopByWPT(uint32 iWPT); - DBAsyncWork* Find(uint32 iWPT); - bool Push(DBAsyncWork* iDBAW); - - void CheckTimeouts(); -private: - Mutex MLock; - uint32 pTimeout; - LinkedList list; -}; - -// Container class for multiple queries -class DBAsyncWork { -public: - DBAsyncWork(Database *db, DBAsyncFinishedQueue* iDBAFQ, uint32 iWPT = 0, DBAsync::Type iType = DBAsync::Both, uint32 iTimeout = 0); - DBAsyncWork(Database *db, DBWorkCompleteCallBack iCB, uint32 iWPT = 0, DBAsync::Type iType = DBAsync::Both, uint32 iTimeout = 0); - ~DBAsyncWork(); - - bool AddQuery(DBAsyncQuery** iDBAQ); - bool AddQuery(uint32 iQPT, char** iQuery, uint32 iQueryLen = 0xFFFFFFFF, bool iGetResultSet = true, bool iGetErrbuf = true); - uint32 WPT(); - DBAsync::Type Type(); - - // Pops finished queries off the work - DBAsyncQuery* PopAnswer(); - uint32 QueryCount(); - - Database *GetDB() const { return(m_db); } - - bool CheckTimeout(uint32 iFQTimeout); - bool SetWorkID(uint32 iWorkID); - uint32 GetWorkID(); -protected: - friend class DBAsync; - DBAsync::Status SetStatus(DBAsync::Status iStatus); - bool Cancel(); - bool IsCancled(); - DBAsyncQuery* PopQuery(); // Get query to be run - void PushAnswer(DBAsyncQuery* iDBAQ); // Push answer back into workset - - // not mutex'd cause only to be accessed from dbasync class - uint32 pExecuteAfter; -private: - Mutex MLock; - uint32 pQuestionCount; - uint32 pAnswerCount; - uint32 pWorkID; - uint32 pWPT; - uint32 pTimeout; - uint32 pTSFinish; // timestamp when finished - DBAsyncFinishedQueue* pDBAFQ; //we do now own this pointer - DBWorkCompleteCallBack pCB; - DBAsync::Status pstatus; - DBAsync::Type pType; - MyQueue todo; - MyQueue done; - MyQueue todel; - Database *const m_db; //we do now own this pointer -}; - -// Container class for the query information -class DBAsyncQuery { -public: - DBAsyncQuery(uint32 iQPT, char** iQuery, uint32 iQueryLen = 0xFFFFFFFF, bool iGetResultSet = true, bool iGetErrbuf = true); - DBAsyncQuery(uint32 iQPT, const char* iQuery, uint32 iQueryLen = 0xFFFFFFFF, bool iGetResultSet = true, bool iGetErrbuf = true); - ~DBAsyncQuery(); - - bool GetAnswer(char* errbuf = 0, MYSQL_RES** result = 0, uint32* affected_rows = 0, uint32* last_insert_id = 0, uint32* errnum = 0); - inline uint32 QPT() { return pQPT; } -protected: - friend class DBAsyncWork; - uint32 pQPT; - - friend class DBAsync; - void Process(DBcore* iDBC); - - void Init(uint32 iQPT, bool iGetResultSet, bool iGetErrbuf); - DBAsync::Status pstatus; - char* pQuery; - uint32 pQueryLen; - bool pGetResultSet; - bool pGetErrbuf; - - bool pmysqlsuccess; - char* perrbuf; - uint32 perrnum; - uint32 paffected_rows; - uint32 plast_insert_id; - MYSQL_RES* presult; -}; - - -void AsyncLoadVariables(DBAsync *dba, Database *db); - - -#endif - diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index c561e3a22..061f498d4 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -800,9 +800,12 @@ struct SuspendedMinion_Struct ** Length: 4308 bytes ** OpCode: 0x006a */ -static const uint32 MAX_PP_LANGUAGE = 28; -static const uint32 MAX_PP_SPELLBOOK = 480; // Increased to 480 to support SoF -static const uint32 MAX_PP_MEMSPELL = 9; +static const uint32 MAX_PP_LANGUAGE = 28; +static const uint32 MAX_PP_SPELLBOOK = 720; // Set for all functions +static const uint32 MAX_PP_MEMSPELL = 12; // Set to latest client so functions can work right +static const uint32 MAX_PP_REF_SPELLBOOK = 480; // Set for Player Profile size retain +static const uint32 MAX_PP_REF_MEMSPELL = 9; // Set for Player Profile size retain + static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 240; static const uint32 MAX_GROUP_MEMBERS = 6; @@ -880,7 +883,7 @@ struct PlayerProfile_Struct /*0245*/ uint8 guildbanker; /*0246*/ uint8 unknown0246[6]; // /*0252*/ uint32 intoxication; -/*0256*/ uint32 spellSlotRefresh[MAX_PP_MEMSPELL]; //in ms +/*0256*/ uint32 spellSlotRefresh[MAX_PP_REF_MEMSPELL]; //in ms /*0292*/ uint32 abilitySlotRefresh; /*0296*/ uint8 haircolor; // Player hair color /*0297*/ uint8 beardcolor; // Player beard color @@ -919,9 +922,9 @@ struct PlayerProfile_Struct /*2505*/ uint8 unknown2541[47]; // ? /*2552*/ uint8 languages[MAX_PP_LANGUAGE]; /*2580*/ uint8 unknown2616[4]; -/*2584*/ uint32 spell_book[MAX_PP_SPELLBOOK]; +/*2584*/ uint32 spell_book[MAX_PP_REF_SPELLBOOK]; /*4504*/ uint8 unknown4540[128]; // Was [428] all 0xff -/*4632*/ uint32 mem_spells[MAX_PP_MEMSPELL]; +/*4632*/ uint32 mem_spells[MAX_PP_REF_MEMSPELL]; /*4668*/ uint8 unknown4704[32]; // /*4700*/ float y; // Player y position /*4704*/ float x; // Player x position diff --git a/common/mysql_request_result.cpp b/common/mysql_request_result.cpp index ffbc3ef40..df598c8cd 100644 --- a/common/mysql_request_result.cpp +++ b/common/mysql_request_result.cpp @@ -32,6 +32,7 @@ MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, u void MySQLRequestResult::FreeInternals() { + safe_delete_array(m_ErrorBuffer); if (m_Result != nullptr) diff --git a/common/mysql_request_result.h b/common/mysql_request_result.h index cd561be56..693c4d43a 100644 --- a/common/mysql_request_result.h +++ b/common/mysql_request_result.h @@ -40,7 +40,7 @@ public: MySQLRequestResult& operator=(MySQLRequestResult&& other); bool Success() const { return m_Success;} - std::string ErrorMessage() const {return std::string(m_ErrorBuffer);} + std::string ErrorMessage() const { return m_ErrorBuffer ? std::string(m_ErrorBuffer) : std::string(""); } uint32 ErrorNumber() const {return m_ErrorNumber;} uint32 RowsAffected() const {return m_RowsAffected;} uint32 RowCount() const {return m_RowCount;} diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 6124d37f1..f6f960cc3 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -61,46 +61,6 @@ bool SharedDatabase::SetHideMe(uint32 account_id, uint8 hideme) return true; } -bool SharedDatabase::SetPlayerProfile(uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 affected_rows = 0; - bool ret = false; - - if (RunQuery(query, SetPlayerProfile_MQ(&query, account_id, charid, pp, inv, ext, current_zone, current_instance, MaxXTargets), errbuf, 0, &affected_rows)) { - ret = (affected_rows != 0); - } - - if (!ret) { - LogFile->write(EQEMuLog::Error, "SetPlayerProfile query '%s' %s", query, errbuf); - } - - safe_delete_array(query); - return ret; -} - -// Generate SQL for updating player profile -uint32 SharedDatabase::SetPlayerProfile_MQ(char** query, uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { - *query = new char[396 + sizeof(PlayerProfile_Struct)* 2 + sizeof(ExtendedProfile_Struct)* 2 + 4]; - char* end = *query; - if (!current_zone) - current_zone = pp->zone_id; - - if (!current_instance) - current_instance = pp->zoneInstance; - - if (strlen(pp->name) == 0) // Sanity check in case pp never loaded - return false; - - end += sprintf(end, "UPDATE character_ SET timelaston=unix_timestamp(now()),name=\'%s\', zonename=\'%s\', zoneid=%u, instanceid=%u, x = %f, y = %f, z = %f, ", pp->name, GetZoneName(current_zone), current_zone, current_instance, pp->x, pp->y, pp->z); - // end += DoEscapeString(end, (char*)pp, sizeof(PlayerProfile_Struct)); - end += sprintf(end, " extprofile=\'"); - end += DoEscapeString(end, (char*)ext, sizeof(ExtendedProfile_Struct)); - end += sprintf(end, "\',class=%d,level=%d,xtargets=%u WHERE id=%u", pp->class_, pp->level, MaxXTargets, charid); - - return (uint32)(end - (*query)); -} - uint8 SharedDatabase::GetGMSpeed(uint32 account_id) { @@ -386,12 +346,11 @@ int32 SharedDatabase::GetSharedPlatinum(uint32 account_id) return 0; } -bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) -{ +bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET sharedplat = sharedplat + %i WHERE id = %i", amount_to_add, account_id), errbuf)) { + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE `account` SET `sharedplat` = `sharedplat` + %i WHERE id = %i", amount_to_add, account_id), errbuf)) { std::cerr << "Error in SetSharedPlatinum query '" << query << "' " << errbuf << std::endl; safe_delete_array(query); return false; @@ -401,35 +360,24 @@ bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) return true; } -bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; +bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) { const Item_Struct* myitem; - - RunQuery - ( - query, - MakeAnyLenString - ( - &query, - "SELECT itemid, item_charges, slot FROM starting_items " - "WHERE (race = %i or race = 0) AND (class = %i or class = 0) AND " - "(deityid = %i or deityid=0) AND (zoneid = %i or zoneid = 0) AND " - "gm <= %i ORDER BY id", - si_race, si_class, si_deity, si_current_zone, admin_level - ), - errbuf, - &result - ); - safe_delete_array(query); - - while((row = mysql_fetch_row(result))) { - int itemid = atoi(row[0]); - int charges = atoi(row[1]); - int slot = atoi(row[2]); + uint32 itemid = 0; + uint32 charges = 0; + uint32 slot = 0; + auto query = StringFormat( + "SELECT `itemid`, `item_charges`, `slot` FROM `starting_items`" + " WHERE (`race` = %i OR `race` = 0)" + " AND (`class` = %i OR `class` = 0)" + " AND (`deityid` = %i OR `deityid` = 0)" + " AND (`zoneid` = %i OR `zoneid` = 0)" + " AND gm <= %i ORDER BY id", + si_race, si_class, si_deity, si_current_zone, admin_level); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + itemid = atoi(row[0]); + charges = atoi(row[1]); + slot = atoi(row[2]); myitem = GetItem(itemid); if(!myitem) continue; @@ -439,9 +387,6 @@ bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, inv->PutItem(slot, *myinst); safe_delete(myinst); } - - if(result) mysql_free_result(result); - return true; } @@ -675,8 +620,13 @@ bool SharedDatabase::GetInventory(uint32 account_id, char* name, Inventory* inv) bool ret = false; // Retrieve character inventory - if (RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5," - "instnodrop,custom_data FROM inventory INNER JOIN character_ ch ON ch.id=charid WHERE ch.name='%s' AND ch.account_id=%i ORDER BY slotid", + if (RunQuery(query, MakeAnyLenString(&query, + " SELECT `slotid`, `itemid`, `charges`, `color`, `augslot1`, `augslot2`, `augslot3`, `augslot4`, `augslot5`, `instnodrop`, `custom_data`" + " FROM `inventory`" + " INNER JOIN character_ ch ON ch.id = charid" + " WHERE ch.NAME = '%s'" + " AND ch.account_id = % i" + " ORDER BY `slotid`", name, account_id), errbuf, &result)) { while ((row = mysql_fetch_row(result))) { @@ -736,7 +686,7 @@ bool SharedDatabase::GetInventory(uint32 account_id, char* name, Inventory* inv) } } } - if (slot_id>=8000 && slot_id <= 8999) + if (slot_id >= 8000 && slot_id <= 8999) put_slot_id = inv->PushCursor(*inst); else put_slot_id = inv->PutItem(slot_id, *inst); @@ -2053,8 +2003,7 @@ const LootDrop_Struct* SharedDatabase::GetLootDrop(uint32 lootdrop_id) { return nullptr; } -void SharedDatabase::GetPlayerInspectMessage(char* playername, InspectMessage_Struct* message) { - +void SharedDatabase::GetPlayerInspectMessage(char* playername, InspectMessage_Struct* message) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; diff --git a/common/shareddb.h b/common/shareddb.h index c55163465..5401bfa0a 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -34,9 +34,6 @@ public: SharedDatabase(const char* host, const char* user, const char* passwd, const char* database,uint32 port); virtual ~SharedDatabase(); - /* Temp */ - uint32 SetPlayerProfile_MQ(char** query, uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets); - bool SetPlayerProfile(uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets); /* * General Character Related Stuff */ diff --git a/world/client.cpp b/world/client.cpp index fc7e525db..c8fea66c9 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -64,8 +64,6 @@ extern ClientList client_list; extern uint32 numclients; extern volatile bool RunLoops; - - Client::Client(EQStreamInterface* ieqs) : autobootup_timeout(RuleI(World, ZoneAutobootTimeoutMS)), CLE_keepalive_timer(RuleI(World, ClientKeepaliveTimeoutMS)), @@ -128,9 +126,8 @@ void Client::SendLogServer() safe_delete(outapp); } -void Client::SendEnterWorld(std::string name) -{ -char char_name[32]= { 0 }; +void Client::SendEnterWorld(std::string name) { + char char_name[32]= { 0 }; if (pZoning && database.GetLiveChar(GetAccountID(), char_name)) { if(database.GetAccountIDByChar(char_name) != GetAccountID()) { eqs->Close(); @@ -472,7 +469,6 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) { } bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { - if (GetAccountID() == 0) { clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account"); return false; @@ -491,19 +487,10 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { 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; - } + if(!database.CheckNameFilter(char_name)) { valid = false; } + else if (char_name[0] < 'A' && char_name[0] > 'Z') { valid = false; } /* Name must begin with an upper-case letter. */ + else if (database.ReserveName(GetAccountID(), char_name)) { valid = true; } + else { valid = false; } outapp->pBuffer[0] = valid? 1 : 0; QueuePacket(outapp); safe_delete(outapp); @@ -642,13 +629,11 @@ bool Client::HandleCharacterCreateRequestPacket(const EQApplicationPacket *app) } bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { - if (GetAccountID() == 0) - { + if (GetAccountID() == 0) { clog(WORLD__CLIENT_ERR,"Account ID not set; unable to create character."); return false; } - else if (app->size != sizeof(CharCreate_Struct)) - { + 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 @@ -657,8 +642,7 @@ bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { } CharCreate_Struct *cc = (CharCreate_Struct*)app->pBuffer; - if(OPCharCreate(char_name, cc) == false) - { + if(OPCharCreate(char_name, cc) == false) { database.DeleteCharacter(char_name); EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApproveName, 1); outapp->pBuffer[0] = 0; @@ -675,8 +659,7 @@ bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { return true; } -bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { - +bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if (GetAccountID() == 0) { clog(WORLD__CLIENT_ERR,"Enter world with no logged in account"); eqs->Close(); @@ -713,8 +696,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { return true; } - if(!pZoning && ew->return_home && !ew->tutorial) - { + if(!pZoning && ew->return_home && !ew->tutorial) { CharacterSelect_Struct* cs = new CharacterSelect_Struct; memset(cs, 0, sizeof(CharacterSelect_Struct)); database.GetCharSelectInfo(GetAccountID(), cs); @@ -733,12 +715,10 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { } safe_delete(cs); - if(home_enabled) - { + if(home_enabled) { zoneID = database.MoveCharacterToBind(charid,4); } - else - { + 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(); @@ -807,16 +787,16 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { 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){ + 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); + strcpy(gj->yourname, char_name); + strcpy(gj->membername, leader); QueuePacket(outapp3); safe_delete(outapp3); } @@ -895,8 +875,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { bool Client::HandleDeleteCharacterPacket(const EQApplicationPacket *app) { uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer); - if(char_acct_id == GetAccountID()) - { + if(char_acct_id == GetAccountID()) { clog(WORLD__CLIENT,"Delete character: %s",app->pBuffer); database.DeleteCharacter((char *)app->pBuffer); SendCharInfo(); @@ -1347,8 +1326,7 @@ void Client::SendApproveWorld() safe_delete(outapp); } -bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) -{ +bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { PlayerProfile_Struct pp; ExtendedProfile_Struct ext; Inventory inv; @@ -1356,12 +1334,11 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) char startzone[50]={0}; uint32 i; struct in_addr in; - - - int stats_sum = cc->STR + cc->STA + cc->AGI + cc->DEX + - cc->WIS + cc->INT + cc->CHA; + + int stats_sum = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA; in.s_addr = GetIP(); + clog(WORLD__CLIENT,"Character creation request from %s LS#%d (%s:%d) : ", GetCLE()->LSName(), GetCLE()->LSID(), inet_ntoa(in), GetPort()); clog(WORLD__CLIENT,"Name: %s", name); clog(WORLD__CLIENT,"Race: %d Class: %d Gender: %d Deity: %d Start zone: %d", @@ -1374,38 +1351,23 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) clog(WORLD__CLIENT,"Hairstyle: %d Haircolor: %d", cc->hairstyle, cc->haircolor); clog(WORLD__CLIENT,"Beard: %d Beardcolor: %d", cc->beard, cc->beardcolor); - // validate the char creation struct + /* Validate the char creation struct */ if(ClientVersionBit & BIT_SoFAndLater) { - if(!CheckCharCreateInfoSoF(cc)) - { + if(!CheckCharCreateInfoSoF(cc)) { clog(WORLD__CLIENT_ERR,"CheckCharCreateInfo did not validate the request (bad race/class/stats)"); return false; } } else { - if(!CheckCharCreateInfoTitanium(cc)) - { + if(!CheckCharCreateInfoTitanium(cc)) { clog(WORLD__CLIENT_ERR,"CheckCharCreateInfo did not validate the request (bad race/class/stats)"); return false; } } - // Convert incoming cc_s to the new PlayerProfile_Struct + /* Convert incoming cc_s to the new PlayerProfile_Struct */ memset(&pp, 0, sizeof(PlayerProfile_Struct)); // start building the profile - InitExtendedProfile(&ext); - strn0cpy(pp.name, name, 63); - // clean the capitalization of the name -#if 0 // on second thought, don't - this will just make the creation fail -// because the name won't match what was already reserved earlier - for (i = 0; pp.name[i] && i < 63; i++) - { - if(!isalpha(pp.name[i])) - return false; - pp.name[i] = tolower(pp.name[i]); - } - pp.name[0] = toupper(pp.name[0]); -#endif pp.race = cc->race; pp.class_ = cc->class_; @@ -1432,44 +1394,39 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) pp.lastlogin = bday; pp.level = 1; pp.points = 5; - pp.cur_hp = 1000; // 1k hp during dev only - //what was the point of this? zone dosent handle this: - //pp.expAA = 0xFFFFFFFF; - + pp.cur_hp = 1000; // 1k hp during dev only pp.hunger_level = 6000; pp.thirst_level = 6000; - - // FIXME: FV roleplay, database goodness... - - // Racial Languages - SetRacialLanguages( &pp ); // bUsh - SetRaceStartingSkills( &pp ); // bUsh - SetClassStartingSkills( &pp ); // bUsh + /* Racial Languages */ + SetRacialLanguages( &pp ); + SetRaceStartingSkills( &pp ); + SetClassStartingSkills( &pp ); pp.skills[SkillSenseHeading] = 200; // Some one fucking fix this to use a field name. -Doodman //pp.unknown3596[28] = 15; // @bp: This is to enable disc usage // strcpy(pp.servername, WorldConfig::get()->ShortName.c_str()); - for(i = 0; i < MAX_PP_SPELLBOOK; i++) + for (i = 0; i < MAX_PP_REF_SPELLBOOK; i++) pp.spell_book[i] = 0xFFFFFFFF; - for(i = 0; i < MAX_PP_MEMSPELL; i++) + for(i = 0; i < MAX_PP_REF_MEMSPELL; i++) pp.mem_spells[i] = 0xFFFFFFFF; for(i = 0; i < BUFF_COUNT; i++) pp.buffs[i].spellid = 0xFFFF; + /* + Was memset(pp.unknown3704, 0xffffffff, 8); + but I dont think thats what you really wanted to do... + memset is byte based + */ - //was memset(pp.unknown3704, 0xffffffff, 8); - //but I dont think thats what you really wanted to do... - //memset is byte based - - //If server is PVP by default, make all character set to it. + /* If server is PVP by default, make all character set to it. */ pp.pvp = database.GetServerType() == 1 ? 1 : 0; - //If it is an SoF Client and the SoF Start Zone rule is set, send new chars there + /* If it is an SoF Client and the SoF Start Zone rule is set, send new chars there */ if((ClientVersionBit & BIT_SoFAndLater) && (RuleI(World, SoFStartZoneID) > 0)) { clog(WORLD__CLIENT,"Found 'SoFStartZoneID' rule setting: %i", (RuleI(World, SoFStartZoneID))); pp.zone_id = (RuleI(World, SoFStartZoneID)); @@ -1478,11 +1435,9 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) else clog(WORLD__CLIENT_ERR,"Error getting zone id for Zone ID %i", (RuleI(World, SoFStartZoneID))); } - else - { - // if there's a startzone variable put them in there - if(database.GetVariable("startzone", startzone, 50)) - { + else { + /* if there's a startzone variable put them in there */ + if(database.GetVariable("startzone", startzone, 50)) { clog(WORLD__CLIENT,"Found 'startzone' variable setting: %s", startzone); pp.zone_id = database.GetZoneID(startzone); if(pp.zone_id) @@ -1490,8 +1445,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) else clog(WORLD__CLIENT_ERR,"Error getting zone id for '%s'", startzone); } - else // otherwise use normal starting zone logic - { + else{ /* otherwise use normal starting zone logic */ bool ValidStartZone = false; if(ClientVersionBit & BIT_TitaniumAndEarlier) @@ -1504,14 +1458,12 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) } } - if(!pp.zone_id) - { + if(!pp.zone_id) { pp.zone_id = 1; // qeynos pp.x = pp.y = pp.z = -1; } - if(!pp.binds[0].zoneId) - { + if(!pp.binds[0].zoneId) { pp.binds[0].zoneId = pp.zone_id; pp.binds[0].x = pp.x; pp.binds[0].y = pp.y; @@ -1519,7 +1471,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) pp.binds[0].heading = pp.heading; } - // set starting city location to the initial bind point + /* Set Starting city */ pp.binds[4] = pp.binds[0]; @@ -1528,28 +1480,23 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) clog(WORLD__CLIENT,"Bind location: %s %0.2f, %0.2f, %0.2f", database.GetZoneName(pp.binds[0].zoneId), pp.binds[0].x, pp.binds[0].y, pp.binds[0].z); - - // Starting Items inventory + /* Starting Items inventory */ database.SetStartingItems(&pp, &inv, pp.race, pp.class_, pp.deity, pp.zone_id, pp.name, GetAdmin()); - // now we give the pp and the inv we made to StoreCharacter // to see if we can store it - if (!database.StoreCharacter(GetAccountID(), &pp, &inv, &ext)) - { + if (!database.StoreCharacter(GetAccountID(), &pp, &inv)) { clog(WORLD__CLIENT_ERR,"Character creation failed: %s", pp.name); return false; } - else - { + else { clog(WORLD__CLIENT,"Character creation successful: %s", pp.name); return true; } } // returns true if the request is ok, false if there's an error -bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) -{ +bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) { if(!cc) return false; _log(WORLD__CLIENT, "Validating char creation info..."); diff --git a/world/net.cpp b/world/net.cpp index 2d186c1af..3c4ab53c3 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -67,7 +67,6 @@ #endif -#include "../common/dbasync.h" #include "../common/emu_tcp_server.h" #include "../common/patches/patches.h" #include "zoneserver.h" @@ -98,7 +97,6 @@ UCSConnection UCSLink; QueryServConnection QSLink; LauncherList launcher_list; AdventureManager adventure_manager; -DBAsync *dbasync = nullptr; volatile bool RunLoops = true; uint32 numclients = 0; uint32 numzones = 0; @@ -175,7 +173,6 @@ int main(int argc, char** argv) { _log(WORLD__INIT_ERR, "Cannot continue without a database connection."); return 1; } - dbasync = new DBAsync(&database); guild_mgr.SetDatabase(&database); if (argc >= 2) { diff --git a/world/worlddb.cpp b/world/worlddb.cpp index deccc9207..fdbcc704d 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -21,7 +21,6 @@ #include "../common/string_util.h" #include "../common/eq_packet_structs.h" #include "../common/item.h" -#include "../common/dbasync.h" #include "../common/rulesys.h" #include #include @@ -390,12 +389,16 @@ bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Stru in_pp->x = in_pp->y = in_pp->z = in_pp->heading = in_pp->zone_id = 0; in_pp->binds[0].x = in_pp->binds[0].y = in_pp->binds[0].z = in_pp->binds[0].zoneId = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "SELECT x,y,z,heading,bind_id FROM start_zones WHERE zone_id=%i AND player_class=%i " - "AND player_deity=%i AND player_race=%i", - in_cc->start_zone, - in_cc->class_, - in_cc->deity, - in_cc->race), errbuf, &result)) + if(!RunQuery(query, MakeAnyLenString(&query, "SELECT `x`, `y`, ``z, `heading`, `bind_id` " + " FROM `start_zones` " + " WHERE `zone_id` = %i " + " AND `player_class` = %i " + " AND player_deity=%i" + " AND player_race=%i", + in_cc->start_zone, + in_cc->class_, + in_cc->deity, + in_cc->race), errbuf, &result)) { LogFile->write(EQEMuLog::Status, "SoF Start zone query failed: %s : %s\n", query, errbuf); safe_delete_array(query); diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index af0752a95..6529a1f3d 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -652,17 +652,17 @@ bool ZoneServer::Process() { client->Clearance(wtz->response); } case ServerOP_ZoneToZoneRequest: { - // - // solar: ZoneChange is received by the zone the player is in, then the - // zone sends a ZTZ which ends up here. This code then find the target - // (ingress point) and boots it if needed, then sends the ZTZ to it. - // The ingress server will decide wether the player can enter, then will - // send back the ZTZ to here. This packet is passed back to the egress - // server, which will send a ZoneChange response back to the client - // which can be an error, or a success, in which case the client will - // disconnect, and their zone location will be saved when ~Client is - // called, so it will be available when they ask to zone. - // + // + // solar: ZoneChange is received by the zone the player is in, then the + // zone sends a ZTZ which ends up here. This code then find the target + // (ingress point) and boots it if needed, then sends the ZTZ to it. + // The ingress server will decide wether the player can enter, then will + // send back the ZTZ to here. This packet is passed back to the egress + // server, which will send a ZoneChange response back to the client + // which can be an error, or a success, in which case the client will + // disconnect, and their zone location will be saved when ~Client is + // called, so it will be available when they ask to zone. + // if(pack->size != sizeof(ZoneToZone_Struct)) @@ -675,43 +675,31 @@ bool ZoneServer::Process() { zlog(WORLD__ZONE,"ZoneToZone request for %s current zone %d req zone %d\n", ztz->name, ztz->current_zone_id, ztz->requested_zone_id); - printf("\n\n ZoneToZone request for %s current zone %d req zone %d\n\n", - ztz->name, ztz->current_zone_id, ztz->requested_zone_id); - - if(GetZoneID() == ztz->current_zone_id && GetInstanceID() == ztz->current_instance_id) // this is a request from the egress zone - { + /* This is a request from the egress zone */ + if(GetZoneID() == ztz->current_zone_id && GetInstanceID() == ztz->current_instance_id) { zlog(WORLD__ZONE,"Processing ZTZ for egress from zone for client %s\n", ztz->name); - if - ( - ztz->admin < 80 && - ztz->ignorerestrictions < 2 && - zoneserver_list.IsZoneLocked(ztz->requested_zone_id) - ) - { + if (ztz->admin < 80 && ztz->ignorerestrictions < 2 && zoneserver_list.IsZoneLocked(ztz->requested_zone_id)) { ztz->response = 0; SendPacket(pack); break; } ZoneServer *ingress_server = nullptr; - if(ztz->requested_instance_id > 0) - { + if(ztz->requested_instance_id > 0) { ingress_server = zoneserver_list.FindByInstanceID(ztz->requested_instance_id); } - else - { - ingress_server = zoneserver_list.FindByZoneID(ztz->requested_zone_id); - + else { + ingress_server = zoneserver_list.FindByZoneID(ztz->requested_zone_id); } - if(ingress_server) // found a zone already running - { + /* Zone was already running*/ + if(ingress_server) { _log(WORLD__ZONE,"Found a zone already booted for %s\n", ztz->name); ztz->response = 1; } - else // need to boot one - { + /* Boot the Zone*/ + else { int server_id; if ((server_id = zoneserver_list.TriggerBootup(ztz->requested_zone_id, ztz->requested_instance_id))){ _log(WORLD__ZONE,"Successfully booted a zone for %s\n", ztz->name); @@ -719,8 +707,7 @@ bool ZoneServer::Process() { ztz->response = 1; ingress_server = zoneserver_list.FindByID(server_id); } - else - { + else { _log(WORLD__ZONE_ERR,"FAILED to boot a zone for %s\n", ztz->name); // bootup failed, send back error code 0 ztz->response = 0; @@ -728,27 +715,24 @@ bool ZoneServer::Process() { } if(ztz->response!=0 && client) client->LSZoneChange(ztz); - SendPacket(pack); // send back to egress server - if(ingress_server) // if we couldn't boot one, this is 0 - { - ingress_server->SendPacket(pack); // inform target server - } + SendPacket(pack); // send back to egress server + if(ingress_server) { + ingress_server->SendPacket(pack); // inform target server + } } - else // this is response from the ingress server, route it back to the egress server - { + /* Response from Ingress server, route back to egress */ + else{ + zlog(WORLD__ZONE,"Processing ZTZ for ingress to zone for client %s\n", ztz->name); ZoneServer *egress_server = nullptr; - if(ztz->current_instance_id > 0) - { + if(ztz->current_instance_id > 0) { egress_server = zoneserver_list.FindByInstanceID(ztz->current_instance_id); } - else - { + else { egress_server = zoneserver_list.FindByZoneID(ztz->current_zone_id); } - if(egress_server) - { + if(egress_server) { egress_server->SendPacket(pack); } } @@ -786,21 +770,18 @@ bool ZoneServer::Process() { delete whom; break; } - case ServerOP_RequestOnlineGuildMembers: - { + case ServerOP_RequestOnlineGuildMembers: { ServerRequestOnlineGuildMembers_Struct *srogms = (ServerRequestOnlineGuildMembers_Struct*) pack->pBuffer; zlog(GUILDS__IN_PACKETS, "ServerOP_RequestOnlineGuildMembers Recieved. FromID=%i GuildID=%i", srogms->FromID, srogms->GuildID); client_list.SendOnlineGuildMembers(srogms->FromID, srogms->GuildID); break; } - case ServerOP_ClientVersionSummary: - { + case ServerOP_ClientVersionSummary: { ServerRequestClientVersionSummary_Struct *srcvss = (ServerRequestClientVersionSummary_Struct*) pack->pBuffer; client_list.SendClientVersionSummary(srcvss->Name); break; } - case ServerOP_ReloadRules: - { + case ServerOP_ReloadRules: { zoneserver_list.SendPacket(pack); RuleManager::Instance()->LoadRules(&database, "default"); break; @@ -1275,30 +1256,10 @@ bool ZoneServer::Process() { case ServerOP_QueryServGeneric: case ServerOP_Speech: case ServerOP_QSPlayerLogTrades: - { - QSLink.SendPacket(pack); - break; - } case ServerOP_QSPlayerLogHandins: - { - QSLink.SendPacket(pack); - break; - } case ServerOP_QSPlayerLogNPCKills: - { - QSLink.SendPacket(pack); - break; - } case ServerOP_QSPlayerLogDeletes: - { - QSLink.SendPacket(pack); - break; - } case ServerOP_QSPlayerLogMoves: - { - QSLink.SendPacket(pack); - break; - } case ServerOP_QSPlayerLogMerchantTransactions: { QSLink.SendPacket(pack); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 0b6ebc10a..8cd086405 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -119,7 +119,6 @@ SET(zone_sources zone_logsys.cpp zone_config.cpp zonedb.cpp - zonedbasync.cpp zoning.cpp ) @@ -207,7 +206,6 @@ SET(zone_headers zone.h zone_config.h zonedb.h - zonedbasync.h zonedump.h ) diff --git a/zone/client.cpp b/zone/client.cpp index c56d47b4d..962715b86 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -73,8 +73,6 @@ extern uint32 numclients; extern PetitionList petition_list; bool commandlogged; char entirecommand[255]; -extern DBAsyncFinishedQueue MTdbafq; -extern DBAsync *dbasync; Client::Client(EQStreamInterface* ieqs) : Mob("No name", // name @@ -544,8 +542,9 @@ bool Client::Save(uint8 iCommitNow) { /* Save Character Currency */ database.SaveCharacterCurrency(this->CharacterID(), &m_pp); - /* Save Character AA */ - // SaveAA(); + /* Save Current Bind Points : Sets Instance to 0 because it is currently not implemented */ + database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[0].zoneId, 0, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, 0, 0); /* Regular bind */ + database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[4].zoneId, 0, m_pp.binds[4].x, m_pp.binds[4].y, m_pp.binds[4].z, 0, 1); /* Home Bind */ /* Save Character Buffs */ database.SaveBuffs(this); @@ -557,15 +556,11 @@ bool Client::Save(uint8 iCommitNow) { /* Save Mercs */ if (GetMercInfo().MercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)){ GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); } - if(GetMercTimer()->Enabled()) { GetMercInfo().MercTimerRemaining = GetMercTimer()->GetRemainingTime(); } + if (GetMercTimer()->Enabled()) { GetMercInfo().MercTimerRemaining = GetMercTimer()->GetRemainingTime(); } if (GetMerc() && !dead) { } else { memset(&m_mercinfo, 0, sizeof(struct MercInfo)); } m_pp.lastlogin = time(nullptr); - if (pQueuedSaveWorkID) { - dbasync->CancelWork(pQueuedSaveWorkID); - pQueuedSaveWorkID = 0; - } if (GetPet() && !GetPet()->IsFamiliar() && GetPet()->CastToNPC()->GetPetSpellID() && !dead) { NPC *pet = GetPet()->CastToNPC(); @@ -585,34 +580,10 @@ bool Client::Save(uint8 iCommitNow) { p_timers.Store(&database); - /* Save Character Task */ - SaveTaskState(); + database.SaveCharacterTribute(this->CharacterID(), &m_pp); - // if (iCommitNow <= 1) { - // // char* query = 0; - // // uint32_breakdown workpt; - // // workpt.b4() = DBA_b4_Entity; - // // workpt.w2_3() = GetID(); - // // workpt.b1() = DBA_b1_Entity_Client_Save; - // // DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Write, 0xFFFFFFFF); - // // dbaw->AddQuery(iCommitNow == 0 ? true : false, &query, database.SetPlayerProfile_MQ(&query, account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets), false); - // // if (iCommitNow == 0){ - // // pQueuedSaveWorkID = dbasync->AddWork(&dbaw, 2500); - // // } - // // else { - // // dbasync->AddWork(&dbaw, 0); - // // SaveBackup(); - // // } - // // safe_delete_array(query); - // // return true; - // } - // else if (database.SetPlayerProfile(account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets)) { - // SaveBackup(); - // } - // else { - // std::cerr << "Failed to update player profile" << std::endl; - // return false; - // } + /* Save Character Task */ + SaveTaskState(); /* Save Character Data */ database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp); @@ -624,12 +595,6 @@ bool Client::Save(uint8 iCommitNow) { } void Client::SaveBackup() { - if (!RunLoops) - return; - char* query = 0; - DBAsyncWork* dbaw = new DBAsyncWork(&database, &DBAsyncCB_CharacterBackup, this->CharacterID(), DBAsync::Read); - dbaw->AddQuery(0, &query, MakeAnyLenString(&query, "Select id, UNIX_TIMESTAMP()-UNIX_TIMESTAMP(ts) as age from character_backup where charid=%u and backupreason=0 order by ts asc", this->CharacterID()), true); - dbasync->AddWork(&dbaw, 0); } CLIENTPACKET::CLIENTPACKET() @@ -1393,12 +1358,14 @@ bool Client::UpdateLDoNPoints(int32 points, uint32 theme) void Client::SetSkill(SkillUseTypes skillid, uint16 value) { if (skillid > HIGHEST_SKILL) - return; - m_pp.skills[skillid] = value; // We need to be able to #setskill 254 and 255 to reset skills + return; + m_pp.skills[skillid] = value; // We need to be able to #setskill 254 and 255 to reset skills + + database.SaveCharacterSkill(this->CharacterID(), skillid, value); EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; - skill->skillId=skillid; + skill->skillId=skillid; skill->value=value; QueuePacket(outapp); safe_delete(outapp); @@ -1414,10 +1381,12 @@ void Client::IncreaseLanguageSkill(int skill_id, int value) { if (m_pp.languages[skill_id] > 100) //Lang skill above max m_pp.languages[skill_id] = 100; + database.SaveCharacterLanguage(this->CharacterID(), skill_id, m_pp.languages[skill_id]); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; skill->skillId = 100 + skill_id; - skill->value = m_pp.languages[skill_id]; + skill->value = m_pp.languages[skill_id]; QueuePacket(outapp); safe_delete(outapp); @@ -2165,32 +2134,27 @@ void Client::AddMoneyToPP(uint64 copper, bool updateclient){ uint64 tmp2; tmp = copper; - // Add Amount of Platinum + /* Add Amount of Platinum */ tmp2 = tmp/1000; int32 new_val = m_pp.platinum + tmp2; - if(new_val < 0) { - m_pp.platinum = 0; - } else { - m_pp.platinum = m_pp.platinum + tmp2; - } + if(new_val < 0) { m_pp.platinum = 0; } + else { m_pp.platinum = m_pp.platinum + tmp2; } tmp-=tmp2*1000; //if (updateclient) // SendClientMoneyUpdate(3,tmp2); - // Add Amount of Gold + /* Add Amount of Gold */ tmp2 = tmp/100; new_val = m_pp.gold + tmp2; - if(new_val < 0) { - m_pp.gold = 0; - } else { - m_pp.gold = m_pp.gold + tmp2; - } + if(new_val < 0) { m_pp.gold = 0; } + else { m_pp.gold = m_pp.gold + tmp2; } + tmp-=tmp2*100; //if (updateclient) // SendClientMoneyUpdate(2,tmp2); - // Add Amount of Silver + /* Add Amount of Silver */ tmp2 = tmp/10; new_val = m_pp.silver + tmp2; if(new_val < 0) { @@ -2226,8 +2190,7 @@ void Client::AddMoneyToPP(uint64 copper, bool updateclient){ LogFile->write(EQEMuLog::Debug, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); } -void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool updateclient){ - +void Client::EVENT_ITEM_ScriptStopReturn(){ /* Set a timestamp in an entity variable for plugin check_handin.pl in return_items This will stopgap players from items being returned if global_npc.pl has a catch all return_items */ @@ -2236,6 +2199,10 @@ void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 plat gettimeofday(&read_time, 0); sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec); this->SetEntityVariable("Stop_Return", buffer); +} + +void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool updateclient){ + this->EVENT_ITEM_ScriptStopReturn(); int32 new_value = m_pp.platinum + platinum; if(new_value >= 0 && new_value > m_pp.platinum) @@ -3184,6 +3151,7 @@ void Client::SetLanguageSkill(int langid, int value) value = 100; //Max lang value m_pp.languages[langid] = value; + database.SaveCharacterLanguage(this->CharacterID(), langid, value); EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; diff --git a/zone/client.h b/zone/client.h index bab48e8ea..e33dcd755 100644 --- a/zone/client.h +++ b/zone/client.h @@ -234,7 +234,6 @@ public: bool KeyRingCheck(uint32 item_id); void KeyRingList(); virtual bool IsClient() const { return true; } - virtual void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw); void CompleteConnect(); bool TryStacking(ItemInst* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true); void SendTraderPacket(Client* trader, uint32 Unknown72 = 51); @@ -654,14 +653,14 @@ public: void OnDisconnect(bool hard_disconnect); - uint16 GetSkillPoints() {return m_pp.points;} - void SetSkillPoints(int inp) {m_pp.points = inp;} + uint16 GetSkillPoints() { return m_pp.points;} + void SetSkillPoints(int inp) { m_pp.points = inp;} void IncreaseSkill(int skill_id, int value = 1) { if (skill_id <= HIGHEST_SKILL) { m_pp.skills[skill_id] += value; } } void IncreaseLanguageSkill(int skill_id, int value = 1); - virtual uint16 GetSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return((itembonuses.skillmod[skill_id] > 0)? m_pp.skills[skill_id]*(100 + itembonuses.skillmod[skill_id])/100 : m_pp.skills[skill_id]); } return 0; } + virtual uint16 GetSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return((itembonuses.skillmod[skill_id] > 0) ? m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100 : m_pp.skills[skill_id]); } return 0; } uint32 GetRawSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; } - bool HasSkill(SkillUseTypes skill_id) const; + bool HasSkill(SkillUseTypes skill_id) const; bool CanHaveSkill(SkillUseTypes skill_id) const; void SetSkill(SkillUseTypes skill_num, uint16 value); void AddSkill(SkillUseTypes skillid, uint16 value); @@ -782,6 +781,7 @@ public: int16 acmod(); // Item methods + void EVENT_ITEM_ScriptStopReturn(); uint32 NukeItem(uint32 itemnum, uint8 where_to_check = (invWhereWorn | invWherePersonal | invWhereBank | invWhereSharedBank | invWhereTrading | invWhereCursor)); void SetTint(int16 slot_id, uint32 color); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 87d884eb7..3b34878d5 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -77,9 +77,6 @@ extern volatile bool ZoneLoaded; extern WorldServer worldserver; extern PetitionList petition_list; extern EntityList entity_list; -extern DBAsyncFinishedQueue MTdbafq; -extern DBAsync *dbasync; - typedef void (Client::*ClientPacketProc)(const EQApplicationPacket *app); //Use a map for connecting opcodes since it dosent get used a lot and is sparse @@ -525,7 +522,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) client_state = CLIENT_KICKED; return; } - strcpy(name, cze->char_name); /* Check for Client Spoofing */ @@ -592,19 +588,20 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (LFG){ LFG = atoi(row[12]); } if (firstlogon){ firstlogon = atoi(row[15]); } } - + loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ + database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ database.LoadCharacterMaterialColor(cid, &m_pp); /* Load Character Material */ + database.LoadCharacterPotions(cid, &m_pp); /* Load Character Potion Belt */ database.LoadCharacterCurrency(cid, &m_pp); /* Load Character Currency into PP */ database.LoadCharacterData(cid, &m_pp); /* Load Character Data from DB into PP */ - database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); /* Move to another method when can, this is pointless... */ - database.LoadCharacterCurrency(cid, &m_pp); /* Load Character Currency */ database.LoadCharacterSkills(cid, &m_pp); /* Load Character Skills */ - database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ + database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); /* Move to another method when can, this is pointless... */ database.LoadCharacterSpellBook(cid, &m_pp); /* Load Character Spell Book */ database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */ database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */ + database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ if (level){ level = m_pp.level; } @@ -612,9 +609,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (gmhideme) { trackable = false; } /* Set Con State for Reporting */ conn_state = PlayerProfileLoaded; - - // m_pp.zone_id = zone->GetZoneID(); - // m_pp.zoneInstance = zone->GetInstanceID(); /* Set Total Seconds Played */ TotalSecondsPlayed = m_pp.timePlayedMin * 60; @@ -9168,38 +9162,6 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) return; } -void Client::DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { - Entity::DBAWComplete(workpt_b1, dbaw); - switch (workpt_b1) { - case DBA_b1_Entity_Client_Save: { - clock_t t = std::clock(); /* Function timer start */ - - char errbuf[MYSQL_ERRMSG_SIZE]; - uint32 affected_rows = 0; - DBAsyncQuery* dbaq = dbaw->PopAnswer(); - if (dbaq->GetAnswer(errbuf, 0, &affected_rows) && affected_rows == 1) { - if (dbaq->QPT()) { - SaveBackup(); - } - } - else { - std::cout << "Async client save failed. '" << errbuf << "'" << std::endl; - Message(13, "Error: Asyncronous save of your character failed."); - if (Admin() >= 200) - Message(13, "errbuf: %s", errbuf); - } - pQueuedSaveWorkID = 0; - - LogFile->write(EQEMuLog::Status, "Client::DBAWComplete Save Character Async done... Took %f seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC); - break; - } - default: { - std::cout << "Error: Client::DBAWComplete(): Unknown workpt_b1" << std::endl; - break; - } - } -} - /* Finish client connecting state */ void Client::CompleteConnect() { UpdateWho(); @@ -9221,7 +9183,7 @@ void Client::CompleteConnect() { SendAppearancePacket(AT_GuildID, GuildID(), false); SendAppearancePacket(AT_GuildRank, GuildRank(), false); } - for (uint32 spellInt = 0; spellInt < MAX_PP_SPELLBOOK; spellInt++) { + for (uint32 spellInt = 0; spellInt < MAX_PP_REF_SPELLBOOK; spellInt++) { if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000) m_pp.spell_book[spellInt] = 0xFFFFFFFF; } @@ -9417,7 +9379,7 @@ void Client::CompleteConnect() { SendWearChange(x); Mob *pet = GetPet(); if (pet != nullptr) { - for (x = 0; x < 8; x++) + for (x = 0; x < 8; x++) pet->SendWearChange(x); } @@ -9497,6 +9459,8 @@ void Client::CompleteConnect() { alternate_currency_loaded = true; ProcessAlternateCurrencyQueue(); + + CalcItemScale(); DoItemEnterZone(); @@ -10504,8 +10468,7 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) { } } -void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) { - +void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) { if(app->size != sizeof(MovePotionToBelt_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PotionBelt expected %i got %i", sizeof(MovePotionToBelt_Struct), app->size); @@ -10519,6 +10482,7 @@ void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) { m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID; m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon; strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, BaseItem->Name, sizeof(BaseItem->Name)); + database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.items[mptbs->SlotNumber].item_id, m_pp.potionbelt.items[mptbs->SlotNumber].icon); } } else { @@ -10526,9 +10490,6 @@ void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) { m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0; strncpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, "\0", 1); } - - Save(); - } void Client::Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app) { diff --git a/zone/client_process.cpp b/zone/client_process.cpp index fd8cb3740..4c9c8ca2d 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1717,6 +1717,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app) } uint16 skilllevel = GetRawSkill(skill); + if(skilllevel == 0) { //this is a new skill.. uint16 t_level = SkillTrainLevel(skill, GetClass()); @@ -1726,7 +1727,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app) } SetSkill(skill, t_level); - } else { + } else { switch(skill) { case SkillBrewing: case SkillMakePoison: diff --git a/zone/command.cpp b/zone/command.cpp index b1d195bf4..ea85fb9af 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -225,7 +225,6 @@ int command_init(void) { command_add("worldshutdown","- Shut down world and all zones",200,command_worldshutdown) || command_add("sendzonespawns","- Refresh spawn list for all clients in zone",150,command_sendzonespawns) || command_add("dbspawn2","[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table",100,command_dbspawn2) || - command_add("copychar","[character name] [new character] [new account id] - Create a copy of a character",100,command_copychar) || command_add("shutdown","- Shut this zone process down",150,command_shutdown) || command_add("delacct","[accountname] - Delete an account",150,command_delacct) || command_add("setpass","[accountname] [password] - Set local password for accountname",150,command_setpass) || @@ -2227,28 +2226,6 @@ void command_dbspawn2(Client *c, const Seperator *sep) } } -void command_copychar(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0]==0 || sep->arg[2][0] == 0 || sep->arg[3][0] == 0) - c->Message(0, "Usage: #copychar [character name] [new character] [new account id]"); - //CheckUsedName.... TRUE=No Char, FALSE=Char/Error - //If there is no source... - else if (database.CheckUsedName((char*)sep->arg[1])) { - c->Message(0, "Source character not found!"); - } - else { - //If there is a name is not used.... - if (database.CheckUsedName((char*) sep->arg[2])) { - if (!database.CopyCharacter((char*) sep->arg[1], (char*) sep->arg[2], atoi(sep->arg[3]))) - c->Message(0, "Character copy operation failed!"); - else - c->Message(0, "Character copy complete."); - } - else - c->Message(0, "Target character already exists!"); - } -} - void command_shutdown(Client *c, const Seperator *sep) { CatchSignal(2); @@ -2423,8 +2400,8 @@ void command_showskills(Client *c, const Seperator *sep) c->Message(0, "Skills for %s", t->GetName()); for (SkillUseTypes i=Skill1HBlunt; i <= HIGHEST_SKILL; i=(SkillUseTypes)(i+1)) - c->Message(0, "Skill [%d] is at [%d]", i, t->GetSkill(i)); -} + c->Message(0, "Skill [%d] is at [%d] - %u", i, t->GetSkill(i), t->GetRawSkill(i)); +} void command_findspell(Client *c, const Seperator *sep) { @@ -2801,7 +2778,7 @@ void command_charbackup(Client *c, const Seperator *sep) if (sep->IsNumber(2)) charid = atoi(sep->arg[2]); else - database.GetAccountIDByChar(sep->arg[2], &charid); + database.GetAccountIDByChar(sep->arg[2]); if (charid) { if (database.RunQuery(query, MakeAnyLenString(&query, "Select id, backupreason, charid, account_id, zoneid, DATE_FORMAT(ts, '%%m/%%d/%%Y %%H:%%i:%%s') " @@ -2828,7 +2805,7 @@ void command_charbackup(Client *c, const Seperator *sep) if (sep->IsNumber(2)) charid = atoi(sep->arg[2]); else - database.GetAccountIDByChar(sep->arg[2], &charid); + database.GetAccountIDByChar(sep->arg[2]); if (charid && sep->IsNumber(3)) { uint32 cbid = atoi(sep->arg[3]); @@ -8725,6 +8702,7 @@ void command_traindisc(Client *c, const Seperator *sep) break; //continue the 1st loop } else if(t->GetPP().disciplines.values[r] == 0) { t->GetPP().disciplines.values[r] = curspell; + database.SaveCharacterDisc(c->CharacterID(), r, curspell); t->SendDisciplineUpdate(); t->Message(0, "You have learned a new discipline!"); count++; //success counter diff --git a/zone/command.h b/zone/command.h index 6a880625e..ef7302c51 100644 --- a/zone/command.h +++ b/zone/command.h @@ -125,7 +125,6 @@ void command_worldshutdown(Client *c, const Seperator *sep); void command_sendzonespawns(Client *c, const Seperator *sep); void command_zsave(Client *c, const Seperator *sep); void command_dbspawn2(Client *c, const Seperator *sep); -void command_copychar(Client *c, const Seperator *sep); void command_shutdown(Client *c, const Seperator *sep); void command_delacct(Client *c, const Seperator *sep); void command_setpass(Client *c, const Seperator *sep); diff --git a/zone/effects.cpp b/zone/effects.cpp index 5e07d5f9a..10748c0e1 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -606,6 +606,7 @@ bool Client::TrainDiscipline(uint32 itemid) { return(false); } else if(m_pp.disciplines.values[r] == 0) { m_pp.disciplines.values[r] = spell_id; + database.SaveCharacterDisc(this->CharacterID(), r, spell_id); SendDisciplineUpdate(); Message(0, "You have learned a new discipline!"); return(true); diff --git a/zone/entity.cpp b/zone/entity.cpp index 2ca3f640d..b59822d73 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -40,7 +40,6 @@ #include "../common/spdat.h" #include "../common/features.h" #include "string_ids.h" -#include "../common/dbasync.h" #include "guild_mgr.h" #include "raids.h" #include "quest_parser_collection.h" @@ -57,7 +56,6 @@ extern WorldServer worldserver; extern NetConnection net; extern uint32 numclients; extern PetitionList petition_list; -extern DBAsync *dbasync; extern char errorname[32]; extern uint16 adverrornum; @@ -65,12 +63,11 @@ extern uint16 adverrornum; Entity::Entity() { id = 0; - pDBAsyncWorkID = 0; } Entity::~Entity() { - dbasync->CancelWork(pDBAsyncWorkID); + } Client *Entity::CastToClient() diff --git a/zone/entity.h b/zone/entity.h index cc302fe75..9599aa7fc 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -28,7 +28,6 @@ #include "zonedb.h" #include "zonedump.h" -#include "zonedbasync.h" #include "qglobals.h" class EQApplicationPacket; @@ -53,7 +52,7 @@ class Bot; class BotRaids; #endif -extern EntityList entity_list; +extern EntityList entity_list; class Entity { @@ -100,7 +99,6 @@ public: inline const uint16& GetID() const { return id; } virtual const char* GetName() { return ""; } - virtual void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { pDBAsyncWorkID = 0; } bool CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, float trg_x, float trg_y, float trg_z, float perwalk=1); #ifdef BOTS diff --git a/zone/exp.cpp b/zone/exp.cpp index 9415022ca..a82aa25d9 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -51,14 +51,7 @@ static uint32 MaxBankedRaidLeadershipPoints(int Level) void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { - /* Set a timestamp in an entity variable for plugin check_handin.pl in return_items - This will stopgap players from items being returned if global_npc.pl has a catch all return_items - */ - struct timeval read_time; - char buffer[50]; - gettimeofday(&read_time, 0); - sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec); - this->SetEntityVariable("Stop_Return", buffer); + this->EVENT_ITEM_ScriptStopReturn(); uint32 add_exp = in_add_exp; diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 02cf6e568..69570faf2 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -200,14 +200,7 @@ bool Client::CheckLoreConflict(const Item_Struct* item) { } bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) { - /* Set a timestamp in an entity variable for plugin check_handin.pl in return_items - This will stopgap players from items being returned if global_npc.pl has a catch all return_items - */ - struct timeval read_time; - char buffer[50]; - gettimeofday(&read_time, 0); - sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec); - this->SetEntityVariable("Recieved_Item", buffer); + this->EVENT_ITEM_ScriptStopReturn(); // TODO: update calling methods and script apis to handle a failure return @@ -2320,10 +2313,8 @@ void Client::CreateBandolier(const EQApplicationPacket *app) { _log(INVENTORY__BANDOLIER, "Char: %s Creating Bandolier Set %i, Set Name: %s", GetName(), bs->number, bs->name); strcpy(m_pp.bandoliers[bs->number].name, bs->name); - const ItemInst* InvItem; - - const Item_Struct *BaseItem; - + const ItemInst* InvItem; + const Item_Struct *BaseItem; int16 WeaponSlot; for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) { @@ -2334,6 +2325,7 @@ void Client::CreateBandolier(const EQApplicationPacket *app) { _log(INVENTORY__BANDOLIER, "Char: %s adding item %s to slot %i", GetName(),BaseItem->Name, WeaponSlot); m_pp.bandoliers[bs->number].items[BandolierSlot].item_id = BaseItem->ID; m_pp.bandoliers[bs->number].items[BandolierSlot].icon = BaseItem->Icon; + database.SaveCharacterBandolier(this->CharacterID(), bs->number, BandolierSlot, m_pp.bandoliers[bs->number].items[BandolierSlot].item_id, m_pp.bandoliers[bs->number].items[BandolierSlot].icon, bs->name); } else { _log(INVENTORY__BANDOLIER, "Char: %s no item in slot %i", GetName(), WeaponSlot); @@ -2341,21 +2333,17 @@ void Client::CreateBandolier(const EQApplicationPacket *app) { m_pp.bandoliers[bs->number].items[BandolierSlot].icon = 0; } } - Save(); } void Client::RemoveBandolier(const EQApplicationPacket *app) { - - // Delete bandolier with the specified number - BandolierDelete_Struct *bds = (BandolierDelete_Struct*)app->pBuffer; _log(INVENTORY__BANDOLIER, "Char: %s removing set", GetName(), bds->number); memset(m_pp.bandoliers[bds->number].name, 0, 32); for(int i = bandolierMainHand; i <= bandolierAmmo; i++) { m_pp.bandoliers[bds->number].items[i].item_id = 0; - m_pp.bandoliers[bds->number].items[i].icon = 0; + m_pp.bandoliers[bds->number].items[i].icon = 0; } - Save(); + database.DeleteCharacterBandolier(this->CharacterID(), bds->number); } void Client::SetBandolier(const EQApplicationPacket *app) { diff --git a/zone/net.cpp b/zone/net.cpp index 8a8421786..498ed5102 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -97,8 +97,6 @@ extern Zone* zone; EQStreamFactory eqsf(ZoneStream); npcDecayTimes_Struct npcCorpseDecayTimes[100]; TitleManager title_manager; -DBAsyncFinishedQueue MTdbafq; -DBAsync *dbasync = nullptr; QueryServ *QServ = 0; TaskManager *taskmanager = 0; QuestParserCollection *parse = 0; @@ -168,8 +166,6 @@ int main(int argc, char** argv) { _log(ZONE__INIT_ERR, "Cannot continue without a database connection."); return 1; } - dbasync = new DBAsync(&database); - dbasync->AddFQ(&MTdbafq); guild_mgr.SetDatabase(&database); GuildBanks = nullptr; @@ -444,10 +440,6 @@ int main(int argc, char** argv) { } } - DBAsyncWork* dbaw = 0; - while ((dbaw = MTdbafq.Pop())) { - DispatchFinishedDBAsync(dbaw); - } if (InterserverTimer.Check()) { InterserverTimer.Start(); database.ping(); @@ -507,8 +499,6 @@ int main(int argc, char** argv) { //Fix for Linux world server problem. eqsf.Close(); worldserver.Disconnect(); - dbasync->CommitWrites(); - dbasync->StopThread(); safe_delete(taskmanager); command_deinit(); safe_delete(parse); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index fc31178ae..776376bdf 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -974,6 +974,7 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { SpellGlobalCheckResult = initiator->SpellGlobalCheck(curspell, Char_ID); if (SpellGlobalCheckResult) { initiator->GetPP().disciplines.values[r] = curspell; + database.SaveCharacterDisc(Char_ID, r, curspell); initiator->SendDisciplineUpdate(); initiator->Message(0, "You have learned a new discipline!"); count++; //success counter @@ -981,11 +982,12 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { break; //continue the 1st loop } else { - initiator->GetPP().disciplines.values[r] = curspell; - initiator->SendDisciplineUpdate(); - initiator->Message(0, "You have learned a new discipline!"); - count++; //success counter - break; //continue the 1st loop + initiator->GetPP().disciplines.values[r] = curspell; + database.SaveCharacterDisc(Char_ID, r, curspell); + initiator->SendDisciplineUpdate(); + initiator->Message(0, "You have learned a new discipline!"); + count++; //success counter + break; //continue the 1st loop } } //if we get to this point, there's already a discipline in this slot, so we skip it } diff --git a/zone/spells.cpp b/zone/spells.cpp index 94921c685..1f37a21e4 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4933,8 +4933,9 @@ void Client::UntrainDisc(int slot, bool update_client) if(slot >= MAX_PP_DISCIPLINES || slot < 0) return; - mlog(CLIENT__SPELLS, "Discipline %d untrained from slot %d", m_pp.disciplines.values[slot], slot); + mlog(CLIENT__SPELLS, "Discipline %d untrained from slot %d", m_pp.disciplines.values[slot], slot); m_pp.disciplines.values[slot] = 0; + database.DeleteCharacterDisc(this->CharacterID(), slot); if(update_client) { diff --git a/zone/trading.cpp b/zone/trading.cpp index 23d8d77af..414b397fc 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -913,14 +913,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if(!tradingWith->IsMoving()) tradingWith->FaceTarget(this); - /* Set a timestamp in an entity variable for plugin check_handin.pl in return_items - This will stopgap players from items being returned if global_npc.pl has a catch all return_items - */ - struct timeval read_time; - char buffer[50]; - gettimeofday(&read_time, 0); - sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec); - this->SetEntityVariable("Stop_Return", buffer); + this->EVENT_ITEM_ScriptStopReturn(); } } @@ -938,7 +931,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st parse->AddVar(temp1, temp2); snprintf(temp1, 100, "platinum.%d", tradingWith->GetNPCTypeID()); snprintf(temp2, 100, "%u", trade->pp); - parse->AddVar(temp1, temp2); + parse->AddVar(temp1, temp2); if(tradingWith->GetAppearance() != eaDead) { tradingWith->FaceTarget(this); diff --git a/zone/tribute.cpp b/zone/tribute.cpp index 95da9b5a3..08bbf4662 100644 --- a/zone/tribute.cpp +++ b/zone/tribute.cpp @@ -254,8 +254,10 @@ int32 Client::TributeItem(uint32 slot, uint32 quantity) { return(0); } - //make sure they have enough of them - //and remove it from inventory + /* + Make sure they have enough of them + and remove it from inventory + */ if(inst->IsStackable()) { if(inst->GetCharges() < (int32)quantity) //dont have enough.... return(0); @@ -267,7 +269,7 @@ int32 Client::TributeItem(uint32 slot, uint32 quantity) { pts *= quantity; - //add the tribute value in points + /* Add the tribute value in points */ AddTributePoints(pts); return(pts); } @@ -279,7 +281,7 @@ int32 Client::TributeMoney(uint32 platinum) { return(0); } - //add the tribute value in points + /* Add the tribute value in points */ AddTributePoints(platinum); return(platinum); } diff --git a/zone/zone.cpp b/zone/zone.cpp index a9c63b22f..9ae17381d 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -77,8 +77,6 @@ extern bool staticzone; Zone* zone = 0; volatile bool ZoneLoaded = false; extern QuestParserCollection* parse; -extern DBAsyncFinishedQueue MTdbafq; -extern DBAsync *dbasync; bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { const char* zonename = database.GetZoneName(iZoneID); @@ -705,7 +703,6 @@ void Zone::Shutdown(bool quite) zone->ResetAuth(); safe_delete(zone); - dbasync->CommitWrites(); entity_list.ClearAreas(); parse->ReloadQuests(true); UpdateWindowTitle(); @@ -844,8 +841,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) } Zone::~Zone() { - if(pQueuedMerchantsWorkID != 0) - dbasync->CancelWork(pQueuedMerchantsWorkID); spawn2_list.Clear(); safe_delete(zonemap); safe_delete(watermap); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 794b1240f..77e5739ee 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -626,8 +626,7 @@ TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id){ return loadti; } -ItemInst* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) { - +ItemInst* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) { char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; MYSQL_RES *result; @@ -687,8 +686,7 @@ void ZoneDatabase::SaveTraderItem(uint32 CharID, uint32 ItemID, uint32 SerialNum safe_delete_array(query); } -void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) { - +void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) { _log(TRADING__CLIENT, "ZoneDatabase::UpdateTraderItemCharges(%i, %i, %i)", CharID, SerialNumber, Charges); char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; @@ -784,8 +782,7 @@ void ZoneDatabase::DeleteBuyLines(uint32 CharID){ safe_delete_array(query); } -void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char* ItemName, uint32 Quantity, uint32 Price) { - +void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char* ItemName, uint32 Quantity, uint32 Price) { char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; if (!(RunQuery(query,MakeAnyLenString(&query, "replace INTO buyer VALUES(%i,%i, %i,\"%s\",%i,%i)", @@ -795,8 +792,7 @@ void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, cons safe_delete_array(query); } -void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { - +void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; @@ -806,8 +802,7 @@ void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { safe_delete_array(query); } -void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) { - +void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) { if(Quantity <= 0) { RemoveBuyLine(CharID, BuySlot); return; @@ -1016,7 +1011,7 @@ bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* } bool ZoneDatabase::LoadCharacterFactionValues(uint32 character_id, faction_map & val_list) { - std::string query = StringFormat("SELECT faction_id,current_value FROM faction_values WHERE char_id = %i", character_id); + std::string query = StringFormat("SELECT `faction_id`, `current_value` FROM `faction_values` WHERE `char_id` = %i", character_id); auto results = database.QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { val_list[atoi(row[0])] = atoi(row[1]); } return true; @@ -1055,8 +1050,12 @@ bool ZoneDatabase::LoadCharacterLanguages(uint32 character_id, PlayerProfile_Str "`value` " "FROM " "`character_languages` " - "WHERE `id` = %u ORDER BY `language_id`", character_id); + "WHERE `id` = %u ORDER BY `lang_id`", character_id); auto results = database.QueryDatabase(query); int i = 0; + /* Initialize Languages */ + for (i = 0; i < MAX_PP_LANGUAGE; i++){ + pp->languages[i] = 0; + } for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); pp->languages[i] = atoi(row[1]); } return true; } @@ -1069,8 +1068,8 @@ bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_S "`character_disciplines`" "WHERE `id` = %u ORDER BY `disc_id`", character_id); auto results = database.QueryDatabase(query); int i = 0; - for (auto row = results.begin(); row != results.end(); ++row) { pp->disciplines.values[i] = atoi(row[0]); i++; } - return true; + for (auto row = results.begin(); row != results.end(); ++row) { pp->disciplines.values[i] = atoi(row[0]); i++; } + return true; } bool ZoneDatabase::LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp){ @@ -1081,7 +1080,11 @@ bool ZoneDatabase::LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct "FROM " "`character_skills` " "WHERE `id` = %u ORDER BY `skill_id`", character_id); - auto results = database.QueryDatabase(query); int i = 0; + auto results = database.QueryDatabase(query); int i = 0; + /* Initialize Skill */ + for (i = 0; i < MAX_PP_SKILL; i++){ + pp->skills[i] = 0; + } for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); pp->skills[i] = atoi(row[1]); } return true; } @@ -1131,44 +1134,162 @@ bool ZoneDatabase::LoadCharacterCurrency(uint32 character_id, PlayerProfile_Stru return true; } +bool ZoneDatabase::LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT slot, blue, green, red, use_tint, color FROM `character_material` WHERE `id` = %u LIMIT 9", character_id); + auto results = database.QueryDatabase(query); int i = 0; int r = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + r = 0; + i = atoi(row[r]); /* Slot */ r++; + pp->item_tint[i].rgb.blue = atoi(row[r]); r++; + pp->item_tint[i].rgb.green = atoi(row[r]); r++; + pp->item_tint[i].rgb.red = atoi(row[r]); r++; + pp->item_tint[i].rgb.use_tint = atoi(row[r]); + } + return true; +} + +bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT `bandolier_id`, `bandolier_slot`, `item_id`, `icon`, `bandolier_name` FROM `character_bandolier` WHERE `id` = %u LIMIT 16", character_id); + auto results = database.QueryDatabase(query); int i = 0; int r = 0; int si = 0; + for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ + for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ + pp->bandoliers[i].items[si].icon = 0; + } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + r = 0; + i = atoi(row[r]); /* Bandolier ID */ r++; + si = atoi(row[r]); /* Bandolier Slot */ r++; + pp->bandoliers[i].items[si].item_id = atoi(row[r]); r++; + pp->bandoliers[i].items[si].icon = atoi(row[r]); r++; + strcpy(pp->bandoliers[i].name, row[r]); r++; + si++; + } + return true; +} + +bool ZoneDatabase::LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT `tier`, `tribute` FROM `character_tribute` WHERE `id` = %u", character_id); + auto results = database.QueryDatabase(query); int i = 0; + for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ + pp->tributes[i].tribute = 0; + pp->tributes[i].tier = 0; + } + for (auto row = results.begin(); row != results.end(); ++row) { + pp->tributes[i].tier = atoi(row[0]); + pp->tributes[i].tribute = atoi(row[1]); + } + return true; +} + +bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT `potion_id`, `item_id`, `icon` FROM `character_potionbelt` WHERE `id` = %u LIMIT 4", character_id); + auto results = database.QueryDatabase(query); int i = 0; + for (i = 0; i < EmuConstants::POTION_BELT_SIZE; i++){ + pp->potionbelt.items[i].icon = 0; + pp->potionbelt.items[i].item_id = 0; + strncpy(pp->potionbelt.items[i].item_name, "\0", 1); + } + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); /* Potion belt slot number */ + pp->potionbelt.items[i].item_id = atoi(row[1]); + pp->potionbelt.items[i].icon = atoi(row[2]); + } + return true; +} + bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("SELECT id, zone_id, instance_id, x, y, z, heading FROM character_bind_home WHERE `id` = %u", character_id); + std::string query = StringFormat("SELECT zone_id, instance_id, x, y, z, heading, is_home FROM character_bind WHERE `id` = %u LIMIT 2", character_id); auto results = database.QueryDatabase(query); int i = 0; for (auto row = results.begin(); row != results.end(); ++row) { - pp->binds[4].zoneId = atoi(row[i]); i++; - i++; /* Instance ID can go here eventually */ - pp->binds[4].x = atoi(row[i]); i++; - pp->binds[4].y = atoi(row[i]); i++; - pp->binds[4].z = atoi(row[i]); i++; - pp->binds[4].heading = atoi(row[i]); i++; + i = 0; + /* Is home bind */ + if (atoi(row[6]) == 1){ + pp->binds[4].zoneId = atoi(row[i]); i++; + i++; /* Instance ID can go here eventually */ + pp->binds[4].x = atoi(row[i]); i++; + pp->binds[4].y = atoi(row[i]); i++; + pp->binds[4].z = atoi(row[i]); i++; + pp->binds[4].heading = atoi(row[i]); i++; + } + /* Is regular bind point */ + else{ + pp->binds[0].zoneId = atoi(row[i]); i++; + i++; /* Instance ID can go here eventually */ + pp->binds[0].x = atoi(row[i]); i++; + pp->binds[0].y = atoi(row[i]); i++; + pp->binds[0].z = atoi(row[i]); i++; + pp->binds[0].heading = atoi(row[i]); i++; + } } return true; } +bool ZoneDatabase::SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value){ + std::string query = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, lang_id, value); QueryDatabase(query); + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterLanguage for character ID: %i, lang_id:%u value:%u done", character_id, lang_id, value); + return true; +} + +bool ZoneDatabase::SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home){ + /* Save Home Bind Point */ + std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", character_id, zone_id, instance_id, x, y, z, heading, is_home); + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterBindPoint for character ID: %i zone_id: %u instance_id: %u x: %f y: %f z: %f heading: %f ishome: %u", character_id, zone_id, instance_id, x, y, z, heading, is_home); + auto results = QueryDatabase(query); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } + return true; +} + bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color){ std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, color, use_tint) VALUES (%u, %u, %u, 255)", character_id, slot_id, color); QueryDatabase(query); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterMaterialColor for character ID: %i, slot_id: %u color: %u done", character_id, slot_id, color); return true; } -bool ZoneDatabase::LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("SELECT slot, blue, green, red, use_tint, color FROM `character_material` WHERE `id` = %u LIMIT 9", character_id); - auto results = database.QueryDatabase(query); int i = 0; int r = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - r = 0; - i = atoi(row[r]); /* Slot */ r++; - pp->item_tint[i].rgb.blue = atoi(row[r]); r++; - pp->item_tint[i].rgb.green = atoi(row[r]); r++; - pp->item_tint[i].rgb.red = atoi(row[r]); r++; - pp->item_tint[i].rgb.use_tint = atoi(row[r]); - printf("Material Load: %u %u %u %u\n", pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint); +bool ZoneDatabase::SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value){ + std::string query = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, skill_id, value); QueryDatabase(query); + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterSkill for character ID: %i, skill_id:%u value:%u done", character_id, skill_id, value); + return true; +} + +bool ZoneDatabase::SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id){ + std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); QueryDatabase(query); + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterDisc for character ID: %i, slot:%u disc_id:%u value:%u done", character_id, slot_id, disc_id); + return true; +} + +bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); QueryDatabase(query); + /* Save Tributes only if we have values... */ + for (int i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ + if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != UINT32_MAX){ + std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); QueryDatabase(query); + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + } } return true; } +bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name){ + char bandolier_name_esc[64]; + DoEscapeString(bandolier_name_esc, bandolier_name, strlen(bandolier_name)); + std::string query = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%u, %u, %u, %u, %u,'%s')", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name_esc); + auto results = QueryDatabase(query); + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterBandolier for character ID: %i, bandolier_id: %u, bandolier_slot: %u item_id: %u, icon:%u band_name:%s done", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name); + if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } + return true; +} + +bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon) { + std::string query = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%u, %u, %u, %u)", character_id, potion_id, item_id, icon); + auto results = QueryDatabase(query); + if (!results.RowsAffected()){ std::cout << "ERROR Potionbelt Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } + return true; +} + bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp){ clock_t t = std::clock(); /* Function timer start */ - if (pp->tribute_time_remaining < 0 || pp->tribute_time_remaining == 4294967295){ pp->tribute_time_remaining = 0; } std::string query = StringFormat( "REPLACE INTO `character_data` (" " id, " @@ -1352,97 +1473,97 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla "%u," // raid_auto_consent pp->raidAutoconsent, " raid_auto_consent, " "%u," // guild_auto_consent pp->guildAutoconsent, " guild_auto_consent, " "%u" // RestTimer pp->RestTimer, " RestTimer) " - ")", - character_id, - account_id, - pp->name, - pp->last_name, - pp->gender, - pp->race, - pp->class_, - pp->level, - pp->deity, - pp->birthday, - pp->lastlogin, - pp->timePlayedMin, - pp->pvp, - pp->level2, - pp->anon, - pp->gm, - pp->intoxication, - pp->haircolor, - pp->beardcolor, - pp->eyecolor1, - pp->eyecolor2, - pp->hairstyle, - pp->beard, - pp->ability_time_seconds, - pp->ability_number, - pp->ability_time_minutes, - pp->ability_time_hours, - pp->title, - pp->suffix, - pp->exp, - pp->points, - pp->mana, - pp->cur_hp, - pp->STR, - pp->STA, - pp->CHA, - pp->DEX, - pp->INT, - pp->AGI, - pp->WIS, - pp->face, - pp->y, - pp->x, - pp->z, - pp->heading, - pp->pvp2, - pp->pvptype, - pp->autosplit, - pp->zone_change_count, - pp->drakkin_heritage, - pp->drakkin_tattoo, - pp->drakkin_details, - pp->toxicity, - pp->hunger_level, - pp->thirst_level, - pp->ability_up, - pp->zone_id, - pp->zoneInstance, - pp->leadAAActive, - pp->ldon_points_guk, - pp->ldon_points_mir, - pp->ldon_points_mmc, - pp->ldon_points_ruj, - pp->ldon_points_tak, - pp->ldon_points_available, - pp->tribute_time_remaining, - pp->showhelm, - pp->career_tribute_points, - pp->tribute_points, - pp->tribute_active, - pp->endurance, - pp->group_leadership_exp, - pp->raid_leadership_exp, - pp->group_leadership_points, - pp->raid_leadership_points, - pp->air_remaining, - pp->PVPKills, - pp->PVPDeaths, - pp->PVPCurrentPoints, - pp->PVPCareerPoints, - pp->PVPBestKillStreak, - pp->PVPWorstDeathStreak, - pp->PVPCurrentKillStreak, - pp->aapoints_spent, - pp->expAA, - pp->aapoints, - pp->groupAutoconsent, - pp->raidAutoconsent, - pp->guildAutoconsent, - pp->RestTimer + ")", + character_id, // " id, " + account_id, // " account_id, " + pp->name, // " `name`, " + pp->last_name, // " last_name, " + pp->gender, // " gender, " + pp->race, // " race, " + pp->class_, // " class, " + pp->level, // " `level`, " + pp->deity, // " deity, " + pp->birthday, // " birthday, " + pp->lastlogin, // " last_login, " + pp->timePlayedMin, // " time_played, " + pp->pvp, // " pvp_status, " + pp->level2, // " level2, " + pp->anon, // " anon, " + pp->gm, // " gm, " + pp->intoxication, // " intoxication, " + pp->haircolor, // " hair_color, " + pp->beardcolor, // " beard_color, " + pp->eyecolor1, // " eye_color_1, " + pp->eyecolor2, // " eye_color_2, " + pp->hairstyle, // " hair_style, " + pp->beard, // " beard, " + pp->ability_time_seconds, // " ability_time_seconds, " + pp->ability_number, // " ability_number, " + pp->ability_time_minutes, // " ability_time_minutes, " + pp->ability_time_hours, // " ability_time_hours, " + pp->title, // " title, " + pp->suffix, // " suffix, " + pp->exp, // " exp, " + pp->points, // " points, " + pp->mana, // " mana, " + pp->cur_hp, // " cur_hp, " + pp->STR, // " str, " + pp->STA, // " sta, " + pp->CHA, // " cha, " + pp->DEX, // " dex, " + pp->INT, // " `int`, " + pp->AGI, // " agi, " + pp->WIS, // " wis, " + pp->face, // " face, " + pp->y, // " y, " + pp->x, // " x, " + pp->z, // " z, " + pp->heading, // " heading, " + pp->pvp2, // " pvp2, " + pp->pvptype, // " pvp_type, " + pp->autosplit, // " autosplit_enabled, " + pp->zone_change_count, // " zone_change_count, " + pp->drakkin_heritage, // " drakkin_heritage, " + pp->drakkin_tattoo, // " drakkin_tattoo, " + pp->drakkin_details, // " drakkin_details, " + pp->toxicity, // " toxicity, " + pp->hunger_level, // " hunger_level, " + pp->thirst_level, // " thirst_level, " + pp->ability_up, // " ability_up, " + pp->zone_id, // " zone_id, " + pp->zoneInstance, // " zone_instance, " + pp->leadAAActive, // " leadership_exp_on, " + pp->ldon_points_guk, // " ldon_points_guk, " + pp->ldon_points_mir, // " ldon_points_mir, " + pp->ldon_points_mmc, // " ldon_points_mmc, " + pp->ldon_points_ruj, // " ldon_points_ruj, " + pp->ldon_points_tak, // " ldon_points_tak, " + pp->ldon_points_available, // " ldon_points_available, " + pp->tribute_time_remaining, // " tribute_time_remaining, " + pp->showhelm, // " show_helm, " + pp->career_tribute_points, // " career_tribute_points, " + pp->tribute_points, // " tribute_points, " + pp->tribute_active, // " tribute_active, " + pp->endurance, // " endurance, " + pp->group_leadership_exp, // " group_leadership_exp, " + pp->raid_leadership_exp, // " raid_leadership_exp, " + pp->group_leadership_points, // " group_leadership_points, " + pp->raid_leadership_points, // " raid_leadership_points, " + pp->air_remaining, // " air_remaining, " + pp->PVPKills, // " pvp_kills, " + pp->PVPDeaths, // " pvp_deaths, " + pp->PVPCurrentPoints, // " pvp_current_points, " + pp->PVPCareerPoints, // " pvp_career_points, " + pp->PVPBestKillStreak, // " pvp_best_kill_streak, " + pp->PVPWorstDeathStreak, // " pvp_worst_death_streak, " + pp->PVPCurrentKillStreak, // " pvp_current_kill_streak, " + pp->aapoints_spent, // " aa_points_spent, " + pp->expAA, // " aa_exp, " + pp->aapoints, // " aa_points, " + pp->groupAutoconsent, // " group_auto_consent, " + pp->raidAutoconsent, // " raid_auto_consent, " + pp->guildAutoconsent, // " guild_auto_consent, " + pp->RestTimer // " RestTimer) " ); auto results = database.QueryDatabase(query); if (!results.RowsAffected()){ std::cout << "ERROR ZoneDatabase:SaveCharacterData: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } @@ -1459,6 +1580,10 @@ bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Stru if (pp->silver_bank < 0) { pp->silver_bank = 0; } if (pp->gold_bank < 0) { pp->gold_bank = 0; } if (pp->platinum_bank < 0) { pp->platinum_bank = 0; } + if (pp->platinum_cursor < 0) { pp->platinum_cursor = 0; } + if (pp->gold_cursor < 0) { pp->gold_cursor = 0; } + if (pp->silver_cursor < 0) { pp->silver_cursor = 0; } + if (pp->copper_cursor < 0) { pp->copper_cursor = 0; } std::string query = StringFormat( "REPLACE INTO `character_currency` (id, platinum, gold, silver, copper," "platinum_bank, gold_bank, silver_bank, copper_bank," @@ -1499,12 +1624,16 @@ bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 cur bool ZoneDatabase::SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot){ std::string rquery = StringFormat("UPDATE `character_spells` SET `slot_id` = %u WHERE `slot_id` = %u AND `id` = %u", to_slot, from_slot, character_id); - clock_t t = std::clock(); /* Function timer start */ + clock_t t = std::clock(); /* Function timer start */ auto results = QueryDatabase(rquery); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterSpellSwap for character ID: %u, from_slot: %u to_slot: %u spell: %u time: %f seconds", character_id, from_slot, to_slot, spell_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; } +bool ZoneDatabase::SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ + std::string query = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); QueryDatabase(query); return true; +} + bool ZoneDatabase::SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ std::string query = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); QueryDatabase(query); return true; } @@ -1513,8 +1642,12 @@ bool ZoneDatabase::DeleteCharacterSpell(uint32 character_id, uint32 spell_id, ui std::string query = StringFormat("DELETE FROM `character_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); QueryDatabase(query); return true; } -bool ZoneDatabase::SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ - std::string query = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); QueryDatabase(query); return true; +bool ZoneDatabase::DeleteCharacterDisc(uint32 character_id, uint32 slot_id){ + std::string query = StringFormat("DELETE FROM `character_disciplines` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); QueryDatabase(query); return true; +} + +bool ZoneDatabase::DeleteCharacterBandolier(uint32 character_id, uint32 band_id){ + std::string query = StringFormat("DELETE FROM `character_bandolier` WHERE `bandolier_id` = %u AND `id` = %u", band_id, character_id); QueryDatabase(query); return true; } bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ @@ -1715,7 +1848,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { tmpNPCType->aggroradius = (int32)atoi(row[r++]); // set defaultvalue for aggroradius if (tmpNPCType->aggroradius <= 0) - tmpNPCType->aggroradius = 70; + tmpNPCType->aggroradius = 70; tmpNPCType->assistradius = (int32)atoi(row[r++]); if (tmpNPCType->assistradius <= 0) tmpNPCType->assistradius = tmpNPCType->aggroradius; @@ -3715,4 +3848,6 @@ void ZoneDatabase::StoreCharacterLookup(uint32 char_id) { " FROM `character_` " " WHERE `id` = %i ", char_id); QueryDatabase(c_lookup); -} \ No newline at end of file +} + + diff --git a/zone/zonedb.h b/zone/zonedb.h index 705377a39..659dad673 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -6,6 +6,7 @@ #include "../common/loottable.h" #include "zonedump.h" #include "../common/faction.h" +#include //#include "doors.h" struct wplist { @@ -212,9 +213,7 @@ public: ZoneDatabase(const char* host, const char* user, const char* passwd, const char* database,uint32 port); virtual ~ZoneDatabase(); - /* - * Objects and World Containers - */ + /* Objects and World Containers */ void LoadWorldContainer(uint32 parentid, ItemInst* container); void SaveWorldContainer(uint32 zone_id, uint32 parent_id, const ItemInst* container); void DeleteWorldContainer(uint32 parent_id,uint32 zone_id); @@ -223,10 +222,7 @@ public: void DeleteObject(uint32 id); Ground_Spawns* LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs); - /* - * Traders - */ - + /* Traders */ void SaveTraderItem(uint32 char_id,uint32 itemid,uint32 uniqueid, int32 charges,uint32 itemcost,uint8 slot); void UpdateTraderItemCharges(int char_id, uint32 ItemInstID, int32 charges); void UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice); @@ -236,16 +232,13 @@ public: Trader_Struct* LoadTraderItem(uint32 char_id); TraderCharges_Struct* LoadTraderItemWithCharges(uint32 char_id); - // Buyer/Barter - // + /* Buyer/Barter */ void AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char *ItemName, uint32 Quantity, uint32 Price); void RemoveBuyLine(uint32 CharID, uint32 BuySlot); void DeleteBuyLines(uint32 CharID); void UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity); - /* - * General Character Related Stuff - */ + /* General Character Related Stuff */ void StoreCharacterLookup(uint32 char_id); bool SetServerFilters(char* name, ServerSideFilters_Struct *ssfs); uint32 GetServerFilters(char* name, ServerSideFilters_Struct *ssfs); @@ -267,28 +260,36 @@ public: bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp); /* Character Data Saves */ - bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); - bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp); - bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); - bool SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot); - bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); - bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); - bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); + bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home); + bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); + bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp); + bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); + bool SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot); + bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); + bool SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value); + bool SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value); + bool SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id); + bool SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); + bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name); + bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon); /* Character Data Deletes */ - bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); - bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool DeleteCharacterDisc(uint32 character_id, uint32 slot_id); + bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id); - /* - * Character Inventory - */ + /* Character Inventory */ bool NoRentExpired(const char* name); - /* - * Corpses - */ + /* Corpses */ bool GetDecayTimes(npcDecayTimes_Struct* npcCorpseDecayTimes); uint32 CreatePlayerCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, uchar* data, uint32 datasize, float x, float y, float z, float heading); bool CreatePlayerCorpseBackup(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, uchar* data, uint32 datasize, float x, float y, float z, float heading); @@ -314,9 +315,7 @@ public: uint32 GetPlayerCorpseItemAt(uint32 corpse_id, uint16 slotid); uint32 GetPlayerCorpseTimeLeft(uint8 corpse, uint8 type); - /* - * Faction - */ + /* Faction */ bool GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction = 0); bool GetFactionData(FactionMods* fd, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id); //rembrant, needed for factions Dec, 16 2001 bool GetFactionName(int32 faction_id, char* name, uint32 buflen); // rembrant, needed for factions Dec, 16 2001 @@ -324,9 +323,7 @@ public: bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // rembrant, needed for factions Dec, 16 2001 bool LoadFactionData(); - /* - * AAs - */ + /* AAs */ bool LoadAAEffects(); bool LoadAAEffects2(); bool LoadSwarmSpells(); @@ -338,9 +335,7 @@ public: uint32 CountAAEffects(); void FillAAEffects(SendAA_Struct* aa_struct); - /* - * Zone related - */ + /* Zone related */ bool GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct *data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &allow_mercs, uint8 &zone_type, int &ruleset, char **map_filename); bool SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd); bool LoadStaticZonePoints(LinkedList* zone_point_list,const char* zonename, uint32 version); @@ -348,9 +343,7 @@ public: uint8 GetUseCFGSafeCoords(); int getZoneShutDownDelay(uint32 zoneID, uint32 version); - /* - * Spawns and Spawn Points - */ + /* Spawns and Spawn Points */ bool LoadSpawnGroups(const char* zone_name, uint16 version, SpawnGroupList* spawn_group_list); bool LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_group_list); bool PopulateZoneSpawnList(uint32 zoneid, LinkedList &spawn2_list, int16 version, uint32 repopdelay = 0); @@ -360,9 +353,7 @@ public: uint32 GetSpawnTimeLeft(uint32 id, uint16 instance_id); void UpdateSpawn2Status(uint32 id, uint8 new_status); - /* - * Grids/Paths - */ + /* Grids/Paths */ uint32 GetFreeGrid(uint16 zoneid); void DeleteGrid(Client *c, uint32 sg2, uint32 grid_num, bool grid_too,uint16 zoneid); void DeleteWaypoint(Client *c, uint32 grid_num, uint32 wp_num,uint16 zoneid); @@ -378,9 +369,7 @@ public: int GetHighestGrid(uint32 zoneid); int GetHighestWaypoint(uint32 zoneid, uint32 gridid); - /* - * NPCs - */ + /* NPCs */ const NPCType* GetNPCType(uint32 id); uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete bool SetSpecialAttkFlag(uint8 id, const char* flag); @@ -394,9 +383,7 @@ public: DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID); DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID); - /* - * Mercs - */ + /* Mercs */ const NPCType* GetMercType(uint32 id, uint16 raceid, uint32 clientlevel); void LoadMercEquipment(Merc *merc); void SaveMercBuffs(Merc *merc); @@ -408,9 +395,7 @@ public: //void LoadMercTypesForMercMerchant(NPC *merchant); //void LoadMercsForMercMerchant(NPC *merchant); - /* - * Petitions - */ + /* Petitions */ void UpdateBug(BugStruct* bug); void UpdateBug(PetitionBug_Struct* bug); void DeletePetitionFromDB(Petition* wpet); @@ -418,16 +403,11 @@ public: void InsertPetitionToDB(Petition* wpet); void RefreshPetitionsFromDB(); - - /* - * Merchants - */ + /* Merchants */ void SaveMerchantTemp(uint32 npcid, uint32 slot, uint32 item, uint32 charges); void DeleteMerchantTemp(uint32 npcid, uint32 slot); - /* - * Tradeskills - */ + /* Tradeskills */ bool GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec); bool GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec); uint32 GetZoneForage(uint32 ZoneID, uint8 skill); /* for foraging */ @@ -436,14 +416,10 @@ public: bool EnableRecipe(uint32 recipe_id); bool DisableRecipe(uint32 recipe_id); - /* - * Tribute - */ + /* Tribute */ bool LoadTributes(); - /* - * Doors - */ + /* Doors */ bool DoorIsOpen(uint8 door_id,const char* zone_name); void SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name); bool LoadDoors(int32 iDoorCount, Door *into, const char *zone_name, int16 version); @@ -456,64 +432,46 @@ public: int32 GetDoorsDBCountPlusOne(const char *zone_name, int16 version); void InsertDoor(uint32 did, uint16 ddoorid, const char* ddoor_name, float dxpos, float dypos, float dzpos, float dheading, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize); - /* - * Blocked Spells - */ - + /* Blocked Spells */ int32 GetBlockedSpellsCount(uint32 zoneid); bool LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid); - /* - * Traps - */ + /* Traps */ bool LoadTraps(const char* zonename, int16 version); char* GetTrapMessage(uint32 trap_id); - /* - * Time - */ + /* Time */ uint32 GetZoneTZ(uint32 zoneid, uint32 version); bool SetZoneTZ(uint32 zoneid, uint32 version, uint32 tz); - /* - * Group - */ + /* Group */ void RefreshGroupFromDB(Client *c); uint8 GroupCount(uint32 groupid); - /* - * Raid - */ + + /* Raid */ uint8 RaidGroupCount(uint32 raidid, uint32 groupid); - /* - * Instancing - */ + /* Instancing */ void ListAllInstances(Client* c, uint32 charid); - /* - * QGlobals - */ + /* QGlobals */ void QGlobalPurge(); - /* - * Alternate Currency - */ + /* Alternate Currency */ void LoadAltCurrencyValues(uint32 char_id, std::map ¤cy); void UpdateAltCurrencyValue(uint32 char_id, uint32 currency_id, uint32 value); /* - * Misc stuff. - * PLEASE DO NOT ADD TO THIS COLLECTION OF CRAP UNLESS YOUR METHOD - * REALLY HAS NO BETTER SECTION + * Misc stuff. + * PLEASE DO NOT ADD TO THIS COLLECTION OF CRAP UNLESS YOUR METHOD + * REALLY HAS NO BETTER SECTION */ bool logevents(const char* accountname,uint32 accountid,uint8 status,const char* charname,const char* target, const char* descriptiontype, const char* description,int event_nid); void GetEventLogs(const char* name,char* target,uint32 account_id=0,uint8 eventid=0,char* detail=0,char* timestamp=0, CharacterEventLog_Struct* cel=0); uint32 GetKarma(uint32 acct_id); void UpdateKarma(uint32 acct_id, uint32 amount); - /* - * Things which really dont belong here... - */ + /* Things which really dont belong here... */ int16 CommandRequirement(const char* commandname); protected: diff --git a/zone/zonedbasync.cpp b/zone/zonedbasync.cpp deleted file mode 100644 index 454252ab6..000000000 --- a/zone/zonedbasync.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "../common/debug.h" -#include -#include "entity.h" -#include "masterentity.h" -#include "../common/string_util.h" -#include "../common/breakdowns.h" -#include - -extern EntityList entity_list; - -void DispatchFinishedDBAsync(DBAsyncWork* dbaw) { - uint32_breakdown workpt; - workpt = dbaw->WPT(); - switch (workpt.b4()) { - case DBA_b4_Entity: { - Entity* entity = entity_list.GetID(workpt.w2_3()); - if (!entity) - break; - entity->DBAWComplete(workpt.b1(), dbaw); - break; - } - default: { - std::cout << "Error: DispatchFinishedDBAsync(): Unknown workpt.b4: " << (int) workpt.b4() << ", workpt=" << workpt << std::endl; - break; - } - } - safe_delete(dbaw); -} - -#define MAX_TO_DELETE 10 -#define MAX_BACKUPS 5 -bool DBAsyncCB_CharacterBackup(DBAsyncWork* iWork) { // return true means delete data - char errbuf[MYSQL_ERRMSG_SIZE] = "dbaq == 0"; - MYSQL_RES* result = 0; - MYSQL_ROW row; - char* query = 0; - uint32 i; - uint8 ToDeleteIndex = 0; - uint32 ToDelete[MAX_TO_DELETE]; - memset(ToDelete, 0, sizeof(ToDelete)); - - uint32 BackupAges[MAX_BACKUPS]; // must be sorted, highest value in lowest index - memset(BackupAges, 0, sizeof(BackupAges)); - - bool FoundBackup[MAX_BACKUPS]; - memset(FoundBackup, 0, sizeof(FoundBackup)); - - BackupAges[0] = 86400; - BackupAges[1] = 3600; - - DBAsyncQuery* dbaq = iWork->PopAnswer(); - if (dbaq && dbaq->GetAnswer(errbuf, &result)) { - while ((row = mysql_fetch_row(result))) { - for (i=0; i BackupAges[i]) - i = MAX_BACKUPS; - else if (!FoundBackup[i]) { - FoundBackup[i] = true; - break; - } - } - if (i >= MAX_BACKUPS) - ToDelete[ToDeleteIndex++] = atoi(row[0]); - if (ToDeleteIndex >= MAX_TO_DELETE) - break; - } - if (ToDelete[0]) { - uint32 len = 0, size = 0; - AppendAnyLenString(&query, &size, &len, "Delete from character_backup where id=%u", ToDelete[0]); - for (uint8 i=1; iwrite(EQEMuLog::Error, "Error in DBAsyncCB_CharacterBackup query2 '%s' %s", query, errbuf); - safe_delete_array(query); - return true; - } - safe_delete_array(query); - } - bool needtoinsert = false; - for (i=0; iWPT()), errbuf)) { - std::cout << "Error in DBAsyncCB_CharacterBackup query3 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return true; - } - safe_delete_array(query); - } - } - else { -// std::cout << "Error in DBAsyncCB_CharacterBackup query1 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return true; - } - return true; -} diff --git a/zone/zonedbasync.h b/zone/zonedbasync.h deleted file mode 100644 index b91389322..000000000 --- a/zone/zonedbasync.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef ZONEDBASYNC_H -#define ZONEDBASYNC_H - -#include "../common/dbasync.h" -void DispatchFinishedDBAsync(DBAsyncWork* iDBAW); -bool DBAsyncCB_CharacterBackup(DBAsyncWork* iWork); - -#define DBA_b4_Main 1 -#define DBA_b4_Worldserver 2 -#define DBA_b4_Zone 3 -#define DBA_b4_Entity 4 - -#define DBA_b1_Entity_SeeQPT 0 -#define DBA_b1_Entity_Client_InfoForLogin 1 -#define DBA_b1_Entity_Client_Save 2 -#define DBA_b1_Entity_Client_Backup 3 -#define DBA_b1_Entity_Corpse_Backup 4 -#define DBA_b1_Zone_MerchantLists 5 -#define DBA_b1_Zone_MerchantListsTemp 6 - -#endif - diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 88351ee3e..0eb204d1f 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -50,10 +50,6 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { #endif ZoneChange_Struct* zc=(ZoneChange_Struct*)app->pBuffer; - printf("INCOMING CLIENT\n\n"); - printf("%s\n", zc->char_name); - printf("%u\n", zc->zoneID); - uint16 target_zone_id = 0; uint16 target_instance_id = zc->instanceID; ZonePoint* zone_point = nullptr; @@ -568,7 +564,6 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z if(ReadyToZone) { zone_mode = zm; - printf("\n\n ZONE MODE %u \n\n", zm); if(zm == ZoneToBindPoint) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + iZoneNameLength); ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer; @@ -602,10 +597,6 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z gmg->instance_id = instance_id; gmg->type = 0x01; //an observed value, not sure of meaning - printf("gmg->zone_id %u \n", gmg->zone_id); - printf("gmg->x %u \n", gmg->x); - printf("gmg->y %u \n", gmg->y); - outapp->priority = 6; FastQueuePacket(&outapp); safe_delete(outapp); @@ -726,6 +717,7 @@ void Client::SetBindPoint(int to_zone, float new_x, float new_y, float new_z) { m_pp.binds[0].y = new_y; m_pp.binds[0].z = new_z; } + database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[0].zoneId, 0, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, 0, 0); } void Client::GoToBind(uint8 bindnum) { From 970f7e01a9b51cf1ec42e11cfb4177e56daa83f2 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 10:08:02 -0700 Subject: [PATCH 067/368] Moved create new NPC command case (0) from NPCSpawnDB into method CreateNewNPCCommand --- zone/npc.cpp | 159 ++++++++++++++++++++++++++------------------------ zone/zonedb.h | 1 + 2 files changed, 85 insertions(+), 75 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 151852a2b..d7b5eea79 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -962,6 +962,88 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, } } +uint32 ZoneDatabase::CreateNewNPCCommand(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 tmp = 0; + uint32 tmp2 = 0; + uint32 last_insert_id = 0; + uint32 npc_type_id = 0; + uint32 spawngroupid; + if (extra && c && c->GetZoneID()) + { + // Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000) + int starting_npc_id = c->GetZoneID() * 1000; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", starting_npc_id, (starting_npc_id + 1000)), errbuf, &result)) { + row = mysql_fetch_row(result); + if(row) + { + if (row[0]) + { + npc_type_id = atoi(row[0]) + 1; + // Prevent the npc_type id from exceeding the range for this zone + if (npc_type_id >= (starting_npc_id + 1000)) + { + npc_type_id = 0; + } + } + else + { + // row[0] is nullptr - No npc_type IDs set in this range yet + npc_type_id = starting_npc_id; + } + } + safe_delete_array(query); + mysql_free_result(result); + } + } + char tmpstr[64]; + EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); + if (npc_type_id) + { + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (id, name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(%i,\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); + safe_delete(query); + return false; + } + } + else + { + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); + safe_delete(query); + return false; + } + } + if(c) c->LogSQL(query); + safe_delete_array(query); + snprintf(tmpstr, sizeof(tmpstr), "%s-%s", zone, spawn->GetName()); + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (id, name) values(%i, '%s')", tmp, tmpstr), errbuf, 0, 0, &spawngroupid)) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); + safe_delete(query); + return false; + } + if(c) c->LogSQL(query); + safe_delete_array(query); + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, spawn->GetHeading(), spawngroupid), errbuf, 0, 0, &tmp)) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); + safe_delete(query); + return false; + } + if(c) c->LogSQL(query); + safe_delete_array(query); + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", spawngroupid, npc_type_id, 100), errbuf, 0)) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); + safe_delete(query); + return false; + } + if(c) c->LogSQL(query); + safe_delete_array(query); + return true; +} + uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; @@ -972,80 +1054,7 @@ uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_ver uint32 last_insert_id = 0; switch (command) { case 0: { // Create a new NPC and add all spawn related data - uint32 npc_type_id = 0; - uint32 spawngroupid; - if (extra && c && c->GetZoneID()) - { - // Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000) - int starting_npc_id = c->GetZoneID() * 1000; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", starting_npc_id, (starting_npc_id + 1000)), errbuf, &result)) { - row = mysql_fetch_row(result); - if(row) - { - if (row[0]) - { - npc_type_id = atoi(row[0]) + 1; - // Prevent the npc_type id from exceeding the range for this zone - if (npc_type_id >= (starting_npc_id + 1000)) - { - npc_type_id = 0; - } - } - else - { - // row[0] is nullptr - No npc_type IDs set in this range yet - npc_type_id = starting_npc_id; - } - } - - safe_delete_array(query); - mysql_free_result(result); - } - } - char tmpstr[64]; - EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); - if (npc_type_id) - { - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (id, name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(%i,\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - } - else - { - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - } - if(c) c->LogSQL(query); - safe_delete_array(query); - snprintf(tmpstr, sizeof(tmpstr), "%s-%s", zone, spawn->GetName()); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (id, name) values(%i, '%s')", tmp, tmpstr), errbuf, 0, 0, &spawngroupid)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, spawn->GetHeading(), spawngroupid), errbuf, 0, 0, &tmp)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", spawngroupid, npc_type_id, 100), errbuf, 0)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - break; + return CreateNewNPCCommand(command, zone, zone_version, c, spawn, extra); } case 1:{ // Add new spawn group and spawn point for an existing NPC Type ID tmp2 = spawn->GetNPCTypeID(); @@ -1740,7 +1749,7 @@ void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) else ns->spawn.IsMercenary = 0; } - + //Not recommended if using above (However, this will work better on older clients). if (RuleB(Pets, UnTargetableSwarmPet)) { if(GetOwnerID() || GetSwarmOwner()) { diff --git a/zone/zonedb.h b/zone/zonedb.h index 218654dc1..ca27549ee 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -372,6 +372,7 @@ public: */ const NPCType* GetNPCType(uint32 id); uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete + uint32 CreateNewNPCCommand(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into); From 444174ef573b73779c095e7ee95b4fbf6468aa65 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 12:26:05 -0700 Subject: [PATCH 068/368] CreateNewNPCCommand converted to QueryDatabase --- zone/npc.cpp | 137 +++++++++++++++++++++++++++++--------------------- zone/zonedb.h | 2 +- 2 files changed, 81 insertions(+), 58 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index d7b5eea79..1e75be7c9 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -962,85 +962,108 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, } } -uint32 ZoneDatabase::CreateNewNPCCommand(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 tmp = 0; - uint32 tmp2 = 0; - uint32 last_insert_id = 0; +uint32 ZoneDatabase::CreateNewNPCCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra) { + uint32 npc_type_id = 0; - uint32 spawngroupid; - if (extra && c && c->GetZoneID()) + + if (extra && client && client->GetZoneID()) { // Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000) - int starting_npc_id = c->GetZoneID() * 1000; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", starting_npc_id, (starting_npc_id + 1000)), errbuf, &result)) { - row = mysql_fetch_row(result); - if(row) + int starting_npc_id = client->GetZoneID() * 1000; + + std::string query = StringFormat("SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", + starting_npc_id, starting_npc_id + 1000); + auto results = QueryDatabase(query); + if (results.Success()) { + if (results.RowCount() != 0) { - if (row[0]) - { - npc_type_id = atoi(row[0]) + 1; - // Prevent the npc_type id from exceeding the range for this zone - if (npc_type_id >= (starting_npc_id + 1000)) - { - npc_type_id = 0; - } - } - else - { - // row[0] is nullptr - No npc_type IDs set in this range yet - npc_type_id = starting_npc_id; - } - } - safe_delete_array(query); - mysql_free_result(result); - } - } + auto row = results.begin(); + npc_type_id = atoi(row[0]) + 1; + // Prevent the npc_type id from exceeding the range for this zone + if (npc_type_id >= (starting_npc_id + 1000)) + npc_type_id = 0; + } + else // No npc_type IDs set in this range yet + npc_type_id = starting_npc_id; + } + } + char tmpstr[64]; EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); + std::string query; if (npc_type_id) { - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (id, name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(%i,\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); + query = StringFormat("INSERT INTO npc_types (id, name, level, race, class, hp, gender, " + "texture, helmtexture, size, loottable_id, merchant_id, face, " + "runspeed, prim_melee_type, sec_melee_type) " + "VALUES(%i, \"%s\" , %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", + npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; } + npc_type_id = results.LastInsertedID(); } else { - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); + query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, " + "texture, helmtexture, size, loottable_id, merchant_id, face, " + "runspeed, prim_melee_type, sec_melee_type) " + "VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", + tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; } + npc_type_id = results.LastInsertedID(); } - if(c) c->LogSQL(query); - safe_delete_array(query); - snprintf(tmpstr, sizeof(tmpstr), "%s-%s", zone, spawn->GetName()); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (id, name) values(%i, '%s')", tmp, tmpstr), errbuf, 0, 0, &spawngroupid)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawngroup (id, name) VALUES(%i, '%s-%s')", 0, zone, spawn->GetName()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, spawn->GetHeading(), spawngroupid), errbuf, 0, 0, &tmp)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); + uint32 spawngroupid = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " + "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", + zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, + spawn->GetHeading(), spawngroupid); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", spawngroupid, npc_type_id, 100), errbuf, 0)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)", + spawngroupid, npc_type_id, 100); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - if(c) c->LogSQL(query); - safe_delete_array(query); + + if(client) + client->LogSQL(query.c_str()); + return true; } diff --git a/zone/zonedb.h b/zone/zonedb.h index ca27549ee..af5c413dd 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -372,7 +372,7 @@ public: */ const NPCType* GetNPCType(uint32 id); uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete - uint32 CreateNewNPCCommand(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra); + uint32 CreateNewNPCCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into); From 3e041052eea1e79dbc0899aa833f94ccc38300a5 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 12:38:11 -0700 Subject: [PATCH 069/368] Moved add new NPC spawngroup command case (1) from NPCSpawnDB into method AddNewNPCSpawnGroupCommand --- zone/npc.cpp | 89 ++++++++++++++++++++++++++++++--------------------- zone/zonedb.h | 1 + 2 files changed, 54 insertions(+), 36 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 1e75be7c9..91a844b35 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1067,6 +1067,58 @@ uint32 ZoneDatabase::CreateNewNPCCommand(uint8 command, const char* zone, uint32 return true; } +uint32 ZoneDatabase::AddNewNPCSpawnGroupCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 last_insert_id = 0; + uint32 tmp2 = spawn->GetNPCTypeID(); + char tmpstr[64]; + snprintf(tmpstr, sizeof(tmpstr), "%s%s%i", zone, spawn->GetName(),Timer::GetCurrentTime()); + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (name) values('%s')", tmpstr), errbuf, 0, 0, &last_insert_id)) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); + safe_delete(query); + return false; + } + + if(client) + client->LogSQL(query); + safe_delete_array(query); + + uint32 respawntime = 0; + uint32 spawnid = 0; + + if (respawnTime) + respawntime = respawnTime; + else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0) + respawntime = spawn->respawn2->RespawnTimer(); + else + respawntime = 1200; + + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, spawn->GetHeading(), last_insert_id), errbuf, 0, 0, &spawnid)) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); + safe_delete(query); + return false; + } + + if(client) + client->LogSQL(query); + safe_delete_array(query); + + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", last_insert_id, tmp2, 100), errbuf, 0)) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); + safe_delete(query); + return false; + } + + if(client) + client->LogSQL(query); + + safe_delete_array(query); + return spawnid; +} + uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; @@ -1080,42 +1132,7 @@ uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_ver return CreateNewNPCCommand(command, zone, zone_version, c, spawn, extra); } case 1:{ // Add new spawn group and spawn point for an existing NPC Type ID - tmp2 = spawn->GetNPCTypeID(); - char tmpstr[64]; - snprintf(tmpstr, sizeof(tmpstr), "%s%s%i", zone, spawn->GetName(),Timer::GetCurrentTime()); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (name) values('%s')", tmpstr), errbuf, 0, 0, &last_insert_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - uint32 respawntime = 0; - uint32 spawnid = 0; - if (extra) - respawntime = extra; - else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0) - respawntime = spawn->respawn2->RespawnTimer(); - else - respawntime = 1200; - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, spawn->GetHeading(), last_insert_id), errbuf, 0, 0, &spawnid)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", last_insert_id, tmp2, 100), errbuf, 0)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return spawnid; - break; + return AddNewNPCSpawnGroupCommand(command, zone, zone_version, c, spawn, extra); } case 2: { // Update npc_type appearance and other data on targeted spawn if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET name=\"%s\", level=%i, race=%i, class=%i, hp=%i, gender=%i, texture=%i, helmtexture=%i, size=%i, loottable_id=%i, merchant_id=%i, face=%i, WHERE id=%i", spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, spawn->GetNPCTypeID()), errbuf, 0)) { diff --git a/zone/zonedb.h b/zone/zonedb.h index af5c413dd..50a556649 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -373,6 +373,7 @@ public: const NPCType* GetNPCType(uint32 id); uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete uint32 CreateNewNPCCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra); + uint32 AddNewNPCSpawnGroupCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into); From 6d4f7413a51620bd96cec43e4a90190c348f2273 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 14:53:55 -0700 Subject: [PATCH 070/368] AddNewNPCSpawnGroupCommand converted to QueryDatabase --- zone/npc.cpp | 119 +++++++++++++++++++++++++++----------------------- zone/zonedb.h | 1 + 2 files changed, 66 insertions(+), 54 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 91a844b35..98fac2948 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1068,55 +1068,75 @@ uint32 ZoneDatabase::CreateNewNPCCommand(uint8 command, const char* zone, uint32 } uint32 ZoneDatabase::AddNewNPCSpawnGroupCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime) { + uint32 last_insert_id = 0; + + std::string query = StringFormat("INSERT INTO spawngroup (name) VALUES('%s%s%i')", + zone, spawn->GetName(), Timer::GetCurrentTime()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + last_insert_id = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + uint32 respawntime = 0; + uint32 spawnid = 0; + if (respawnTime) + respawntime = respawnTime; + else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0) + respawntime = spawn->respawn2->RespawnTimer(); + else + respawntime = 1200; + + query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " + "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", + zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, + spawn->GetHeading(), last_insert_id); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + spawnid = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)", + last_insert_id, spawn->GetNPCTypeID(), 100); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if(client) + client->LogSQL(query.c_str()); + + return spawnid; +} + +uint32 ZoneDatabase::UpdateNPCTypeAppearance(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; + uint32 tmp = 0; + uint32 tmp2 = 0; uint32 last_insert_id = 0; - uint32 tmp2 = spawn->GetNPCTypeID(); - char tmpstr[64]; - snprintf(tmpstr, sizeof(tmpstr), "%s%s%i", zone, spawn->GetName(),Timer::GetCurrentTime()); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (name) values('%s')", tmpstr), errbuf, 0, 0, &last_insert_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - - if(client) - client->LogSQL(query); - safe_delete_array(query); - - uint32 respawntime = 0; - uint32 spawnid = 0; - - if (respawnTime) - respawntime = respawnTime; - else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0) - respawntime = spawn->respawn2->RespawnTimer(); - else - respawntime = 1200; - - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, spawn->GetHeading(), last_insert_id), errbuf, 0, 0, &spawnid)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - - if(client) - client->LogSQL(query); - safe_delete_array(query); - - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", last_insert_id, tmp2, 100), errbuf, 0)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - - if(client) - client->LogSQL(query); - - safe_delete_array(query); - return spawnid; + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET name=\"%s\", level=%i, race=%i, class=%i, hp=%i, gender=%i, texture=%i, helmtexture=%i, size=%i, loottable_id=%i, merchant_id=%i, face=%i, WHERE id=%i", spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, spawn->GetNPCTypeID()), errbuf, 0)) { + if(c) + c->LogSQL(query); + safe_delete_array(query); + return true; + } + else { + safe_delete_array(query); + return false; + } } uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { @@ -1135,16 +1155,7 @@ uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_ver return AddNewNPCSpawnGroupCommand(command, zone, zone_version, c, spawn, extra); } case 2: { // Update npc_type appearance and other data on targeted spawn - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET name=\"%s\", level=%i, race=%i, class=%i, hp=%i, gender=%i, texture=%i, helmtexture=%i, size=%i, loottable_id=%i, merchant_id=%i, face=%i, WHERE id=%i", spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, spawn->GetNPCTypeID()), errbuf, 0)) { - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - } - else { - safe_delete_array(query); - return false; - } - break; + return UpdateNPCTypeAppearance(command, zone, zone_version, c, spawn); } case 3: { // delete spawn from spawning, but leave in npc_types table if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()), errbuf, &result)) { diff --git a/zone/zonedb.h b/zone/zonedb.h index 50a556649..2829ac48e 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -374,6 +374,7 @@ public: uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete uint32 CreateNewNPCCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra); uint32 AddNewNPCSpawnGroupCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime); + uint32 UpdateNPCTypeAppearance(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into); From a6b57a3423d81a840dafff6077d142792b6f9cbc Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 15:32:41 -0700 Subject: [PATCH 071/368] Moved updating npc type appearance command case (2) from NPCSpawnDB into method UpdateNPCTypeAppearance --- zone/npc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 98fac2948..f4dfe072d 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1127,9 +1127,10 @@ uint32 ZoneDatabase::UpdateNPCTypeAppearance(uint8 command, const char* zone, ui uint32 tmp = 0; uint32 tmp2 = 0; uint32 last_insert_id = 0; + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET name=\"%s\", level=%i, race=%i, class=%i, hp=%i, gender=%i, texture=%i, helmtexture=%i, size=%i, loottable_id=%i, merchant_id=%i, face=%i, WHERE id=%i", spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, spawn->GetNPCTypeID()), errbuf, 0)) { - if(c) - c->LogSQL(query); + if(client) + client->LogSQL(query); safe_delete_array(query); return true; } From 822c8425bd5cfba543e6fa72c21826bfef930d69 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 15:45:31 -0700 Subject: [PATCH 072/368] UpdateNPCTypeAppearance converted to QueryDatabase --- zone/npc.cpp | 41 ++++++++++++++++++----------------------- zone/zonedb.h | 6 +++--- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index f4dfe072d..2f04da2e0 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -962,7 +962,7 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, } } -uint32 ZoneDatabase::CreateNewNPCCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra) { +uint32 ZoneDatabase::CreateNewNPCCommand(const char* zone, uint32 zone_version,Client *client, NPC* spawn, uint32 extra) { uint32 npc_type_id = 0; @@ -1067,7 +1067,7 @@ uint32 ZoneDatabase::CreateNewNPCCommand(uint8 command, const char* zone, uint32 return true; } -uint32 ZoneDatabase::AddNewNPCSpawnGroupCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime) { +uint32 ZoneDatabase::AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime) { uint32 last_insert_id = 0; std::string query = StringFormat("INSERT INTO spawngroup (name) VALUES('%s%s%i')", @@ -1119,25 +1119,20 @@ uint32 ZoneDatabase::AddNewNPCSpawnGroupCommand(uint8 command, const char* zone, return spawnid; } -uint32 ZoneDatabase::UpdateNPCTypeAppearance(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 tmp = 0; - uint32 tmp2 = 0; - uint32 last_insert_id = 0; +uint32 ZoneDatabase::UpdateNPCTypeAppearance(Client *client, NPC* spawn) { - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET name=\"%s\", level=%i, race=%i, class=%i, hp=%i, gender=%i, texture=%i, helmtexture=%i, size=%i, loottable_id=%i, merchant_id=%i, face=%i, WHERE id=%i", spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, spawn->GetNPCTypeID()), errbuf, 0)) { - if(client) - client->LogSQL(query); - safe_delete_array(query); - return true; - } - else { - safe_delete_array(query); - return false; - } + std::string query = StringFormat("UPDATE npc_types SET name = \"%s\", level = %i, race = %i, class = %i, " + "hp = %i, gender = %i, texture = %i, helmtexture = %i, size = %i, " + "loottable_id = %i, merchant_id = %i, face = %i, WHERE id = %i", + spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, spawn->GetNPCTypeID()); + auto results = QueryDatabase(query); + if (!results.Success() && client) + client->LogSQL(query.c_str()); + + return results.Success() == true? 1: 0; } uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { @@ -1150,13 +1145,13 @@ uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_ver uint32 last_insert_id = 0; switch (command) { case 0: { // Create a new NPC and add all spawn related data - return CreateNewNPCCommand(command, zone, zone_version, c, spawn, extra); + return CreateNewNPCCommand(zone, zone_version, c, spawn, extra); } case 1:{ // Add new spawn group and spawn point for an existing NPC Type ID - return AddNewNPCSpawnGroupCommand(command, zone, zone_version, c, spawn, extra); + return AddNewNPCSpawnGroupCommand(zone, zone_version, c, spawn, extra); } case 2: { // Update npc_type appearance and other data on targeted spawn - return UpdateNPCTypeAppearance(command, zone, zone_version, c, spawn); + return UpdateNPCTypeAppearance(c, spawn); } case 3: { // delete spawn from spawning, but leave in npc_types table if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()), errbuf, &result)) { diff --git a/zone/zonedb.h b/zone/zonedb.h index 2829ac48e..490d6fa6d 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -372,9 +372,9 @@ public: */ const NPCType* GetNPCType(uint32 id); uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete - uint32 CreateNewNPCCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra); - uint32 AddNewNPCSpawnGroupCommand(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime); - uint32 UpdateNPCTypeAppearance(uint8 command, const char* zone, uint32 zone_version, Client *client, NPC* spawn); + uint32 CreateNewNPCCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra); + uint32 AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime); + uint32 UpdateNPCTypeAppearance(Client *client, NPC* spawn); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into); From 89a0bbb8bfd1ce514c22e6412f153106a1f521fb Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 16:28:03 -0700 Subject: [PATCH 073/368] Moved deleting npc spawn command case (3) from NPCSpawnDB into method DeleteSpawnLeaveInNPCTypeTable --- zone/npc.cpp | 86 +++++++++++++++++++++++++++++++-------------------- zone/zonedb.h | 1 + 2 files changed, 54 insertions(+), 33 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 2f04da2e0..be1be1ebd 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1135,6 +1135,58 @@ uint32 ZoneDatabase::UpdateNPCTypeAppearance(Client *client, NPC* spawn) { return results.Success() == true? 1: 0; } +uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 tmp = 0; + uint32 tmp2 = 0; + if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()), errbuf, &result)) { + safe_delete_array(query); + return 0; + } + safe_delete_array(query); + + row = mysql_fetch_row(result); + if (row == nullptr) + return 0; + if (row[0]) + tmp = atoi(row[0]); + if (row[1]) + tmp2 = atoi(row[1]); + + if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { + safe_delete(query); + return 0; + } + + if(client) + client->LogSQL(query); + + safe_delete_array(query); + + if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { + safe_delete(query); + return 0; + } + + if(client) + client->LogSQL(query); + + safe_delete_array(query); + if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { + safe_delete(query); + return 0; + } + + if(client) + client->LogSQL(query); + + safe_delete_array(query); + return 1; +} + uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; @@ -1154,39 +1206,7 @@ uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_ver return UpdateNPCTypeAppearance(c, spawn); } case 3: { // delete spawn from spawning, but leave in npc_types table - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()), errbuf, &result)) { - safe_delete_array(query); - return 0; - } - safe_delete_array(query); - - row = mysql_fetch_row(result); - if (row == nullptr) return false; - if (row[0]) tmp = atoi(row[0]); - if (row[1]) tmp2 = atoi(row[1]); - - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - - - break; + return DeleteSpawnLeaveInNPCTypeTable(zone, c, spawn); } case 4: { //delete spawn from DB (including npc_type) if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND version=%u AND spawngroupID=%i", zone, zone_version, spawn->GetSp2()), errbuf, &result)) { diff --git a/zone/zonedb.h b/zone/zonedb.h index 490d6fa6d..74a1b9797 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -374,6 +374,7 @@ public: uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete uint32 CreateNewNPCCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra); uint32 AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime); + uint32 DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn); uint32 UpdateNPCTypeAppearance(Client *client, NPC* spawn); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); From f7ecfe7257f7fb3a3e40a2cdc5e43d2f4a3650eb Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 16:44:52 -0700 Subject: [PATCH 074/368] DeleteSpawnLeaveInNPCType converted to QueryDatabase --- zone/npc.cpp | 116 +++++++++++++++++++++++++++++--------------------- zone/zonedb.h | 1 + 2 files changed, 69 insertions(+), 48 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index be1be1ebd..1a69dc95c 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1136,39 +1136,87 @@ uint32 ZoneDatabase::UpdateNPCTypeAppearance(Client *client, NPC* spawn) { } uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn) { + uint32 id = 0; + uint32 spawngroupID = 0; + + std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE " + "zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + if (row[0]) + id = atoi(row[0]); + + if (row[1]) + spawngroupID = atoi(row[1]); + + query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + return 1; +} + +uint32 ZoneDatabase::DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; uint32 tmp = 0; uint32 tmp2 = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()), errbuf, &result)) { + if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND version=%u AND spawngroupID=%i", zone, zone_version, spawn->GetSp2()), errbuf, &result)) { safe_delete_array(query); - return 0; + return(0); } safe_delete_array(query); row = mysql_fetch_row(result); + if (row == nullptr) - return 0; + return false; if (row[0]) tmp = atoi(row[0]); if (row[1]) tmp2 = atoi(row[1]); + mysql_free_result(result); if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { safe_delete(query); - return 0; + return false; } if(client) client->LogSQL(query); - safe_delete_array(query); - + safe_delete_array(query); if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { safe_delete(query); - return 0; + return false; } if(client) @@ -1177,14 +1225,23 @@ uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *cl safe_delete_array(query); if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { safe_delete(query); - return 0; + return false; } if(client) client->LogSQL(query); - safe_delete_array(query); - return 1; + safe_delete_array(query); + if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM npc_types WHERE id='%i'", spawn->GetNPCTypeID()), errbuf,0)) { + safe_delete(query); + return false; + } + + if(client) + client->LogSQL(query); + + safe_delete_array(query); + return true; } uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { @@ -1209,44 +1266,7 @@ uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_ver return DeleteSpawnLeaveInNPCTypeTable(zone, c, spawn); } case 4: { //delete spawn from DB (including npc_type) - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND version=%u AND spawngroupID=%i", zone, zone_version, spawn->GetSp2()), errbuf, &result)) { - safe_delete_array(query); - return(0); - } - safe_delete_array(query); - - row = mysql_fetch_row(result); - if (row == nullptr) return false; - if (row[0]) tmp = atoi(row[0]); - if (row[1]) tmp2 = atoi(row[1]); - mysql_free_result(result); - - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM npc_types WHERE id='%i'", spawn->GetNPCTypeID()), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - break; + return DeleteSpawnRemoveFromNPCTypeTable(zone, zone_version, c, spawn); } case 5: { // add a spawn from spawngroup if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, c->GetX(), c->GetY(), c->GetZ(), 120, c->GetHeading(), extra), errbuf, 0, 0, &tmp)) { diff --git a/zone/zonedb.h b/zone/zonedb.h index 74a1b9797..80ad6133c 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -375,6 +375,7 @@ public: uint32 CreateNewNPCCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra); uint32 AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime); uint32 DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn); + uint32 DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn); uint32 UpdateNPCTypeAppearance(Client *client, NPC* spawn); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); From d755aa48bc1de915a1aea2c80dc011f690523ce6 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 17:15:09 -0700 Subject: [PATCH 075/368] DeleteSpawnRemoveFromNPCTypeTable converted to QueryDatabase --- zone/npc.cpp | 80 +++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 1a69dc95c..caf513e40 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1183,65 +1183,61 @@ uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *cl } uint32 ZoneDatabase::DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 tmp = 0; - uint32 tmp2 = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND version=%u AND spawngroupID=%i", zone, zone_version, spawn->GetSp2()), errbuf, &result)) { - safe_delete_array(query); - return(0); - } - safe_delete_array(query); - row = mysql_fetch_row(result); + uint32 id = 0; + uint32 spawngroupID = 0; + + std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE zone = '%s' " + "AND version = %u AND spawngroupID = %i", + zone, zone_version, spawn->GetSp2()); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); - if (row == nullptr) - return false; if (row[0]) - tmp = atoi(row[0]); + id = atoi(row[0]); + if (row[1]) - tmp2 = atoi(row[1]); - mysql_free_result(result); + spawngroupID = atoi(row[1]); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { - safe_delete(query); - return false; - } + query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id); + results = QueryDatabase(query); + if (!results.Success()) + return 0; if(client) - client->LogSQL(query); + client->LogSQL(query.c_str()); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } + query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; if(client) - client->LogSQL(query); + client->LogSQL(query.c_str()); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } + query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; if(client) - client->LogSQL(query); + client->LogSQL(query.c_str()); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM npc_types WHERE id='%i'", spawn->GetNPCTypeID()), errbuf,0)) { - safe_delete(query); - return false; - } + query = StringFormat("DELETE FROM npc_types WHERE id = '%i'", spawn->GetNPCTypeID()); + results = QueryDatabase(query); + if (!results.Success()) + return 0; if(client) - client->LogSQL(query); + client->LogSQL(query.c_str()); - safe_delete_array(query); - return true; + return 1; } uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { From a21667244377674365198760e36a48e245e256b5 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 18:13:23 -0700 Subject: [PATCH 076/368] Moved add spawn from spawn group command case (5) from NPCSpawnDB into method AddSpawnFromSpawnGroup --- zone/npc.cpp | 30 ++++++++++++++++++++---------- zone/zonedb.h | 1 + 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index caf513e40..f7b7320f2 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1240,6 +1240,24 @@ uint32 ZoneDatabase::DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 return 1; } +uint32 ZoneDatabase::AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 tmp = 0; + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, client->GetX(), client->GetY(), client->GetZ(), 120, client->GetHeading(), spawnGroupID), errbuf, 0, 0, &tmp)) { + safe_delete(query); + return false; + } + + if(client) + client->LogSQL(query); + safe_delete_array(query); + + return true; +} + uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; @@ -1265,16 +1283,8 @@ uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_ver return DeleteSpawnRemoveFromNPCTypeTable(zone, zone_version, c, spawn); } case 5: { // add a spawn from spawngroup - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, c->GetX(), c->GetY(), c->GetZ(), 120, c->GetHeading(), extra), errbuf, 0, 0, &tmp)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - return true; - break; - } + return AddSpawnFromSpawnGroup(zone, zone_version, c, spawn, extra); + } case 6: { // add npc_type uint32 npc_type_id; char tmpstr[64]; diff --git a/zone/zonedb.h b/zone/zonedb.h index 80ad6133c..46d596cd3 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -376,6 +376,7 @@ public: uint32 AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime); uint32 DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn); uint32 DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn); + uint32 AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID); uint32 UpdateNPCTypeAppearance(Client *client, NPC* spawn); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); From 0799b47c9ccdc7b12ee6647019a7ca66ed67a7ee Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Thu, 4 Sep 2014 18:20:41 -0700 Subject: [PATCH 077/368] AddSpawnFromSpawnGroup converted to QueryDatabase --- zone/npc.cpp | 72 +++++++++++++++++++++++++++++---------------------- zone/zonedb.h | 1 + 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index f7b7320f2..70b07c501 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1241,31 +1241,52 @@ uint32 ZoneDatabase::DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 } uint32 ZoneDatabase::AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 tmp = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, client->GetX(), client->GetY(), client->GetZ(), 120, client->GetHeading(), spawnGroupID), errbuf, 0, 0, &tmp)) { - safe_delete(query); - return false; - } + + uint32 last_insert_id = 0; + std::string query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " + "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", + zone, zone_version, client->GetX(), client->GetY(), client->GetZ(), + 120, client->GetHeading(), spawnGroupID); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; if(client) - client->LogSQL(query); - safe_delete_array(query); + client->LogSQL(query.c_str()); - return true; + return 1; +} + +uint32 ZoneDatabase::AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) { + + uint32 npc_type_id; + char numberlessName[64]; + + EntityList::RemoveNumbers(strn0cpy(numberlessName, spawn->GetName(), sizeof(numberlessName))); + std::string query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, " + "texture, helmtexture, size, loottable_id, merchant_id, face, " + "runspeed, prim_melee_type, sec_melee_type) " + "VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", + numberlessName, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + npc_type_id = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + if(client) + client->Message(0, "%s npc_type ID %i created successfully!", numberlessName, npc_type_id); + + return 1; } uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 tmp = 0; - uint32 tmp2 = 0; - uint32 last_insert_id = 0; + switch (command) { case 0: { // Create a new NPC and add all spawn related data return CreateNewNPCCommand(zone, zone_version, c, spawn, extra); @@ -1286,18 +1307,7 @@ uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_ver return AddSpawnFromSpawnGroup(zone, zone_version, c, spawn, extra); } case 6: { // add npc_type - uint32 npc_type_id; - char tmpstr[64]; - EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if(c) c->Message(0, "%s npc_type ID %i created successfully!", tmpstr, npc_type_id); - return true; - break; + return AddNPCTypes(zone, zone_version, c, spawn, extra); } } return false; diff --git a/zone/zonedb.h b/zone/zonedb.h index 46d596cd3..039a35d82 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -377,6 +377,7 @@ public: uint32 DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn); uint32 DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn); uint32 AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID); + uint32 AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID); uint32 UpdateNPCTypeAppearance(Client *client, NPC* spawn); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); From 15fa2b371ca4a1f52b394dfb1ce1b4da10c4372c Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Fri, 5 Sep 2014 10:45:36 -0700 Subject: [PATCH 078/368] LearnMembers converted to QueryDatabase --- zone/groups.cpp | 57 +++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index 5c34cf359..83c997836 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -195,7 +195,7 @@ void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinu if (members[i] != nullptr && members[i]->IsClient()) { // If Group Member is Client Client *c = members[i]->CastToClient(); //I could not get MoneyOnCorpse to work, so we use this - c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); + c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); c->Message(2, msg.c_str()); } } @@ -957,31 +957,28 @@ void Group::TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float } bool Group::LearnMembers() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT name FROM group_id WHERE groupid=%lu", (unsigned long)GetID()), - errbuf,&result)){ - safe_delete_array(query); - if(mysql_num_rows(result) < 1) { //could prolly be 2 - mysql_free_result(result); - LogFile->write(EQEMuLog::Error, "Error getting group members for group %lu: %s", (unsigned long)GetID(), errbuf); - return(false); - } - int i = 0; - while((row = mysql_fetch_row(result))) { - if(!row[0]) - continue; - members[i] = nullptr; - strn0cpy(membername[i], row[0], 64); + std::string query = StringFormat("SELECT name FROM group_id WHERE groupid = %lu", (unsigned long)GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return false; - i++; - } - mysql_free_result(result); + if (results.RowCount() == 0) { + LogFile->write(EQEMuLog::Error, "Error getting group members for group %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str()); + return false; + } + + int memberIndex = 0; + for(auto row = results.begin(); row != results.end(); ++row) { + if(!row[0]) + continue; + + members[memberIndex] = nullptr; + strn0cpy(membername[memberIndex], row[0], 64); + + memberIndex++; } - return(true); + return true; } void Group::VerifyGroup() { @@ -1065,7 +1062,7 @@ void Group::HealGroup(uint32 heal_amt, Mob* caster, int32 range) if (!range) range = 200; - + float distance; float range2 = range*range; @@ -1102,10 +1099,10 @@ void Group::BalanceHP(int32 penalty, int32 range, Mob* caster, int32 limit) return; if (!range) - range = 200; - + range = 200; + int dmgtaken = 0, numMem = 0, dmgtaken_tmp = 0; - + float distance; float range2 = range*range; @@ -1152,16 +1149,16 @@ void Group::BalanceMana(int32 penalty, int32 range, Mob* caster, int32 limit) return; if (!range) - range = 200; + range = 200; float distance; float range2 = range*range; - + int manataken = 0, numMem = 0, manataken_tmp = 0; unsigned int gi = 0; for(; gi < MAX_GROUP_MEMBERS; gi++) { - if(members[gi] && (members[gi]->GetMaxMana() > 0)){ + if(members[gi] && (members[gi]->GetMaxMana() > 0)){ distance = caster->DistNoRoot(*members[gi]); if(distance <= range2){ From b5ec35e672b0bb098ef14c7d18f0e60dbfc6aafa Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Fri, 5 Sep 2014 15:27:12 -0700 Subject: [PATCH 079/368] DelegateMainTank converted to QueryDatabase --- zone/groups.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index 83c997836..9dc417aa1 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1326,15 +1326,12 @@ void Group::DelegateMainTank(const char *NewMainTankName, uint8 toggle) } if(updateDB) { - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = nullptr; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET maintank='%s' WHERE gid=%i LIMIT 1", - MainTankName.c_str(), GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group main tank: %s\n", errbuff); - - safe_delete_array(Query); + std::string query = StringFormat("UPDATE group_leaders SET maintank = '%s' WHERE gid = %i LIMIT 1", + MainTankName.c_str(), GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set group main tank: %s\n", results.ErrorMessage().c_str()); } } From 99fe610f72a4811f566062521cee2044a7545fb7 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Fri, 5 Sep 2014 15:46:06 -0700 Subject: [PATCH 080/368] DelegatePuller converted to QueryDatabase --- zone/groups.cpp | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index 9dc417aa1..5033d344c 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1372,15 +1372,13 @@ void Group::DelegateMainAssist(const char *NewMainAssistName, uint8 toggle) } if(updateDB) { - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = nullptr; + std::string query = StringFormat("UPDATE group_leaders SET assist = '%s' WHERE gid = %i LIMIT 1", + MainAssistName.c_str(), GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set group main assist: %s\n", results.ErrorMessage().c_str()); - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET assist='%s' WHERE gid=%i LIMIT 1", - MainAssistName.c_str(), GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group main assist: %s\n", errbuff); - - safe_delete_array(Query); } } @@ -1421,15 +1419,13 @@ void Group::DelegatePuller(const char *NewPullerName, uint8 toggle) } if(updateDB) { - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = nullptr; + std::string query = StringFormat("UPDATE group_leaders SET puller = '%s' WHERE gid = %i LIMIT 1", + PullerName.c_str(), GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set group main puller: %s\n", results.ErrorMessage().c_str()); - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET puller='%s' WHERE gid=%i LIMIT 1", - PullerName.c_str(), GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group main puller: %s\n", errbuff); - - safe_delete_array(Query); } } From b525a32b6e78b1d70369e8be90c72b045b96e603 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Fri, 5 Sep 2014 15:54:07 -0700 Subject: [PATCH 081/368] UndelegateMainTank converted to QueryDatabase --- zone/groups.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index 5033d344c..4ed4fc41f 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1571,15 +1571,11 @@ void Group::UnDelegateMainTank(const char *OldMainTankName, uint8 toggle) // informing them of the change and update the group_leaders table. // if(OldMainTankName == MainTankName) { - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET maintank='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group main tank: %s\n", errbuff); - - safe_delete_array(Query); + std::string query = StringFormat("UPDATE group_leaders SET maintank = '' WHERE gid = %i LIMIT 1", GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear group main tank: %s\n", results.ErrorMessage().c_str()); if(!toggle) { for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { From 5b7aaff1503e90a6a13026eeee24275bdb0e05d1 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Fri, 5 Sep 2014 15:58:21 -0700 Subject: [PATCH 082/368] UnDelegateMainAssist converted to QueryDatabase --- zone/groups.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index 4ed4fc41f..63dc63822 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1621,15 +1621,10 @@ void Group::UnDelegateMainAssist(const char *OldMainAssistName, uint8 toggle) safe_delete(outapp); - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET assist='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group main assist: %s\n", errbuff); - - safe_delete_array(Query); + std::string query = StringFormat("UPDATE group_leaders SET assist = '' WHERE gid = %i LIMIT 1", GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear group main assist: %s\n", results.ErrorMessage().c_str()); if(!toggle) { From 31177b7dc7feae0c96d89800316b4913ce30a081 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Fri, 5 Sep 2014 16:04:34 -0700 Subject: [PATCH 083/368] UnDelegatePuller converted to QueryDatabase --- zone/groups.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index 63dc63822..6628d67d5 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1648,15 +1648,11 @@ void Group::UnDelegatePuller(const char *OldPullerName, uint8 toggle) // informing them of the change and update the group_leaders table. // if(OldPullerName == PullerName) { - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET puller='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group main puller: %s\n", errbuff); - - safe_delete_array(Query); + std::string query = StringFormat("UPDATE group_leaders SET puller = '' WHERE gid = %i LIMIT 1", GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear group main puller: %s\n", results.ErrorMessage().c_str()); if(!toggle) { for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { From ef1f1562f004e0d223fa5767dc3e26f900d78c9b Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Fri, 5 Sep 2014 16:13:50 -0700 Subject: [PATCH 084/368] DelegateMarkNPC converted to QueryDatabase --- zone/groups.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index 6628d67d5..2e3d8ecb0 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1787,16 +1787,11 @@ void Group::DelegateMarkNPC(const char *NewNPCMarkerName) if(members[i] && members[i]->IsClient()) NotifyMarkNPC(members[i]->CastToClient()); - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET marknpc='%s' WHERE gid=%i LIMIT 1", - NewNPCMarkerName, GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group mark npc: %s\n", errbuff); - - safe_delete_array(Query); - + std::string query = StringFormat("UPDATE group_leaders SET marknpc = '%s' WHERE gid = %i LIMIT 1", + NewNPCMarkerName, GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set group mark npc: %s\n", results.ErrorMessage().c_str()); } void Group::NotifyMarkNPC(Client *c) From 59618e0038ca2582a678e32c0422222367ed8227 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Fri, 5 Sep 2014 16:14:28 -0700 Subject: [PATCH 085/368] UnDelegateMarkNPC converted to QueryDatabase --- zone/groups.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index 2e3d8ecb0..6c0200250 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1868,15 +1868,12 @@ void Group::UnDelegateMarkNPC(const char *OldNPCMarkerName) NPCMarkerName.clear(); - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = 0; + std::string query = StringFormat("UPDATE group_leaders SET marknpc = '' WHERE gid = %i LIMIT 1", GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear group marknpc: %s\n", results.ErrorMessage().c_str()); - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET marknpc='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group marknpc: %s\n", errbuff); - - safe_delete_array(Query); } void Group::SaveGroupLeaderAA() From ca7dd7d741334b0e47f94f73e74404c585c1febf Mon Sep 17 00:00:00 2001 From: akkadius Date: Sat, 6 Sep 2014 13:53:54 -0500 Subject: [PATCH 086/368] - Improved speed of character database conversion x1000 by changing query style - Adjusted AA MySQL saves for 100x speed increase - Removed StoreCharacter lookup methods as they will no longer be necessary - Some other cleanup --- common/database.cpp | 128 +++++++++++++++++++++++++++++++------------- zone/aa.cpp | 11 ++-- zone/client.cpp | 28 ++++------ zone/zonedb.cpp | 10 ---- zone/zonedb.h | 1 - 5 files changed, 108 insertions(+), 70 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 5de92ba08..ca51636ce 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -793,8 +793,7 @@ void Database::GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID } -void Database::GetCharName(uint32 char_id, char* name) { - +void Database::GetCharName(uint32 char_id, char* name) { std::string query = StringFormat("SELECT `name` FROM `character_data` WHERE id='%i'", char_id); auto results = QueryDatabase(query); @@ -1209,7 +1208,7 @@ bool Database::CheckDatabaseConversions() { // querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); // WHERE `account_id` = 11001 int char_iter_count = 0; - querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); + querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `account_id` = 11001"); if (RunQuery(query, querylen, errbuf, &result)) { safe_delete_array(query); while (row = mysql_fetch_row(result)) { @@ -1454,8 +1453,8 @@ bool Database::CheckDatabaseConversions() { ")", character_id, account_id, - pp->name, - pp->last_name, + EscapeString(pp->name).c_str(), + EscapeString(pp->last_name).c_str(), pp->gender, pp->race, pp->class_, @@ -1479,8 +1478,8 @@ bool Database::CheckDatabaseConversions() { pp->ability_number, pp->ability_time_minutes, pp->ability_time_hours, - pp->title, - pp->suffix, + EscapeString(pp->title).c_str(), + EscapeString(pp->suffix).c_str(), pp->exp, pp->points, pp->mana, @@ -1546,15 +1545,26 @@ bool Database::CheckDatabaseConversions() { results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR PP Data Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + // str.append(str2); + /* Run AA Convert */ - for (i = 0; i < MAX_PP_AA_ARRAY; i++){ + /* + We set a first entry variable because we need the first initial piece of the query to be declared + This is to speed up the INSERTS and trim down the amount of individual sends during the process. + The speed difference is dramatic + */ + int first_entry = 0; + for (i = 1; i < MAX_PP_AA_ARRAY; i++){ if (pp->aa_array[i].AA > 0 && pp->aa_array[i].value > 0){ - rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" - " VALUES (%u, %u, %u, %u)", - character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR AA Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - } - } + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" + " VALUES (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); + } + } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR AA Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Bind Home Convert */ rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" @@ -1569,71 +1579,117 @@ bool Database::CheckDatabaseConversions() { results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Language Convert */ + first_entry = 0; for (i = 0; i < MAX_PP_LANGUAGE; i++){ if (pp->languages[i] > 0){ - rquery = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, i, pp->languages[i]); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Language Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, i, pp->languages[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->languages[i]); } } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Language Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Skill Convert */ + first_entry = 0; for (i = 0; i < MAX_PP_SKILL; i++){ - if (pp->skills[i] > 0){ - rquery = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, i, pp->skills[i]); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Skill Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (pp->skills[i] > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, i, pp->skills[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->skills[i]); } } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Skill Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Spell Convert */ + first_entry = 0; for (i = 0; i < MAX_PP_SPELLBOOK; i++){ if (pp->spell_book[i] > 0 && pp->spell_book[i] != 4294967295 && pp->spell_book[i] < 40000 && pp->spell_book[i] != 1){ - rquery = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->spell_book[i]); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->spell_book[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->spell_book[i]); } } - /* Run Max Memmed Spell Convert */ + // std::cout << rquery << "\n"; + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + /* Run Max Memmed Spell Convert */ + first_entry = 0; for (i = 0; i < MAX_PP_MEMSPELL; i++){ - if (pp->mem_spells[i] > 0){ - rquery = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->mem_spells[i]); - QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Memmed Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (pp->mem_spells[i] > 0 && pp->mem_spells[i] != 65535){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->mem_spells[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->mem_spells[i]); } } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Memmed Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Discipline Convert */ + first_entry = 0; for (i = 0; i < MAX_PP_DISCIPLINES; i++){ if (pp->disciplines.values[i] > 0){ - rquery = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Discipline Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); } } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Discipline Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Material Color Convert */ + first_entry = 0; for (i = 0; i < _MaterialCount; i++){ if (pp->item_tint[i].color > 0){ - rquery = StringFormat("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); - // printf("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u);\n", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Color Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); } } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Color Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Tribute Convert */ + first_entry = 0; for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ if (pp->tributes[i].tribute > 0){ - rquery = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Tribute Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); } } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Tribute Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Bandolier Convert */ + first_entry = 0; for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ if (pp->bandoliers[i].items[si].item_id > 0){ - rquery = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); } } } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } /* Run Potion Belt Convert */ + first_entry = 0; for (i = 0; i <= EmuConstants::POTION_BELT_SIZE; i++){ if (pp->potionbelt.items[i].item_id > 0){ - rquery = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Potion Belt Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); + } } + results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Potion Belt Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } /* Print out the entire Player Profile for testing */ diff --git a/zone/aa.cpp b/zone/aa.cpp index 2af81a619..f546e59f0 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1035,8 +1035,7 @@ void Client::BuyAA(AA_Action* action) uint32 real_cost; std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(action->ability); - if(RequiredLevel != AARequiredLevelAndCost.end()) - { + if(RequiredLevel != AARequiredLevelAndCost.end()) { real_cost = RequiredLevel->second.Cost; } else @@ -1064,8 +1063,10 @@ void Client::BuyAA(AA_Action* action) SendAATable(); - //we are building these messages ourself instead of using the stringID to work around patch discrepencies - //these are AA_GAIN_ABILITY (410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2 + /* + We are building these messages ourself instead of using the stringID to work around patch discrepencies + these are AA_GAIN_ABILITY (410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2 + */ /* Initial purchase of an AA ability */ if (cur_level < 1){ @@ -1088,8 +1089,6 @@ void Client::BuyAA(AA_Action* action) } } - - SendAAStats(); CalcBonuses(); diff --git a/zone/client.cpp b/zone/client.cpp index 962715b86..2a85bfab7 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -477,13 +477,10 @@ void Client::ReportConnectingState() { }; } -inline double clock_diff_to_sec(long clock_diff) -{ - return double(clock_diff) / CLOCKS_PER_SEC; -} - bool Client::SaveAA(){ clock_t t = std::clock(); /* Function timer start */ + int first_entry = 0; + std::string rquery; /* Save Player AA */ int spentpoints = 0; for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { @@ -509,12 +506,15 @@ bool Client::SaveAA(){ m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { if (aa[a]->AA > 0 && aa[a]->value){ - std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" - " VALUES (%u, %u, %u, %u)", - character_id, a, aa[a]->AA, aa[a]->value); - auto results = database.QueryDatabase(rquery); + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" + " VALUES (%u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value); } } + auto results = database.QueryDatabase(rquery); LogFile->write(EQEMuLog::Status, "Issuing Client AA Save... CID: %i Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; } @@ -581,15 +581,9 @@ bool Client::Save(uint8 iCommitNow) { p_timers.Store(&database); database.SaveCharacterTribute(this->CharacterID(), &m_pp); + SaveTaskState(); /* Save Character Task */ + database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp); /* Save Character Data */ - /* Save Character Task */ - SaveTaskState(); - - /* Save Character Data */ - database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp); - - /* Mirror Character Data */ - database.StoreCharacterLookup(this->CharacterID()); LogFile->write(EQEMuLog::Status, "Client::Save %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 77e5739ee..ebf3957b6 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3841,13 +3841,3 @@ bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, std::list Date: Sat, 6 Sep 2014 21:50:29 -0500 Subject: [PATCH 087/368] - Ported inspect_messages to character_inspect_messages - Ported character leadership abilities to character_leadership_abilities - Removed player profile debug printing - Refactored total time entitled on account to load from the sum of time_played from all characters in character_data --- common/database.cpp | 1648 +++++++++++++++++----------------------- common/extprofile.h | 35 +- common/shareddb.cpp | 66 +- common/shareddb.h | 4 +- zone/aa.cpp | 13 +- zone/client.cpp | 25 +- zone/client_packet.cpp | 43 +- zone/zone.cpp | 2 +- zone/zonedb.cpp | 236 +++--- zone/zonedb.h | 7 +- 10 files changed, 935 insertions(+), 1144 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index ca51636ce..7c8c988cf 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -329,37 +329,39 @@ bool Database::DeleteCharacter(char *name) { for (auto row = results.begin(); row != results.end(); ++row) { charid = atoi(row[0]); } if (charid <= 0){ std::cerr << "Database::DeleteCharacter :: Character not found, stopping delete...\n"; return false; } - query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); - query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_inspect_messages` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); #ifdef BOTS query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", charid); #else @@ -819,14 +821,11 @@ static inline void loadbar(unsigned int x, unsigned int n, unsigned int w = 50) } bool Database::CheckDatabaseConversions() { - /* Set all of this ugliness */ - char errbuf[MYSQL_ERRMSG_SIZE]; char errbuf2[MYSQL_ERRMSG_SIZE]; char errbuf3[MYSQL_ERRMSG_SIZE]; - char *query = 0; char *query2 = 0; char *query3 = 0; - uint32 querylen; uint32 querylen2; uint32 querylen3; - MYSQL_RES *result; MYSQL_RES *result2; MYSQL_RES *result3; - MYSQL_ROW row; MYSQL_ROW row2; MYSQL_ROW row3; - unsigned long* lengths; + unsigned int lengths; + unsigned int lengths_e; + std::string squery; PlayerProfile_Struct* pp; + ExtendedProfile_Struct* e_pp; uint32 pplen = 0; uint32 i; int character_id = 0; @@ -870,101 +869,109 @@ bool Database::CheckDatabaseConversions() { if (results.RowCount() == 0){ printf("Table: `character_data` doesn't exist... creating..."); rquery = StringFormat( - "CREATE TABLE `character_data` ( " - "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " - "`account_id` int(11) NOT NULL DEFAULT '0', " - "`name` varchar(64) NOT NULL DEFAULT '', " - "`last_name` varchar(64) NOT NULL DEFAULT '', " - "`gender` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`race` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`class` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`level` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`deity` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`birthday` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`last_login` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`time_played` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`pvp_status` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`level2` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`anon` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`gm` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`intoxication` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`hair_color` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`beard_color` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`eye_color_1` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`eye_color_2` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`hair_style` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`beard` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ability_time_seconds` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ability_number` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ability_time_minutes` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ability_time_hours` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`title` varchar(32) NOT NULL DEFAULT '', " - "`suffix` varchar(32) NOT NULL DEFAULT '', " - "`exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`points` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`mana` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`cur_hp` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`str` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`sta` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`cha` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`dex` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`int` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`agi` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`wis` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`face` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`y` float NOT NULL DEFAULT '0', " - "`x` float NOT NULL DEFAULT '0', " - "`z` float NOT NULL DEFAULT '0', " - "`heading` float NOT NULL DEFAULT '0', " - "`pvp2` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`pvp_type` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`autosplit_enabled` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`zone_change_count` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`drakkin_heritage` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`drakkin_tattoo` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`drakkin_details` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`toxicity` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`hunger_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`thirst_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ability_up` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`zone_id` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`zone_instance` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`leadership_exp_on` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ldon_points_guk` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ldon_points_mir` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ldon_points_mmc` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ldon_points_ruj` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ldon_points_tak` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`ldon_points_available` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`tribute_time_remaining` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`show_helm` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`career_tribute_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`tribute_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`tribute_active` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`endurance` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`group_leadership_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`raid_leadership_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`group_leadership_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`raid_leadership_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`air_remaining` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`pvp_kills` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`pvp_deaths` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`pvp_current_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`pvp_career_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`pvp_best_kill_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`pvp_worst_death_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`pvp_current_kill_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`aa_points_spent` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`aa_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`aa_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "`group_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`raid_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`guild_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " - "`RestTimer` int(11) UNSIGNED NOT NULL DEFAULT 0, " - "PRIMARY KEY(`id`), " - "UNIQUE KEY `name` (`name`), " - "KEY `account_id` (`account_id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " + "CREATE TABLE `character_data` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`account_id` int(11) NOT NULL DEFAULT '0', " + "`name` varchar(64) NOT NULL DEFAULT '', " + "`last_name` varchar(64) NOT NULL DEFAULT '', " + "`title` varchar(32) NOT NULL DEFAULT '', " + "`suffix` varchar(32) NOT NULL DEFAULT '', " + "`zone_id` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`zone_instance` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`y` float NOT NULL DEFAULT '0', " + "`x` float NOT NULL DEFAULT '0', " + "`z` float NOT NULL DEFAULT '0', " + "`heading` float NOT NULL DEFAULT '0', " + "`gender` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`race` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`class` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`deity` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`birthday` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`last_login` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`time_played` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`level2` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`anon` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`gm` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`face` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`hair_color` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`hair_style` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`beard` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`beard_color` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`eye_color_1` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`eye_color_2` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`drakkin_heritage` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`drakkin_tattoo` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`drakkin_details` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ability_time_seconds` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ability_number` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ability_time_minutes` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ability_time_hours` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`aa_points_spent` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`aa_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`aa_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`group_leadership_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`raid_leadership_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`group_leadership_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`raid_leadership_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`cur_hp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`mana` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`endurance` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`intoxication` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`str` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`sta` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`cha` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`dex` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`int` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`agi` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`wis` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`zone_change_count` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`toxicity` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`hunger_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`thirst_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ability_up` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_guk` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_mir` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_mmc` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_ruj` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_tak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_available` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`tribute_time_remaining` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`career_tribute_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`tribute_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`tribute_active` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_status` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_kills` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_deaths` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_current_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_career_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_best_kill_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_worst_death_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_current_kill_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp2` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_type` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`show_helm` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`group_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`raid_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`guild_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`leadership_exp_on` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`RestTimer` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`air_remaining` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`autosplit_enabled` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`lfp` tinyint(1) unsigned NOT NULL DEFAULT '0', " + "`lfg` tinyint(1) unsigned NOT NULL DEFAULT '0', " + "`mailkey` char(16) NOT NULL DEFAULT '', " + "`xtargets` tinyint(3) unsigned NOT NULL DEFAULT '5', " + "`firstlogon` tinyint(3) NOT NULL DEFAULT '0', " + "`e_aa_effects` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`e_percent_to_aa` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`e_expended_aa_spent` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "PRIMARY KEY(`id`), " + "UNIQUE KEY `name` (`name`), " + "KEY `account_id` (`account_id`) " + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " ); QueryDatabase(rquery); printf(" done...\n"); @@ -1200,813 +1207,594 @@ bool Database::CheckDatabaseConversions() { QueryDatabase(rquery); printf(" done...\n"); } - + /* Check for table `character_potionbelt` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_inspect_messages'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_inspect_messages` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_inspect_messages` ( " + "`id` int(11) unsigned NOT NULL AUTO_INCREMENT, " + "`inspect_message` varchar(255) NOT NULL DEFAULT '', " + "PRIMARY KEY(`id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ); + QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_leadership_abilities` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_leadership_abilities'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_leadership_abilities` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_leadership_abilities` (" + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`slot` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`rank` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + "PRIMARY KEY(`id`,`slot`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " + ); + QueryDatabase(rquery); + printf(" done...\n"); + } /* Done */ printf("Starting conversion...\n\n"); } - // querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `id` = 61238"); // WHERE `account_id` = 11001 + // Testing account = 11001 int char_iter_count = 0; - querylen = MakeAnyLenString(&query, "SELECT `id` FROM `character_` WHERE `account_id` = 11001"); - if (RunQuery(query, querylen, errbuf, &result)) { - safe_delete_array(query); - while (row = mysql_fetch_row(result)) { - char_iter_count++; - querylen2 = MakeAnyLenString(&query2, "SELECT `id`, `profile`, `name`, `level`, `account_id` FROM `character_` WHERE `id` = %i", atoi(row[0])); - if (RunQuery(query2, querylen2, errbuf2, &result2)){ - safe_delete_array(query2); - row2 = mysql_fetch_row(result2); - pp = (PlayerProfile_Struct*)row2[1]; - character_id = atoi(row[0]); - account_id = atoi(row2[4]); - - /* Verify PP Integrity */ - lengths = mysql_fetch_lengths(result2); - if (lengths[1] == sizeof(PlayerProfile_Struct)) { - memcpy(pp, row2[1], sizeof(PlayerProfile_Struct)); - // printf("FINE: Player profile '%s' %i length Expected: %i, Got: %i \n", row2[2], atoi(row2[3]), sizeof(PlayerProfile_Struct), lengths[1]); - } - /* Continue of PP Size does not match (Usually a created character never logged in) */ - else { - // printf("NO PP: Player profile '%s' %i length mismatch Expected: %i, Got: %i \n", row2[2], atoi(row2[3]), sizeof(PlayerProfile_Struct), lengths[1]); - continue; - } + rquery = StringFormat("SELECT `id` FROM `character_` WHERE `account_id` = 11001"); + results = QueryDatabase(rquery); - /* Loading Status on conversion */ - if (runconvert == 1){ - std::cout << "\r" << char_iter_count << "/" << number_of_characters << " " << std::flush; - loadbar(char_iter_count, number_of_characters, 50); + uint8 firstlogon = 0; + uint8 lfg = 0; + uint8 lfp = 0; + std::string mailkey; + uint8 xtargets = 0; + std::string inspectmessage; - /* Run Currency Convert */ - std::string rquery = StringFormat("REPLACE INTO `character_currency` (id, platinum, gold, silver, copper," - "platinum_bank, gold_bank, silver_bank, copper_bank," - "platinum_cursor, gold_cursor, silver_cursor, copper_cursor, " - "radiant_crystals, career_radiant_crystals, ebon_crystals, career_ebon_crystals)" - "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u)", - character_id, - pp->platinum, - pp->gold, - pp->silver, - pp->copper, - pp->platinum_bank, - pp->gold_bank, - pp->silver_bank, - pp->copper_bank, - pp->platinum_cursor, - pp->gold_cursor, - pp->silver_cursor, - pp->copper_cursor, - pp->currentRadCrystals, - pp->careerRadCrystals, - pp->currentEbonCrystals, - pp->careerEbonCrystals - ); - auto results = QueryDatabase(rquery); + for (auto row = results.begin(); row != results.end(); ++row) { + char_iter_count++; + squery = StringFormat("SELECT `id`, `profile`, `name`, `level`, `account_id`, `firstlogon`, `lfg`, `lfp`, `mailkey`, `xtargets`, `inspectmessage`, `extprofile` FROM `character_` WHERE `id` = %i", atoi(row[0])); + auto results2 = QueryDatabase(squery); + auto row2 = results2.begin(); + pp = (PlayerProfile_Struct*)row2[1]; + e_pp = (ExtendedProfile_Struct*)row2[11]; + character_id = atoi(row[0]); + account_id = atoi(row2[4]); + /* Convert some data from the character_ table that is still relevant */ + firstlogon = atoi(row2[5]); + lfg = atoi(row2[6]); + lfp = atoi(row2[7]); + mailkey = row2[8]; + xtargets = atoi(row2[9]); + inspectmessage = row2[10]; - if (pp->tribute_time_remaining < 0 || pp->tribute_time_remaining == 4294967295){ pp->tribute_time_remaining = 0; } - - /* Run Character Data Convert */ - rquery = StringFormat( - "REPLACE INTO `character_data` (" - "id," - "account_id," - "`name`," - "last_name," - "gender," - "race," - "class," - "`level`," - "deity," - "birthday," - "last_login," - "time_played," - "pvp_status," - "level2," - "anon," - "gm," - "intoxication," - "hair_color," - "beard_color," - "eye_color_1," - "eye_color_2," - "hair_style," - "beard," - "ability_time_seconds," - "ability_number," - "ability_time_minutes," - "ability_time_hours," - "title," - "suffix," - "exp," - "points," - "mana," - "cur_hp," - "str," - "sta," - "cha," - "dex," - "`int`," - "agi," - "wis," - "face," - "y," - "x," - "z," - "heading," - "pvp2," - "pvp_type," - "autosplit_enabled," - "zone_change_count," - "drakkin_heritage," - "drakkin_tattoo," - "drakkin_details," - "toxicity," - "hunger_level," - "thirst_level," - "ability_up," - "zone_id," - "zone_instance," - "leadership_exp_on," - "ldon_points_guk," - "ldon_points_mir," - "ldon_points_mmc," - "ldon_points_ruj," - "ldon_points_tak," - "ldon_points_available," - "tribute_time_remaining," - "show_helm," - "career_tribute_points," - "tribute_points," - "tribute_active," - "endurance," - "group_leadership_exp," - "raid_leadership_exp," - "group_leadership_points," - "raid_leadership_points," - "air_remaining," - "pvp_kills," - "pvp_deaths," - "pvp_current_points," - "pvp_career_points," - "pvp_best_kill_streak," - "pvp_worst_death_streak," - "pvp_current_kill_streak," - "aa_points_spent," - "aa_exp," - "aa_points," - "group_auto_consent," - "raid_auto_consent," - "guild_auto_consent," - "RestTimer)" - "VALUES (" - "%u," // id - "%u," // account_id - "'%s'," // `name` - "'%s'," // last_name - "%u," // gender - "%u," // race - "%u," // class - "%u," // `level` - "%u," // deity - "%u," // birthday - "%u," // last_login - "%u," // time_played - "%u," // pvp_status - "%u," // level2 - "%u," // anon - "%u," // gm - "%u," // intoxication - "%u," // hair_color - "%u," // beard_color - "%u," // eye_color_1 - "%u," // eye_color_2 - "%u," // hair_style - "%u," // beard - "%u," // ability_time_seconds - "%u," // ability_number - "%u," // ability_time_minutes - "%u," // ability_time_hours - "'%s'," // title - "'%s'," // suffix - "%u," // exp - "%u," // points - "%u," // mana - "%u," // cur_hp - "%u," // str - "%u," // sta - "%u," // cha - "%u," // dex - "%u," // `int` - "%u," // agi - "%u," // wis - "%u," // face - "%f," // y - "%f," // x - "%f," // z - "%f," // heading - "%u," // pvp2 - "%u," // pvp_type - "%u," // autosplit_enabled - "%u," // zone_change_count - "%u," // drakkin_heritage - "%u," // drakkin_tattoo - "%u," // drakkin_details - "%i," // toxicity - "%u," // hunger_level - "%u," // thirst_level - "%u," // ability_up - "%u," // zone_id - "%u," // zone_instance - "%u," // leadership_exp_on - "%u," // ldon_points_guk - "%u," // ldon_points_mir - "%u," // ldon_points_mmc - "%u," // ldon_points_ruj - "%u," // ldon_points_tak - "%u," // ldon_points_available - "%u," // tribute_time_remaining - "%u," // show_helm - "%u," // career_tribute_points - "%u," // tribute_points - "%u," // tribute_active - "%u," // endurance - "%u," // group_leadership_exp - "%u," // raid_leadership_exp - "%u," // group_leadership_points - "%u," // raid_leadership_points - "%u," // air_remaining - "%u," // pvp_kills - "%u," // pvp_deaths - "%u," // pvp_current_points - "%u," // pvp_career_points - "%u," // pvp_best_kill_streak - "%u," // pvp_worst_death_streak - "%u," // pvp_current_kill_streak - "%u," // aa_points_spent - "%u," // aa_exp - "%u," // aa_points - "%u," // group_auto_consent - "%u," // raid_auto_consent - "%u," // guild_auto_consent - "%u" // RestTimer - ")", - character_id, - account_id, - EscapeString(pp->name).c_str(), - EscapeString(pp->last_name).c_str(), - pp->gender, - pp->race, - pp->class_, - pp->level, - pp->deity, - pp->birthday, - pp->lastlogin, - pp->timePlayedMin, - pp->pvp, - pp->level2, - pp->anon, - pp->gm, - pp->intoxication, - pp->haircolor, - pp->beardcolor, - pp->eyecolor1, - pp->eyecolor2, - pp->hairstyle, - pp->beard, - pp->ability_time_seconds, - pp->ability_number, - pp->ability_time_minutes, - pp->ability_time_hours, - EscapeString(pp->title).c_str(), - EscapeString(pp->suffix).c_str(), - pp->exp, - pp->points, - pp->mana, - pp->cur_hp, - pp->STR, - pp->STA, - pp->CHA, - pp->DEX, - pp->INT, - pp->AGI, - pp->WIS, - pp->face, - pp->y, - pp->x, - pp->z, - pp->heading, - pp->pvp2, - pp->pvptype, - pp->autosplit, - pp->zone_change_count, - pp->drakkin_heritage, - pp->drakkin_tattoo, - pp->drakkin_details, - pp->toxicity, - pp->hunger_level, - pp->thirst_level, - pp->ability_up, - pp->zone_id, - pp->zoneInstance, - pp->leadAAActive, - pp->ldon_points_guk, - pp->ldon_points_mir, - pp->ldon_points_mmc, - pp->ldon_points_ruj, - pp->ldon_points_tak, - pp->ldon_points_available, - pp->tribute_time_remaining, - pp->showhelm, - pp->career_tribute_points, - pp->tribute_points, - pp->tribute_active, - pp->endurance, - pp->group_leadership_exp, - pp->raid_leadership_exp, - pp->group_leadership_points, - pp->raid_leadership_points, - pp->air_remaining, - pp->PVPKills, - pp->PVPDeaths, - pp->PVPCurrentPoints, - pp->PVPCareerPoints, - pp->PVPBestKillStreak, - pp->PVPWorstDeathStreak, - pp->PVPCurrentKillStreak, - pp->aapoints_spent, - pp->expAA, - pp->aapoints, - pp->groupAutoconsent, - pp->raidAutoconsent, - pp->guildAutoconsent, - pp->RestTimer - ); - results = QueryDatabase(rquery); - if (!results.RowsAffected()){ std::cout << "ERROR PP Data Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - - // str.append(str2); - - /* Run AA Convert */ - /* - We set a first entry variable because we need the first initial piece of the query to be declared - This is to speed up the INSERTS and trim down the amount of individual sends during the process. - The speed difference is dramatic - */ - int first_entry = 0; - for (i = 1; i < MAX_PP_AA_ARRAY; i++){ - if (pp->aa_array[i].AA > 0 && pp->aa_array[i].value > 0){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" - " VALUES (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); - } - } - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR AA Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - - /* Run Bind Home Convert */ - rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" - " VALUES (%u, %u, %u, %f, %f, %f, %f, 1)", - character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - - /* Run Bind Convert */ - rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" - " VALUES (%u, %u, %u, %f, %f, %f, %f, 0)", - character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - - /* Run Language Convert */ - first_entry = 0; - for (i = 0; i < MAX_PP_LANGUAGE; i++){ - if (pp->languages[i] > 0){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, i, pp->languages[i]); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->languages[i]); - } - } - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Language Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - /* Run Skill Convert */ - first_entry = 0; - for (i = 0; i < MAX_PP_SKILL; i++){ - if (pp->skills[i] > 0){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, i, pp->skills[i]); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->skills[i]); - } - } - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Skill Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - /* Run Spell Convert */ - first_entry = 0; - for (i = 0; i < MAX_PP_SPELLBOOK; i++){ - if (pp->spell_book[i] > 0 && pp->spell_book[i] != 4294967295 && pp->spell_book[i] < 40000 && pp->spell_book[i] != 1){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->spell_book[i]); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->spell_book[i]); - } - } - // std::cout << rquery << "\n"; - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - /* Run Max Memmed Spell Convert */ - first_entry = 0; - for (i = 0; i < MAX_PP_MEMSPELL; i++){ - if (pp->mem_spells[i] > 0 && pp->mem_spells[i] != 65535){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->mem_spells[i]); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->mem_spells[i]); - } - } - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Memmed Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - /* Run Discipline Convert */ - first_entry = 0; - for (i = 0; i < MAX_PP_DISCIPLINES; i++){ - if (pp->disciplines.values[i] > 0){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); - } - } - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Discipline Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - /* Run Material Color Convert */ - first_entry = 0; - for (i = 0; i < _MaterialCount; i++){ - if (pp->item_tint[i].color > 0){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); - } - } - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Color Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - /* Run Tribute Convert */ - first_entry = 0; - for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - if (pp->tributes[i].tribute > 0){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); - } - } - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Tribute Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - /* Run Bandolier Convert */ - first_entry = 0; - for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ - for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ - if (pp->bandoliers[i].items[si].item_id > 0){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); - } - } - } - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - /* Run Potion Belt Convert */ - first_entry = 0; - for (i = 0; i <= EmuConstants::POTION_BELT_SIZE; i++){ - if (pp->potionbelt.items[i].item_id > 0){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); - - } - } - results = QueryDatabase(rquery); if (!results.RowsAffected()){ std::cout << "ERROR Potion Belt Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - } - - /* Print out the entire Player Profile for testing */ - if (printppdebug == 1) { - printf("ID: %i \n", atoi(row[0])); - printf("checksum: %i \n", pp->checksum); - printf("name: %s \n", pp->name); - printf("last_name: %s \n", pp->last_name); - printf("gender: %i \n", pp->gender); - printf("race: %i \n", pp->race); - printf("class_: %i \n", pp->class_); - printf("unknown0112: %i \n", pp->unknown0112); - printf("level: %i \n", pp->level); - - printf("\n=== BIND DATA (Array Size 5) ===\n"); - for (i = 0; i < 5; i++){ - printf("Bind Num: %i ZoneID: %u x: %f y: %f z: %f heading: %f \n", i, pp->binds[i].zoneId, pp->binds[i].x, pp->binds[i].y, pp->binds[i].z, pp->binds[i].heading); - } - printf("\n"); - - printf("deity: %u \n", pp->deity); - printf("guild_id: %u \n", pp->guild_id); - printf("birthday: %u \n", pp->birthday); - printf("lastlogin: %u \n", pp->lastlogin); - printf("timePlayedMin: %u \n", pp->timePlayedMin); - printf("pvp: %u \n", pp->pvp); - printf("level2: %u \n", pp->level2); - printf("anon: %u \n", pp->anon); - printf("gm: %u \n", pp->gm); - printf("guildrank: %u \n", pp->guildrank); - printf("guildbanker: %u \n", pp->guildbanker); - printf("unknown0246[6]: %u \n", pp->unknown0246[6]); - printf("intoxication: %u \n", pp->intoxication); - - printf("\n=== Spell Slot Refresh spellSlotRefresh[MAX_PP_MEMSPELL] ===\n"); - for (i = 0; i < MAX_PP_MEMSPELL; i++){ - printf("Slot: %i Value: %u \n", i, pp->spellSlotRefresh[i]); - } - printf("\n\n"); - - printf("abilitySlotRefresh: %u \n", pp->abilitySlotRefresh); - printf("haircolor: %u \n", pp->haircolor); - printf("beardcolor: %u \n", pp->beardcolor); - printf("eyecolor1: %u \n", pp->eyecolor1); - printf("eyecolor2: %u \n", pp->eyecolor2); - printf("hairstyle: %u \n", pp->hairstyle); - printf("beard: %u \n", pp->beard); - printf("ability_time_seconds: %u \n", pp->ability_time_seconds); - printf("ability_number: %u \n", pp->ability_number); - printf("ability_time_minutes: %u \n", pp->ability_time_minutes); - printf("ability_time_hours: %u \n", pp->ability_time_hours); - - printf("\n=== Color Material Data ===\n"); - for (i = 0; i < 10; i++){ - printf("Slot: %i Blue: %u Green: %u Red: %i Use_Tint: %u Color: %u \n", i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); - } - printf("\n\n"); - - printf("\n=== AA Data ===\n"); - for (i = 0; i < MAX_PP_AA_ARRAY; i++){ - printf("ID: %u Value %u\n", pp->aa_array[i].AA, pp->aa_array[i].value); - } - printf("\n\n"); - - printf("%i unknown2384\n", pp->unknown2384); - printf("servername: %s \n", pp->servername); - printf("title: %s \n", pp->title); - printf("suffix: %s \n", pp->suffix); - printf("guildid2: %u \n", pp->guildid2); - printf("exp: %u \n", pp->exp); - printf("unknown2492: %u \n", pp->unknown2492); - printf("points: %u \n", pp->points); - printf("mana: %u \n", pp->mana); - printf("cur_hp: %u \n", pp->cur_hp); - printf("unknown2508: %u \n", pp->unknown2508); - printf("STR: %u \n", pp->STR); - printf("STA: %u \n", pp->STA); - printf("CHA: %u \n", pp->CHA); - printf("DEX: %u \n", pp->DEX); - printf("INT: %u \n", pp->INT); - printf("AGI: %u \n", pp->AGI); - printf("WIS: %u \n", pp->WIS); - printf("face: %u \n", pp->face); - printf("unknown2541[47]: %u \n", pp->unknown2541[47]); - printf("languages[MAX_PP_LANGUAGE]: %u \n", pp->languages[MAX_PP_LANGUAGE]); - - printf("\n=== languages[MAX_PP_LANGUAGE] ===\n"); - for (i = 0; i < MAX_PP_LANGUAGE; i++){ - printf("ID: %u Value: %u \n", i, pp->languages[i]); - } - printf("\n\n"); - - printf("unknown2616[4]: %u \n", pp->unknown2616[4]); - - printf("\n=== Spell Book ===\n"); - for (i = 0; i < MAX_PP_SPELLBOOK; i++){ - printf("Spell Book Slot: %i Spell: %u \n", i, pp->spell_book[i]); - } - printf("\n\n"); - - printf("unknown4540[128]: %u \n", pp->unknown4540[128]); - - printf("\n=== mem_spells[MAX_PP_MEMSPELL] ===\n"); - for (i = 0; i < MAX_PP_MEMSPELL; i++){ - printf("ID: %u Value: %u \n", i, pp->mem_spells[i]); - } - printf("\n\n"); - - printf("unknown4704[32]: %u \n", pp->unknown4704[32]); - printf("y: %4.2f \n", pp->y); - printf("x: %4.2f \n", pp->x); - printf("z: %4.2f \n", pp->z); - printf("heading: %4.2f \n", pp->heading); - printf("unknown4752[4]: %u \n", pp->unknown4752[4]); - - printf("platinum: %u \n", pp->platinum); - printf("gold: %u \n", pp->gold); - printf("silver: %u \n", pp->silver); - printf("copper: %u \n", pp->copper); - printf("platinum_bank: %u \n", pp->platinum_bank); - printf("gold_bank: %u \n", pp->gold_bank); - printf("silver_bank: %u \n", pp->silver_bank); - printf("copper_bank: %u \n", pp->copper_bank); - printf("platinum_cursor: %u \n", pp->platinum_cursor); - printf("gold_cursor: %u \n", pp->gold_cursor); - printf("silver_cursor: %u \n", pp->silver_cursor); - printf("copper_cursor: %u \n", pp->copper_cursor); - printf("platinum_shared: %u \n", pp->platinum_shared); - printf("unknown4808[24]: %u \n", pp->unknown4808[24]); - - - - printf("\n=== skills[MAX_PP_SKILL] ===\n"); - for (i = 0; i < MAX_PP_SKILL; i++){ - printf("ID: %u Value: %u \n", i, pp->skills[i]); - } - printf("\n\n"); - - printf("unknown5132[184]: %u \n", pp->unknown5132[184]); - printf("pvp2: %u \n", pp->pvp2); - printf("unknown5420: %u \n", pp->unknown5420); - printf("pvptype: %u \n", pp->pvptype); - printf("unknown5428: %u \n", pp->unknown5428); - printf("ability_down: %u \n", pp->ability_down); - printf("unknown5436[8]: %u \n", pp->unknown5436[8]); - printf("autosplit: %u \n", pp->autosplit); - printf("unknown5448[8]: %u \n", pp->unknown5448[8]); - printf("zone_change_count: %u \n", pp->zone_change_count); - printf("unknown5460[16]: %u \n", pp->unknown5460[16]); - printf("drakkin_heritage: %u \n", pp->drakkin_heritage); - printf("drakkin_tattoo: %u \n", pp->drakkin_tattoo); - printf("drakkin_details: %u \n", pp->drakkin_details); - printf("expansions: %u \n", pp->expansions); - printf("toxicity: %u \n", pp->toxicity); - printf("unknown5496: %s \n", pp->unknown5496); - printf("hunger_level: %i \n", pp->hunger_level); - printf("thirst_level: %i \n", pp->thirst_level); - printf("ability_up: %u \n", pp->ability_up); - printf("unknown5524: %s \n", pp->unknown5524); - printf("zone_id: %u \n", pp->zone_id); - printf("zoneInstance: %u \n", pp->zoneInstance); - - // SpellBuff_Struct buffs[BUFF_COUNT]; - - printf("groupMembers: %s \n", pp->groupMembers); - printf("unknown6428: %s \n", pp->unknown6428); - printf("entityid: %u \n", pp->entityid); - printf("leadAAActive: %u \n", pp->leadAAActive); - printf("unknown7092: %u \n", pp->unknown7092); - printf("ldon_points_guk: %i \n", pp->ldon_points_guk); - printf("ldon_points_mir: %i \n", pp->ldon_points_mir); - printf("ldon_points_mmc: %i \n", pp->ldon_points_mmc); - printf("ldon_points_ruj: %i \n", pp->ldon_points_ruj); - printf("ldon_points_tak: %i \n", pp->ldon_points_tak); - printf("ldon_points_available: %i \n", pp->ldon_points_available); - printf("ldon_wins_guk: %i \n", pp->ldon_wins_guk); - printf("ldon_wins_mir: %i \n", pp->ldon_wins_mir); - printf("ldon_wins_mmc: %i \n", pp->ldon_wins_mmc); - printf("ldon_wins_ruj: %i \n", pp->ldon_wins_ruj); - printf("ldon_wins_tak: %i \n", pp->ldon_wins_tak); - printf("ldon_losses_guk: %i \n", pp->ldon_losses_guk); - printf("ldon_losses_mir: %i \n", pp->ldon_losses_mir); - printf("ldon_losses_mmc: %i \n", pp->ldon_losses_mmc); - printf("ldon_losses_ruj: %i \n", pp->ldon_losses_ruj); - printf("ldon_losses_tak: %i \n", pp->ldon_losses_tak); - printf("unknown7160[72]: %u \n", pp->unknown7160[72]); - printf("tribute_time_remaining: %u \n", pp->tribute_time_remaining); - printf("showhelm: %u \n", pp->showhelm); - printf("career_tribute_points: %u \n", pp->career_tribute_points); - printf("unknown7244: %u \n", pp->unknown7244); - printf("tribute_points: %u \n", pp->tribute_points); - printf("unknown7252: %u \n", pp->unknown7252); - printf("tribute_active: %u \n", pp->tribute_active); - - printf("\n=== Tribute_Struct tributes[EmuConstants::TRIBUTE_SIZE] ===\n"); - for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - printf("ID: %u Tribute: %u Tier: %u \n", i, pp->tributes[i].tribute, pp->tributes[i].tier); - } - printf("\n\n"); - - // Tribute_Struct tributes[EmuConstants::TRIBUTE_SIZE]; - // /*7264*/ Disciplines_Struct disciplines; - - printf("\n=== Disciplines_Struct disciplines ===\n"); - for (i = 0; i < MAX_PP_DISCIPLINES; i++){ - printf("ID: %u Disc Value: %u \n", i, pp->disciplines.values[i]); - } - printf("\n\n"); - - printf("\n=== recastTimers[MAX_RECAST_TYPES] ===\n"); - for (i = 0; i < MAX_RECAST_TYPES; i++){ - printf("ID: %u Value: %u \n", i, pp->recastTimers[i]); - } - printf("\n\n"); - - printf("unknown7780: %s \n", pp->unknown7780); - printf("endurance: %u \n", pp->endurance); - printf("group_leadership_exp: %u \n", pp->group_leadership_exp); - printf("raid_leadership_exp: %u \n", pp->raid_leadership_exp); - printf("group_leadership_points: %u \n", pp->group_leadership_points); - printf("raid_leadership_points: %u \n", pp->raid_leadership_points); - - // LeadershipAA_Struct leader_abilities; - - printf("\n=== LeadershipAA_Struct leader_abilities ===\n"); - for (i = 0; i <= MAX_LEADERSHIP_AA_ARRAY; i++){ - printf("ID: %u Rank: %u \n", i, pp->leader_abilities.ranks[i]); - } - printf("\n\n"); - - printf("unknown8088[132]: %u \n", pp->unknown8088[132]); - printf("air_remaining: %u \n", pp->air_remaining); - printf("PVPKills: %u \n", pp->PVPKills); - printf("PVPDeaths: %u \n", pp->PVPDeaths); - printf("PVPCurrentPoints: %u \n", pp->PVPCurrentPoints); - printf("PVPCareerPoints: %u \n", pp->PVPCareerPoints); - printf("PVPBestKillStreak: %u \n", pp->PVPBestKillStreak); - printf("PVPWorstDeathStreak: %u \n", pp->PVPWorstDeathStreak); - printf("PVPCurrentKillStreak: %u \n", pp->PVPCurrentKillStreak); - - // PVPStatsEntry_Struct PVPLastKill; - - printf("\n=== PVPStatsEntry_Struct PVPLastKill ===\n"); - printf("Char Name: %s Level: %u Race: %u Class: %u Zone: %u Time: %u Points: %u \n", pp->PVPLastKill.Name, pp->PVPLastKill.Level, pp->PVPLastKill.Race, pp->PVPLastKill.Class, pp->PVPLastKill.Zone, pp->PVPLastKill.Time, pp->PVPLastKill.Points); - printf("\n\n"); - - // /*8304*/ PVPStatsEntry_Struct PVPLastDeath; - - printf("\n=== PVPStatsEntry_Struct PVPLastDeath ===\n"); - printf("Char Name: %s Level: %u Race: %u Class: %u Zone: %u Time: %u Points: %u \n", pp->PVPLastDeath.Name, pp->PVPLastDeath.Level, pp->PVPLastDeath.Race, pp->PVPLastDeath.Class, pp->PVPLastDeath.Zone, pp->PVPLastDeath.Time, pp->PVPLastDeath.Points); - printf("\n\n"); - - printf("PVPNumberOfKillsInLast24Hours: %u \n", pp->PVPNumberOfKillsInLast24Hours); - - // PVPStatsEntry_Struct PVPRecentKills[50]; - - printf("\n===PVPStatsEntry_Struct PVPRecentKills[50] ===\n"); - for (i = 0; i < 50; i++){ - printf("ID: %u Char Name: %s Level: %u Race: %u Class: %u Zone: %u Time: %u Points: %u \n", i, pp->PVPRecentKills[i].Name, pp->PVPRecentKills[i].Level, pp->PVPRecentKills[i].Race, pp->PVPRecentKills[i].Class, pp->PVPRecentKills[i].Zone, pp->PVPRecentKills[i].Time, pp->PVPRecentKills[i].Points); - } - printf("\n\n"); - - printf("aapoints_spent: %u \n", pp->aapoints_spent); - printf("expAA: %u \n", pp->expAA); - printf("aapoints: %u \n", pp->aapoints); - printf("unknown12844[36]: %u \n", pp->unknown12844[36]); - - // Bandolier_Struct bandoliers[EmuConstants::BANDOLIERS_COUNT]; - - printf("\n=== Bandolier_Struct bandoliers[EmuConstants::BANDOLIERS_COUNT] ===\n"); - uint16 si = 0; - for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ - // BandolierItem_Struct items[EmuConstants::BANDOLIER_SIZE]; - for (si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ - printf("ID: %u item_id: %u icon: %u items name: %s name: %s\n", i, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].items[si].item_name, pp->bandoliers[i].name); - } - } - printf("\n\n"); - - printf("unknown14160[4506]: %u \n", pp->unknown14160[4506]); - - // SuspendedMinion_Struct SuspendedMinion; // No longer in use (In DB) - - // printf("\n=== SuspendedMinion_Struct SuspendedMinion ===\n"); - // printf("SpellID: %u HP: %u Mana: %u \n", i, pp->leader_abilities.ranks[i]); - - // printf("\n\n"); - - printf("timeentitledonaccount: %u \n", pp->timeentitledonaccount); - - // PotionBelt_Struct potionbelt; //there should be 3 more of these - - printf("\n=== PotionBelt_Struct potionbelt ===\n"); - for (i = 0; i < EmuConstants::POTION_BELT_SIZE; i++){ - printf("ID: %u Icon: %u Item ID: %u Item Name: %s\n", i, pp->potionbelt.items[i].icon, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].item_name); - } - printf("\n\n"); - - printf("unknown19568[8]: %u \n", pp->unknown19568[8]); - printf("currentRadCrystals: %u \n", pp->currentRadCrystals); - printf("careerRadCrystals: %u \n", pp->careerRadCrystals); - printf("currentEbonCrystals: %u \n", pp->currentEbonCrystals); - printf("careerEbonCrystals: %u \n", pp->careerEbonCrystals); - printf("groupAutoconsent: %u \n", pp->groupAutoconsent); - printf("raidAutoconsent: %u \n", pp->raidAutoconsent); - printf("guildAutoconsent: %u \n", pp->guildAutoconsent); - printf("unknown19595[5]: %u \n", pp->unknown19595[5]); - printf("RestTimer: %u \n", pp->RestTimer); - - - printf("\n"); - } - - mysql_free_result(result2); - } + /* Verify PP Integrity */ + lengths = results2.LengthOfColumn(1); + if (lengths == sizeof(PlayerProfile_Struct)) { /* If PP is the size it is expected to be */ + memcpy(pp, row2[1], sizeof(PlayerProfile_Struct)); + // std::cout << "SIZE OK\n" << std::endl; + } + /* Continue of PP Size does not match (Usually a created character never logged in) */ + else { continue; } + + lengths_e = results2.LengthOfColumn(11); + if (lengths_e == sizeof(ExtendedProfile_Struct)) { + memcpy(e_pp, row2[11], sizeof(ExtendedProfile_Struct)); + } + if (e_pp->expended_aa > 4000000){ e_pp->expended_aa = 0; } + + // std::cout << "Expended AA: " << e_pp->expended_aa << "\n" << std::endl; + + /* Loading Status on conversion */ + if (runconvert == 1){ + std::cout << "\r" << char_iter_count << "/" << number_of_characters << " " << std::flush; + loadbar(char_iter_count, number_of_characters, 50); + + /* Run inspect message convert */ + if (inspectmessage != ""){ + std::string rquery = StringFormat("REPLACE INTO `character_inspect_messages` (id, inspect_message)" + "VALUES (%u, '%s')", + character_id, + EscapeString(inspectmessage).c_str() + ); + auto results = QueryDatabase(rquery); + if (!results.RowsAffected()){ std::cout << "ERROR Inspect Messages Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + } + + /* Run Currency Convert */ + std::string rquery = StringFormat("REPLACE INTO `character_currency` (id, platinum, gold, silver, copper," + "platinum_bank, gold_bank, silver_bank, copper_bank," + "platinum_cursor, gold_cursor, silver_cursor, copper_cursor, " + "radiant_crystals, career_radiant_crystals, ebon_crystals, career_ebon_crystals)" + "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u)", + character_id, + pp->platinum, + pp->gold, + pp->silver, + pp->copper, + pp->platinum_bank, + pp->gold_bank, + pp->silver_bank, + pp->copper_bank, + pp->platinum_cursor, + pp->gold_cursor, + pp->silver_cursor, + pp->copper_cursor, + pp->currentRadCrystals, + pp->careerRadCrystals, + pp->currentEbonCrystals, + pp->careerEbonCrystals + ); + auto results = QueryDatabase(rquery); + + if (pp->tribute_time_remaining < 0 || pp->tribute_time_remaining == 4294967295){ pp->tribute_time_remaining = 0; } + + /* Run Character Data Convert */ + rquery = StringFormat( + "REPLACE INTO `character_data` (" + "id," + "account_id," + "`name`," + "last_name," + "gender," + "race," + "class," + "`level`," + "deity," + "birthday," + "last_login," + "time_played," + "pvp_status," + "level2," + "anon," + "gm," + "intoxication," + "hair_color," + "beard_color," + "eye_color_1," + "eye_color_2," + "hair_style," + "beard," + "ability_time_seconds," + "ability_number," + "ability_time_minutes," + "ability_time_hours," + "title," + "suffix," + "exp," + "points," + "mana," + "cur_hp," + "str," + "sta," + "cha," + "dex," + "`int`," + "agi," + "wis," + "face," + "y," + "x," + "z," + "heading," + "pvp2," + "pvp_type," + "autosplit_enabled," + "zone_change_count," + "drakkin_heritage," + "drakkin_tattoo," + "drakkin_details," + "toxicity," + "hunger_level," + "thirst_level," + "ability_up," + "zone_id," + "zone_instance," + "leadership_exp_on," + "ldon_points_guk," + "ldon_points_mir," + "ldon_points_mmc," + "ldon_points_ruj," + "ldon_points_tak," + "ldon_points_available," + "tribute_time_remaining," + "show_helm," + "career_tribute_points," + "tribute_points," + "tribute_active," + "endurance," + "group_leadership_exp," + "raid_leadership_exp," + "group_leadership_points," + "raid_leadership_points," + "air_remaining," + "pvp_kills," + "pvp_deaths," + "pvp_current_points," + "pvp_career_points," + "pvp_best_kill_streak," + "pvp_worst_death_streak," + "pvp_current_kill_streak," + "aa_points_spent," + "aa_exp," + "aa_points," + "group_auto_consent," + "raid_auto_consent," + "guild_auto_consent," + "RestTimer," + "firstlogon," + "lfg," + "lfp," + "mailkey," + "xtargets," + "e_aa_effects," + "e_percent_to_aa," + "e_expended_aa_spent" + ")" + "VALUES (" + "%u," // id + "%u," // account_id + "'%s'," // `name` + "'%s'," // last_name + "%u," // gender + "%u," // race + "%u," // class + "%u," // `level` + "%u," // deity + "%u," // birthday + "%u," // last_login + "%u," // time_played + "%u," // pvp_status + "%u," // level2 + "%u," // anon + "%u," // gm + "%u," // intoxication + "%u," // hair_color + "%u," // beard_color + "%u," // eye_color_1 + "%u," // eye_color_2 + "%u," // hair_style + "%u," // beard + "%u," // ability_time_seconds + "%u," // ability_number + "%u," // ability_time_minutes + "%u," // ability_time_hours + "'%s'," // title + "'%s'," // suffix + "%u," // exp + "%u," // points + "%u," // mana + "%u," // cur_hp + "%u," // str + "%u," // sta + "%u," // cha + "%u," // dex + "%u," // `int` + "%u," // agi + "%u," // wis + "%u," // face + "%f," // y + "%f," // x + "%f," // z + "%f," // heading + "%u," // pvp2 + "%u," // pvp_type + "%u," // autosplit_enabled + "%u," // zone_change_count + "%u," // drakkin_heritage + "%u," // drakkin_tattoo + "%u," // drakkin_details + "%i," // toxicity + "%u," // hunger_level + "%u," // thirst_level + "%u," // ability_up + "%u," // zone_id + "%u," // zone_instance + "%u," // leadership_exp_on + "%u," // ldon_points_guk + "%u," // ldon_points_mir + "%u," // ldon_points_mmc + "%u," // ldon_points_ruj + "%u," // ldon_points_tak + "%u," // ldon_points_available + "%u," // tribute_time_remaining + "%u," // show_helm + "%u," // career_tribute_points + "%u," // tribute_points + "%u," // tribute_active + "%u," // endurance + "%u," // group_leadership_exp + "%u," // raid_leadership_exp + "%u," // group_leadership_points + "%u," // raid_leadership_points + "%u," // air_remaining + "%u," // pvp_kills + "%u," // pvp_deaths + "%u," // pvp_current_points + "%u," // pvp_career_points + "%u," // pvp_best_kill_streak + "%u," // pvp_worst_death_streak + "%u," // pvp_current_kill_streak + "%u," // aa_points_spent + "%u," // aa_exp + "%u," // aa_points + "%u," // group_auto_consent + "%u," // raid_auto_consent + "%u," // guild_auto_consent + "%u," // RestTimer + "%u," // First Logon - References online status for EVENT_CONNECT/EVENT_DISCONNECt + "%u," // Looking for Group + "%u," // Looking for P? + "'%s'," // Mailkey + "%u," // X Targets + "%u," // AA Effects + "%u," // Percent to AA + "%u" // e_expended_aa_spent + ")", + character_id, + account_id, + EscapeString(pp->name).c_str(), + EscapeString(pp->last_name).c_str(), + pp->gender, + pp->race, + pp->class_, + pp->level, + pp->deity, + pp->birthday, + pp->lastlogin, + pp->timePlayedMin, + pp->pvp, + pp->level2, + pp->anon, + pp->gm, + pp->intoxication, + pp->haircolor, + pp->beardcolor, + pp->eyecolor1, + pp->eyecolor2, + pp->hairstyle, + pp->beard, + pp->ability_time_seconds, + pp->ability_number, + pp->ability_time_minutes, + pp->ability_time_hours, + EscapeString(pp->title).c_str(), + EscapeString(pp->suffix).c_str(), + pp->exp, + pp->points, + pp->mana, + pp->cur_hp, + pp->STR, + pp->STA, + pp->CHA, + pp->DEX, + pp->INT, + pp->AGI, + pp->WIS, + pp->face, + pp->y, + pp->x, + pp->z, + pp->heading, + pp->pvp2, + pp->pvptype, + pp->autosplit, + pp->zone_change_count, + pp->drakkin_heritage, + pp->drakkin_tattoo, + pp->drakkin_details, + pp->toxicity, + pp->hunger_level, + pp->thirst_level, + pp->ability_up, + pp->zone_id, + pp->zoneInstance, + pp->leadAAActive, + pp->ldon_points_guk, + pp->ldon_points_mir, + pp->ldon_points_mmc, + pp->ldon_points_ruj, + pp->ldon_points_tak, + pp->ldon_points_available, + pp->tribute_time_remaining, + pp->showhelm, + pp->career_tribute_points, + pp->tribute_points, + pp->tribute_active, + pp->endurance, + pp->group_leadership_exp, + pp->raid_leadership_exp, + pp->group_leadership_points, + pp->raid_leadership_points, + pp->air_remaining, + pp->PVPKills, + pp->PVPDeaths, + pp->PVPCurrentPoints, + pp->PVPCareerPoints, + pp->PVPBestKillStreak, + pp->PVPWorstDeathStreak, + pp->PVPCurrentKillStreak, + pp->aapoints_spent, + pp->expAA, + pp->aapoints, + pp->groupAutoconsent, + pp->raidAutoconsent, + pp->guildAutoconsent, + pp->RestTimer, + firstlogon, + lfg, + lfp, + mailkey.c_str(), + xtargets, + e_pp->aa_effects, + e_pp->perAA, + e_pp->expended_aa + ); + results = QueryDatabase(rquery); + if (!results.RowsAffected()){ std::cout << "ERROR PP Data Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + + /* + We set a first entry variable because we need the first initial piece of the query to be declared + This is to speed up the INSERTS and trim down the amount of individual sends during the process. + The speed difference is dramatic + */ + /* Run AA Convert */ + int first_entry = 0; rquery = ""; + for (i = 1; i < MAX_PP_AA_ARRAY; i++){ + if (pp->aa_array[i].AA > 0 && pp->aa_array[i].value > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" + " VALUES (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR AA Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + + /* Run Bind Home Convert */ + rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, 1)", + character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + + /* Run Bind Convert */ + rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, 0)", + character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + + /* Run Language Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_LANGUAGE; i++){ + if (pp->languages[i] > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, i, pp->languages[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->languages[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Language Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + /* Run Skill Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_SKILL; i++){ + if (pp->skills[i] > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, i, pp->skills[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->skills[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Skill Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + /* Run Spell Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_REF_SPELLBOOK; i++){ + if (pp->spell_book[i] > 0 && pp->spell_book[i] != 4294967295 && pp->spell_book[i] < 40000 && pp->spell_book[i] != 1){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->spell_book[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->spell_book[i]); + } + } + // std::cout << rquery << "\n"; + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + /* Run Max Memmed Spell Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_REF_MEMSPELL; i++){ + if (pp->mem_spells[i] > 0 && pp->mem_spells[i] != 65535 && pp->mem_spells[i] != 4294967295){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->mem_spells[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->mem_spells[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Memmed Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + /* Run Discipline Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_DISCIPLINES; i++){ + if (pp->disciplines.values[i] > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Discipline Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + /* Run Material Color Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < _MaterialCount; i++){ + if (pp->item_tint[i].color > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Color Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + /* Run Tribute Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ + if (pp->tributes[i].tribute > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Tribute Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + /* Run Bandolier Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ + for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ + if (pp->bandoliers[i].items[si].item_id > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); + } + } + } + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + /* Run Potion Belt Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i <= EmuConstants::POTION_BELT_SIZE; i++){ + if (pp->potionbelt.items[i].item_id > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); + + } + } + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Potion Belt Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + /* Run Leadership AA Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i <= MAX_LEADERSHIP_AA_ARRAY; i++){ + if (pp->leader_abilities.ranks[i] > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, rank) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Leadership AA Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } } - mysql_free_result(result); } if (runconvert == 1){ printf("\n\nCharacter blob conversion complete, continuing world bootup...\n"); } diff --git a/common/extprofile.h b/common/extprofile.h index 2e29fbf4a..a8447d4fc 100644 --- a/common/extprofile.h +++ b/common/extprofile.h @@ -21,7 +21,6 @@ #include "eq_packet_structs.h" #include "item.h" - #pragma pack(1) /* @@ -37,24 +36,24 @@ */ struct ExtendedProfile_Struct { // Pet stuff - uint16 pet_id; - uint16 old_pet_hp; - uint16 old_pet_mana; - SpellBuff_Struct pet_buffs[BUFF_COUNT]; - uint32 pet_items[_MaterialCount]; - char merc_name[64]; + uint16 pet_id; /* Not Used */ + uint16 old_pet_hp; /* Not Used */ + uint16 old_pet_mana; /* Not Used */ + SpellBuff_Struct pet_buffs[BUFF_COUNT]; /* Not Used */ + uint32 pet_items[_MaterialCount]; /* Not Used */ + char merc_name[64]; /* Used */ - uint32 aa_effects; - uint32 perAA; //% of exp going to AAs - uint32 expended_aa; // Total of expended AA - uint32 pet_hp; - uint32 pet_mana; - uint32 mercTemplateID; - uint32 mercSuspendedTime; - bool mercIsSuspended; - uint32 mercTimerRemaining; - uint8 mercGender; - int32 mercState; + uint32 aa_effects; /* Used */ + uint32 perAA; /* Used: % of exp going to AAs */ + uint32 expended_aa; /* Used: Total of expended AA */ + uint32 pet_hp; /* Not Used */ + uint32 pet_mana; /* Not Used */ + uint32 mercTemplateID; /* Not Used */ + uint32 mercSuspendedTime; /* Not Used */ + bool mercIsSuspended; /* Not Used */ + uint32 mercTimerRemaining; /* Not Used */ + uint8 mercGender; /* Not Used */ + int32 mercState; /* Not Used */ }; #pragma pack() diff --git a/common/shareddb.cpp b/common/shareddb.cpp index f6f960cc3..37bfe299c 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -114,31 +114,12 @@ bool SharedDatabase::SetGMSpeed(uint32 account_id, uint8 gmspeed) } uint32 SharedDatabase::GetTotalTimeEntitledOnAccount(uint32 AccountID) { - uint32 EntitledTime = 0; - - const char *EntitledQuery = "select sum(ascii(substring(profile, 237, 1)) + (ascii(substring(profile, 238, 1)) * 256) +" - "(ascii(substring(profile, 239, 1)) * 65536) + (ascii(substring(profile, 240, 1)) * 16777216))" - "from character_ where account_id = %i"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, EntitledQuery, AccountID), errbuf, &result)) { - - if (mysql_num_rows(result) == 1) { - - row = mysql_fetch_row(result); - - EntitledTime = atoi(row[0]); - } - - mysql_free_result(result); + std::string query = StringFormat("SELECT `time_played` FROM `character_data` WHERE `account_id` = %u", AccountID); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + EntitledTime += atoi(row[0]); } - - safe_delete_array(query); - return EntitledTime; } @@ -2003,38 +1984,19 @@ const LootDrop_Struct* SharedDatabase::GetLootDrop(uint32 lootdrop_id) { return nullptr; } -void SharedDatabase::GetPlayerInspectMessage(char* playername, InspectMessage_Struct* message) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT inspectmessage FROM character_ WHERE name='%s'", playername), errbuf, &result)) { - safe_delete_array(query); - - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - memcpy(message, row[0], sizeof(InspectMessage_Struct)); - } - - mysql_free_result(result); - } - else { - std::cerr << "Error in GetPlayerInspectMessage query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); +void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message) { + std::string query = StringFormat("SELECT `inspect_message` FROM `character_inspect_messages` WHERE `id` = %u LIMIT 1", character_id); + auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "SharedDatabase::LoadCharacterInspectMessage", query); + auto row = results.begin(); + memcpy(message, "", sizeof(InspectMessage_Struct)); + for (auto row = results.begin(); row != results.end(); ++row) { + memcpy(message, row[0], sizeof(InspectMessage_Struct)); } } -void SharedDatabase::SetPlayerInspectMessage(char* playername, const InspectMessage_Struct* message) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET inspectmessage='%s' WHERE name='%s'", message->text, playername), errbuf)) { - std::cerr << "Error in SetPlayerInspectMessage query '" << query << "' " << errbuf << std::endl; - } - - safe_delete_array(query); +void SharedDatabase::SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message) { + std::string query = StringFormat("REPLACE INTO `character_inspect_messages` (id, inspect_message) VALUES (%u, '%s')", character_id, EscapeString(message->text).c_str()); + auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "SharedDatabase::SaveCharacterInspectMessage", query); } void SharedDatabase::GetBotInspectMessage(uint32 botid, InspectMessage_Struct* message) { diff --git a/common/shareddb.h b/common/shareddb.h index 5401bfa0a..eeef6125a 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -42,8 +42,8 @@ public: bool SetHideMe(uint32 account_id, uint8 hideme); int32 DeleteStalePlayerCorpses(); int32 DeleteStalePlayerBackups(); - void GetPlayerInspectMessage(char* playername, InspectMessage_Struct* message); - void SetPlayerInspectMessage(char* playername, const InspectMessage_Struct* message); + void LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message); + void SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message); void GetBotInspectMessage(uint32 botid, InspectMessage_Struct* message); void SetBotInspectMessage(uint32 botid, const InspectMessage_Struct* message); bool GetCommandSettings(std::map &commands); diff --git a/zone/aa.cpp b/zone/aa.cpp index f546e59f0..ef4b07aee 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -18,6 +18,8 @@ Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net) // Test 1 +#include + #include "../common/debug.h" #include "aa.h" #include "mob.h" @@ -316,13 +318,14 @@ void Client::ActivateAA(aaID activate){ } } // Check if AA is expendable - if (aas_send[activate - activate_val]->special_category == 7) - { + if (aas_send[activate - activate_val]->special_category == 7) { + // Add the AA cost to the extended profile to track overall total m_epp.expended_aa += aas_send[activate]->cost; + SetAA(activate, 0); - Save(); + SaveAA(); /* Save Character AA */ SendAA(activate); SendAATable(); } @@ -1047,7 +1050,7 @@ void Client::BuyAA(AA_Action* action) mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level + 1); m_pp.aapoints -= real_cost; - + /* Do Player Profile rank calculations and set player profile */ SaveAA(); /* Save to Database to avoid having to write the whole AA array to the profile, only write changes*/ @@ -1533,6 +1536,8 @@ void Client::ResetAA(){ m_pp.raid_leadership_points = 0; m_pp.group_leadership_exp = 0; m_pp.raid_leadership_exp = 0; + + database.DeleteCharacterLeadershipAAs(this->CharacterID()); } int Client::GroupLeadershipAAHealthEnhancement() diff --git a/zone/client.cpp b/zone/client.cpp index 2a85bfab7..7c4bf39c0 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -503,7 +503,7 @@ bool Client::SaveAA(){ } } } - m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; + m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { if (aa[a]->AA > 0 && aa[a]->value){ if (first_entry != 1){ @@ -582,7 +582,7 @@ bool Client::Save(uint8 iCommitNow) { database.SaveCharacterTribute(this->CharacterID(), &m_pp); SaveTaskState(); /* Save Character Task */ - database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp); /* Save Character Data */ + database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp, &m_epp); /* Save Character Data */ LogFile->write(EQEMuLog::Status, "Client::Save %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; @@ -4233,7 +4233,6 @@ void Client::VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber) { } void Client::ClearGroupAAs() { - for(unsigned int i = 0; i < MAX_GROUP_LEADERSHIP_AA_ARRAY; i++) m_pp.leader_abilities.ranks[i] = 0; @@ -4243,28 +4242,18 @@ void Client::ClearGroupAAs() { m_pp.raid_leadership_exp = 0; Save(); + database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); } void Client::UpdateGroupAAs(int32 points, uint32 type) { - - switch(type) - { - case 0: - { - m_pp.group_leadership_points += points; - break; - } - case 1: - { - m_pp.raid_leadership_points += points; - break; - } + switch(type) { + case 0: { m_pp.group_leadership_points += points; break; } + case 1: { m_pp.raid_leadership_points += points; break; } } SendLeadershipEXPUpdate(); } -bool Client::IsLeadershipEXPOn() -{ +bool Client::IsLeadershipEXPOn() { if(!m_pp.leadAAActive) return false; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 3b34878d5..2a0ade9db 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -494,8 +494,7 @@ int Client::HandlePacket(const EQApplicationPacket *app) return(true); } -void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) -{ +void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { if(app->size != sizeof(ClientZoneEntry_Struct)) return; ClientZoneEntry_Struct *cze = (ClientZoneEntry_Struct *) app->pBuffer; @@ -543,7 +542,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) std::string query; unsigned long* lengths; - /* Set item materials */ + /* Set item material tint */ for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) if (m_pp.item_tint[i].rgb.use_tint == 1) m_pp.item_tint[i].rgb.use_tint = 0xFF; @@ -569,24 +568,24 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (account_creation){ account_creation = atoul(row[6]); } } - /* Load Character Legacy Data: Temp until I move */ - query = StringFormat("SELECT id,profile,zonename,x,y,z,guild_id,rank,extprofile,class,level,lfp,lfg,instanceid,xtargets,firstlogon FROM character_ LEFT JOIN guild_members ON id=char_id WHERE id=%i", cid); + /* Load Character Data */ + query = StringFormat("SELECT `lfp`, `lfg`, `xtargets`, `firstlogon`, `guild_id`, `rank` FROM `character_data` LEFT JOIN `guild_members` ON `id` = `char_id` WHERE `id` = %i", cid); results = database.QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { m_pp.lastlogin = time(nullptr); - if (row[6]){ - guild_id = atoi(row[6]); + if (row[4]){ + guild_id = atoi(row[4]); if (guildrank) { - if (row[7] != nullptr){ guildrank = atoi(row[7]); } + if (row[5] != nullptr){ guildrank = atoi(row[5]); } else{ guildrank = GUILD_RANK_NONE; } } } if (RuleB(Character, SharedBankPlat)) m_pp.platinum_shared = database.GetSharedPlatinum(database.GetAccountIDByChar(cid)); - if (LFP){ LFP = atoi(row[11]); } - if (LFG){ LFG = atoi(row[12]); } - if (firstlogon){ firstlogon = atoi(row[15]); } + if (LFP){ LFP = atoi(row[0]); } + if (LFG){ LFG = atoi(row[1]); } + if (firstlogon){ firstlogon = atoi(row[3]); } } loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ @@ -595,13 +594,14 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) database.LoadCharacterMaterialColor(cid, &m_pp); /* Load Character Material */ database.LoadCharacterPotions(cid, &m_pp); /* Load Character Potion Belt */ database.LoadCharacterCurrency(cid, &m_pp); /* Load Character Currency into PP */ - database.LoadCharacterData(cid, &m_pp); /* Load Character Data from DB into PP */ + database.LoadCharacterData(cid, &m_pp, &m_epp); /* Load Character Data from DB into PP as well as E_PP */ database.LoadCharacterSkills(cid, &m_pp); /* Load Character Skills */ - database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); /* Move to another method when can, this is pointless... */ + database.LoadCharacterInspectMessage(cid, &m_inspect_message); /* Load Character Inspect Message */ database.LoadCharacterSpellBook(cid, &m_pp); /* Load Character Spell Book */ database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */ database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */ database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ + database.LoadCharacterLeadershipAA(cid, &m_pp); /* Load Character Leadership AA's */ if (level){ level = m_pp.level; } @@ -619,7 +619,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_pp.intoxication = 0; strcpy(name, m_pp.name); strcpy(lastname, m_pp.last_name); - /* If PP is set to wierd coordinates */ + /* If PP is set to weird coordinates */ if ((m_pp.x == -1 && m_pp.y == -1 && m_pp.z == -1) || (m_pp.x == -2 && m_pp.y == -2 && m_pp.z == -2)) { m_pp.x = zone->safe_x(); m_pp.y = zone->safe_y(); @@ -900,8 +900,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (zone->IsPVPZone()) m_pp.pvp = 1; /* Time entitled on Account: Move to account */ - m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; - + m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; /* Reset rest timer if the durations have been lowered in the database */ if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) m_pp.RestTimer = 0; @@ -926,8 +925,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) in hopes that it adds more consistency... Remake pet */ - if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) - { + if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) { MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size); if (GetPet() && GetPet()->IsNPC()) { NPC *pet = GetPet()->CastToNPC(); @@ -1022,8 +1020,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) QueuePacket(outapp); safe_delete(outapp); - SetAttackTimer(); - + SetAttackTimer(); conn_state = ZoneInfoSent; return; @@ -7312,7 +7309,7 @@ void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) { InspectMessage_Struct* newmessage = (InspectMessage_Struct*) insr->text; InspectMessage_Struct& playermessage = this->GetInspectMessage(); memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); - database.SetPlayerInspectMessage(name, &playermessage); + database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); if(tmp != 0 && tmp->IsClient()) { tmp->CastToClient()->QueuePacket(outapp); } // Send answer to requester @@ -7329,7 +7326,7 @@ void Client::Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app) { InspectMessage_Struct* newmessage = (InspectMessage_Struct*) app->pBuffer; InspectMessage_Struct& playermessage = this->GetInspectMessage(); memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); - database.SetPlayerInspectMessage(name, &playermessage); + database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); } #if 0 // I dont think there's an op for this now, and we check this @@ -9559,6 +9556,8 @@ void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) { //sell them the ability. m_pp.group_leadership_points -= cost; m_pp.leader_abilities.ranks[aaid]++; + + database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); } //success, send them an update diff --git a/zone/zone.cpp b/zone/zone.cpp index 9ae17381d..dc8d8600c 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -103,7 +103,7 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { } zone->zonemap = Map::LoadMapFile(zone->map_name); zone->watermap = WaterMap::LoadWaterMapfile(zone->map_name); - zone->pathing = PathManager::LoadPathFile(zone->map_name); + zone->pathing = PathManager::LoadPathFile(zone->map_name); char tmp[10]; if (database.GetVariable("loglevel",tmp, 9)) { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index ebf3957b6..ef14a3657 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -821,7 +821,7 @@ void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) #define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1)) -bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp){ +bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ std::string query = StringFormat( "SELECT " "`name`, " @@ -911,100 +911,106 @@ bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* "group_auto_consent, " "raid_auto_consent, " "guild_auto_consent, " - "RestTimer " + "RestTimer, " + "`e_aa_effects`, " + "`e_percent_to_aa`, " + "`e_expended_aa_spent` " "FROM " "character_data " "WHERE `id` = %i ", character_id); auto results = database.QueryDatabase(query); int r = 0; for (auto row = results.begin(); row != results.end(); ++row) { - strcpy(pp->name, row[r]); r++; - strcpy(pp->last_name, row[r]); r++; - pp->gender = atoi(row[r]); r++; - pp->race = atoi(row[r]); r++; - pp->class_ = atoi(row[r]); r++; - pp->level = atoi(row[r]); r++; - pp->deity = atoi(row[r]); r++; - pp->birthday = atoi(row[r]); r++; - pp->lastlogin = atoi(row[r]); r++; - pp->timePlayedMin = atoi(row[r]); r++; - pp->pvp = atoi(row[r]); r++; - pp->level2 = atoi(row[r]); r++; - pp->anon = atoi(row[r]); r++; - pp->gm = atoi(row[r]); r++; - pp->intoxication = atoi(row[r]); r++; - pp->haircolor = atoi(row[r]); r++; - pp->beardcolor = atoi(row[r]); r++; - pp->eyecolor1 = atoi(row[r]); r++; - pp->eyecolor2 = atoi(row[r]); r++; - pp->hairstyle = atoi(row[r]); r++; - pp->beard = atoi(row[r]); r++; - pp->ability_time_seconds = atoi(row[r]); r++; - pp->ability_number = atoi(row[r]); r++; - pp->ability_time_minutes = atoi(row[r]); r++; - pp->ability_time_hours = atoi(row[r]); r++; - strcpy(pp->title, row[r]); r++; - strcpy(pp->suffix, row[r]); r++; - pp->exp = atoi(row[r]); r++; - pp->points = atoi(row[r]); r++; - pp->mana = atoi(row[r]); r++; - pp->cur_hp = atoi(row[r]); r++; - pp->STR = atoi(row[r]); r++; - pp->STA = atoi(row[r]); r++; - pp->CHA = atoi(row[r]); r++; - pp->DEX = atoi(row[r]); r++; - pp->INT = atoi(row[r]); r++; - pp->AGI = atoi(row[r]); r++; - pp->WIS = atoi(row[r]); r++; - pp->face = atoi(row[r]); r++; - pp->y = atof(row[r]); r++; - pp->x = atof(row[r]); r++; - pp->z = atof(row[r]); r++; - pp->heading = atof(row[r]); r++; - pp->pvp2 = atoi(row[r]); r++; - pp->pvptype = atoi(row[r]); r++; - pp->autosplit = atoi(row[r]); r++; - pp->zone_change_count = atoi(row[r]); r++; - pp->drakkin_heritage = atoi(row[r]); r++; - pp->drakkin_tattoo = atoi(row[r]); r++; - pp->drakkin_details = atoi(row[r]); r++; - pp->toxicity = atoi(row[r]); r++; - pp->hunger_level = atoi(row[r]); r++; - pp->thirst_level = atoi(row[r]); r++; - pp->ability_up = atoi(row[r]); r++; - pp->zone_id = atoi(row[r]); r++; - pp->zoneInstance = atoi(row[r]); r++; - pp->leadAAActive = atoi(row[r]); r++; - pp->ldon_points_guk = atoi(row[r]); r++; - pp->ldon_points_mir = atoi(row[r]); r++; - pp->ldon_points_mmc = atoi(row[r]); r++; - pp->ldon_points_ruj = atoi(row[r]); r++; - pp->ldon_points_tak = atoi(row[r]); r++; - pp->ldon_points_available = atoi(row[r]); r++; - pp->tribute_time_remaining = atoi(row[r]); r++; - pp->showhelm = atoi(row[r]); r++; - pp->career_tribute_points = atoi(row[r]); r++; - pp->tribute_points = atoi(row[r]); r++; - pp->tribute_active = atoi(row[r]); r++; - pp->endurance = atoi(row[r]); r++; - pp->group_leadership_exp = atoi(row[r]); r++; - pp->raid_leadership_exp = atoi(row[r]); r++; - pp->group_leadership_points = atoi(row[r]); r++; - pp->raid_leadership_points = atoi(row[r]); r++; - pp->air_remaining = atoi(row[r]); r++; - pp->PVPKills = atoi(row[r]); r++; - pp->PVPDeaths = atoi(row[r]); r++; - pp->PVPCurrentPoints = atoi(row[r]); r++; - pp->PVPCareerPoints = atoi(row[r]); r++; - pp->PVPBestKillStreak = atoi(row[r]); r++; - pp->PVPWorstDeathStreak = atoi(row[r]); r++; - pp->PVPCurrentKillStreak = atoi(row[r]); r++; - pp->aapoints_spent = atoi(row[r]); r++; - pp->expAA = atoi(row[r]); r++; - pp->aapoints = atoi(row[r]); r++; - pp->groupAutoconsent = atoi(row[r]); r++; - pp->raidAutoconsent = atoi(row[r]); r++; - pp->guildAutoconsent = atoi(row[r]); r++; - pp->RestTimer = atoi(row[r]); r++; + strcpy(pp->name, row[r]); r++; // "`name`, " + strcpy(pp->last_name, row[r]); r++; // "last_name, " + pp->gender = atoi(row[r]); r++; // "gender, " + pp->race = atoi(row[r]); r++; // "race, " + pp->class_ = atoi(row[r]); r++; // "class, " + pp->level = atoi(row[r]); r++; // "`level`, " + pp->deity = atoi(row[r]); r++; // "deity, " + pp->birthday = atoi(row[r]); r++; // "birthday, " + pp->lastlogin = atoi(row[r]); r++; // "last_login, " + pp->timePlayedMin = atoi(row[r]); r++; // "time_played, " + pp->pvp = atoi(row[r]); r++; // "pvp_status, " + pp->level2 = atoi(row[r]); r++; // "level2, " + pp->anon = atoi(row[r]); r++; // "anon, " + pp->gm = atoi(row[r]); r++; // "gm, " + pp->intoxication = atoi(row[r]); r++; // "intoxication, " + pp->haircolor = atoi(row[r]); r++; // "hair_color, " + pp->beardcolor = atoi(row[r]); r++; // "beard_color, " + pp->eyecolor1 = atoi(row[r]); r++; // "eye_color_1, " + pp->eyecolor2 = atoi(row[r]); r++; // "eye_color_2, " + pp->hairstyle = atoi(row[r]); r++; // "hair_style, " + pp->beard = atoi(row[r]); r++; // "beard, " + pp->ability_time_seconds = atoi(row[r]); r++; // "ability_time_seconds, " + pp->ability_number = atoi(row[r]); r++; // "ability_number, " + pp->ability_time_minutes = atoi(row[r]); r++; // "ability_time_minutes, " + pp->ability_time_hours = atoi(row[r]); r++; // "ability_time_hours, " + strcpy(pp->title, row[r]); r++; // "title, " + strcpy(pp->suffix, row[r]); r++; // "suffix, " + pp->exp = atoi(row[r]); r++; // "exp, " + pp->points = atoi(row[r]); r++; // "points, " + pp->mana = atoi(row[r]); r++; // "mana, " + pp->cur_hp = atoi(row[r]); r++; // "cur_hp, " + pp->STR = atoi(row[r]); r++; // "str, " + pp->STA = atoi(row[r]); r++; // "sta, " + pp->CHA = atoi(row[r]); r++; // "cha, " + pp->DEX = atoi(row[r]); r++; // "dex, " + pp->INT = atoi(row[r]); r++; // "`int`, " + pp->AGI = atoi(row[r]); r++; // "agi, " + pp->WIS = atoi(row[r]); r++; // "wis, " + pp->face = atoi(row[r]); r++; // "face, " + pp->y = atof(row[r]); r++; // "y, " + pp->x = atof(row[r]); r++; // "x, " + pp->z = atof(row[r]); r++; // "z, " + pp->heading = atof(row[r]); r++; // "heading, " + pp->pvp2 = atoi(row[r]); r++; // "pvp2, " + pp->pvptype = atoi(row[r]); r++; // "pvp_type, " + pp->autosplit = atoi(row[r]); r++; // "autosplit_enabled, " + pp->zone_change_count = atoi(row[r]); r++; // "zone_change_count, " + pp->drakkin_heritage = atoi(row[r]); r++; // "drakkin_heritage, " + pp->drakkin_tattoo = atoi(row[r]); r++; // "drakkin_tattoo, " + pp->drakkin_details = atoi(row[r]); r++; // "drakkin_details, " + pp->toxicity = atoi(row[r]); r++; // "toxicity, " + pp->hunger_level = atoi(row[r]); r++; // "hunger_level, " + pp->thirst_level = atoi(row[r]); r++; // "thirst_level, " + pp->ability_up = atoi(row[r]); r++; // "ability_up, " + pp->zone_id = atoi(row[r]); r++; // "zone_id, " + pp->zoneInstance = atoi(row[r]); r++; // "zone_instance, " + pp->leadAAActive = atoi(row[r]); r++; // "leadership_exp_on, " + pp->ldon_points_guk = atoi(row[r]); r++; // "ldon_points_guk, " + pp->ldon_points_mir = atoi(row[r]); r++; // "ldon_points_mir, " + pp->ldon_points_mmc = atoi(row[r]); r++; // "ldon_points_mmc, " + pp->ldon_points_ruj = atoi(row[r]); r++; // "ldon_points_ruj, " + pp->ldon_points_tak = atoi(row[r]); r++; // "ldon_points_tak, " + pp->ldon_points_available = atoi(row[r]); r++; // "ldon_points_available, " + pp->tribute_time_remaining = atoi(row[r]); r++; // "tribute_time_remaining, " + pp->showhelm = atoi(row[r]); r++; // "show_helm, " + pp->career_tribute_points = atoi(row[r]); r++; // "career_tribute_points, " + pp->tribute_points = atoi(row[r]); r++; // "tribute_points, " + pp->tribute_active = atoi(row[r]); r++; // "tribute_active, " + pp->endurance = atoi(row[r]); r++; // "endurance, " + pp->group_leadership_exp = atoi(row[r]); r++; // "group_leadership_exp, " + pp->raid_leadership_exp = atoi(row[r]); r++; // "raid_leadership_exp, " + pp->group_leadership_points = atoi(row[r]); r++; // "group_leadership_points, " + pp->raid_leadership_points = atoi(row[r]); r++; // "raid_leadership_points, " + pp->air_remaining = atoi(row[r]); r++; // "air_remaining, " + pp->PVPKills = atoi(row[r]); r++; // "pvp_kills, " + pp->PVPDeaths = atoi(row[r]); r++; // "pvp_deaths, " + pp->PVPCurrentPoints = atoi(row[r]); r++; // "pvp_current_points, " + pp->PVPCareerPoints = atoi(row[r]); r++; // "pvp_career_points, " + pp->PVPBestKillStreak = atoi(row[r]); r++; // "pvp_best_kill_streak, " + pp->PVPWorstDeathStreak = atoi(row[r]); r++; // "pvp_worst_death_streak, " + pp->PVPCurrentKillStreak = atoi(row[r]); r++; // "pvp_current_kill_streak, " + pp->aapoints_spent = atoi(row[r]); r++; // "aa_points_spent, " + pp->expAA = atoi(row[r]); r++; // "aa_exp, " + pp->aapoints = atoi(row[r]); r++; // "aa_points, " + pp->groupAutoconsent = atoi(row[r]); r++; // "group_auto_consent, " + pp->raidAutoconsent = atoi(row[r]); r++; // "raid_auto_consent, " + pp->guildAutoconsent = atoi(row[r]); r++; // "guild_auto_consent, " + pp->RestTimer = atoi(row[r]); r++; // "RestTimer, " + m_epp->aa_effects = atoi(row[r]); r++; // "`e_aa_effects`, " + m_epp->perAA = atoi(row[r]); r++; // "`e_percent_to_aa`, " + m_epp->expended_aa = atoi(row[r]); r++; // "`e_expended_aa_spent` " LogFile->write(EQEMuLog::Status, "Loading Character Data for character ID: %i, done", character_id); } return true; @@ -1060,6 +1066,16 @@ bool ZoneDatabase::LoadCharacterLanguages(uint32 character_id, PlayerProfile_Str return true; } +bool ZoneDatabase::LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT slot, rank FROM character_leadership_abilities WHERE `id` = %u", character_id); + auto results = database.QueryDatabase(query); uint32 slot = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + slot = atoi(row[0]); + pp->leader_abilities.ranks[slot] = atoi(row[1]); + } + return true; +} + bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat( "SELECT " @@ -1288,7 +1304,23 @@ bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, return true; } -bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp){ +bool ZoneDatabase::SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp){ + uint8 first_entry = 0; std::string query = ""; + for (int i = 0; i <= MAX_LEADERSHIP_AA_ARRAY; i++){ + if (pp->leader_abilities.ranks[i] > 0){ + if (first_entry != 1){ + query = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, rank) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + first_entry = 1; + } + query = query + StringFormat(", (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + } + } + auto results = QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterLeadershipAA", query); + return true; +} + +bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ clock_t t = std::clock(); /* Function timer start */ std::string query = StringFormat( "REPLACE INTO `character_data` (" @@ -1381,7 +1413,11 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla " group_auto_consent, " " raid_auto_consent, " " guild_auto_consent, " - " RestTimer) " + " RestTimer, " + " e_aa_effects, " + " e_percent_to_aa, " + " e_expended_aa_spent " + ") " "VALUES (" "%u," // id " id, " "%u," // account_id " account_id, " @@ -1472,7 +1508,10 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla "%u," // group_auto_consent pp->groupAutoconsent, " group_auto_consent, " "%u," // raid_auto_consent pp->raidAutoconsent, " raid_auto_consent, " "%u," // guild_auto_consent pp->guildAutoconsent, " guild_auto_consent, " - "%u" // RestTimer pp->RestTimer, " RestTimer) " + "%u," // RestTimer pp->RestTimer, " RestTimer) " + "%u," // e_aa_effects + "%u," // e_percent_to_aa + "%u" // e_expended_aa_spent ")", character_id, // " id, " account_id, // " account_id, " @@ -1563,7 +1602,10 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla pp->groupAutoconsent, // " group_auto_consent, " pp->raidAutoconsent, // " raid_auto_consent, " pp->guildAutoconsent, // " guild_auto_consent, " - pp->RestTimer // " RestTimer) " + pp->RestTimer, // " RestTimer) " + m_epp->aa_effects, + m_epp->perAA, + m_epp->expended_aa ); auto results = database.QueryDatabase(query); if (!results.RowsAffected()){ std::cout << "ERROR ZoneDatabase:SaveCharacterData: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } @@ -1650,7 +1692,11 @@ bool ZoneDatabase::DeleteCharacterBandolier(uint32 character_id, uint32 band_id) std::string query = StringFormat("DELETE FROM `character_bandolier` WHERE `bandolier_id` = %u AND `id` = %u", band_id, character_id); QueryDatabase(query); return true; } -bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ +bool ZoneDatabase::DeleteCharacterLeadershipAAs(uint32 character_id){ + std::string query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", character_id); QueryDatabase(query); return true; +} + +bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ std::string query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); QueryDatabase(query); return true; } diff --git a/zone/zonedb.h b/zone/zonedb.h index 3409537ec..0405fb3a9 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -255,18 +255,19 @@ public: bool LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); /* Character Data Saves */ bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home); bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); - bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp); + bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); bool SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot); bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); @@ -278,12 +279,14 @@ public: bool SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name); bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon); + bool SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); /* Character Data Deletes */ bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool DeleteCharacterDisc(uint32 character_id, uint32 slot_id); bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id); + bool DeleteCharacterLeadershipAAs(uint32 character_id); /* Character Inventory */ bool NoRentExpired(const char* name); From 4c12d31e4a256c6aeb7610f23211440bffa3d5c8 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 6 Sep 2014 22:35:19 -0500 Subject: [PATCH 088/368] Removed command character backup Changed all remaining references from the character_ table to the character_data --- common/database.cpp | 2 +- common/guild_base.cpp | 6 +-- common/shareddb.cpp | 4 +- ucs/database.cpp | 8 ++-- world/adventure_manager.cpp | 2 +- world/worlddb.cpp | 49 +++++----------------- world/zoneserver.cpp | 2 +- zone/client_packet.cpp | 2 +- zone/command.cpp | 84 +------------------------------------ zone/command.h | 1 - zone/guild_mgr.cpp | 53 ----------------------- zone/zonedb.cpp | 2 +- 12 files changed, 26 insertions(+), 189 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 7c8c988cf..0fb0cf846 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -728,7 +728,7 @@ uint32 Database::GetAccountIDByChar(const char* charname) { // Retrieve account_id for a given char_id uint32 Database::GetAccountIDByChar(uint32 char_id) { - std::string query = StringFormat("SELECT `account_id` FROM `character_` WHERE `id` = %i LIMIT 1", char_id); + std::string query = StringFormat("SELECT `account_id` FROM `character_data` WHERE `id` = %i LIMIT 1", char_id); auto results = QueryDatabase(query); if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in GetAccountIDByChar query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); diff --git a/common/guild_base.cpp b/common/guild_base.cpp index 99e54934e..78fee70f9 100644 --- a/common/guild_base.cpp +++ b/common/guild_base.cpp @@ -892,10 +892,10 @@ bool BaseGuildManager::QueryWithLogging(std::string query, const char *errmsg) { " FROM vwBotCharacterMobs AS c LEFT JOIN vwGuildMembers AS g ON c.id=g.char_id AND c.mobtype = g.mobtype " #else #define GuildMemberBaseQuery \ -"SELECT c.id,c.name,c.class,c.level,c.timelaston,c.zoneid," \ +"SELECT c.id,c.name,c.class,c.level,c.last_login,c.zone_id," \ " g.guild_id,g.rank,g.tribute_enable,g.total_tribute,g.last_tribute," \ " g.banker,g.public_note,g.alt " \ -" FROM character_ AS c LEFT JOIN guild_members AS g ON c.id=g.char_id " +" FROM `character_data` AS c LEFT JOIN guild_members AS g ON c.id=g.char_id " #endif static void ProcessGuildMember(MySQLRequestRow row, CharGuildInfo &into) { //fields from `characer_` @@ -1236,7 +1236,7 @@ BaseGuildManager::GuildInfo::GuildInfo() { uint32 BaseGuildManager::DoesAccountContainAGuildLeader(uint32 AccountID) { std::string query = StringFormat("SELECT guild_id FROM guild_members WHERE char_id IN " - "(SELECT id FROM character_ WHERE account_id = %i) AND rank = 2", + "(SELECT id FROM `character_data` WHERE account_id = %i) AND rank = 2", AccountID); auto results = m_db->QueryDatabase(query); if (!results.Success()) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 37bfe299c..d3bcf27f8 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -384,7 +384,7 @@ bool SharedDatabase::GetSharedBank(uint32 id, Inventory* inv, bool is_charid) { if (is_charid) { len_query = MakeAnyLenString(&query, "SELECT sb.slotid,sb.itemid,sb.charges,sb.augslot1,sb.augslot2,sb.augslot3,sb.augslot4,sb.augslot5,sb.custom_data from sharedbank sb " - "INNER JOIN character_ ch ON ch.account_id=sb.acctid " + "INNER JOIN character_data ch ON ch.account_id=sb.acctid " "WHERE ch.id=%i", id); } else { @@ -604,7 +604,7 @@ bool SharedDatabase::GetInventory(uint32 account_id, char* name, Inventory* inv) if (RunQuery(query, MakeAnyLenString(&query, " SELECT `slotid`, `itemid`, `charges`, `color`, `augslot1`, `augslot2`, `augslot3`, `augslot4`, `augslot5`, `instnodrop`, `custom_data`" " FROM `inventory`" - " INNER JOIN character_ ch ON ch.id = charid" + " INNER JOIN `character_data` ch ON ch.id = charid" " WHERE ch.NAME = '%s'" " AND ch.account_id = % i" " ORDER BY `slotid`", diff --git a/ucs/database.cpp b/ucs/database.cpp index d3c44174c..38f3e06f7 100644 --- a/ucs/database.cpp +++ b/ucs/database.cpp @@ -139,7 +139,7 @@ int Database::FindAccount(const char *characterName, Client *client) { client->ClearCharacters(); std::string query = StringFormat("SELECT `id`, `account_id`, `level` " - "FROM `character_` WHERE `name` = '%s' LIMIT 1", + "FROM `character_data` WHERE `name` = '%s' LIMIT 1", characterName); auto results = QueryDatabase(query); if (!results.Success()) { @@ -159,7 +159,7 @@ int Database::FindAccount(const char *characterName, Client *client) { _log(UCS__TRACE, "Account ID for %s is %i", characterName, accountID); - query = StringFormat("SELECT `id`, `name`, `level` FROM `character_` " + query = StringFormat("SELECT `id`, `name`, `level` FROM `character_data` " "WHERE `account_id` = %i AND `name` != '%s'", accountID, characterName); results = QueryDatabase(query); @@ -174,7 +174,7 @@ int Database::FindAccount(const char *characterName, Client *client) { bool Database::VerifyMailKey(std::string characterName, int IPAddress, std::string MailKey) { - std::string query = StringFormat("SELECT `mailkey` FROM `character_` WHERE `name`='%s' LIMIT 1", + std::string query = StringFormat("SELECT `mailkey` FROM `character_data` WHERE `name`='%s' LIMIT 1", characterName.c_str()); auto results = QueryDatabase(query); if (!results.Success()) { @@ -202,7 +202,7 @@ bool Database::VerifyMailKey(std::string characterName, int IPAddress, std::stri int Database::FindCharacter(const char *characterName) { char *safeCharName = RemoveApostrophes(characterName); - std::string query = StringFormat("SELECT `id` FROM `character_` WHERE `name`='%s' LIMIT 1", safeCharName); + std::string query = StringFormat("SELECT `id` FROM `character_data` WHERE `name`='%s' LIMIT 1", safeCharName); auto results = QueryDatabase(query); if (!results.Success()) { _log(UCS__ERROR, "FindCharacter failed. %s %s", query.c_str(), results.ErrorMessage().c_str()); diff --git a/world/adventure_manager.cpp b/world/adventure_manager.cpp index 047aca326..4df67ef69 100644 --- a/world/adventure_manager.cpp +++ b/world/adventure_manager.cpp @@ -1069,7 +1069,7 @@ void AdventureManager::LoadLeaderboardInfo() leaderboard_info_percentage_tak.clear(); std::string query = "SELECT ch.name, ch.id, adv_stats.* FROM adventure_stats " - "AS adv_stats LEFT JOIN character_ AS ch ON adv_stats.player_id = ch.id;"; + "AS adv_stats LEFT JOIN `character_data` AS ch ON adv_stats.player_id = ch.id;"; auto results = database.QueryDatabase(query); if(!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::GetLeaderboardInfo: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); diff --git a/world/worlddb.cpp b/world/worlddb.cpp index fdbcc704d..4192e0d84 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -182,45 +182,16 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* } int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum) { - // if an invalid bind point is specified, use the primary bind - if (bindnum > 4) - bindnum = 0; + /* if an invalid bind point is specified, use the primary bind */ + if (bindnum > 4){ bindnum = 0; } + int is_home = 0; + if (bindnum == 4){ is_home = 1; } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 affected_rows = 0; - PlayerProfile_Struct pp; - - bool PPValid = false; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT profile from character_ where id='%i'", CharID), errbuf, &result)) { - row = mysql_fetch_row(result); - unsigned long* lengths = mysql_fetch_lengths(result); - if (lengths[0] == sizeof(PlayerProfile_Struct)) { - memcpy(&pp, row[0], sizeof(PlayerProfile_Struct)); - PPValid = true; - } - mysql_free_result(result); + std::string query = StringFormat("SELECT `zone_id` FROM `character_bind` WHERE `id` = %u AND `is_home` = %u LIMIT 1", CharID, is_home); + auto results = database.QueryDatabase(query); int i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + return atoi(row[0]); } - safe_delete_array(query); - - if(!PPValid) return 0; - - const char *BindZoneName = StaticGetZoneName(pp.binds[bindnum].zoneId); - - if(!strcmp(BindZoneName, "UNKNWN")) return pp.zone_id; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET zonename = '%s',zoneid=%i,x=%f, y=%f, z=%f, instanceid=0 WHERE id='%i'", - BindZoneName, pp.binds[bindnum].zoneId, pp.binds[bindnum].x, pp.binds[bindnum].y, pp.binds[bindnum].z, - CharID), errbuf, 0,&affected_rows)) { - - return pp.zone_id; - } - safe_delete_array(query); - - return pp.binds[bindnum].zoneId; } bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc) @@ -481,7 +452,7 @@ void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) { else sprintf(MailKeyString, "%08X", MailKey); - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET mailkey = '%s' WHERE id='%i'", + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE `character_data` SET mailkey = '%s' WHERE id='%i'", MailKeyString, CharID), errbuf)) LogFile->write(EQEMuLog::Error, "WorldDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, errbuf); @@ -497,7 +468,7 @@ bool WorldDatabase::GetCharacterLevel(const char *name, int &level) MYSQL_RES *result; MYSQL_ROW row; - if(RunQuery(query, MakeAnyLenString(&query, "SELECT level FROM character_ WHERE name='%s'", name), errbuf, &result)) + if(RunQuery(query, MakeAnyLenString(&query, "SELECT `level` FROM `character_data` WHERE `name` = '%s'", name), errbuf, &result)) { if(row = mysql_fetch_row(result)) { diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 6529a1f3d..4796104a7 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -454,7 +454,7 @@ bool ZoneServer::Process() { timeinfo = localtime ( &rawtime ); char *telldate=asctime(timeinfo); - std::string query = StringFormat("SELECT name FROM character_ WHERE name = '%s'",scm->deliverto); + std::string query = StringFormat("SELECT name FROM `character_data` WHERE name = '%s'",scm->deliverto); auto results = database.QueryDatabase(query); if (!results.Success()) break; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2a0ade9db..0e8ae509f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4750,7 +4750,7 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) } #ifdef BOTS else if (invitee->IsBot()) { - // The guild system is too tightly coupled with the character_ table so we have to avoid using much of the system + // The guild system is too tightly coupled with the character_data table so we have to avoid using much of the system Bot::ProcessGuildInvite(this, invitee->CastToBot()); return; } diff --git a/zone/command.cpp b/zone/command.cpp index ea85fb9af..e5ec3f074 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -257,12 +257,11 @@ int command_init(void) { command_add("dbspawn",nullptr,0,command_npctypespawn) || command_add("heal","- Completely heal your target",10,command_heal) || command_add("appearance","[type] [value] - Send an appearance packet for you or your target",150,command_appearance) || - command_add("charbackup","[list/restore] - Query or restore character backups",150,command_charbackup) || command_add("nukeitem","[itemid] - Remove itemid from your player target's inventory",150,command_nukeitem) || command_add("peekinv","[worn/cursor/inv/bank/trade/trib/all] - Print out contents of your player target's inventory",100,command_peekinv) || command_add("findnpctype","[search criteria] - Search database NPC types",100,command_findnpctype) || command_add("findzone","[search criteria] - Search database zones",100,command_findzone) || - command_add("fz",nullptr,100,command_findzone) || + command_add("fz",nullptr,100, command_findzone) || command_add("viewnpctype","[npctype id] - Show info about an npctype",100,command_viewnpctype) || command_add("reloadstatic","- Reload Static Zone Data",150,command_reloadstatic) || command_add("reloadquest"," - Clear quest cache (any argument causes it to also stop all timers)",150,command_reloadqst) || @@ -2767,85 +2766,6 @@ void command_appearance(Client *c, const Seperator *sep) } } -void command_charbackup(Client *c, const Seperator *sep) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - if (strcasecmp(sep->arg[1], "list") == 0) { - uint32 charid = 0; - if (sep->IsNumber(2)) - charid = atoi(sep->arg[2]); - else - database.GetAccountIDByChar(sep->arg[2]); - if (charid) { - if (database.RunQuery(query, MakeAnyLenString(&query, - "Select id, backupreason, charid, account_id, zoneid, DATE_FORMAT(ts, '%%m/%%d/%%Y %%H:%%i:%%s') " - " from character_backup where charid=%u", charid), errbuf, &result)) { - safe_delete(query); - uint32 x = 0; - while ((row = mysql_fetch_row(result))) { - c->Message(0, " %u: %s, %s (%u), reason=%u", atoi(row[0]), row[5], database.GetZoneName(atoi(row[4])), atoi(row[4]), atoi(row[1])); - x++; - } - c->Message(0, " %u backups found.", x); - mysql_free_result(result); - } - else { - c->Message(13, "Query error: '%s' %s", query, errbuf); - safe_delete(query); - } - } - else - c->Message(0, "Usage: #charbackup list [char name/id]"); - } - else if (strcasecmp(sep->arg[1], "restore") == 0) { - uint32 charid = 0; - if (sep->IsNumber(2)) - charid = atoi(sep->arg[2]); - else - database.GetAccountIDByChar(sep->arg[2]); - - if (charid && sep->IsNumber(3)) { - uint32 cbid = atoi(sep->arg[3]); - if (database.RunQuery(query, MakeAnyLenString(&query, - "Insert into character_backup (backupreason, charid, account_id, name, profile, level, class, x, y, z, zoneid, alt_adv) " - " select 1, id, account_id, name, profile, level, class, x, y, z, zoneid, alt_adv from character_ where id=%u", charid), errbuf)) { - if (database.RunQuery(query, MakeAnyLenString(&query, - "update character_ inner join character_backup on character_.id = character_backup.charid " - " set character_.name = character_backup.name, " - " character_.profile = character_backup.profile, " - " character_.level = character_backup.level, " - " character_.class = character_backup.class, " - " character_.x = character_backup.x, " - " character_.y = character_backup.y, " - " character_.z = character_backup.z, " - " character_.zoneid = character_backup.zoneid " - " where character_backup.charid=%u and character_backup.id=%u", charid, cbid), errbuf)) { - safe_delete(query); - c->Message(0, "Character restored."); - } - else { - c->Message(13, "Query error: '%s' %s", query, errbuf); - safe_delete(query); - } - } - else { - c->Message(13, "Query error: '%s' %s", query, errbuf); - safe_delete(query); - } - } - else - c->Message(0, "Usage: #charbackup list [char name/id]"); - } - else { - c->Message(0, "#charbackup sub-commands:"); - c->Message(0, " list [char name/id]"); - c->Message(0, " restore [char name/id] [backup#]"); - } -} - void command_nukeitem(Client *c, const Seperator *sep) { int numitems, itemid; @@ -6296,7 +6216,7 @@ void command_ban(Client *c, const Seperator *sep) } else { - database.RunQuery(query, MakeAnyLenString(&query, "SELECT account_id from character_ where name = '%s'", sep->arg[1]), errbuf, &result); + database.RunQuery(query, MakeAnyLenString(&query, "SELECT account_id from `character_data` where name = '%s'", sep->arg[1]), errbuf, &result); if(query) { safe_delete_array(query); diff --git a/zone/command.h b/zone/command.h index ef7302c51..d495faee7 100644 --- a/zone/command.h +++ b/zone/command.h @@ -150,7 +150,6 @@ void command_texture(Client *c, const Seperator *sep); void command_npctypespawn(Client *c, const Seperator *sep); void command_heal(Client *c, const Seperator *sep); void command_appearance(Client *c, const Seperator *sep); -void command_charbackup(Client *c, const Seperator *sep); void command_nukeitem(Client *c, const Seperator *sep); void command_peekinv(Client *c, const Seperator *sep); void command_findnpctype(Client *c, const Seperator *sep); diff --git a/zone/guild_mgr.cpp b/zone/guild_mgr.cpp index 37cc7a3de..2172df3c6 100644 --- a/zone/guild_mgr.cpp +++ b/zone/guild_mgr.cpp @@ -24,59 +24,6 @@ #include "client.h" #include "entity.h" -/* - -CREATE TABLE guilds ( - id MEDIUMINT UNSIGNED NOT NULL, - name VARCHAR(32) NOT NULL, - leader int NOT NULL, - minstatus SMALLINT NOT NULL, - tribute INT UNSIGNED NOT NULL, - motd TEXT NOT NULL DEFAULT '', - PRIMARY KEY(id), - UNIQUE KEY(name), - UNIQUE KEY(leader) -); - -CREATE TABLE guild_ranks ( - guild_id MEDIUMINT UNSIGNED NOT NULL, - rank TINYINT UNSIGNED NOT NULL, - title VARCHAR(128) NOT NULL, - can_hear TINYINT UNSIGNED NOT NULL, - can_speak TINYINT UNSIGNED NOT NULL, - can_invite TINYINT UNSIGNED NOT NULL, - can_remove TINYINT UNSIGNED NOT NULL, - can_promote TINYINT UNSIGNED NOT NULL, - can_demote TINYINT UNSIGNED NOT NULL, - can_motd TINYINT UNSIGNED NOT NULL, - can_warpeace TINYINT UNSIGNED NOT NULL, - PRIMARY KEY(guild_id,rank) -); - -# guild1 < guild2 by definition. -CREATE TABLE guild_relations ( - guild1 MEDIUMINT UNSIGNED NOT NULL, - guild2 MEDIUMINT UNSIGNED NOT NULL, - relation TINYINT NOT NULL, - PRIMARY KEY(guild1, guild1) -); - -CREATE TABLE guild_members ( - char_id INT NOT NULL, - guild_id MEDIUMINT UNSIGNED NOT NULL, - rank TINYINT UNSIGNED NOT NULL, - tribute_enable TINYINT UNSIGNED NOT NULL DEFAULT 0, - total_tribute INT UNSIGNED NOT NULL DEFAULT 0, - last_tribute INT UNSIGNED NOT NULL DEFAULT 0, - banker TINYINT UNSIGNED NOT NULL DEFAULT 0, - public_note TEXT NOT NULL DEFAULT '', - PRIMARY KEY(char_id) -); - - -*/ - - ZoneGuildManager guild_mgr; GuildBankManager *GuildBanks; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index ef14a3657..9a5d95eee 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1705,7 +1705,7 @@ bool ZoneDatabase::NoRentExpired(const char* name){ char *query = 0; MYSQL_RES *result; MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "Select (UNIX_TIMESTAMP(NOW())-timelaston) from character_ where name='%s'", name), errbuf, &result)) { + if (RunQuery(query, MakeAnyLenString(&query, "Select (UNIX_TIMESTAMP(NOW()) - last_login) from `character_data` where name='%s'", name), errbuf, &result)) { safe_delete_array(query); if (mysql_num_rows(result) == 1) { row = mysql_fetch_row(result); From a14371ba5ca55d5b664494acd7594cceab0ebff5 Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 7 Sep 2014 04:00:56 -0500 Subject: [PATCH 089/368] Removed debugging Added player profile data loading safety net checking --- common/database.cpp | 3 ++- common/eq_packet_structs.h | 4 ++-- zone/client_packet.cpp | 25 +++++++++++++++---------- zone/zonedb.cpp | 35 ++++++++++++++++++++++++++++++----- zone/zoning.cpp | 11 ----------- 5 files changed, 49 insertions(+), 29 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 0fb0cf846..614ae73d7 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -649,6 +649,7 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe pp->RestTimer // " RestTimer) " ); auto results = QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate", query); return true; } @@ -1247,7 +1248,7 @@ bool Database::CheckDatabaseConversions() { // Testing account = 11001 int char_iter_count = 0; - rquery = StringFormat("SELECT `id` FROM `character_` WHERE `account_id` = 11001"); + rquery = StringFormat("SELECT `id` FROM `character_`"); results = QueryDatabase(rquery); uint8 firstlogon = 0; diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 061f498d4..64effb762 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -801,8 +801,8 @@ struct SuspendedMinion_Struct ** OpCode: 0x006a */ static const uint32 MAX_PP_LANGUAGE = 28; -static const uint32 MAX_PP_SPELLBOOK = 720; // Set for all functions -static const uint32 MAX_PP_MEMSPELL = 12; // Set to latest client so functions can work right +static const uint32 MAX_PP_SPELLBOOK = 480; // Set for all functions +static const uint32 MAX_PP_MEMSPELL = 9; // Set to latest client so functions can work right static const uint32 MAX_PP_REF_SPELLBOOK = 480; // Set for Player Profile size retain static const uint32 MAX_PP_REF_MEMSPELL = 9; // Set for Player Profile size retain diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0e8ae509f..fd8433e1e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -542,29 +542,26 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { std::string query; unsigned long* lengths; - /* Set item material tint */ - for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) - if (m_pp.item_tint[i].rgb.use_tint == 1) - m_pp.item_tint[i].rgb.use_tint = 0xFF; - uint32 cid = CharacterID(); character_id = cid; /* Global character_id reference */ + clock_t t = std::clock(); /* Function timer start */ + /* Flush and reload factions */ database.RemoveTempFactions(this); database.LoadCharacterFactionValues(cid, factionvalues); /* Load Character Account Data: Temp until I move */ - query = StringFormat("SELECT `status`, `name`, `lsaccount_id`, `gmspeed`, `revoked`, `hideme` FROM `account` WHERE `id` = %i", this->AccountID()); + query = StringFormat("SELECT `status`, `name`, `lsaccount_id`, `gmspeed`, `revoked`, `hideme` FROM `account` WHERE `id` = %u", this->AccountID()); auto results = database.QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { if (admin){ admin = atoi(row[0]); } if (account_name){ strcpy(account_name, row[1]); } if (lsaccountid && atoi(row[2]) > 0){ lsaccountid = atoi(row[2]); } else{ lsaccountid = 0; } - if (gmspeed){ gmspeed = atoi(row[3]); } - if (revoked){ revoked = atoi(row[4]); } - if (gmhideme){ gmhideme = atoi(row[5]); } + gmspeed = atoi(row[3]); + revoked = atoi(row[4]); + gmhideme = atoi(row[5]); if (account_creation){ account_creation = atoul(row[6]); } } @@ -587,7 +584,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { if (LFG){ LFG = atoi(row[1]); } if (firstlogon){ firstlogon = atoi(row[3]); } } - + loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ @@ -603,6 +600,13 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ database.LoadCharacterLeadershipAA(cid, &m_pp); /* Load Character Leadership AA's */ + /* Set item material tint */ + for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) + if (m_pp.item_tint[i].rgb.use_tint == 1 || m_pp.item_tint[i].rgb.use_tint == 255) + m_pp.item_tint[i].rgb.use_tint = 0xFF; + + std::cout << "Character Data Load Took " << (((float)(std::clock() - t)) / CLOCKS_PER_SEC) << " seconds\n" << std::endl; + if (level){ level = m_pp.level; } /* If GM, not trackable */ @@ -4981,6 +4985,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) /* Memorized Spell */ if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id){ + uint16 spell_to_cast = 0; if (castspell->slot < MAX_PP_MEMSPELL) { spell_to_cast = m_pp.mem_spells[castspell->slot]; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 9a5d95eee..82b38c44e 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1032,7 +1032,12 @@ bool ZoneDatabase::LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_ "`character_memmed_spells` " "WHERE `id` = %u ORDER BY `slot_id`", character_id); auto results = database.QueryDatabase(query); int i = 0; - for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); pp->mem_spells[i] = atoi(row[1]); } + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); + if (i < MAX_PP_MEMSPELL){ + pp->mem_spells[i] = atoi(row[1]); + } + } return true; } @@ -1045,7 +1050,12 @@ bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Str "`character_spells` " "WHERE `id` = %u ORDER BY `slot_id`", character_id); auto results = database.QueryDatabase(query); int i = 0; - for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); pp->spell_book[i] = atoi(row[1]); } + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); + if (i < MAX_PP_SPELLBOOK){ + pp->spell_book[i] = atoi(row[1]); + } + } return true; } @@ -1062,7 +1072,12 @@ bool ZoneDatabase::LoadCharacterLanguages(uint32 character_id, PlayerProfile_Str for (i = 0; i < MAX_PP_LANGUAGE; i++){ pp->languages[i] = 0; } - for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); pp->languages[i] = atoi(row[1]); } + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); + if (i < MAX_PP_LANGUAGE){ + pp->languages[i] = atoi(row[1]); + } + } return true; } @@ -1084,7 +1099,12 @@ bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_S "`character_disciplines`" "WHERE `id` = %u ORDER BY `disc_id`", character_id); auto results = database.QueryDatabase(query); int i = 0; - for (auto row = results.begin(); row != results.end(); ++row) { pp->disciplines.values[i] = atoi(row[0]); i++; } + for (auto row = results.begin(); row != results.end(); ++row) { + if (i < MAX_PP_DISCIPLINES){ + pp->disciplines.values[i] = atoi(row[0]); + } + i++; + } return true; } @@ -1101,7 +1121,12 @@ bool ZoneDatabase::LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct for (i = 0; i < MAX_PP_SKILL; i++){ pp->skills[i] = 0; } - for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); pp->skills[i] = atoi(row[1]); } + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); + if (i < MAX_PP_SKILL){ + pp->skills[i] = atoi(row[1]); + } + } return true; } diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 0eb204d1f..0cbf39a2c 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -386,17 +386,6 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc strcpy(ztz->name, GetName()); ztz->guild_id = GuildID(); worldserver.SendPacket(pack); - - printf("ZONING REQUEST TO WORLD\n"); - printf("ztz->response %u \n", ztz->response); - printf("ztz->current_zone_id %u \n", ztz->current_zone_id); - printf("ztz->current_instance_id %u \n", ztz->current_instance_id); - printf("ztz->requested_zone_id %u \n", ztz->requested_zone_id); - printf("ztz->requested_instance_id %u \n", ztz->requested_instance_id); - printf("ztz->admin %u \n", ztz->admin); - printf("ztz->ignorerestrictions %u \n", ztz->ignorerestrictions); - printf("ztz->guild_id %u \n", ztz->guild_id); - safe_delete(pack); } From 1f9597a9e2fb73cab22c82a24d1a22c742995605 Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 7 Sep 2014 05:35:19 -0500 Subject: [PATCH 090/368] Fix for item dupe via RoF augmenting --- common/item.cpp | 11 +++++++++++ common/item.h | 1 + zone/client_packet.cpp | 6 +++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/common/item.cpp b/common/item.cpp index 127f85614..65f57dbf9 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -1580,6 +1580,17 @@ int8 ItemInst::AvailableAugmentSlot(int32 augtype) const return (i < EmuConstants::ITEM_COMMON_SIZE) ? i : INVALID_INDEX; } +bool ItemInst::IsAugmentSlotAvailable(int32 augtype, uint8 slot) const { + if (m_item->ItemClass != ItemClassCommon || !m_item) + return -1; + + if ((!GetItem(slot) && m_item->AugSlotVisible[slot]) && augtype == -1 || (m_item->AugSlotType[slot] && ((1 << (m_item->AugSlotType[slot] - 1)) & augtype))) { + return true; + } + + return false; +} + // Retrieve item inside container ItemInst* ItemInst::GetItem(uint8 index) const { diff --git a/common/item.h b/common/item.h index f279bd2aa..c24c708b1 100644 --- a/common/item.h +++ b/common/item.h @@ -274,6 +274,7 @@ public: inline bool IsAugmentable() const { return m_item->AugSlotType[0]!=0 || m_item->AugSlotType[1]!=0 || m_item->AugSlotType[2]!=0 || m_item->AugSlotType[3]!=0 || m_item->AugSlotType[4]!=0; } bool AvailableWearSlot(uint32 aug_wear_slots) const; int8 AvailableAugmentSlot(int32 augtype) const; + bool IsAugmentSlotAvailable(int32 augtype, uint8 slot) const; inline int32 GetAugmentType() const { return m_item->AugType; } inline bool IsExpendable() const { return ((m_item->Click.Type == ET_Expendable ) || (m_item->ItemType == ItemTypePotion)); } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index fa29c07a6..02bedc17c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -6784,10 +6784,10 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) if(tobe_auged && auged_with) { - if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && + if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) && (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) { - tobe_auged->PutAugment(slot, *auged_with); + tobe_auged->PutAugment(in_augment->augment_index, *auged_with); ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); if(aug) { @@ -6800,7 +6800,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) } else { - Message(13, "Error: Could not find augmentation at index %i. Aborting."); + Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index); return; } From 899cf32e6b8d5a586ccb18a709e7c40d1a3e22db Mon Sep 17 00:00:00 2001 From: akkadius Date: Tue, 9 Sep 2014 16:03:24 -0500 Subject: [PATCH 091/368] Added some DB logging to catch any potential query errors, this logs to eqemu_query_error_log.txt at the root folder, currently no rule toggling for this Corrected some character name reservation logic Adjusted telnet console so it does not throw 'Command Unknown' when simply hitting enter key Adjusted System MSG for worldshutdown to use minutes instead of seconds Added warning for when Shared platinum is disabled at the rule level for players to NOT put platinum in the shared bank when they insert platinum in the shared platinum slot Changed a place in the code where disciplines were trained, someone had uint16 allocated to CharacterID and this was causing issues for characters with large ID's in the database Added a fix for bind points getting overwritten with invalid zone data, setting everything to 0,0,0,0 - Since I've added this change I've had no issues for players Fixed some spell book swap logic in the code, removed swap function. Fixed issue with guild ranks not loading properly Commented out some of the non-working tell-que code Took out some of the auto increment settings in the auto database conversion that don't affect anything anyways Added some additional escape string sequences for queries that needed them Added ThrowDBError logging to catch any potential query issues with saves or load functions --- common/database.cpp | 78 ++++++++++++++++++++++++++--------------- world/client.cpp | 2 +- world/console.cpp | 5 ++- world/zonelist.cpp | 4 +-- world/zoneserver.cpp | 50 +++++++++++++------------- zone/client_packet.cpp | 29 ++++++++------- zone/client_process.cpp | 7 +++- zone/command.cpp | 4 +-- zone/questmgr.cpp | 8 ++--- zone/zonedb.cpp | 42 ++++++++++++---------- zone/zonedb.h | 1 - 11 files changed, 130 insertions(+), 100 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 287e65a9d..e6983b368 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -18,6 +18,10 @@ #include "../common/debug.h" #include "../common/rulesys.h" #include +#include + +using namespace std; + #include #include #include @@ -306,11 +310,21 @@ bool Database::SetAccountStatus(const char* name, int16 status) { bool Database::ReserveName(uint32 account_id, char* name) { std::string query = StringFormat("INSERT INTO `character_data` SET `account_id` = %i, `name` = '%s'", account_id, name); auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::ReserveName", query); + if (!results.Success() || results.ErrorMessage() != ""){ return false; } return true; } bool Database::ThrowDBError(std::string ErrorMessage, std::string query_title, std::string query){ - if (ErrorMessage != ""){ std::cout << "\nERROR " << query_title << ": " << ErrorMessage << "\n\n" << query << "\n" << std::endl; return true; } + if (ErrorMessage != ""){ + std::cout << "\nERROR " << query_title << ": " << ErrorMessage << "\n\n" << query << "\n" << std::endl; + + /* Write to file temporarily */ + std::ofstream log("eqemu_query_error_log.txt", std::ios_base::app | std::ios_base::out); + log << "\nERROR " << query_title << ": " << ErrorMessage << "\n\n" << query << "\n"; + log.close(); + + return true; + } return false; } @@ -987,7 +1001,7 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_currency` doesn't exist... creating..."); rquery = StringFormat( " CREATE TABLE `character_currency` ( " - " `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + " `id` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `platinum` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `gold` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `silver` int(11) UNSIGNED NOT NULL DEFAULT 0, " @@ -1006,7 +1020,7 @@ bool Database::CheckDatabaseConversions() { " `career_ebon_crystals` int(11) UNSIGNED NOT NULL DEFAULT 0, " " PRIMARY KEY (`id`), " " KEY `id` (`id`) " - " ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; " + " ) ENGINE=InnoDB DEFAULT CHARSET=latin1; " ); QueryDatabase(rquery); printf(" done...\n"); @@ -1018,13 +1032,13 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_alternate_abilities` doesn't exist... creating..."); rquery = StringFormat( " CREATE TABLE `character_alternate_abilities` ( " - " `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + " `id` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `slot` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " " `aa_id` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " " `aa_value` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " " PRIMARY KEY(`id`,`slot`), " " KEY `id` (`id`) " - " ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " + " ) ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); QueryDatabase(rquery); printf(" done...\n"); @@ -1046,7 +1060,7 @@ bool Database::CheckDatabaseConversions() { "`heading` float NOT NULL DEFAULT '0', " "PRIMARY KEY(`id`, `is_home`), " "KEY `id` (`id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); QueryDatabase(rquery); printf(" done...\n"); @@ -1063,7 +1077,7 @@ bool Database::CheckDatabaseConversions() { "`value` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " "PRIMARY KEY(`id`, `lang_id`), " "KEY `id` (`id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); QueryDatabase(rquery); printf(" done...\n"); @@ -1080,7 +1094,7 @@ bool Database::CheckDatabaseConversions() { "`value` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " "PRIMARY KEY(`id`, `skill_id`), " "KEY `id` (`id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); QueryDatabase(rquery); printf(" done...\n"); @@ -1097,7 +1111,7 @@ bool Database::CheckDatabaseConversions() { "`spell_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " "PRIMARY KEY(`id`, `slot_id`), " "KEY `id` (`id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); QueryDatabase(rquery); printf(" done...\n"); @@ -1109,12 +1123,12 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_memmed_spells` doesn't exist... creating..."); rquery = StringFormat( "CREATE TABLE `character_memmed_spells` ( " - "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`id` int(11) UNSIGNED NOT NULL DEFAULT 0, " "`slot_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " "`spell_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " "PRIMARY KEY(`id`, `slot_id`), " "KEY `id` (`id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); QueryDatabase(rquery); printf(" done...\n"); @@ -1126,12 +1140,12 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_disciplines` doesn't exist... creating..."); rquery = StringFormat( " CREATE TABLE `character_disciplines` ( " - " `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + " `id` int(11) UNSIGNED NOT NULL DEFAULT 0, " " `slot_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " " `disc_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " " PRIMARY KEY(`id`, `slot_id`), " " KEY `id` (`id`) " - " ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " + " ) ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); QueryDatabase(rquery); printf(" done...\n"); @@ -1141,7 +1155,7 @@ bool Database::CheckDatabaseConversions() { results = QueryDatabase(rquery); if (results.RowCount() == 0){ printf("Table: `character_material` doesn't exist... creating..."); - rquery = StringFormat( + rquery = StringFormat( "CREATE TABLE `character_material` ( " "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT," "`slot` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," @@ -1164,11 +1178,11 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_tribute` doesn't exist... creating..."); rquery = StringFormat( "CREATE TABLE `character_tribute` ( " - "`id` int(11) unsigned NOT NULL AUTO_INCREMENT, " + "`id` int(11) unsigned NOT NULL DEFAULT 0, " "`tier` tinyint(11) unsigned NOT NULL DEFAULT '0', " "`tribute` int(11) UNSIGNED NOT NULL DEFAULT '0', " "KEY `id` (`id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); QueryDatabase(rquery); printf(" done...\n"); @@ -1180,7 +1194,7 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_bandolier` doesn't exist... creating..."); rquery = StringFormat( "CREATE TABLE `character_bandolier` ( " - "`id` int(11) unsigned NOT NULL AUTO_INCREMENT, " + "`id` int(11) unsigned NOT NULL DEFAULT 0, " "`bandolier_id` tinyint(11) unsigned NOT NULL DEFAULT '0', " "`bandolier_slot` tinyint(11) unsigned NOT NULL DEFAULT '0', " "`item_id` int(11) UNSIGNED NOT NULL DEFAULT '0', " @@ -1188,7 +1202,7 @@ bool Database::CheckDatabaseConversions() { "`bandolier_name` varchar(32) NOT NULL DEFAULT '0', " "PRIMARY KEY(`id`,`bandolier_id`, `bandolier_slot`), " "KEY `id` (`id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); QueryDatabase(rquery); printf(" done...\n"); @@ -1200,13 +1214,13 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_potionbelt` doesn't exist... creating..."); rquery = StringFormat( "CREATE TABLE `character_potionbelt` ( " - "`id` int(11) unsigned NOT NULL AUTO_INCREMENT, " + "`id` int(11) unsigned NOT NULL DEFAULT 0, " "`potion_id` tinyint(11) unsigned NOT NULL DEFAULT '0', " "`item_id` int(11) UNSIGNED NOT NULL DEFAULT '0', " "`icon` int(11) UNSIGNED NOT NULL DEFAULT '0', " "PRIMARY KEY(`id`,`potion_id`), " "KEY `id` (`id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); QueryDatabase(rquery); printf(" done...\n"); @@ -1218,11 +1232,11 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_inspect_messages` doesn't exist... creating..."); rquery = StringFormat( "CREATE TABLE `character_inspect_messages` ( " - "`id` int(11) unsigned NOT NULL AUTO_INCREMENT, " + "`id` int(11) unsigned NOT NULL DEFAULT 0, " "`inspect_message` varchar(255) NOT NULL DEFAULT '', " "PRIMARY KEY(`id`), " "KEY `id` (`id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); QueryDatabase(rquery); printf(" done...\n"); @@ -1234,12 +1248,12 @@ bool Database::CheckDatabaseConversions() { printf("Table: `character_leadership_abilities` doesn't exist... creating..."); rquery = StringFormat( "CREATE TABLE `character_leadership_abilities` (" - "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`id` int(11) UNSIGNED NOT NULL DEFAULT 0, " "`slot` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " "`rank` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " "PRIMARY KEY(`id`,`slot`), " "KEY `id` (`id`) " - ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); QueryDatabase(rquery); printf(" done...\n"); @@ -2650,15 +2664,21 @@ uint32 Database::GetGroupID(const char* name){ /* Is this really getting used properly... A half implementation ? Akkadius */ char* Database::GetGroupLeaderForLogin(const char* name, char* leaderbuf){ + leaderbuf = ""; std::string query = StringFormat("SELECT `groupid` FROM `group_id` WHERE `name = '%s'", name); auto results = QueryDatabase(query); auto row = results.begin(); uint32 group_id = 0; - if (row[0]){ group_id = atoi(row[0]); } + for (auto row = results.begin(); row != results.end(); ++row) { + if (row[0]){ group_id = atoi(row[0]); } + } - query = StringFormat("SELECT `name` FROM `group_id` WHERE `name` != '%s' AND `groupid` = %u", name, group_id); - results = QueryDatabase(query); - row = results.begin(); - if (row[0]){ strcpy(leaderbuf, row[0]); } + if (group_id > 0){ + query = StringFormat("SELECT `leadername` FROM `group_leader` WHERE `gid` = '%u' AND `groupid` = %u LIMIT 1", group_id); + results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + if (row[0]){ strcpy(leaderbuf, row[0]); } + } + } return leaderbuf; } diff --git a/world/client.cpp b/world/client.cpp index c8fea66c9..e7e999ce5 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -486,7 +486,7 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { outapp->pBuffer = new uchar[1]; outapp->size = 1; - bool valid; + bool valid = false; if(!database.CheckNameFilter(char_name)) { valid = false; } else if (char_name[0] < 'A' && char_name[0] > 'Z') { valid = false; } /* Name must begin with an upper-case letter. */ else if (database.ReserveName(GetAccountID(), char_name)) { valid = true; } diff --git a/world/console.cpp b/world/console.cpp index d5433067e..6caa9ce86 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -113,7 +113,7 @@ bool Console::SendChannelMessage(const ServerChannelMessage_Struct* scm) { break; } case 7: { - SendMessage(1, "%s tells you, '%s'", scm->from, scm->message); + SendMessage(1, "[%s] tells you, '%s'", scm->from, scm->message); ServerPacket* pack = new ServerPacket(ServerOP_ChannelMessage, sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1); memcpy(pack->pBuffer, scm, pack->size); ServerChannelMessage_Struct* scm2 = (ServerChannelMessage_Struct*) pack->pBuffer; @@ -847,6 +847,9 @@ void Console::ProcessCommand(const char* command) { zoneserver_list.SendPacket(pack); safe_delete(pack); } + else if (strcasecmp(sep.arg[0], "") == 0){ + /* Hit Enter with no command */ + } else { SendMessage(1, "Command unknown."); } diff --git a/world/zonelist.cpp b/world/zonelist.cpp index fdf9db5cf..c97010aa8 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -87,7 +87,7 @@ void ZSList::Process() { CatchSignal(2); } if(reminder && reminder->Check()){ - SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World coming down, everyone log out now. World will shut down in %i seconds...",shutdowntimer->GetRemainingTime()/1000); + SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World coming down, everyone log out now. World will shut down in %i minutes...", ((shutdowntimer->GetRemainingTime()/1000) / 60)); } LinkedListIterator iterator(list); @@ -718,7 +718,7 @@ void ZSList::GetZoneIDList(std::vector &zones) { void ZSList::WorldShutDown(uint32 time, uint32 interval) { if( time > 0 ) { - SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World coming down in %i seconds, everyone log out before this time.",time); + SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World coming down in %i minutes, everyone log out before this time.", (time / 60)); time *= 1000; interval *= 1000; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 4796104a7..c740ff3bc 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -448,31 +448,31 @@ bool ZoneServer::Process() { else if (cle->Online() == CLE_Status_Zoning) { if (!scm->noreply) { - time_t rawtime; - struct tm * timeinfo; - time ( &rawtime ); - timeinfo = localtime ( &rawtime ); - char *telldate=asctime(timeinfo); - - std::string query = StringFormat("SELECT name FROM `character_data` WHERE name = '%s'",scm->deliverto); - auto results = database.QueryDatabase(query); - if (!results.Success()) - break; - - if (results.RowCount() == 0) { - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); - break; - } - - query = StringFormat("INSERT INTO tellque " - "(Date, Receiver, Sender, Message) " - "VALUES('%s', '%s', '%s', '%s')", - telldate, scm->deliverto, scm->from, scm->message); - results = database.QueryDatabase(query); - if (results.Success()) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to the %s's que.", scm->to); - else - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); + // time_t rawtime; + // struct tm * timeinfo; + // time ( &rawtime ); + // timeinfo = localtime ( &rawtime ); + // char *telldate=asctime(timeinfo); + // + // std::string query = StringFormat("SELECT name FROM `character_data` WHERE name = '%s'",scm->deliverto); + // auto results = database.QueryDatabase(query); + // if (!results.Success()) + // break; + // + // if (results.RowCount() == 0) { + // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); + // break; + // } + // + // query = StringFormat("INSERT INTO tellque " + // "(Date, Receiver, Sender, Message) " + // "VALUES('%s', '%s', '%s', '%s')", + // telldate, scm->deliverto, scm->from, scm->message); + // results = database.QueryDatabase(query); + // if (results.Success()) + // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to the %s's que.", scm->to); + // else + // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); } // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 02bedc17c..6865eadfc 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -571,21 +571,20 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { results = database.QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { m_pp.lastlogin = time(nullptr); - if (row[4]){ + if (atoi(row[4]) > 0){ guild_id = atoi(row[4]); - if (guildrank) { - if (row[5] != nullptr){ guildrank = atoi(row[5]); } - else{ guildrank = GUILD_RANK_NONE; } - } + if (row[5] != nullptr){ guildrank = atoi(row[5]); } + else{ guildrank = GUILD_RANK_NONE; } } - if (RuleB(Character, SharedBankPlat)) - m_pp.platinum_shared = database.GetSharedPlatinum(database.GetAccountIDByChar(cid)); - + if (LFP){ LFP = atoi(row[0]); } if (LFG){ LFG = atoi(row[1]); } if (firstlogon){ firstlogon = atoi(row[3]); } } + if (RuleB(Character, SharedBankPlat)) + m_pp.platinum_shared = database.GetSharedPlatinum(this->AccountID()); + loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ @@ -622,6 +621,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { /* If we can maintain intoxication across zones, check for it */ if (!RuleB(Character, MaintainIntoxicationAcrossZones)) m_pp.intoxication = 0; + strcpy(name, m_pp.name); strcpy(lastname, m_pp.last_name); /* If PP is set to weird coordinates */ @@ -751,10 +751,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { aa_points[id] = aa[a]->value; } - if (SPDAT_RECORDS > 0) - { - for (uint32 z = 0; z 0) { + for (uint32 z = 0; z= (uint32)SPDAT_RECORDS) UnmemSpell(z, false); } @@ -5020,7 +5018,8 @@ void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) m_pp.spell_book[swapspell->from_slot] = m_pp.spell_book[swapspell->to_slot]; m_pp.spell_book[swapspell->to_slot] = swapspelltemp; - database.SaveCharacterSpellSwap(this->CharacterID(), swapspelltemp, swapspell->from_slot, swapspell->to_slot); + database.SaveCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot); + database.SaveCharacterSpell(this->CharacterID(), swapspelltemp, swapspell->to_slot); QueuePacket(app); return; @@ -9573,7 +9572,6 @@ void Client::CompleteConnect() { entity_list.SendUntargetable(this); - client_data_loaded = true; int x; for (x = 0; x < 8; x++) SendWearChange(x); @@ -9659,7 +9657,8 @@ void Client::CompleteConnect() { alternate_currency_loaded = true; ProcessAlternateCurrencyQueue(); - + /* This needs to be set, this determines whether or not data was loaded properly before a save */ + client_data_loaded = true; CalcItemScale(); DoItemEnterZone(); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index bf4c5694b..544200910 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1547,7 +1547,12 @@ void Client::OPMoveCoin(const EQApplicationPacket* app) if (from_bucket == &m_pp.platinum_shared) amount_to_add = 0 - amount_to_take; - database.SetSharedPlatinum(AccountID(),amount_to_add); + database.SetSharedPlatinum(AccountID(),amount_to_add); + } + } + else{ + if (to_bucket == &m_pp.platinum_shared || from_bucket == &m_pp.platinum_shared){ + this->Message(13, "::: WARNING! ::: SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE"); } } } diff --git a/zone/command.cpp b/zone/command.cpp index 189f3daf2..c1e304dd1 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2156,8 +2156,8 @@ void command_worldshutdown(Client *c, const Seperator *sep) uint32 interval=0; if (worldserver.Connected()) { if(sep->IsNumber(1) && sep->IsNumber(2) && ((time=atoi(sep->arg[1]))>0) && ((interval=atoi(sep->arg[2]))>0)) { - worldserver.SendEmoteMessage(0,0,15,":SYSTEM MSG:World coming down in %i seconds, everyone log out before this time.",time); - c->Message(0, "Sending shutdown packet now, World will shutdown in: %i Seconds with an interval of: %i",time,interval); + worldserver.SendEmoteMessage(0,0,15,":SYSTEM MSG:World coming down in %i minutes, everyone log out before this time.", (time / 60 )); + c->Message(0, "Sending shutdown packet now, World will shutdown in: %i minutes with an interval of: %i seconds", (time / 60), interval); ServerPacket* pack = new ServerPacket(ServerOP_ShutdownAll,sizeof(WorldShutDown_Struct)); WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; wsd->time=time*1000; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 776376bdf..e86e4d601 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -946,7 +946,7 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { uint16 count; uint16 curspell; - uint16 Char_ID = initiator->CharacterID(); + uint32 Char_ID = initiator->CharacterID(); bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals); bool SpellGlobalCheckResult = 0; @@ -960,7 +960,7 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { spells[curspell].skill != 52 && ( !RuleB(Spells, UseCHAScribeHack) || spells[curspell].effectid[EFFECT_COUNT - 1] != 10 ) ) - { + { if(IsDiscipline(curspell)){ //we may want to come up with a function like Client::GetNextAvailableSpellBookSlot() to help speed this up a little for(uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) { @@ -974,12 +974,12 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { SpellGlobalCheckResult = initiator->SpellGlobalCheck(curspell, Char_ID); if (SpellGlobalCheckResult) { initiator->GetPP().disciplines.values[r] = curspell; - database.SaveCharacterDisc(Char_ID, r, curspell); + database.SaveCharacterDisc(Char_ID, r, curspell); initiator->SendDisciplineUpdate(); initiator->Message(0, "You have learned a new discipline!"); count++; //success counter } - break; //continue the 1st loop + break; //continue the 1st loop } else { initiator->GetPP().disciplines.values[r] = curspell; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 82b38c44e..d6b59a8c4 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1050,6 +1050,10 @@ bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Str "`character_spells` " "WHERE `id` = %u ORDER BY `slot_id`", character_id); auto results = database.QueryDatabase(query); int i = 0; + /* Initialize Spells */ + for (i = 0; i < MAX_PP_SPELLBOOK; i++){ + pp->spell_book[i] = 0; + } for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); if (i < MAX_PP_SPELLBOOK){ @@ -1241,7 +1245,7 @@ bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struc } bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("SELECT zone_id, instance_id, x, y, z, heading, is_home FROM character_bind WHERE `id` = %u LIMIT 2", character_id); + std::string query = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %u LIMIT 2", character_id); auto results = database.QueryDatabase(query); int i = 0; for (auto row = results.begin(); row != results.end(); ++row) { i = 0; @@ -1274,29 +1278,34 @@ bool ZoneDatabase::SaveCharacterLanguage(uint32 character_id, uint32 lang_id, ui } bool ZoneDatabase::SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home){ + if (zone_id <= 0){ return false; } /* Save Home Bind Point */ std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", character_id, zone_id, instance_id, x, y, z, heading, is_home); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterBindPoint for character ID: %i zone_id: %u instance_id: %u x: %f y: %f z: %f heading: %f ishome: %u", character_id, zone_id, instance_id, x, y, z, heading, is_home); auto results = QueryDatabase(query); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterBindPoint", query); return true; } bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color){ - std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, color, use_tint) VALUES (%u, %u, %u, 255)", character_id, slot_id, color); QueryDatabase(query); + std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, color, use_tint) VALUES (%u, %u, %u, 255)", character_id, slot_id, color); auto results = QueryDatabase(query); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterMaterialColor for character ID: %i, slot_id: %u color: %u done", character_id, slot_id, color); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterMaterialColor", query); return true; } bool ZoneDatabase::SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value){ - std::string query = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, skill_id, value); QueryDatabase(query); + std::string query = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, skill_id, value); auto results = QueryDatabase(query); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterSkill for character ID: %i, skill_id:%u value:%u done", character_id, skill_id, value); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterSkill", query); return true; } bool ZoneDatabase::SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id){ - std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); QueryDatabase(query); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterDisc for character ID: %i, slot:%u disc_id:%u value:%u done", character_id, slot_id, disc_id); + std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); auto results = QueryDatabase(query); + LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterDisc for character ID: %i, slot:%u disc_id:%u done", character_id, slot_id, disc_id); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterDisc", query); return true; } @@ -1317,6 +1326,7 @@ bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_i DoEscapeString(bandolier_name_esc, bandolier_name, strlen(bandolier_name)); std::string query = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%u, %u, %u, %u, %u,'%s')", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name_esc); auto results = QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterBandolier", query); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterBandolier for character ID: %i, bandolier_id: %u, bandolier_slot: %u item_id: %u, icon:%u band_name:%s done", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name); if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } return true; @@ -1325,6 +1335,7 @@ bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_i bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon) { std::string query = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%u, %u, %u, %u)", character_id, potion_id, item_id, icon); auto results = QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterPotionBelt", query); if (!results.RowsAffected()){ std::cout << "ERROR Potionbelt Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } return true; } @@ -1540,8 +1551,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla ")", character_id, // " id, " account_id, // " account_id, " - pp->name, // " `name`, " - pp->last_name, // " last_name, " + EscapeString(pp->name).c_str(), // " `name`, " + EscapeString(pp->last_name).c_str(), // " last_name, " pp->gender, // " gender, " pp->race, // " race, " pp->class_, // " class, " @@ -1565,8 +1576,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla pp->ability_number, // " ability_number, " pp->ability_time_minutes, // " ability_time_minutes, " pp->ability_time_hours, // " ability_time_hours, " - pp->title, // " title, " - pp->suffix, // " suffix, " + EscapeString(pp->title).c_str(), // " title, " + EscapeString(pp->suffix).c_str(), // " suffix, " pp->exp, // " exp, " pp->points, // " points, " pp->mana, // " mana, " @@ -1633,7 +1644,7 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla m_epp->expended_aa ); auto results = database.QueryDatabase(query); - if (!results.RowsAffected()){ std::cout << "ERROR ZoneDatabase:SaveCharacterData: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } + ThrowDBError(results.ErrorMessage(), "ZoneDatabase:SaveCharacterData", query); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; } @@ -1675,6 +1686,7 @@ bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Stru pp->currentEbonCrystals, pp->careerEbonCrystals); auto results = database.QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterCurrency", query); LogFile->write(EQEMuLog::Status, "Saving Currency for character ID: %i, done", character_id); return true; } @@ -1684,19 +1696,11 @@ bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 cur " VALUES (%u, %u, %u)", character_id, aa_id, current_level); auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterAA", rquery); LogFile->write(EQEMuLog::Status, "Saving AA for character ID: %u, aa_id: %u current_level: %u", character_id, aa_id, current_level); return true; } -bool ZoneDatabase::SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot){ - std::string rquery = StringFormat("UPDATE `character_spells` SET `slot_id` = %u WHERE `slot_id` = %u AND `id` = %u", - to_slot, from_slot, character_id); - clock_t t = std::clock(); /* Function timer start */ - auto results = QueryDatabase(rquery); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterSpellSwap for character ID: %u, from_slot: %u to_slot: %u spell: %u time: %f seconds", character_id, from_slot, to_slot, spell_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); - return true; -} - bool ZoneDatabase::SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ std::string query = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); QueryDatabase(query); return true; } diff --git a/zone/zonedb.h b/zone/zonedb.h index 0405fb3a9..497beb322 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -269,7 +269,6 @@ public: bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); - bool SaveCharacterSpellSwap(uint32 character_id, uint32 spell_id, uint32 from_slot, uint32 to_slot); bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); From 0c38b46bf11d36da91e27480b43eef98476ad1e7 Mon Sep 17 00:00:00 2001 From: akkadius Date: Tue, 9 Sep 2014 16:15:25 -0500 Subject: [PATCH 092/368] Remove namespace std --- common/database.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index e6983b368..bb3a3eab4 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -19,9 +19,6 @@ #include "../common/rulesys.h" #include #include - -using namespace std; - #include #include #include From d7dc733480584875a58f53aa42c14cf7e399bddd Mon Sep 17 00:00:00 2001 From: akkadius Date: Thu, 11 Sep 2014 00:44:12 -0500 Subject: [PATCH 093/368] Small db changes --- common/database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/database.cpp b/common/database.cpp index bb3a3eab4..4c4d86731 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -306,7 +306,7 @@ bool Database::SetAccountStatus(const char* name, int16 status) { /* This initially creates the character during character create */ bool Database::ReserveName(uint32 account_id, char* name) { std::string query = StringFormat("INSERT INTO `character_data` SET `account_id` = %i, `name` = '%s'", account_id, name); - auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::ReserveName", query); + auto results = QueryDatabase(query); if (!results.Success() || results.ErrorMessage() != ""){ return false; } return true; } From e390531dcd24579e9c8e8cf260658dcaa9b36bc0 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Thu, 11 Sep 2014 03:14:34 -0500 Subject: [PATCH 094/368] Initial character creation escape sequences Added initial skill/language/bind saves to character creation --- common/database.cpp | 53 ++++++++++++++++++++++++++++------ world/client.cpp | 6 ++-- world/worlddb.cpp | 65 +++++++++++++++++++++++++++++++----------- world/worlddb.h | 2 +- zone/client_packet.cpp | 2 +- 5 files changed, 98 insertions(+), 30 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 4c4d86731..cf4fb491d 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -573,8 +573,8 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe ")", character_id, // " id, " account_id, // " account_id, " - pp->name, // " `name`, " - pp->last_name, // " last_name, " + EscapeString(pp->name).c_str(), // " `name`, " + EscapeString(pp->last_name).c_str(), // " last_name, " pp->gender, // " gender, " pp->race, // " race, " pp->class_, // " class, " @@ -598,8 +598,8 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe pp->ability_number, // " ability_number, " pp->ability_time_minutes, // " ability_time_minutes, " pp->ability_time_hours, // " ability_time_hours, " - pp->title, // " title, " - pp->suffix, // " suffix, " + EscapeString(pp->title).c_str(), // " title, " + EscapeString(pp->suffix).c_str(), // " suffix, " pp->exp, // " exp, " pp->points, // " points, " pp->mana, // " mana, " @@ -661,13 +661,50 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe pp->raidAutoconsent, // " raid_auto_consent, " pp->guildAutoconsent, // " guild_auto_consent, " pp->RestTimer // " RestTimer) " - ); + ); auto results = QueryDatabase(query); - ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate", query); + ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate Character Data", query); + /* Save Bind Points */ + query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, %i), " + "(%u, %u, %u, %f, %f, %f, %f, %i)", + character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading, 0, + character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading, 1 + ); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate Bind Point", query); + + /* Save Skills */ + int firstquery = 0; + for (int i = 0; i < MAX_PP_SKILL; i++){ + if (pp->skills[i] > 0){ + if (firstquery != 1){ + firstquery = 1; + query = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, i, pp->skills[i]); + } + else{ + query = query + StringFormat(", (%u, %u, %u)", character_id, i, pp->skills[i]); + } + } + } + results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate Starting Skills", query); + + /* Save Language */ + firstquery = 0; + for (int i = 0; i < MAX_PP_LANGUAGE; i++){ + if (pp->languages[i] > 0){ + if (firstquery != 1){ + firstquery = 1; + query = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, i, pp->languages[i]); + } + else{ + query = query + StringFormat(", (%u, %u, %u)", character_id, i, pp->languages[i]); + } + } + } + results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate Starting Languages", query); + return true; } - /* This only for new Character creation storing */ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv) { uint32 charid = 0; char zone[50]; float x, y, z; @@ -690,7 +727,7 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inven y = pp->y; z = pp->z; - /* Saves Player Profile Data to `character_data` */ + /* Saves Player Profile Data */ SaveCharacterCreate(charid, account_id, pp); /* Insert starting inventory... */ diff --git a/world/client.cpp b/world/client.cpp index e7e999ce5..93e0f782b 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -171,7 +171,7 @@ void Client::SendCharInfo() { EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); CharacterSelect_Struct* cs = (CharacterSelect_Struct*)outapp->pBuffer; - database.GetCharSelectInfo(GetAccountID(), cs); + database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit); QueuePacket(outapp); safe_delete(outapp); @@ -699,7 +699,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if(!pZoning && ew->return_home && !ew->tutorial) { CharacterSelect_Struct* cs = new CharacterSelect_Struct; memset(cs, 0, sizeof(CharacterSelect_Struct)); - database.GetCharSelectInfo(GetAccountID(), cs); + database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit); bool home_enabled = false; for(int x = 0; x < 10; ++x) @@ -729,7 +729,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) { CharacterSelect_Struct* cs = new CharacterSelect_Struct; memset(cs, 0, sizeof(CharacterSelect_Struct)); - database.GetCharSelectInfo(GetAccountID(), cs); + database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit); bool tutorial_enabled = false; for(int x = 0; x < 10; ++x) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 4192e0d84..f26061a5f 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -33,9 +33,11 @@ extern std::vector character_create_race_class_combos; // solar: the current stuff is at the bottom of this function -void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs) { +void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs, uint32 ClientVersion) { Inventory *inv; - + uint8 has_home = 0; + uint8 has_bind = 0; + /* Initialize Variables */ for (int i=0; i<10; i++) { strcpy(cs->name[i], ""); @@ -105,24 +107,53 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs->gohome[char_num] = 1; } - /* - This part creates home city entries for characters created before the home bind point was tracked. - Do it here because the player profile is already loaded and it's as good a spot as any. This whole block should - probably be removed at some point, when most accounts are safely converted. - */ - - /* Load Character Bind Data */ - cquery = StringFormat("SELECT zone_id, instance_id, x, y, z, heading FROM character_bind_home WHERE `id` = %i LIMIT 1", character_id); - auto results_bind = database.QueryDatabase(cquery); int r = 0; + /* Set Bind Point Data for any character that may possibly be missing it for any reason */ + cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %i LIMIT 2", character_id); + auto results_bind = database.QueryDatabase(cquery); has_home = 0; has_bind = 0; for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { - uint8 bind_zone_id = atoi(row_b[r]); r++; - uint8 bind_instance_id = atoi(row_b[r]); r++; - uint8 bind_x_id = atoi(row_b[r]); r++; - uint8 bind_y_id = atoi(row_b[r]); r++; - uint8 bind_z_id = atoi(row_b[r]); r++; - uint8 bind_heading_id = atoi(row_b[r]); r++; + if (row_b[6] && atoi(row_b[6]) == 1){ has_home = 1; } + if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; } } + if (has_home == 0 || has_bind == 0){ + cquery = StringFormat("SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i", + cs->class_[char_num], cs->deity[char_num], cs->race[char_num]); + auto results_bind = database.QueryDatabase(cquery); + for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { + /* If a bind_id is specified, make them start there */ + if (atoi(row_d[1]) != 0) { + pp.binds[4].zoneId = (uint32)atoi(row_d[1]); + GetSafePoints(pp.binds[4].zoneId, 0, &pp.binds[4].x, &pp.binds[4].y, &pp.binds[4].z); + } + /* Otherwise, use the zone and coordinates given */ + else { + pp.binds[4].zoneId = (uint32)atoi(row_d[0]); + float x = atof(row_d[2]); + float y = atof(row_d[3]); + float z = atof(row_d[4]); + if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); } + pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z; + + } + } + pp.binds[0] = pp.binds[4]; + /* If no home bind set, set it */ + if (has_home == 0){ + std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", + character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 1); + auto results_bset = QueryDatabase(query); ThrowDBError(results_bset.ErrorMessage(), "WorldDatabase::GetCharSelectInfo Set Home Point", query); + } + /* If no regular bind set, set it */ + if (has_bind == 0){ + std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", + character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0); + auto results_bset = QueryDatabase(query); ThrowDBError(results_bset.ErrorMessage(), "WorldDatabase::GetCharSelectInfo Set Bind Point", query); + } + } + /* Bind End */ + /* Character's equipped items @merth: Haven't done bracer01/bracer02 yet. diff --git a/world/worlddb.h b/world/worlddb.h index 24f43d308..ecb39ef61 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -31,7 +31,7 @@ public: bool GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc); bool GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc); - void GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*); + void GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*, uint32 ClientVersion); int MoveCharacterToBind(int CharID, uint8 bindnum = 0); void GetLauncherList(std::vector &result); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6865eadfc..755309ef6 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -571,7 +571,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { results = database.QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { m_pp.lastlogin = time(nullptr); - if (atoi(row[4]) > 0){ + if (row[4] && atoi(row[4]) > 0){ guild_id = atoi(row[4]); if (row[5] != nullptr){ guildrank = atoi(row[5]); } else{ guildrank = GUILD_RANK_NONE; } From 87bb5deb5cb84577d58465387321b08530bf4aee Mon Sep 17 00:00:00 2001 From: akkadius Date: Tue, 16 Sep 2014 16:03:27 -0500 Subject: [PATCH 095/368] Added quest::crosszonesetentityvariablebynpctypeid(npctype_id, id, m_var) Added quest::crosszonesignalnpcbynpctypeid(npctype_id, data) Added $client->GetTaskActivityDoneCount(THIS, TaskID, ActivityID) --- common/servertalk.h | 25 +++++++++++++++++++------ world/zoneserver.cpp | 4 +++- zone/client_packet.cpp | 2 +- zone/embparser_api.cpp | 39 ++++++++++++++++++++++++++++++++++++++- zone/perl_client.cpp | 30 ++++++++++++++++++++++++++++++ zone/questmgr.cpp | 23 ++++++++++++++++++++++- zone/questmgr.h | 4 +++- zone/worldserver.cpp | 18 ++++++++++++++++++ 8 files changed, 134 insertions(+), 11 deletions(-) diff --git a/common/servertalk.h b/common/servertalk.h index 5337b6b1d..687abf2d7 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -179,13 +179,15 @@ #define ServerOP_CZMessagePlayer 0x4008 #define ServerOP_ReloadWorld 0x4009 -#define ServerOP_QSPlayerLogTrades 0x4010 -#define ServerOP_QSPlayerLogHandins 0x4011 -#define ServerOP_QSPlayerLogNPCKills 0x4012 -#define ServerOP_QSPlayerLogDeletes 0x4013 -#define ServerOP_QSPlayerLogMoves 0x4014 +#define ServerOP_QSPlayerLogTrades 0x4010 +#define ServerOP_QSPlayerLogHandins 0x4011 +#define ServerOP_QSPlayerLogNPCKills 0x4012 +#define ServerOP_QSPlayerLogDeletes 0x4013 +#define ServerOP_QSPlayerLogMoves 0x4014 #define ServerOP_QSPlayerLogMerchantTransactions 0x4015 -#define ServerOP_QSSendQuery 0x4016 +#define ServerOP_QSSendQuery 0x4016 +#define ServerOP_CZSignalNPC 0x4017 +#define ServerOP_CZSetEntityVariableByNPCTypeID 0x4018 /* Query Serv Generic Packet Flag/Type Enumeration */ enum { QSG_LFGuild = 0 }; @@ -1092,6 +1094,11 @@ struct CZClientSignal_Struct { uint32 data; }; +struct CZNPCSignal_Struct { + uint32 npctype_id; + uint32 data; +}; + struct CZClientSignalByName_Struct { char Name[64]; uint32 data; @@ -1233,6 +1240,12 @@ struct CZMessagePlayer_Struct { char Message[512]; }; +struct CZSetEntVarByNPCTypeID_Struct { + uint32 npctype_id; + char id[256]; + char m_var[256]; +}; + struct ReloadWorld_Struct{ uint32 Option; }; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index c740ff3bc..c05962816 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1266,7 +1266,9 @@ bool ZoneServer::Process() { break; } case ServerOP_CZSignalClientByName: - case ServerOP_CZMessagePlayer: + case ServerOP_CZMessagePlayer: + case ServerOP_CZSignalNPC: + case ServerOP_CZSetEntityVariableByNPCTypeID: case ServerOP_CZSignalClient: { zoneserver_list.SendPacket(pack); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 755309ef6..59d43591e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -9435,7 +9435,7 @@ void Client::CompleteConnect() { if (buffs[j1].spellid >(uint32)SPDAT_RECORDS) continue; - const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; + const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { switch (spell.effectid[x1]) { diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 5adbdedbd..6e337cc03 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3402,6 +3402,41 @@ XS(XS__qs_player_event) XSRETURN_EMPTY; } +XS(XS__crosszonesetentityvariablebynpctypeid); +XS(XS__crosszonesetentityvariablebynpctypeid) +{ + dXSARGS; + + if (items != 3) + Perl_croak(aTHX_ "Usage: crosszonesetentityvariablebynpctypeid(npctype_id, id, m_var)"); + + if (items == 3) { + uint32 npctype_id = (uint32)SvIV(ST(0)); + const char *id = (const char *)SvPV_nolen(ST(1)); + const char *m_var = (const char *)SvPV_nolen(ST(2)); + quest_manager.CrossZoneSetEntityVariableByNPCTypeID(npctype_id, id, m_var); + } + + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalnpcbynpctypeid); +XS(XS__crosszonesignalnpcbynpctypeid) +{ + dXSARGS; + + if (items != 2) + Perl_croak(aTHX_ "Usage: crosszonesignalnpcbynpctypeid(npctype_id, data)"); + + if (items == 2) { + uint32 npctype_id = (uint32)SvIV(ST(0)); + uint32 data = (uint32)SvIV(ST(1)); + quest_manager.CrossZoneSignalNPCByNPCTypeID(npctype_id, data); + } + + XSRETURN_EMPTY; +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -3624,7 +3659,9 @@ EXTERN_C XS(boot_quest) newXS(strcpy(buf, "disablerecipe"), XS__disablerecipe, file); newXS(strcpy(buf, "clear_npctype_cache"), XS__clear_npctype_cache, file); newXS(strcpy(buf, "qs_send_query"), XS__qs_send_query, file); - newXS(strcpy(buf, "qs_player_event"), XS__qs_player_event, file); + newXS(strcpy(buf, "qs_player_event"), XS__qs_player_event, file); + newXS(strcpy(buf, "crosszonesetentityvariablebynpctypeid"), XS__crosszonesetentityvariablebynpctypeid, file); + newXS(strcpy(buf, "crosszonesignalnpcbynpctypeid"), XS__crosszonesignalnpcbynpctypeid, file); XSRETURN_YES; } diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 72e2865c9..03da3bc35 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5072,6 +5072,35 @@ XS(XS_Client_UpdateTaskActivity) XSRETURN_EMPTY; } +XS(XS_Client_GetTaskActivityDoneCount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetTaskActivityDoneCount) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::GetTaskActivityDoneCount(THIS, TaskID, ActivityID)"); + { + Client * THIS; + int RETVAL; + int TaskID = (int)SvIV(ST(1)); + int ActivityID = (int)SvIV(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *, tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + + RETVAL = THIS->GetTaskActivityDoneCountFromTaskID(TaskID, ActivityID); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + XS(XS_Client_AssignTask); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_AssignTask) { @@ -6199,6 +6228,7 @@ XS(boot_Client) newXSproto(strcpy(buf, "IsTaskCompleted"), XS_Client_IsTaskCompleted, file, "$$"); newXSproto(strcpy(buf, "IsTaskActive"), XS_Client_IsTaskActive, file, "$$"); newXSproto(strcpy(buf, "IsTaskActivityActive"), XS_Client_IsTaskActivityActive, file, "$$$"); + newXSproto(strcpy(buf, "GetTaskActivityDoneCount"), XS_Client_GetTaskActivityDoneCount, file, "$$$"); newXSproto(strcpy(buf, "GetCorpseCount"), XS_Client_GetCorpseCount, file, "$"); newXSproto(strcpy(buf, "GetCorpseID"), XS_Client_GetCorpseID, file, "$$"); newXSproto(strcpy(buf, "GetCorpseItemAt"), XS_Client_GetCorpseItemAt, file, "$$$"); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index e86e4d601..e779a5aa7 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2951,6 +2951,15 @@ const char* QuestManager::GetZoneLongName(const char *zone) { return ln.c_str(); } +void QuestManager::CrossZoneSignalNPCByNPCTypeID(uint32 npctype_id, uint32 data){ + ServerPacket* pack = new ServerPacket(ServerOP_CZSignalNPC, sizeof(CZNPCSignal_Struct)); + CZNPCSignal_Struct* CZSN = (CZNPCSignal_Struct*)pack->pBuffer; + CZSN->npctype_id = npctype_id; + CZSN->data = data; + worldserver.SendPacket(pack); + safe_delete(pack); +} + void QuestManager::CrossZoneSignalPlayerByCharID(int charid, uint32 data){ ServerPacket* pack = new ServerPacket(ServerOP_CZSignalClient, sizeof(CZClientSignal_Struct)); CZClientSignal_Struct* CZSC = (CZClientSignal_Struct*) pack->pBuffer; @@ -2968,7 +2977,7 @@ void QuestManager::CrossZoneSignalPlayerByName(const char *CharName, uint32 data CZSC->data = data; worldserver.SendPacket(pack); safe_delete(pack); -} +} void QuestManager::CrossZoneMessagePlayerByName(uint32 Type, const char *CharName, const char *Message){ uint32 message_len = strlen(CharName) + 1; @@ -2978,6 +2987,18 @@ void QuestManager::CrossZoneMessagePlayerByName(uint32 Type, const char *CharNam CZSC->Type = Type; strn0cpy(CZSC->CharName, CharName, 64); strn0cpy(CZSC->Message, Message, 512); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::CrossZoneSetEntityVariableByNPCTypeID(uint32 npctype_id, const char *id, const char *m_var){ + uint32 message_len = strlen(id) + 1; + uint32 message_len2 = strlen(m_var) + 1; + ServerPacket* pack = new ServerPacket(ServerOP_CZSetEntityVariableByNPCTypeID, sizeof(CZSetEntVarByNPCTypeID_Struct) + message_len + message_len2); + CZSetEntVarByNPCTypeID_Struct* CZSNBYNID = (CZSetEntVarByNPCTypeID_Struct*)pack->pBuffer; + CZSNBYNID->npctype_id = npctype_id; + strn0cpy(CZSNBYNID->id, id, 256); + strn0cpy(CZSNBYNID->m_var, m_var, 256); worldserver.SendPacket(pack); safe_delete(pack); } diff --git a/zone/questmgr.h b/zone/questmgr.h index 89a0eca78..2d1184942 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -239,8 +239,10 @@ public: uint16 CreateDoor( const char* model, float x, float y, float z, float heading, uint8 opentype, uint16 size); int32 GetZoneID(const char *zone); const char *GetZoneLongName(const char *zone); - void CrossZoneSignalPlayerByCharID(int charid, uint32 data); + void CrossZoneSignalPlayerByCharID(int charid, uint32 data); + void CrossZoneSignalNPCByNPCTypeID(uint32 npctype_id, uint32 data); void CrossZoneSignalPlayerByName(const char *CharName, uint32 data); + void CrossZoneSetEntityVariableByNPCTypeID(uint32 npctype_id, const char *id, const char *m_var); void CrossZoneMessagePlayerByName(uint32 Type, const char *CharName, const char *Message); bool EnableRecipe(uint32 recipe_id); bool DisableRecipe(uint32 recipe_id); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 152ed4f0d..92d34ff71 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1777,6 +1777,24 @@ void WorldServer::Process() { break; } + case ServerOP_CZSetEntityVariableByNPCTypeID: + { + CZSetEntVarByNPCTypeID_Struct* CZM = (CZSetEntVarByNPCTypeID_Struct*)pack->pBuffer; + NPC* n = entity_list.GetNPCByNPCTypeID(CZM->npctype_id); + if (n != 0) { + n->SetEntityVariable(CZM->id, CZM->m_var); + } + break; + } + case ServerOP_CZSignalNPC: + { + CZNPCSignal_Struct* CZCN = (CZNPCSignal_Struct*)pack->pBuffer; + NPC* n = entity_list.GetNPCByNPCTypeID(CZCN->npctype_id); + if (n != 0) { + n->SignalNPC(CZCN->data); + } + break; + } case ServerOP_CZSignalClient: { CZClientSignal_Struct* CZCS = (CZClientSignal_Struct*) pack->pBuffer; From eb497077794afbf0c460cfa21331450260980873 Mon Sep 17 00:00:00 2001 From: akkadius Date: Wed, 17 Sep 2014 02:47:13 -0500 Subject: [PATCH 096/368] Changed conversion routine. At the end of conversion, character_ table will be renamed to character_old so that it does not get hit with trying to convert next world bootup Added some file logging during the initial conversion routine --- common/database.cpp | 91 ++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index cf4fb491d..c38b108f4 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -315,9 +315,9 @@ bool Database::ThrowDBError(std::string ErrorMessage, std::string query_title, s if (ErrorMessage != ""){ std::cout << "\nERROR " << query_title << ": " << ErrorMessage << "\n\n" << query << "\n" << std::endl; - /* Write to file temporarily */ + /* Write to log file */ std::ofstream log("eqemu_query_error_log.txt", std::ios_base::app | std::ios_base::out); - log << "\nERROR " << query_title << ": " << ErrorMessage << "\n\n" << query << "\n"; + log << "ERROR " << query_title << ": " << ErrorMessage << "\n" << query << "\n"; log.close(); return true; @@ -1025,7 +1025,8 @@ bool Database::CheckDatabaseConversions() { "KEY `account_id` (`account_id`) " ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_currency` */ @@ -1056,7 +1057,8 @@ bool Database::CheckDatabaseConversions() { " KEY `id` (`id`) " " ) ENGINE=InnoDB DEFAULT CHARSET=latin1; " ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_alternate_abilities` */ @@ -1074,7 +1076,8 @@ bool Database::CheckDatabaseConversions() { " KEY `id` (`id`) " " ) ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_bind` */ @@ -1096,7 +1099,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`) " ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_languages` */ @@ -1113,7 +1117,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`) " ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_skills` */ @@ -1130,7 +1135,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`) " ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_spells` */ @@ -1147,7 +1153,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`) " ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_memmed_spells` */ @@ -1164,7 +1171,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`) " ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_disciplines` */ @@ -1181,7 +1189,8 @@ bool Database::CheckDatabaseConversions() { " KEY `id` (`id`) " " ) ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_material` */ @@ -1202,7 +1211,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`)" ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_tribute` */ @@ -1218,7 +1228,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`) " ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_bandolier` */ @@ -1238,7 +1249,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`) " ") ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_potionbelt` */ @@ -1256,7 +1268,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`) " ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_potionbelt` */ @@ -1272,7 +1285,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`) " ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } /* Check for table `character_leadership_abilities` */ @@ -1289,7 +1303,8 @@ bool Database::CheckDatabaseConversions() { "KEY `id` (`id`) " ") ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); - QueryDatabase(rquery); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); printf(" done...\n"); } @@ -1356,7 +1371,7 @@ bool Database::CheckDatabaseConversions() { EscapeString(inspectmessage).c_str() ); auto results = QueryDatabase(rquery); - if (!results.RowsAffected()){ std::cout << "ERROR Inspect Messages Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + ThrowDBError(results.ErrorMessage(), "Character Inspect Message Convert", rquery); } /* Run Currency Convert */ @@ -1384,6 +1399,7 @@ bool Database::CheckDatabaseConversions() { pp->careerEbonCrystals ); auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Character Currency Convert", rquery); if (pp->tribute_time_remaining < 0 || pp->tribute_time_remaining == 4294967295){ pp->tribute_time_remaining = 0; } @@ -1688,8 +1704,9 @@ bool Database::CheckDatabaseConversions() { e_pp->perAA, e_pp->expended_aa ); - results = QueryDatabase(rquery); - if (!results.RowsAffected()){ std::cout << "ERROR PP Data Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Character Data Convert", rquery); + /* We set a first entry variable because we need the first initial piece of the query to be declared @@ -1708,19 +1725,19 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); } } - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR AA Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } - + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error AA Convert", rquery); } + /* Run Bind Home Convert */ rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, 1)", character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Bind Home Convert", rquery); } /* Run Bind Convert */ rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, 0)", character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Bind Convert", rquery); } /* Run Language Convert */ first_entry = 0; rquery = ""; @@ -1733,7 +1750,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->languages[i]); } } - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Language Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Language Convert", rquery); } /* Run Skill Convert */ first_entry = 0; rquery = ""; for (i = 0; i < MAX_PP_SKILL; i++){ @@ -1745,7 +1762,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->skills[i]); } } - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Skill Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Skills Convert Convert", rquery); } /* Run Spell Convert */ first_entry = 0; rquery = ""; for (i = 0; i < MAX_PP_REF_SPELLBOOK; i++){ @@ -1758,7 +1775,7 @@ bool Database::CheckDatabaseConversions() { } } // std::cout << rquery << "\n"; - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Spell Convert", rquery); } /* Run Max Memmed Spell Convert */ first_entry = 0; rquery = ""; for (i = 0; i < MAX_PP_REF_MEMSPELL; i++){ @@ -1770,7 +1787,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->mem_spells[i]); } } - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Memmed Spell Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Memmed Spells Convert", rquery); } /* Run Discipline Convert */ first_entry = 0; rquery = ""; for (i = 0; i < MAX_PP_DISCIPLINES; i++){ @@ -1782,7 +1799,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); } } - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Discipline Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Discipline Convert", rquery); } /* Run Material Color Convert */ first_entry = 0; rquery = ""; for (i = 0; i < _MaterialCount; i++){ @@ -1794,7 +1811,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); } } - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Color Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Material Convert", rquery); } /* Run Tribute Convert */ first_entry = 0; rquery = ""; for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ @@ -1806,7 +1823,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); } } - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Tribute Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Tribute Convert", rquery); } /* Run Bandolier Convert */ first_entry = 0; rquery = ""; for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ @@ -1820,7 +1837,7 @@ bool Database::CheckDatabaseConversions() { } } } - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Bandolier Convert", rquery); } /* Run Potion Belt Convert */ first_entry = 0; rquery = ""; for (i = 0; i <= EmuConstants::POTION_BELT_SIZE; i++){ @@ -1833,7 +1850,7 @@ bool Database::CheckDatabaseConversions() { } } - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Potion Belt Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Potion Belt Convert", rquery); } /* Run Leadership AA Convert */ first_entry = 0; rquery = ""; for (i = 0; i <= MAX_LEADERSHIP_AA_ARRAY; i++){ @@ -1845,10 +1862,14 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); } } - if (rquery != ""){ results = QueryDatabase(rquery); } if (!results.RowsAffected()){ std::cout << "ERROR Leadership AA Convert: " << results.ErrorMessage() << "\n\n" << rquery << "\n" << std::endl; } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Leadership AA Convert", rquery); } } } - if (runconvert == 1){ printf("\n\nCharacter blob conversion complete, continuing world bootup...\n"); } + if (runconvert == 1){ + std::string rquery = StringFormat("RENAME TABLE `character_` TO `character_old`"); QueryDatabase(rquery); + printf("\n\nRenaming `character_` table to `character_old`, this is a LARGE table so when you don't need it anymore, I would suggest deleting it yourself...\n"); + printf("\n\nCharacter blob conversion complete, continuing world bootup...\n"); + } return true; } From f30ae9dd5ae8a63f3b569603f65bc69f1e75128d Mon Sep 17 00:00:00 2001 From: akkadius Date: Wed, 17 Sep 2014 02:52:13 -0500 Subject: [PATCH 097/368] Some descriptor adjustments --- common/database.cpp | 58 ++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index c38b108f4..c44c32157 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1026,7 +1026,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_currency` */ @@ -1058,7 +1058,7 @@ bool Database::CheckDatabaseConversions() { " ) ENGINE=InnoDB DEFAULT CHARSET=latin1; " ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_alternate_abilities` */ @@ -1077,7 +1077,7 @@ bool Database::CheckDatabaseConversions() { " ) ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_bind` */ @@ -1100,7 +1100,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_languages` */ @@ -1118,7 +1118,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_skills` */ @@ -1136,7 +1136,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_spells` */ @@ -1154,7 +1154,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_memmed_spells` */ @@ -1172,7 +1172,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_disciplines` */ @@ -1190,7 +1190,7 @@ bool Database::CheckDatabaseConversions() { " ) ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_material` */ @@ -1212,7 +1212,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_tribute` */ @@ -1229,7 +1229,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_bandolier` */ @@ -1250,7 +1250,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_potionbelt` */ @@ -1269,7 +1269,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_potionbelt` */ @@ -1286,7 +1286,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } /* Check for table `character_leadership_abilities` */ @@ -1304,7 +1304,7 @@ bool Database::CheckDatabaseConversions() { ") ENGINE = InnoDB DEFAULT CHARSET = latin1; " ); auto results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", rquery); + ThrowDBError(results.ErrorMessage(), "Table create", rquery); printf(" done...\n"); } @@ -1325,7 +1325,7 @@ bool Database::CheckDatabaseConversions() { std::string inspectmessage; for (auto row = results.begin(); row != results.end(); ++row) { - char_iter_count++; + char_iter_count++; squery = StringFormat("SELECT `id`, `profile`, `name`, `level`, `account_id`, `firstlogon`, `lfg`, `lfp`, `mailkey`, `xtargets`, `inspectmessage`, `extprofile` FROM `character_` WHERE `id` = %i", atoi(row[0])); auto results2 = QueryDatabase(squery); auto row2 = results2.begin(); @@ -1725,19 +1725,19 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); } } - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error AA Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "AA Convert", rquery); } /* Run Bind Home Convert */ rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, 1)", character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Bind Home Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Bind Home Convert", rquery); } /* Run Bind Convert */ rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, 0)", character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Bind Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Bind Convert", rquery); } /* Run Language Convert */ first_entry = 0; rquery = ""; @@ -1750,7 +1750,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->languages[i]); } } - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Language Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Language Convert", rquery); } /* Run Skill Convert */ first_entry = 0; rquery = ""; for (i = 0; i < MAX_PP_SKILL; i++){ @@ -1762,7 +1762,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->skills[i]); } } - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Skills Convert Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Skills Convert Convert", rquery); } /* Run Spell Convert */ first_entry = 0; rquery = ""; for (i = 0; i < MAX_PP_REF_SPELLBOOK; i++){ @@ -1775,7 +1775,7 @@ bool Database::CheckDatabaseConversions() { } } // std::cout << rquery << "\n"; - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Spell Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Spell Convert", rquery); } /* Run Max Memmed Spell Convert */ first_entry = 0; rquery = ""; for (i = 0; i < MAX_PP_REF_MEMSPELL; i++){ @@ -1787,7 +1787,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->mem_spells[i]); } } - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Memmed Spells Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Memmed Spells Convert", rquery); } /* Run Discipline Convert */ first_entry = 0; rquery = ""; for (i = 0; i < MAX_PP_DISCIPLINES; i++){ @@ -1799,7 +1799,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); } } - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Discipline Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Discipline Convert", rquery); } /* Run Material Color Convert */ first_entry = 0; rquery = ""; for (i = 0; i < _MaterialCount; i++){ @@ -1811,7 +1811,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); } } - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Material Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Material Convert", rquery); } /* Run Tribute Convert */ first_entry = 0; rquery = ""; for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ @@ -1823,7 +1823,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); } } - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Tribute Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Tribute Convert", rquery); } /* Run Bandolier Convert */ first_entry = 0; rquery = ""; for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ @@ -1837,7 +1837,7 @@ bool Database::CheckDatabaseConversions() { } } } - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Bandolier Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Bandolier Convert", rquery); } /* Run Potion Belt Convert */ first_entry = 0; rquery = ""; for (i = 0; i <= EmuConstants::POTION_BELT_SIZE; i++){ @@ -1850,7 +1850,7 @@ bool Database::CheckDatabaseConversions() { } } - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Potion Belt Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Potion Belt Convert", rquery); } /* Run Leadership AA Convert */ first_entry = 0; rquery = ""; for (i = 0; i <= MAX_LEADERSHIP_AA_ARRAY; i++){ @@ -1862,7 +1862,7 @@ bool Database::CheckDatabaseConversions() { rquery = rquery + StringFormat(", (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); } } - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Error Character Leadership AA Convert", rquery); } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Leadership AA Convert", rquery); } } } if (runconvert == 1){ From 63ca4cac5e6edab3797cfdb21bbebfce88c800fe Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 17 Sep 2014 05:14:10 -0400 Subject: [PATCH 098/368] Fix for calculation for SE_ManaAbsorbPercentDamage --- zone/attack.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index a515a08c4..d52d2a6ba 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3331,10 +3331,13 @@ int32 Mob::ReduceAllDamage(int32 damage) if(damage <= 0) return damage; - if(spellbonuses.ManaAbsorbPercentDamage[0] && (GetMana() > damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100)) { - damage -= (damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100); - SetMana(GetMana() - damage); - TryTriggerOnValueAmount(false, true); + if(spellbonuses.ManaAbsorbPercentDamage[0]) { + int32 mana_reduced = damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100; + if (GetMana() >= mana_reduced){ + damage -= mana_reduced; + SetMana(GetMana() - mana_reduced); + TryTriggerOnValueAmount(false, true); + } } CheckNumHitsRemaining(NUMHIT_IncomingDamage); From d169d95ab02f5fd32d30671eb191254cd4dc1dbb Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 17 Sep 2014 12:08:01 -0400 Subject: [PATCH 099/368] Fix for resource tap when gaining/loosing HP. --- zone/spell_effects.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f0bc8498b..5f85de374 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6453,8 +6453,12 @@ void Mob::ResourceTap(int32 damage, uint16 spellid){ if (spells[spellid].max[i] && (damage > spells[spellid].max[i])) damage = spells[spellid].max[i]; - if (spells[spellid].base2[i] == 0) //HP Tap - SetHP((GetHP()+ damage)); + if (spells[spellid].base2[i] == 0){ //HP Tap + if (damage > 0) + HealDamage(damage); + else + Damage(this, -damage,0, SkillEvocation,false); + } if (spells[spellid].base2[i] == 1) //Mana Tap SetMana(GetMana() + damage); From 81722962cf672c917b70e1c00384539d2804679e Mon Sep 17 00:00:00 2001 From: akkadius Date: Wed, 17 Sep 2014 14:30:19 -0500 Subject: [PATCH 100/368] KLS Nazi Grammar Fix --- common/database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/database.cpp b/common/database.cpp index c44c32157..a3a279af5 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -896,7 +896,7 @@ bool Database::CheckDatabaseConversions() { printf(" Database currently has character data being stored via \n"); printf(" the legacy character storage method and will proceed with converting...\n\n"); printf(" It is recommended that you backup your database \n"); - printf(" before continuing the automatic conversion proces...\n\n"); + printf(" before continuing the automatic conversion process...\n\n"); printf("----------------------------------------------------------\n\n"); std::cout << "Press ENTER to continue....." << std::endl << std::endl; std::cin.ignore(1); From 6754dfdf6d7ca4094de30769903a9a673633b1ed Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 17 Sep 2014 17:45:40 -0700 Subject: [PATCH 101/368] Small sanity checks that was causing a few errors --- common/database.cpp | 50 +++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index c44c32157..006242b89 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include // Disgrace: for windows compile @@ -1345,10 +1346,12 @@ bool Database::CheckDatabaseConversions() { lengths = results2.LengthOfColumn(1); if (lengths == sizeof(PlayerProfile_Struct)) { /* If PP is the size it is expected to be */ memcpy(pp, row2[1], sizeof(PlayerProfile_Struct)); - // std::cout << "SIZE OK\n" << std::endl; } /* Continue of PP Size does not match (Usually a created character never logged in) */ - else { continue; } + else { + printf("\nCharacter %s(%u) was missing profile data, character not converted.", row2[2] ? row2[2] : "Unknown", character_id); + continue; + } lengths_e = results2.LengthOfColumn(11); if (lengths_e == sizeof(ExtendedProfile_Struct)) { @@ -1356,8 +1359,6 @@ bool Database::CheckDatabaseConversions() { } if (e_pp->expended_aa > 4000000){ e_pp->expended_aa = 0; } - // std::cout << "Expended AA: " << e_pp->expended_aa << "\n" << std::endl; - /* Loading Status on conversion */ if (runconvert == 1){ std::cout << "\r" << char_iter_count << "/" << number_of_characters << " " << std::flush; @@ -1663,7 +1664,7 @@ bool Database::CheckDatabaseConversions() { pp->ability_up, pp->zone_id, pp->zoneInstance, - pp->leadAAActive, + pp->leadAAActive == 0 ? 0 : 1, pp->ldon_points_guk, pp->ldon_points_mir, pp->ldon_points_mmc, @@ -1728,17 +1729,20 @@ bool Database::CheckDatabaseConversions() { if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "AA Convert", rquery); } /* Run Bind Home Convert */ - rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" - " VALUES (%u, %u, %u, %f, %f, %f, %f, 1)", - character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Bind Home Convert", rquery); } + if(pp->binds[4].zoneId < 999 && !std::isnan(pp->binds[4].x) && !std::isnan(pp->binds[4].y) && !std::isnan(pp->binds[4].z) && !std::isnan(pp->binds[4].heading)) { + rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, 1)", + character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Bind Home Convert", rquery); } + } /* Run Bind Convert */ - rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" - " VALUES (%u, %u, %u, %f, %f, %f, %f, 0)", - character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); - if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Bind Convert", rquery); } - + if(pp->binds[0].zoneId < 999 && !std::isnan(pp->binds[0].x) && !std::isnan(pp->binds[0].y) && !std::isnan(pp->binds[0].z) && !std::isnan(pp->binds[0].heading)) { + rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, 0)", + character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Bind Convert", rquery); } + } /* Run Language Convert */ first_entry = 0; rquery = ""; for (i = 0; i < MAX_PP_LANGUAGE; i++){ @@ -1791,7 +1795,7 @@ bool Database::CheckDatabaseConversions() { /* Run Discipline Convert */ first_entry = 0; rquery = ""; for (i = 0; i < MAX_PP_DISCIPLINES; i++){ - if (pp->disciplines.values[i] > 0){ + if(pp->disciplines.values[i] > 0 && pp->disciplines.values[i] < 60000){ if (first_entry != 1){ rquery = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); first_entry = 1; @@ -1827,13 +1831,15 @@ bool Database::CheckDatabaseConversions() { /* Run Bandolier Convert */ first_entry = 0; rquery = ""; for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ - for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ - if (pp->bandoliers[i].items[si].item_id > 0){ - if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); - first_entry = 1; + if(strlen(pp->bandoliers[i].name) < 32) { + for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ + if (pp->bandoliers[i].items[si].item_id > 0){ + if (first_entry != 1) { + rquery = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); } - rquery = rquery + StringFormat(", (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); } } } @@ -1854,7 +1860,7 @@ bool Database::CheckDatabaseConversions() { /* Run Leadership AA Convert */ first_entry = 0; rquery = ""; for (i = 0; i <= MAX_LEADERSHIP_AA_ARRAY; i++){ - if (pp->leader_abilities.ranks[i] > 0){ + if(pp->leader_abilities.ranks[i] > 0 && pp->leader_abilities.ranks[i] < 6){ if (first_entry != 1){ rquery = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, rank) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); first_entry = 1; From 64f5bfd5ceb0220995ab8d2821048285eb4f8c0c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 17 Sep 2014 23:46:54 -0400 Subject: [PATCH 102/368] Make tell message a bit more understandable. --- world/zoneserver.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 7c4f4f963..dbfb98fee 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -437,13 +437,13 @@ bool ZoneServer::Process() { Console* con = 0; con = console_list.FindByAccountName(&scm->deliverto[1]); if (((!con) || (!con->SendChannelMessage(scm))) && (!scm->noreply)) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); break; } ClientListEntry* cle = client_list.FindCharacter(scm->deliverto); if (cle == 0 || cle->Online() < CLE_Status_Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { if (!scm->noreply) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); } else if (cle->Online() == CLE_Status_Zoning) { if (!scm->noreply) @@ -460,7 +460,7 @@ bool ZoneServer::Process() { break; if (results.RowCount() == 0) { - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); break; } @@ -470,9 +470,9 @@ bool ZoneServer::Process() { telldate, scm->deliverto, scm->from, scm->message); results = database.QueryDatabase(query); if (results.Success()) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to the %s's que.", scm->to); + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to %s's queue.", scm->to); else - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to, scm->to); } // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); From 347ae1bc341e6f3f44b778e7a724f3f237dd08e4 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 18 Sep 2014 01:29:30 -0400 Subject: [PATCH 103/368] Fix error in 64f5bfd --- world/zoneserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index dbfb98fee..870bd2447 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -472,7 +472,7 @@ bool ZoneServer::Process() { if (results.Success()) zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to %s's queue.", scm->to); else - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to, scm->to); + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); } // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); From 3d6bb964df6156116d2025f088387e27c76a2bbb Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 18 Sep 2014 14:59:50 -0400 Subject: [PATCH 104/368] Stop nuking MySQLRequestResult Success flag --- common/mysql_request_result.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/mysql_request_result.cpp b/common/mysql_request_result.cpp index ffbc3ef40..d2a3afd65 100644 --- a/common/mysql_request_result.cpp +++ b/common/mysql_request_result.cpp @@ -10,7 +10,7 @@ MySQLRequestResult::MySQLRequestResult() MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, uint32 rowCount, uint32 columnCount, uint32 lastInsertedID, uint32 errorNumber, char *errorBuffer) : m_CurrentRow(result), m_OneBeyondRow() { - m_Result = result; + m_Result = result; m_RowsAffected = rowsAffected; m_RowCount = rowCount; m_ColumnCount = columnCount; @@ -22,12 +22,12 @@ MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, u m_ColumnLengths = nullptr; m_Fields = nullptr; - if (errorBuffer != nullptr) + m_Success = true; + if (errorBuffer != nullptr) m_Success = false; - m_Success = true; - m_ErrorNumber = errorNumber; - m_ErrorBuffer = errorBuffer; + m_ErrorNumber = errorNumber; + m_ErrorBuffer = errorBuffer; } void MySQLRequestResult::FreeInternals() From a2368b4ea763ccb24f62ccec2342f6607dbfe7d1 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 18 Sep 2014 22:55:06 -0400 Subject: [PATCH 105/368] Implement tell queues Default queue size 20 (World:TellQueueSize) This doe not play well with multiple sessions and a toon crashes and relogs Normal tells have issues as well. --- changelog.txt | 7 +++ common/ruletypes.h | 1 + common/servertalk.h | 5 ++ .../git/optional/2014_09_18_TellQueueRule.sql | 1 + .../required/2014_09_18_tellqueuesclean.sql | 1 + world/cliententry.cpp | 20 ++++++ world/cliententry.h | 10 +++ world/zoneserver.cpp | 62 +++++++++---------- zone/client_packet.cpp | 2 + zone/worldserver.cpp | 16 +++++ zone/worldserver.h | 2 + 11 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 utils/sql/git/optional/2014_09_18_TellQueueRule.sql create mode 100644 utils/sql/git/required/2014_09_18_tellqueuesclean.sql diff --git a/changelog.txt b/changelog.txt index a006c9a50..b4384d55b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/18/2014== +demonstar55: Implement tell queues + Currently set to a limit of 20 by default (World:TellQueueSize) I was unable to hit the limit on live though (100+) + The required SQL nukes the old tell queue table, which may or may not be in your DB + Optional SQL adds the rule to the DB to allow easy of change + Note: this does not play well with multiple sessions with the same name on (crash and relog and have multiple sessions) but normal tells don't play well either + == 09/16/2014 == demonstar55: Implement spell formula 137 (BER AA Desperation) Uleat (NateDog): Fix for LoadBuffs() crash when a spell with a non-persistent Illusion effect was loaded. diff --git a/common/ruletypes.h b/common/ruletypes.h index c61590ac4..95fa19854 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -171,6 +171,7 @@ RULE_INT ( World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = R RULE_BOOL (World, IsGMPetitionWindowEnabled, false) RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items. RULE_BOOL (World, IPLimitDisconnectAll, false) +RULE_INT (World, TellQueueSize, 20) RULE_CATEGORY_END() RULE_CATEGORY( Zone ) diff --git a/common/servertalk.h b/common/servertalk.h index 5337b6b1d..03771eeef 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -83,6 +83,7 @@ #define ServerOP_QGlobalUpdate 0x0063 #define ServerOP_QGlobalDelete 0x0064 #define ServerOP_DepopPlayerCorpse 0x0065 +#define ServerOP_RequestTellQueue 0x0066 // client asks for it's tell queues #define ServerOP_RaidAdd 0x0100 //in use #define ServerOP_RaidRemove 0x0101 //in use @@ -1237,6 +1238,10 @@ struct ReloadWorld_Struct{ uint32 Option; }; +struct ServerRequestTellQueue_Struct { + char name[64]; +}; + #pragma pack() #endif diff --git a/utils/sql/git/optional/2014_09_18_TellQueueRule.sql b/utils/sql/git/optional/2014_09_18_TellQueueRule.sql new file mode 100644 index 000000000..2a9b43611 --- /dev/null +++ b/utils/sql/git/optional/2014_09_18_TellQueueRule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'World:TellQueueSize', '20', 'Maximum tell queue size.'); diff --git a/utils/sql/git/required/2014_09_18_tellqueuesclean.sql b/utils/sql/git/required/2014_09_18_tellqueuesclean.sql new file mode 100644 index 000000000..04d9b38d1 --- /dev/null +++ b/utils/sql/git/required/2014_09_18_tellqueuesclean.sql @@ -0,0 +1 @@ +DROP TABLE `tellque`; diff --git a/world/cliententry.cpp b/world/cliententry.cpp index d4b85acc3..fa5b60c43 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -93,6 +93,7 @@ ClientListEntry::~ClientListEntry() { Camp(); // updates zoneserver's numplayers client_list.RemoveCLEReferances(this); } + tell_queue.clear(); } void ClientListEntry::SetChar(uint32 iCharID, const char* iCharName) { @@ -233,6 +234,7 @@ void ClientListEntry::ClearVars(bool iAll) { pLFG = 0; gm = 0; pClientVersion = 0; + tell_queue.clear(); } void ClientListEntry::Camp(ZoneServer* iZS) { @@ -295,3 +297,21 @@ bool ClientListEntry::CheckAuth(uint32 id, const char* iKey, uint32 ip) { return false; } +void ClientListEntry::ProcessTellQueue() +{ + if (!Server()) + return; + + ServerPacket *pack; + auto it = tell_queue.begin(); + while (it != tell_queue.end()) { + pack = new ServerPacket(ServerOP_ChannelMessage, sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1); + memcpy(pack->pBuffer, *it, pack->size); + pack->Deflate(); + Server()->SendPacket(pack); + safe_delete(pack); + it = tell_queue.erase(it); + } + return; +} + diff --git a/world/cliententry.h b/world/cliententry.h index dcf79ca4f..cb096950c 100644 --- a/world/cliententry.h +++ b/world/cliententry.h @@ -5,6 +5,8 @@ #include "../common/md5.h" //#include "../common/eq_packet_structs.h" #include "../common/servertalk.h" +#include "../common/rulesys.h" +#include #define CLE_Status_Never -1 @@ -80,6 +82,11 @@ public: inline const char* GetLFGComments() const { return pLFGComments; } inline uint8 GetClientVersion() { return pClientVersion; } + inline bool TellQueueFull() const { return tell_queue.size() >= RuleI(World, TellQueueSize); } + inline bool TellQueueEmpty() const { return tell_queue.empty(); } + inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); } + void ProcessTellQueue(); + private: void ClearVars(bool iAll = false); @@ -120,6 +127,9 @@ private: uint8 pLFGToLevel; bool pLFGMatchFilter; char pLFGComments[64]; + + // Tell Queue -- really a vector :D + std::vector tell_queue; }; #endif /*CLIENTENTRY_H_*/ diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 870bd2447..211d6f088 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -441,41 +441,27 @@ bool ZoneServer::Process() { break; } ClientListEntry* cle = client_list.FindCharacter(scm->deliverto); - if (cle == 0 || cle->Online() < CLE_Status_Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { + if (cle == 0 || cle->Online() < CLE_Status_Zoning || + (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { if (!scm->noreply) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); - } - else if (cle->Online() == CLE_Status_Zoning) { - if (!scm->noreply) - { - time_t rawtime; - struct tm * timeinfo; - time ( &rawtime ); - timeinfo = localtime ( &rawtime ); - char *telldate=asctime(timeinfo); - - std::string query = StringFormat("SELECT name FROM character_ WHERE name = '%s'",scm->deliverto); - auto results = database.QueryDatabase(query); - if (!results.Success()) - break; - - if (results.RowCount() == 0) { - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); - break; - } - - query = StringFormat("INSERT INTO tellque " - "(Date, Receiver, Sender, Message) " - "VALUES('%s', '%s', '%s', '%s')", - telldate, scm->deliverto, scm->from, scm->message); - results = database.QueryDatabase(query); - if (results.Success()) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to %s's queue.", scm->to); - else - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); - + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, + "%s is not online at this time.", scm->to); + } else if (cle->Online() == CLE_Status_Zoning) { + if (!scm->noreply) { + if (cle->TellQueueFull()) { + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, + "%s's tell queue is full.", scm->to); + } else { + size_t struct_size = sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1; + ServerChannelMessage_Struct *temp = (ServerChannelMessage_Struct *) new uchar[struct_size]; + memset(temp, 0, struct_size); // just in case, was seeing some corrupt messages, but it shouldn't happen + memcpy(temp, scm, struct_size); + temp->noreply = true; + cle->PushToTellQueue(temp); // deallocation is handled in processing or deconstructor + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, + "Your message has been added to %s's queue.", scm->to); + } } - // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); } else if (cle->Server() == 0) { if (!scm->noreply) @@ -1319,6 +1305,16 @@ bool ZoneServer::Process() { zoneserver_list.SendPacket(pack); break; } + case ServerOP_RequestTellQueue: + { + ServerRequestTellQueue_Struct* rtq = (ServerRequestTellQueue_Struct*) pack->pBuffer; + ClientListEntry *cle = client_list.FindCharacter(rtq->name); + if (!cle || cle->TellQueueEmpty()) + break; + + cle->ProcessTellQueue(); + break; + } default: { zlog(WORLD__ZONE_ERR,"Unknown ServerOPcode from zone 0x%04x, size %d",pack->opcode,pack->size); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 261615169..1c98a538f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -9834,6 +9834,8 @@ void Client::CompleteConnect() { } entity_list.RefreshClientXTargets(this); + + worldserver.RequestTellQueue(GetName()); } void Client::Handle_OP_KeyRing(const EQApplicationPacket *app) diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 791a34ee4..be14e7b93 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -2180,3 +2180,19 @@ void WorldServer::HandleLFPMatches(ServerPacket *pack) { safe_delete(outapp); } } + +void WorldServer::RequestTellQueue(const char *who) +{ + if (!who) + return; + + ServerPacket* pack = new ServerPacket(ServerOP_RequestTellQueue, sizeof(ServerRequestTellQueue_Struct)); + ServerRequestTellQueue_Struct* rtq = (ServerRequestTellQueue_Struct*) pack->pBuffer; + + strn0cpy(rtq->name, who, sizeof(rtq->name)); + + SendPacket(pack); + safe_delete(pack); + return; +} + diff --git a/zone/worldserver.h b/zone/worldserver.h index 3c2e03fa6..6feead496 100644 --- a/zone/worldserver.h +++ b/zone/worldserver.h @@ -57,6 +57,8 @@ public: void HandleLFGMatches(ServerPacket *pack); void HandleLFPMatches(ServerPacket *pack); + void RequestTellQueue(const char *who); + private: virtual void OnConnected(); From d26782b0934925f36499594d34a697da1a87917a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 18 Sep 2014 22:56:16 -0400 Subject: [PATCH 106/368] Nuke #viewmessages --- zone/command.cpp | 49 ------------------------------------------------ zone/command.h | 1 - 2 files changed, 50 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index cf180f2bd..b2f08d3eb 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -333,8 +333,6 @@ int command_init(void) { command_add("guilds",nullptr,0,command_guild) || command_add("zonestatus","- Show connected zoneservers, synonymous with /servers",150,command_zonestatus) || command_add("manaburn","- Use AA Wizard class skill manaburn on target",10,command_manaburn) || - command_add("viewmessage","[id] - View messages in your tell queue",100,command_viewmessage) || - command_add("viewmessages",nullptr,0,command_viewmessage) || command_add("doanim","[animnum] [type] - Send an EmoteAnim for you or your target",50,command_doanim) || command_add("randomfeatures","- Temporarily randomizes the Facial Features of your target",80,command_randomfeatures) || command_add("rf",nullptr,80,command_randomfeatures) || @@ -5294,53 +5292,6 @@ void command_manaburn(Client *c, const Seperator *sep) } } -void command_viewmessage(Client *c, const Seperator *sep) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(sep->arg[1][0]==0) - { - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT id,date,receiver,sender,message from tellque where receiver='%s'",c->GetName()), errbuf, &result)) - { - if (mysql_num_rows(result)>0) - { - c->Message(0,"You have messages waiting for you to view."); - c->Message(0,"Type #Viewmessage to view the message."); - c->Message(0," ID , Message Sent Date, Message Sender"); - while ((row = mysql_fetch_row(result))) - c->Message(0,"ID: %s Sent Date: %s Sender: %s ",row[0],row[1],row[3]); - } - else - c->Message(0,"You have no new messages"); - mysql_free_result(result); - } - safe_delete_array(query); - } - else - { - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT id,date,receiver,sender,message from tellque where id=%s",sep->argplus[1]), errbuf, &result)) - { - if (mysql_num_rows(result)==1) - { - row = mysql_fetch_row(result); - mysql_free_result(result); - if (strcasecmp((const char *) c->GetName(), (const char *) row[2]) == 0) - { - c->Message(15,"ID: %s,Sent Date: %s,Sender: %s,Message: %s",row[0],row[1],row[3],row[4]); - database.RunQuery(query, MakeAnyLenString(&query, "Delete from tellque where id=%s",row[0]), errbuf); - } - else - c->Message(13,"Invalid Message Number, check the number and try again."); - } - else - c->Message(13,"Invalid Message Number, check the number and try again."); - } - safe_delete_array(query); - } -} - void command_doanim(Client *c, const Seperator *sep) { if (!sep->IsNumber(1)) diff --git a/zone/command.h b/zone/command.h index 4846dd09d..9be88bbe7 100644 --- a/zone/command.h +++ b/zone/command.h @@ -217,7 +217,6 @@ void command_guild(Client *c, const Seperator *sep); bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value); void command_zonestatus(Client *c, const Seperator *sep); void command_manaburn(Client *c, const Seperator *sep); -void command_viewmessage(Client *c, const Seperator *sep); void command_doanim(Client *c, const Seperator *sep); void command_randomfeatures(Client *c, const Seperator *sep); void command_face(Client *c, const Seperator *sep); From 52608d9b2dbbc5e069e3b2a6e3bea4874788569f Mon Sep 17 00:00:00 2001 From: akkadius Date: Thu, 18 Sep 2014 22:46:28 -0500 Subject: [PATCH 107/368] Character armor dye save fix --- zone/zonedb.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 7ebf6f084..4878b6644 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1217,7 +1217,11 @@ bool ZoneDatabase::SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, u } bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color){ - std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, color, use_tint) VALUES (%u, %u, %u, 255)", character_id, slot_id, color); auto results = QueryDatabase(query); + uint8 red = (color & 0x00FF0000) >> 16; + uint8 green = (color & 0x0000FF00) >> 8; + uint8 blue = (color & 0x000000FF); + + std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, red, green, blue, color, use_tint) VALUES (%u, %u, %u, %u, %u, %u, 255)", character_id, slot_id, red, green, blue, color); auto results = QueryDatabase(query); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterMaterialColor for character ID: %i, slot_id: %u color: %u done", character_id, slot_id, color); ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterMaterialColor", query); return true; From 8e43134bdafa1b44a1238036a644621d8eb0992c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 19 Sep 2014 18:16:52 -0400 Subject: [PATCH 108/368] Add Client::Tell_StringID for tell queue messages --- changelog.txt | 3 +++ zone/client.cpp | 8 ++++++++ zone/client.h | 1 + zone/string_ids.h | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/changelog.txt b/changelog.txt index b4384d55b..5a00e90f0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/19/2014 == +demonstar55: Added Client::Tell_StringID (used in tell queue messages) + == 09/18/2014== demonstar55: Implement tell queues Currently set to a limit of 20 by default (World:TellQueueSize) I was unable to hit the limit on live though (100+) diff --git a/zone/client.cpp b/zone/client.cpp index 345bec4ca..f3d550fd2 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3134,6 +3134,14 @@ void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType fil safe_delete(outapp); } +void Client::Tell_StringID(uint32 string_id, const char *who, const char *message) +{ + char string_id_str[10]; + snprintf(string_id_str, 10, "%d", string_id); + + Message_StringID(MT_TellEcho, TELL_QUEUED_MESSAGE, who, string_id_str, message); +} + void Client::SetTint(int16 in_slot, uint32 color) { Color_Struct new_color; new_color.color = color; diff --git a/zone/client.h b/zone/client.h index 697e4e2d3..430216900 100644 --- a/zone/client.h +++ b/zone/client.h @@ -260,6 +260,7 @@ public: const char *message5 = nullptr, const char *message6 = nullptr, const char *message7 = nullptr, const char *message8 = nullptr, const char *message9 = nullptr); + void Tell_StringID(uint32 string_id, const char *who, const char *message); void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice); void SendTraderItem(uint32 item_id,uint16 quantity); uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity); diff --git a/zone/string_ids.h b/zone/string_ids.h index 056513874..a443fabd9 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -249,6 +249,8 @@ #define PLAYER_CHARMED 1461 //You lose control of yourself! #define TRADER_BUSY 1468 //That Trader is currently with a customer. Please wait until their transaction is finished. #define SENSE_CORPSE_DIRECTION 1563 //You sense a corpse in this direction. +#define QUEUED_TELL 2458 //[queued] +#define QUEUE_TELL_FULL 2459 //[zoing and queue is full] #define SUSPEND_MINION_UNSUSPEND 3267 //%1 tells you, 'I live again...' #define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.' #define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets. @@ -269,6 +271,8 @@ #define CORPSEDRAG_STOP 4066 //You stop dragging the corpse. #define TARGET_TOO_CLOSE 4602 //You are too close to your target. Get farther away. #define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters. +#define TELL_QUEUED_MESSAGE 5045 //You told %1 '%T2. %3' +#define TOLD_NOT_ONLINE 5046 //%1 is not online at this time. #define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. #define PETITION_DELETED 5054 //Your petition was successfully deleted. #define GAIN_RAIDEXP 5085 //You gained raid experience! From 9b70b73759f464cdffc73868ef277643efd28bf3 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 19 Sep 2014 18:17:42 -0400 Subject: [PATCH 109/368] Correct tell queue related messages --- changelog.txt | 1 + common/servertalk.h | 1 + world/zoneserver.cpp | 31 ++++++++++++++++++++++++------- zone/worldserver.cpp | 23 +++++++++++++++-------- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/changelog.txt b/changelog.txt index 5a00e90f0..493e57152 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 09/19/2014 == demonstar55: Added Client::Tell_StringID (used in tell queue messages) +demonstar55: Tell queues (and offline) messages now show correctly == 09/18/2014== demonstar55: Implement tell queues diff --git a/common/servertalk.h b/common/servertalk.h index 03771eeef..a649fe434 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -347,6 +347,7 @@ struct ServerChannelMessage_Struct { uint16 chan_num; uint32 guilddbid; uint16 language; + uint8 queued; // 0 = not queued, 1 = queued, 2 = queue full, 3 = offline char message[0]; }; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 211d6f088..8f37e15a6 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -443,14 +443,26 @@ bool ZoneServer::Process() { ClientListEntry* cle = client_list.FindCharacter(scm->deliverto); if (cle == 0 || cle->Online() < CLE_Status_Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { - if (!scm->noreply) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, - "%s is not online at this time.", scm->to); + if (!scm->noreply) { + ClientListEntry* sender = client_list.FindCharacter(scm->from); + if (!sender) + break; + scm->noreply = true; + scm->queued = 3; // offline + strcpy(scm->deliverto, scm->from); + // ideally this would be trimming off the message too, oh well + sender->Server()->SendPacket(pack); + } } else if (cle->Online() == CLE_Status_Zoning) { if (!scm->noreply) { + ClientListEntry* sender = client_list.FindCharacter(scm->from); if (cle->TellQueueFull()) { - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, - "%s's tell queue is full.", scm->to); + if (!sender) + break; + scm->noreply = true; + scm->queued = 2; // queue full + strcpy(scm->deliverto, scm->from); + sender->Server()->SendPacket(pack); } else { size_t struct_size = sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1; ServerChannelMessage_Struct *temp = (ServerChannelMessage_Struct *) new uchar[struct_size]; @@ -458,8 +470,13 @@ bool ZoneServer::Process() { memcpy(temp, scm, struct_size); temp->noreply = true; cle->PushToTellQueue(temp); // deallocation is handled in processing or deconstructor - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, - "Your message has been added to %s's queue.", scm->to); + + if (!sender) + break; + scm->noreply = true; + scm->queued = 1; // queued + strcpy(scm->deliverto, scm->from); + sender->Server()->SendPacket(pack); } } } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index be14e7b93..c54c2e886 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -168,18 +168,24 @@ void WorldServer::Process() { break; } case ServerOP_ChannelMessage: { - if (!ZoneLoaded) break; + if (!ZoneLoaded) + break; ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*) pack->pBuffer; if (scm->deliverto[0] == 0) { entity_list.ChannelMessageFromWorld(scm->from, scm->to, scm->chan_num, scm->guilddbid, scm->language, scm->message); - } - else { - Client* client; - client = entity_list.GetClientByName(scm->deliverto); - if (client != 0) { + } else { + Client* client = entity_list.GetClientByName(scm->deliverto); + if (client) { if (client->Connected()) { - client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message); - if (!scm->noreply && scm->chan_num!=2) { //dont echo on group chat + if (scm->queued == 1) // tell was queued + client->Tell_StringID(QUEUED_TELL, scm->to, scm->message); + else if (scm->queued == 2) // tell queue was full + client->Tell_StringID(QUEUE_TELL_FULL, scm->to, scm->message); + else if (scm->queued == 3) // person was offline + client->Message_StringID(MT_TellEcho, TOLD_NOT_ONLINE); + else // normal stuff + client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message); + if (!scm->noreply && scm->chan_num != 2) { //dont echo on group chat // if it's a tell, echo back so it shows up scm->noreply = true; scm->chan_num = 14; @@ -1856,6 +1862,7 @@ bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_nu scm->chan_num = chan_num; scm->guilddbid = guilddbid; scm->language = language; + scm->queued = 0; strcpy(scm->message, buffer); pack->Deflate(); From 7621882b4edcda76fb18b18848889a13e42fb9d9 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 20 Sep 2014 02:46:04 -0400 Subject: [PATCH 110/368] Fix toon names being allowed with lower case starting char --- changelog.txt | 1 + world/client.cpp | 26 ++++++++++---------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/changelog.txt b/changelog.txt index 493e57152..892ddbb33 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 09/19/2014 == demonstar55: Added Client::Tell_StringID (used in tell queue messages) demonstar55: Tell queues (and offline) messages now show correctly +demonstar55: Fix starting with capital check == 09/18/2014== demonstar55: Implement tell queues diff --git a/world/client.cpp b/world/client.cpp index f34f4e3df..f5f634083 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -471,8 +471,8 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) { return true; } -bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { - +bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) +{ if (GetAccountID() == 0) { clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account"); return false; @@ -482,7 +482,7 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { 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)); + clog(WORLD__CLIENT, "Name approval request. Name=%s, race=%s, class=%s", char_name, GetRaceName(race), GetEQClassName(clas)); EQApplicationPacket *outapp; outapp = new EQApplicationPacket; @@ -490,27 +490,21 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { outapp->pBuffer = new uchar[1]; outapp->size = 1; - bool valid; - if(!database.CheckNameFilter(char_name)) { + bool valid = false; + if (!database.CheckNameFilter(char_name)) valid = false; - } - else if(char_name[0] < 'A' && char_name[0] > 'Z') { + else if (islower(char_name[0])) //name must begin with an upper-case letter. valid = false; - } - else if (database.ReserveName(GetAccountID(), char_name)) { + else if (database.ReserveName(GetAccountID(), char_name)) valid = true; - } - else { - valid = false; - } - outapp->pBuffer[0] = valid? 1 : 0; + + outapp->pBuffer[0] = valid ? 1 : 0; QueuePacket(outapp); safe_delete(outapp); - if(!valid) { + if (!valid) memset(char_name, 0, sizeof(char_name)); - } return true; } From 15eaf4e6d1309cea96e4d98d69ad641f5cc66784 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 20 Sep 2014 14:57:15 -0400 Subject: [PATCH 111/368] Fix issue with not online message from tells --- zone/worldserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index c54c2e886..1db473b0e 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -182,7 +182,7 @@ void WorldServer::Process() { else if (scm->queued == 2) // tell queue was full client->Tell_StringID(QUEUE_TELL_FULL, scm->to, scm->message); else if (scm->queued == 3) // person was offline - client->Message_StringID(MT_TellEcho, TOLD_NOT_ONLINE); + client->Message_StringID(MT_TellEcho, TOLD_NOT_ONLINE, scm->to); else // normal stuff client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message); if (!scm->noreply && scm->chan_num != 2) { //dont echo on group chat From f82699c39bdd45ccb0754e55490826b851d6053e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 20 Sep 2014 15:55:20 -0400 Subject: [PATCH 112/368] Fix crash in SendEnterWorld on illegally long names --- changelog.txt | 3 +++ world/client.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 892ddbb33..57d36c2ad 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/20/2014 == +demonstar55: Fix crash in SendEnterWorld on illegally long names + == 09/19/2014 == demonstar55: Added Client::Tell_StringID (used in tell queue messages) demonstar55: Tell queues (and offline) messages now show correctly diff --git a/world/client.cpp b/world/client.cpp index f5f634083..0970d6866 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -130,7 +130,7 @@ void Client::SendLogServer() void Client::SendEnterWorld(std::string name) { -char char_name[32]= { 0 }; + char char_name[64] = { 0 }; if (pZoning && database.GetLiveChar(GetAccountID(), char_name)) { if(database.GetAccountIDByChar(char_name) != GetAccountID()) { eqs->Close(); From 33b79a3588c3069c5446735f45dd7954e99c6e97 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 20 Sep 2014 15:59:34 -0400 Subject: [PATCH 113/368] Limit character length to 15 (the client doesn't let you enter more) --- changelog.txt | 1 + common/database.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 57d36c2ad..b521da3fe 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 09/20/2014 == demonstar55: Fix crash in SendEnterWorld on illegally long names +demonstar55: The client only lets you enter 15 characters for your name (UF at least) == 09/19/2014 == demonstar55: Added Client::Tell_StringID (used in tell queue messages) diff --git a/common/database.cpp b/common/database.cpp index 88f01a676..ca287c3b5 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1140,7 +1140,7 @@ bool Database::CheckNameFilter(const char* name, bool surname) else { // the minimum 4 is enforced by the client too - if(!name || strlen(name) < 4 || strlen(name) > 64) + if(!name || strlen(name) < 4 || strlen(name) > 15) { return false; } From 1049e48acadc0bcfa39d1a02d06651253e19a0d0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 20 Sep 2014 16:58:35 -0400 Subject: [PATCH 114/368] Add Spells:SHDProcIDOffByeOne to support newer spell files In June 2009 SoE stopped doing a +1 to the base for SHD procs So UF+ spell files were not working, set this to false to support these spell files --- changelog.txt | 1 + common/ruletypes.h | 1 + .../optional/2014_09_20_SHDProCIDOffByOne.sql | 1 + zone/spell_effects.cpp | 31 ++++++++++--------- 4 files changed, 19 insertions(+), 15 deletions(-) create mode 100644 utils/sql/git/optional/2014_09_20_SHDProCIDOffByOne.sql diff --git a/changelog.txt b/changelog.txt index b521da3fe..9317d6c17 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 09/20/2014 == demonstar55: Fix crash in SendEnterWorld on illegally long names demonstar55: The client only lets you enter 15 characters for your name (UF at least) +demonstar55: Add rule Spells:SHDProcIDOffByOne for pre-UF spell file, set to true, UF+ set to false == 09/19/2014 == demonstar55: Added Client::Tell_StringID (used in tell queue messages) diff --git a/common/ruletypes.h b/common/ruletypes.h index 95fa19854..058d5ce6f 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -321,6 +321,7 @@ RULE_INT ( Spells, AI_PursueDetrimentalChance, 90) // Chance while chasing targe RULE_INT ( Spells, AI_IdleNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while idle. (min time in random) RULE_INT ( Spells, AI_IdleNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) RULE_INT ( Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others. +RULE_BOOL ( Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false) RULE_CATEGORY_END() diff --git a/utils/sql/git/optional/2014_09_20_SHDProCIDOffByOne.sql b/utils/sql/git/optional/2014_09_20_SHDProCIDOffByOne.sql new file mode 100644 index 000000000..de6eb7d85 --- /dev/null +++ b/utils/sql/git/optional/2014_09_20_SHDProCIDOffByOne.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:SHDProcIDOffByOne', 'true', 'SHD procs are off by 1. Set true for pre-UF spell files, false for UF+.'); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f0bc8498b..a81377f5d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5598,28 +5598,29 @@ void Mob::CheckNumHitsRemaining(uint8 type, uint32 buff_slot, uint16 spell_id) } //for some stupid reason SK procs return theirs one base off... -uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index) { +uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index) +{ + if (!RuleB(Spells, SHDProcIDOffByOne)) // UF+ spell files + return spells[spell_id].base[effect_index]; + + // We should actually just be checking if the mob is SHD, but to not force + // custom servers to create new spells, we will still do this bool sk = false; bool other = false; - for(int x = 0; x < 16; x++) - { - if(x == 4){ - if(spells[spell_id].classes[4] < 255) + for (int x = 0; x < 16; x++) { + if (x == 4) { + if (spells[spell_id].classes[4] < 255) sk = true; - } - else{ - if(spells[spell_id].classes[x] < 255) + } else { + if (spells[spell_id].classes[x] < 255) other = true; } } - if(sk && !other) - { - return(spells[spell_id].base[effect_index] + 1); - } - else{ - return(spells[spell_id].base[effect_index]); - } + if (sk && !other) + return spells[spell_id].base[effect_index] + 1; + else + return spells[spell_id].base[effect_index]; } bool Mob::TryDivineSave() From 7140a2054f833306be995c8ae6f3a285e9b0bee2 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 20 Sep 2014 15:09:43 -0700 Subject: [PATCH 115/368] Ban and suspend commands now require a reason that is recorded in the DB --- changelog.txt | 1 + common/database.cpp | 2 +- .../git/required/2014_09_20_ban_messages.sql | 1 + zone/command.cpp | 118 +++++++++++------- 4 files changed, 74 insertions(+), 48 deletions(-) create mode 100644 utils/sql/git/required/2014_09_20_ban_messages.sql diff --git a/changelog.txt b/changelog.txt index b521da3fe..dcf2d55ed 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 09/20/2014 == demonstar55: Fix crash in SendEnterWorld on illegally long names demonstar55: The client only lets you enter 15 characters for your name (UF at least) +KLS: #suspend and #ban now have required messages to record the reason for the ban/suspension. == 09/19/2014 == demonstar55: Added Client::Tell_StringID (used in tell queue messages) diff --git a/common/database.cpp b/common/database.cpp index ca287c3b5..4e7517f16 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -659,7 +659,7 @@ the name "name" or zero if no character with that name was found Zero will also be returned if there is a database error. */ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { - std::string query = StringFormat("SELECT account_id, id FROM character_ WHERE name='%s'", charname); + std::string query = StringFormat("SELECT account_id, id FROM character_ WHERE name='%s'", EscapeString(charname).c_str()); auto results = QueryDatabase(query); diff --git a/utils/sql/git/required/2014_09_20_ban_messages.sql b/utils/sql/git/required/2014_09_20_ban_messages.sql new file mode 100644 index 000000000..4612f9962 --- /dev/null +++ b/utils/sql/git/required/2014_09_20_ban_messages.sql @@ -0,0 +1 @@ +ALTER TABLE `account` ADD COLUMN `ban_reason` TEXT NULL DEFAULT NULL AFTER `expansion`, ADD COLUMN `suspend_reason` TEXT NULL DEFAULT NULL AFTER `ban_reason`; diff --git a/zone/command.cpp b/zone/command.cpp index b2f08d3eb..36dc95921 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6258,36 +6258,49 @@ void command_stun(Client *c, const Seperator *sep) c->Message(0, "Usage: #stun [duration]"); } + void command_ban(Client *c, const Seperator *sep) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(sep->arg[1][0] == 0) + if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { - c->Message(0, "Usage: #ban [charname]"); + c->Message(0, "Usage: #ban "); } else { - database.RunQuery(query, MakeAnyLenString(&query, "SELECT account_id from character_ where name = '%s'", sep->arg[1]), errbuf, &result); - if(query) - { - safe_delete_array(query); + auto account_id = database.GetAccountIDByChar(sep->arg[1]); + + std::string message; + int i = 2; + while(1) { + if(sep->arg[i][0] == 0) { + break; + } + + if(message.length() > 0) { + message.push_back(' '); + } + + message += sep->arg[i]; + ++i; } - if(mysql_num_rows(result)) - { - row = mysql_fetch_row(result); - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE account set status = -2 where id = %i", atoi(row[0])), errbuf, 0); - c->Message(13,"Account number %i with the character %s has been banned.", atoi(row[0]), sep->arg[1]); + if(message.length() == 0) { + c->Message(0, "Usage: #ban "); + return; + } - ServerPacket* pack = new ServerPacket(ServerOP_FlagUpdate, 6); - *((uint32*) pack->pBuffer) = atoi(row[0]); - *((int16*) &pack->pBuffer[4]) = -2; - worldserver.SendPacket(pack); - safe_delete(pack); + if(account_id > 0) + { + database.RunQuery(query, MakeAnyLenString(&query, "UPDATE account set status = -2, ban_reason = '%s' where id = %i", EscapeString(message).c_str(), account_id), errbuf, 0); + c->Message(13, "Account number %i with the character %s has been banned with message: \"%s\"", account_id, sep->arg[1], message.c_str()); + + ServerPacket pack(ServerOP_FlagUpdate, 6); + *((uint32*)&pack.pBuffer[0]) = account_id; + *((int16*)&pack.pBuffer[4]) = -2; + worldserver.SendPacket(&pack); Client *client = nullptr; client = entity_list.GetClientByName(sep->arg[1]); @@ -6297,25 +6310,20 @@ void command_ban(Client *c, const Seperator *sep) } else { - ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; + ServerPacket pack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)pack.pBuffer; strcpy(skp->adminname, c->GetName()); strcpy(skp->name, sep->arg[1]); skp->adminrank = c->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); + worldserver.SendPacket(&pack); } - - mysql_free_result(result); } else { - c->Message(13,"Character does not exist."); - } - if(query) - { - safe_delete_array(query); + c->Message(13, "Character does not exist."); } + + safe_delete_array(query); } } @@ -6325,7 +6333,7 @@ void command_suspend(Client *c, const Seperator *sep) char *query = nullptr; if((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0)) - c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately)"); + c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); else { int Duration = atoi(sep->arg[2]); @@ -6333,22 +6341,40 @@ void command_suspend(Client *c, const Seperator *sep) if(Duration < 0) Duration = 0; - char *EscName = new char[strlen(sep->arg[1]) * 2 + 1]; + std::string message; + if(Duration > 0) { + int i = 3; + while(1) { + if(sep->arg[i][0] == 0) { + break; + } - database.DoEscapeString(EscName, sep->arg[1], strlen(sep->arg[1])); + if(message.length() > 0) { + message.push_back(' '); + } + + message += sep->arg[i]; + ++i; + } + + if(message.length() == 0) { + c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); + return; + } + } int AccountID; - if((AccountID = database.GetAccountIDByChar(EscName)) > 0) + if((AccountID = database.GetAccountIDByChar(sep->arg[1])) > 0) { - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY)" - " WHERE `id` = %i", Duration, AccountID), errbuf, 0); + database.RunQuery(query, MakeAnyLenString(&query, "UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), " + "suspend_reason = '%s' WHERE `id` = %i", Duration, EscapeString(message).c_str(), AccountID), errbuf, 0); if(Duration) - c->Message(13,"Account number %i with the character %s has been temporarily suspended for %i day(s).", AccountID, sep->arg[1], - Duration); + c->Message(13, "Account number %i with the character %s has been temporarily suspended for %i day(s) with the message: \"%s\"", AccountID, sep->arg[1], + Duration, message.c_str()); else - c->Message(13,"Account number %i with the character %s is no longer suspended.", AccountID, sep->arg[1]); + c->Message(13, "Account number %i with the character %s is no longer suspended.", AccountID, sep->arg[1]); safe_delete_array(query); @@ -6358,22 +6384,20 @@ void command_suspend(Client *c, const Seperator *sep) BannedClient->WorldKick(); else { - ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*) pack->pBuffer; + ServerPacket pack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*)pack.pBuffer; strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname)); strn0cpy(sks->name, sep->arg[1], sizeof(sks->name)); sks->adminrank = c->Admin(); - worldserver.SendPacket(pack); - - safe_delete(pack); + worldserver.SendPacket(&pack); } - } else - c->Message(13,"Character does not exist."); - - safe_delete_array(EscName); + } + else { + c->Message(13, "Character does not exist."); + } } } From f9366553a3680dfcfa90d9887c71a5de1e31ebbe Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 21 Sep 2014 01:27:05 -0500 Subject: [PATCH 116/368] Blob changelog.txt --- changelog.txt | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ zone/zoning.cpp | 2 -- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index a006c9a50..44b72c1df 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,99 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/21/2014 == +Akkadius: Player Profile Blob to Database Conversion + - Summary: HUGE difference in database speeds reads/writes and 1:10 datasize difference + - The new character storage engine unlike the character_ table before, is able to properly index data and make use of + proper MySQL/MariaDB caching optimizations and performance has increased phenominally + PERFORMANCE AND STATISTICS FIGURES (Varies on hardware): + - EZ Server Character data size of 2.6GB `character_` table alone now takes up approx 600MB + - Character Data Loads take approx .03 seconds BEFORE MySQL/MariaDB cache + - Character Data Loads take approx .001-.0035 seconds AFTER MySQL/MariaDB cache + - Character Data Saves take approx .0001 - .003 for any particular save operation + - Database Auto Conversion: When the 'character_' table exists, World boot-up will queue an auto-conversion prompt and convert all of your characters, BACKUP + YOUR DATABASE BEFORE CONVERTING, here is an EASY backup script: http://wiki.eqemulator.org/p?MySQL_DB_Backup_Script + - On auto conversion, the following tables are created automatically: + - Table: `character_skills` - Stores Character Skills + - Table: `character_languages` - Stores Character Language + - Table: `character_bind` - Stores Character Bind point and Home Bind point designated by is_home bool field + - Table: `character_alternate_abilities` - Stores all Character AA + - Table: `character_currency` - Stores all Platinum/Gold/Silver/Copper and character related currencies + - Table: `character_data` - Stores basic character data (Fields from `character_` table migrated to this table) + - Table: `character_spells` - Stores character spells + - Table: `character_memmed_spells` - Stores character memorized spells + - Table: `character_disciplines` - Stores character disciplines + - Table: `character_material` - Stores character armor dye textures + - Table: `character_tribute` - Stores character tributes + - Table: `character_bandolier` - Stores character bandoliers + - Table: `character_inspect_messages` - Stores character inspection messages (Moved from `character_` table) + - Table: `character_leadership_abilities` - Stores character Leadership AAs + - Loads: Majority of Player profile loads now occur at Client::Handle_Connect_OP_ZoneEntry + LoadCharacterFactionValues(uint32 character_id, faction_map & val_list); + LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); + LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); + - Saves: Occur all over the code now instead of calling full saves + SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home); + SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); + SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); + SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); + SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); + SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value); + SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value); + SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id); + SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); + SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name); + SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon); + SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); + - Deletes: + DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + DeleteCharacterDisc(uint32 character_id, uint32 slot_id); + DeleteCharacterBandolier(uint32 character_id, uint32 band_id); + DeleteCharacterLeadershipAAs(uint32 character_id); + - Now occur all over the code and only trigger when necessary + - Two FULL saves when looting a corpse, this has been reduced to just currency saves on initial loot and trimmed to one save since AddToMoneyPP did it already + - Every time a player moves coin with any situation (Splits/Trades/Merchant/Skills/Bank Coin Exchange/Coin Moves), a full save is made, this is now just a currency save + - Every time a player skilled up at a skill vendor, a full blob save hit was made, this is not just a currency hit + - Every time an AA was purchased, a full save was made + - Every time a spell was scribed/swapped, disc was trained + - When a client exists a zone, when a client enters a zone + - NOTE: These amount of excessive saves have caused scalability issues that cause the `character_` table to hang which causes process hangs that affect the whole server + because of the slowness of the `character_` table and the blob not allowing any indexing to occur + - All functions that once depended on the `character_` table are now rewritten to appropriately read from the `character_data` table + - Database query errors that occur during conversion or from and load/save/delete character functions are now leveraged via ThrowDBError and logs now go to + Server_Folder_Root/eqemu_query_error_log.txt (You cannot log errors natively through MySQL) + - DBASYNC IS NOW COMPLETELY REMOVED - This was mainly for Character data async loads/saves and merchantlist loads + - Side implementations: + Perl Exports: + - quest::crosszonesetentityvariablebynpctypeid(npctype_id, id, m_var) - Sets entity variables world wide with specified npctype_id + - quest::crosszonesignalnpcbynpctypeid(npctype_id, data) - Signals all NPC entities world wide with specified npctype_id + - $client->GetTaskActivityDoneCount(TaskID, ActivityID) - Gets task activity done count by task id and activity id for client entity + + VIEW TABLE SIZE AFTER CONVERT: + + SELECT CONCAT(table_schema, '.', table_name) as table_name, + CONCAT(ROUND(table_rows / 1000000, 2), 'M') rows, + CONCAT(ROUND(data_length / ( 1024 * 1024 * 1024 ), 2), 'G') DATA, + CONCAT(ROUND(index_length / ( 1024 * 1024 * 1024 ), 2), 'G') idx, + CONCAT(ROUND(( data_length + index_length ) / ( 1024 * 1024 * 1024 ), 2), 'G') total_size, + ROUND(index_length / data_length, 2) idxfrac + FROM information_schema.TABLES + WHERE `table_name` LIKE 'character_%' + ORDER BY DATA DESC; + == 09/16/2014 == demonstar55: Implement spell formula 137 (BER AA Desperation) Uleat (NateDog): Fix for LoadBuffs() crash when a spell with a non-persistent Illusion effect was loaded. diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 0cbf39a2c..6a8abb6d4 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -368,8 +368,6 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc outapp->priority = 6; FastQueuePacket(&outapp); - printf("INTERZONE PROCESS\n"); - zone->StartShutdownTimer(AUTHENTICATION_TIMEOUT * 1000); } else { // vesuvias - zoneing to another zone so we need to the let the world server From b4f399361695df6f1eb990dc7761a21b3d66fc05 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 20 Sep 2014 23:56:04 -0700 Subject: [PATCH 117/368] Should compile on gcc now --- common/database.cpp | 2 +- common/database.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index aed844f64..d53141e71 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include // Disgrace: for windows compile diff --git a/common/database.h b/common/database.h index c70464094..e1d4c6fd6 100644 --- a/common/database.h +++ b/common/database.h @@ -222,7 +222,7 @@ public: uint32 GetRaidID(const char* name); const char *GetRaidLeaderName(uint32 rid); - bool Database::CheckDatabaseConversions(); + bool CheckDatabaseConversions(); /* * Database Variables From 388c4bc574626473c0dde908cb6efd6f06da554d Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 21 Sep 2014 02:27:57 -0500 Subject: [PATCH 118/368] Fix where if players instance is expired when logging back into it, they will get sent properly to bind. Issue was players player profile instance ID did not match the bind instance ID. --- zone/client_packet.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a27b7dd05..d3c0f5f42 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -614,6 +614,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { /* Set Con State for Reporting */ conn_state = PlayerProfileLoaded; + m_pp.zone_id = zone->GetZoneID(); + m_pp.zoneInstance = zone->GetInstanceID(); + /* Set Total Seconds Played */ TotalSecondsPlayed = m_pp.timePlayedMin * 60; /* Set Max AA XP */ From 4ed88e348e6a0382193775d25edc20caa95d4d0a Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 21 Sep 2014 02:58:56 -0500 Subject: [PATCH 119/368] Merge aftermath --- common/database.cpp | 9 +++++++-- common/database.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 78eec13a7..f66496654 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -782,10 +782,15 @@ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { } if (results.RowCount() != 1) - return 0; + return 0; auto row = results.begin(); - if (row[0]){ accountId = atoi(row[0]); } + + uint32 accountId = atoi(row[0]); + + if (oCharID) + *oCharID = atoi(row[1]); + return accountId; } diff --git a/common/database.h b/common/database.h index e1d4c6fd6..c4d3af336 100644 --- a/common/database.h +++ b/common/database.h @@ -132,7 +132,7 @@ public: */ bool CheckNameFilter(const char* name, bool surname = false); bool CheckUsedName(const char* name); - uint32 GetAccountIDByChar(const char* charname); + uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0); uint32 GetAccountIDByChar(uint32 char_id); uint32 GetAccountIDByName(const char* accname, int16* status = 0, uint32* lsid = 0); uint32 GetGuildIDByCharID(uint32 char_id); From 3ef5d8ef0a5ecc76bd2decebf60e3a845249191c Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 21 Sep 2014 03:43:27 -0500 Subject: [PATCH 120/368] gcc fix vs --- zone/zonedb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/zonedb.h b/zone/zonedb.h index 497beb322..08f442897 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -6,7 +6,7 @@ #include "../common/loottable.h" #include "zonedump.h" #include "../common/faction.h" -#include +#include //#include "doors.h" struct wplist { From db0d1116f85226a065b10371db2523862bad70f4 Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 21 Sep 2014 04:12:36 -0500 Subject: [PATCH 121/368] make travis shut up --- zone/zonedb.cpp | 2 +- zone/zonedb.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 4878b6644..15d2a3f4d 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1245,7 +1245,7 @@ bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struc std::string query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); QueryDatabase(query); /* Save Tributes only if we have values... */ for (int i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != UINT32_MAX){ + if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 0xffffffffui32){ std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); QueryDatabase(query); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); } diff --git a/zone/zonedb.h b/zone/zonedb.h index 08f442897..647e9bfbd 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -6,7 +6,8 @@ #include "../common/loottable.h" #include "zonedump.h" #include "../common/faction.h" -#include +#include + //#include "doors.h" struct wplist { From 4525b512ac14894a9873f345a752602ecd8568fd Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 21 Sep 2014 02:25:05 -0700 Subject: [PATCH 122/368] Build fix plus future travis update --- .travis.yml | 4 +++- zone/zonedb.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5bdd64324..71606e74c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,9 @@ script: - make - ./bin/tests branches: - only: master + only: + - master + - stable notifications: email: false irc: diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 15d2a3f4d..dbf3c2742 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1245,7 +1245,7 @@ bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struc std::string query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); QueryDatabase(query); /* Save Tributes only if we have values... */ for (int i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 0xffffffffui32){ + if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 0xffffffffu){ std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); QueryDatabase(query); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); } From cda2217634cbe76584c96d09ede39cfb048a00f6 Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 21 Sep 2014 04:26:26 -0500 Subject: [PATCH 123/368] .......................................... --- zone/zonedb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 15d2a3f4d..917928520 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1245,7 +1245,7 @@ bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struc std::string query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); QueryDatabase(query); /* Save Tributes only if we have values... */ for (int i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 0xffffffffui32){ + if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 4294967295){ std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); QueryDatabase(query); LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); } From cd8e30a2ec361ea770d8a765956b403e76bb30fa Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 21 Sep 2014 15:59:02 -0700 Subject: [PATCH 124/368] Bug with MySQLRequestResult --- common/mysql_request_result.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/mysql_request_result.cpp b/common/mysql_request_result.cpp index b4bf30842..e7f147b51 100644 --- a/common/mysql_request_result.cpp +++ b/common/mysql_request_result.cpp @@ -101,6 +101,7 @@ MySQLRequestResult::MySQLRequestResult(MySQLRequestResult&& moveItem) m_RowsAffected = moveItem.m_RowsAffected; m_LastInsertedID = moveItem.m_LastInsertedID; m_ColumnLengths = moveItem.m_ColumnLengths; + m_ColumnCount = moveItem.m_ColumnCount; m_Fields = moveItem.m_Fields; // Keeps deconstructor from double freeing @@ -128,6 +129,7 @@ MySQLRequestResult& MySQLRequestResult::operator=(MySQLRequestResult&& other) m_CurrentRow = other.m_CurrentRow; m_OneBeyondRow = other.m_OneBeyondRow; m_ColumnLengths = other.m_ColumnLengths; + m_ColumnCount = other.m_ColumnCount; m_Fields = other.m_Fields; // Keeps deconstructor from double freeing From 909dda7c0906ac76bec7eb99845e7ac687e86649 Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 21 Sep 2014 19:01:50 -0500 Subject: [PATCH 125/368] Array iter fix --- common/database.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 9bfe21a75..a19961209 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1846,7 +1846,7 @@ bool Database::CheckDatabaseConversions() { if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Tribute Convert", rquery); } /* Run Bandolier Convert */ first_entry = 0; rquery = ""; - for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ + for (i = 0; i < EmuConstants::BANDOLIERS_COUNT; i++){ if(strlen(pp->bandoliers[i].name) < 32) { for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ if (pp->bandoliers[i].items[si].item_id > 0){ @@ -1862,7 +1862,7 @@ bool Database::CheckDatabaseConversions() { if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Bandolier Convert", rquery); } /* Run Potion Belt Convert */ first_entry = 0; rquery = ""; - for (i = 0; i <= EmuConstants::POTION_BELT_SIZE; i++){ + for (i = 0; i < EmuConstants::POTION_BELT_SIZE; i++){ if (pp->potionbelt.items[i].item_id > 0){ if (first_entry != 1){ rquery = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); @@ -1875,7 +1875,7 @@ bool Database::CheckDatabaseConversions() { if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Potion Belt Convert", rquery); } /* Run Leadership AA Convert */ first_entry = 0; rquery = ""; - for (i = 0; i <= MAX_LEADERSHIP_AA_ARRAY; i++){ + for (i = 0; i < MAX_LEADERSHIP_AA_ARRAY; i++){ if(pp->leader_abilities.ranks[i] > 0 && pp->leader_abilities.ranks[i] < 6){ if (first_entry != 1){ rquery = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, rank) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); From e34c47414fd1dd45eec22587d12bde2c30502249 Mon Sep 17 00:00:00 2001 From: JJ Date: Sun, 21 Sep 2014 20:02:01 -0400 Subject: [PATCH 126/368] Consistent directory locations under common. --- common/patches/client62_constants.h | 2 +- common/patches/rof_constants.h | 2 +- common/patches/sod_constants.h | 2 +- common/patches/sof_constants.h | 2 +- common/patches/titanium_constants.h | 2 +- common/patches/underfoot_constants.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/patches/client62_constants.h b/common/patches/client62_constants.h index e8eb38faa..7d2125028 100644 --- a/common/patches/client62_constants.h +++ b/common/patches/client62_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef CLIENT62_CONSTANTS_H_ #define CLIENT62_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace Client62 { namespace maps { diff --git a/common/patches/rof_constants.h b/common/patches/rof_constants.h index 7be8baf72..62a161dc5 100644 --- a/common/patches/rof_constants.h +++ b/common/patches/rof_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef RoF_CONSTANTS_H_ #define RoF_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace RoF { namespace maps { diff --git a/common/patches/sod_constants.h b/common/patches/sod_constants.h index 76427d55e..9a8907f13 100644 --- a/common/patches/sod_constants.h +++ b/common/patches/sod_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef SoD_CONSTANTS_H_ #define SoD_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace SoD { namespace maps { diff --git a/common/patches/sof_constants.h b/common/patches/sof_constants.h index 0ae0d728d..d80fa2dcb 100644 --- a/common/patches/sof_constants.h +++ b/common/patches/sof_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef SoF_CONSTANTS_H_ #define SoF_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace SoF { namespace maps { diff --git a/common/patches/titanium_constants.h b/common/patches/titanium_constants.h index c07244f2d..71625362a 100644 --- a/common/patches/titanium_constants.h +++ b/common/patches/titanium_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef Titanium_CONSTANTS_H_ #define Titanium_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace Titanium { namespace maps { diff --git a/common/patches/underfoot_constants.h b/common/patches/underfoot_constants.h index d34eaeb5b..fdbe54086 100644 --- a/common/patches/underfoot_constants.h +++ b/common/patches/underfoot_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef Underfoot_CONSTANTS_H_ #define Underfoot_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace Underfoot { namespace maps { From 7d242045ec637d4fdfa63a4be6cc52190d44b874 Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 21 Sep 2014 23:34:53 -0500 Subject: [PATCH 127/368] Another merge aftermath overwriting character_data reference to old table --- common/database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/database.cpp b/common/database.cpp index a19961209..7f7af3f2c 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -771,7 +771,7 @@ uint32 Database::GetCharacterID(const char *name) { Zero will also be returned if there is a database error. */ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { - std::string query = StringFormat("SELECT account_id, id FROM character_ WHERE name='%s'", EscapeString(charname).c_str()); + std::string query = StringFormat("SELECT `account_id`, `id` FROM `character_data` WHERE name='%s'", EscapeString(charname).c_str()); auto results = QueryDatabase(query); From a5a8bfb0f4488ddf89fd788f8d47cc6d98a97c0e Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 21 Sep 2014 23:48:51 -0500 Subject: [PATCH 128/368] Bye bye commented old junk --- common/guilds.cpp | 316 +--------------------------------------------- 1 file changed, 1 insertion(+), 315 deletions(-) diff --git a/common/guilds.cpp b/common/guilds.cpp index 4f5c08bb6..ddd5d9a59 100644 --- a/common/guilds.cpp +++ b/common/guilds.cpp @@ -24,318 +24,4 @@ #ifndef WIN32 #include //for htonl -#endif - -/* -void Database::GetGuildMembers(uint32 guild_id, GuildMember_Struct* gms){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 count=0; - uint32 length=0; - if (RunQuery(query, MakeAnyLenString(&query, "Select name,profile,timelaston,guildrank,publicnote from character_ where guild=%i", guild_id), errbuf, &result)) { - safe_delete_array(query); - while( ( row = mysql_fetch_row(result) ) ){ - strcpy(gms->member[count].name,row[0]); - length+=strlen(row[0])+strlen(row[4]); - PlayerProfile_Struct* pps=(PlayerProfile_Struct*)row[1]; - gms->member[count].level=htonl(pps->level); - gms->member[count].zoneid=(pps->zone_id*256); - gms->member[count].timelaston=htonl(atol(row[2])); - gms->member[count].class_=htonl(pps->class_); - gms->member[count].rank=atoi(row[3]); - strcpy(gms->member[count].publicnote,row[4]); - count++; - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetGuildMembers query '%s': %s", query, errbuf); - safe_delete_array(query); - } - gms->count=count; - gms->length=length; -} - -uint32 Database::NumberInGuild(uint32 guild_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "Select count(id) from character_ where guild=%i", guild_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in NumberInGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return 0; - } - return 0; -} -bool Database::SetGuild(char* name, uint32 guild_id, uint8 guildrank) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET guild=%i, guildrank=%i WHERE name='%s'", guild_id, guildrank, name), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) - return true; - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in SetGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - return false; -} - -bool Database::SetGuild(uint32 charid, uint32 guild_id, uint8 guildrank) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET guild=%i, guildrank=%i WHERE id=%i", guild_id, guildrank, charid), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) - return true; - - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in SetGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - -bool Database::DeleteGuild(uint32 guild_id) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char *query2 = 0; - uint32 affected_rows = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "DELETE FROM guilds WHERE id=%i;", guild_id), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) { - if(!RunQuery(query2, MakeAnyLenString(&query2, "update character_ set guild=0,guildrank=0 where guild=%i", guild_id), errbuf, 0, &affected_rows)) - LogFile->write(EQEMuLog::Error, "Error in DeleteGuild cleanup query '%s': %s", query2, errbuf); - safe_delete_array(query2); - return true; - } - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in DeleteGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - -bool Database::RenameGuild(uint32 guild_id, const char* name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - char buf[65]; - DoEscapeString(buf, name, strlen(name)) ; - - if (RunQuery(query, MakeAnyLenString(&query, "Update guilds set name='%s' WHERE id=%i;", buf, guild_id), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) - return true; - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in RenameGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - - - -bool Database::EditGuild(uint32 guild_id, uint8 ranknum, GuildRankLevel_Struct* grl) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - int chars = 0; - uint32 affected_rows = 0; - char buf[203]; - char buf2[8]; - DoEscapeString(buf, grl->rankname, strlen(grl->rankname)) ; - buf2[GUILD_HEAR] = grl->heargu + '0'; - buf2[GUILD_SPEAK] = grl->speakgu + '0'; - buf2[GUILD_INVITE] = grl->invite + '0'; - buf2[GUILD_REMOVE] = grl->remove + '0'; - buf2[GUILD_PROMOTE] = grl->promote + '0'; - buf2[GUILD_DEMOTE] = grl->demote + '0'; - buf2[GUILD_MOTD] = grl->motd + '0'; - buf2[GUILD_WARPEACE] = grl->warpeace + '0'; - - if (ranknum == 0) - chars = MakeAnyLenString(&query, "Update guilds set rank%ititle='%s' WHERE id=%i;", ranknum, buf, guild_id); - else - chars = MakeAnyLenString(&query, "Update guilds set rank%ititle='%s', rank%i='%s' WHERE id=%i;", ranknum, buf, ranknum, buf2, guild_id); - - if (RunQuery(query, chars, errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) - return true; - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in EditGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - -bool Database::GetGuildNameByID(uint32 guild_id, char * name) { - if (!name || !guild_id) return false; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "select name from guilds where id='%i'", guild_id), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if (row[0]) - sprintf(name,"%s",row[0]); - mysql_free_result(result); - return true; - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetGuildNameByID query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - -uint32 Database::GetGuildIDbyLeader(uint32 leader) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM guilds WHERE leader=%i", leader), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint32 tmp = atoi(row[0]); - mysql_free_result(result); - return tmp; - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in Getguild_idbyLeader query '%s': %s", query, errbuf); - safe_delete_array(query); - } - - return 0; -} - -bool Database::SetGuildLeader(uint32 guild_id, uint32 leader) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "UPDATE guilds SET leader=%i WHERE id=%i", leader, guild_id), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) - return true; - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in SetGuildLeader query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - -bool Database::SetGuildMOTD(uint32 guild_id, const char* motd) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char* motdbuf = 0; - uint32 affected_rows = 0; - - motdbuf = new char[(strlen(motd)*2)+3]; - - DoEscapeString(motdbuf, motd, strlen(motd)) ; - - if (RunQuery(query, MakeAnyLenString(&query, "Update guilds set motd='%s' WHERE id=%i;", motdbuf, guild_id), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - delete motdbuf; - if (affected_rows == 1) - return true; - else - return false; - } - else - { - LogFile->write(EQEMuLog::Error, "Error in SetGuildMOTD query '%s': %s", query, errbuf); - safe_delete_array(query); - delete motdbuf; - return false; - } - - return false; -} - -string Database::GetGuildMOTD(uint32 guild_id) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - string motd_str; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT motd FROM guilds WHERE id=%i", guild_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if (row[0]) - motd_str = row[0]; - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetGuildMOTD query '%s': %s", query, errbuf); - safe_delete_array(query); - } - return motd_str; -} -*/ - +#endif \ No newline at end of file From 837b9b7ec7bd966795833b696c0c49fa3d4410bf Mon Sep 17 00:00:00 2001 From: akkadius Date: Sun, 21 Sep 2014 23:54:52 -0500 Subject: [PATCH 129/368] NoRentExpired `character_` reference --- zone/zonedb.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index dbf3c2742..a3b1f691d 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1662,8 +1662,7 @@ bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spe } bool ZoneDatabase::NoRentExpired(const char* name){ - std::string query = StringFormat("SELECT (UNIX_TIMESTAMP(NOW())-timelaston) " - "FROM character_ WHERE name = '%s'", name); + std::string query = StringFormat("SELECT (UNIX_TIMESTAMP(NOW()) - last_login) FROM `character_data` WHERE name = '%s'", name); auto results = QueryDatabase(query); if (!results.Success()) return false; From c26a6959e486f5684588d7ae04e14d3a3bb39c39 Mon Sep 17 00:00:00 2001 From: akkadius Date: Mon, 22 Sep 2014 01:00:28 -0500 Subject: [PATCH 130/368] #resetaa now covers the function of #resetaa and #refundaa #resetaa will wipe all AA data, refund the spent points into the available points and send character to character select properly Removed #refundaa Removed a lot of debug code for blob conversion Changed status logging for loads/saves to Debug category --- changelog.txt | 7 +++++++ zone/aa.cpp | 8 ++++++++ zone/client.cpp | 10 +++------- zone/client_packet.cpp | 4 ---- zone/command.cpp | 22 +--------------------- zone/zonedb.cpp | 28 +++++++++++++++------------- zone/zonedb.h | 1 + 7 files changed, 35 insertions(+), 45 deletions(-) diff --git a/changelog.txt b/changelog.txt index 01a190eb1..b002c0524 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/22/2014 == +Akkadius: #resetaa now covers the function of #resetaa and #refundaa + - #resetaa will wipe all AA data, refund the spent points into the available points and send character to character select properly +Akkadius: Removed #refundaa +Akkadius: Removed a lot of debug code for blob conversion +Akkadius: Changed status logging for loads/saves to Debug category + == 09/21/2014 == Akkadius: Player Profile Blob to Database Conversion - Summary: HUGE difference in database speeds reads/writes and 1:10 datasize difference diff --git a/zone/aa.cpp b/zone/aa.cpp index ef4b07aee..1f933b30e 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1520,11 +1520,15 @@ bool ZoneDatabase::LoadAAEffects2() { return true; } void Client::ResetAA(){ + RefundAA(); uint32 i; for(i=0;iAA = 0; aa[i]->value = 0; + m_pp.aa_array[MAX_PP_AA_ARRAY].AA = 0; + m_pp.aa_array[MAX_PP_AA_ARRAY].value = 0; } + std::map::iterator itr; for(itr=aa_points.begin();itr!=aa_points.end();++itr) aa_points[itr->first] = 0; @@ -1537,7 +1541,11 @@ void Client::ResetAA(){ m_pp.group_leadership_exp = 0; m_pp.raid_leadership_exp = 0; + database.DeleteCharacterAAs(this->CharacterID()); + SaveAA(); + SendAATable(); database.DeleteCharacterLeadershipAAs(this->CharacterID()); + Kick(); } int Client::GroupLeadershipAAHealthEnhancement() diff --git a/zone/client.cpp b/zone/client.cpp index ed2475779..fad7d1a80 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -478,7 +478,6 @@ void Client::ReportConnectingState() { } bool Client::SaveAA(){ - clock_t t = std::clock(); /* Function timer start */ int first_entry = 0; std::string rquery; /* Save Player AA */ @@ -515,7 +514,6 @@ bool Client::SaveAA(){ } } auto results = database.QueryDatabase(rquery); - LogFile->write(EQEMuLog::Status, "Issuing Client AA Save... CID: %i Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; } @@ -523,8 +521,6 @@ bool Client::Save(uint8 iCommitNow) { if(!ClientDataLoaded()) return false; - clock_t t = std::clock(); /* Function timer start */ - /* Wrote current basics to PP for saves */ m_pp.x = x_pos; m_pp.y = y_pos; @@ -584,7 +580,6 @@ bool Client::Save(uint8 iCommitNow) { SaveTaskState(); /* Save Character Task */ database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp, &m_epp); /* Save Character Data */ - LogFile->write(EQEMuLog::Status, "Client::Save %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; } @@ -7968,7 +7963,7 @@ void Client::RefundAA() { for(int j = 0; j < cur; j++) { m_pp.aapoints += curaa->cost + (curaa->cost_inc * j); refunded = true; - } + } } else { @@ -7980,8 +7975,9 @@ void Client::RefundAA() { } if(refunded) { + SaveAA(); Save(); - Kick(); + // Kick(); } } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ea832f28b..928eca1ad 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -546,8 +546,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { uint32 cid = CharacterID(); character_id = cid; /* Global character_id reference */ - clock_t t = std::clock(); /* Function timer start */ - /* Flush and reload factions */ database.RemoveTempFactions(this); database.LoadCharacterFactionValues(cid, factionvalues); @@ -605,8 +603,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { if (m_pp.item_tint[i].rgb.use_tint == 1 || m_pp.item_tint[i].rgb.use_tint == 255) m_pp.item_tint[i].rgb.use_tint = 0xFF; - std::cout << "Character Data Load Took " << (((float)(std::clock() - t)) / CLOCKS_PER_SEC) << " seconds\n" << std::endl; - if (level){ level = m_pp.level; } /* If GM, not trackable */ diff --git a/zone/command.cpp b/zone/command.cpp index 9a5dd3f6c..fe8d58faa 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -149,7 +149,7 @@ Access Levels: int command_init(void) { if ( - command_add("resetaa","- Resets a Player's AA in their profile.",200,command_resetaa) || + command_add("resetaa","- Resets a Player's AA in their profile and refunds spent AA's to unspent, disconnects player.",200,command_resetaa) || command_add("qtest","- QueryServ testing command.",255,command_qtest) || command_add("bind","- Sets your targets bind spot to their current location",200,command_bind) || command_add("sendop","[opcode] - LE's Private test command, leave it alone",200,command_sendop) || @@ -403,7 +403,6 @@ int command_init(void) { command_add("guildapprove","[guildapproveid] - Approve a guild with specified ID (guild creator receives the id)",0,command_guildapprove) || command_add("guildlist","[guildapproveid] - Lists character names who have approved the guild specified by the approve id",0,command_guildlist) || command_add("altactivate", "[argument] - activates alternate advancement abilities, use altactivate help for more information", 0, command_altactivate) || - command_add("refundaa", "- Refunds your target's AA points, will disconnect them in the process as well.", 100, command_refundaa) || #ifdef BOTS command_add("bot","- Type \"#bot help\" to the see the list of available commands for bots.", 0, command_bot) || @@ -8534,25 +8533,6 @@ void command_altactivate(Client *c, const Seperator *sep){ } } -void command_refundaa(Client *c, const Seperator *sep){ - Client* refundee = nullptr; - if(c) { - if(c->GetTarget()){ - if(c->GetTarget()->IsClient()) - refundee = c->GetTarget()->CastToClient(); - else - c->Message(0, "Your target must be a client."); - } - else{ - c->Message(0, "You must have a target selected."); - } - - if(refundee) { - refundee->RefundAA(); - } - } -} - void command_traindisc(Client *c, const Seperator *sep) { uint8 max_level, min_level; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index a3b1f691d..1327bd8ee 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -939,7 +939,6 @@ bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* m_epp->aa_effects = atoi(row[r]); r++; // "`e_aa_effects`, " m_epp->perAA = atoi(row[r]); r++; // "`e_percent_to_aa`, " m_epp->expended_aa = atoi(row[r]); r++; // "`e_expended_aa_spent` " - LogFile->write(EQEMuLog::Status, "Loading Character Data for character ID: %i, done", character_id); } return true; } @@ -1102,7 +1101,6 @@ bool ZoneDatabase::LoadCharacterCurrency(uint32 character_id, PlayerProfile_Stru pp->careerRadCrystals = atoi(row[13]); pp->currentEbonCrystals = atoi(row[14]); pp->careerEbonCrystals = atoi(row[15]); - LogFile->write(EQEMuLog::Status, "Loading Currency for character ID: %i, done", character_id); } return true; } @@ -1201,7 +1199,7 @@ bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Str bool ZoneDatabase::SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value){ std::string query = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, lang_id, value); QueryDatabase(query); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterLanguage for character ID: %i, lang_id:%u value:%u done", character_id, lang_id, value); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterLanguage for character ID: %i, lang_id:%u value:%u done", character_id, lang_id, value); return true; } @@ -1210,7 +1208,7 @@ bool ZoneDatabase::SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, u /* Save Home Bind Point */ std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", character_id, zone_id, instance_id, x, y, z, heading, is_home); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterBindPoint for character ID: %i zone_id: %u instance_id: %u x: %f y: %f z: %f heading: %f ishome: %u", character_id, zone_id, instance_id, x, y, z, heading, is_home); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterBindPoint for character ID: %i zone_id: %u instance_id: %u x: %f y: %f z: %f heading: %f ishome: %u", character_id, zone_id, instance_id, x, y, z, heading, is_home); auto results = QueryDatabase(query); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterBindPoint", query); return true; @@ -1222,21 +1220,21 @@ bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_i uint8 blue = (color & 0x000000FF); std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, red, green, blue, color, use_tint) VALUES (%u, %u, %u, %u, %u, %u, 255)", character_id, slot_id, red, green, blue, color); auto results = QueryDatabase(query); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterMaterialColor for character ID: %i, slot_id: %u color: %u done", character_id, slot_id, color); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterMaterialColor for character ID: %i, slot_id: %u color: %u done", character_id, slot_id, color); ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterMaterialColor", query); return true; } bool ZoneDatabase::SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value){ std::string query = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, skill_id, value); auto results = QueryDatabase(query); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterSkill for character ID: %i, skill_id:%u value:%u done", character_id, skill_id, value); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterSkill for character ID: %i, skill_id:%u value:%u done", character_id, skill_id, value); ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterSkill", query); return true; } bool ZoneDatabase::SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id){ std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); auto results = QueryDatabase(query); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterDisc for character ID: %i, slot:%u disc_id:%u done", character_id, slot_id, disc_id); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterDisc for character ID: %i, slot:%u disc_id:%u done", character_id, slot_id, disc_id); ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterDisc", query); return true; } @@ -1247,7 +1245,7 @@ bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struc for (int i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 0xffffffffu){ std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); QueryDatabase(query); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); } } return true; @@ -1259,7 +1257,7 @@ bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_i std::string query = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%u, %u, %u, %u, %u,'%s')", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name_esc); auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterBandolier", query); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterBandolier for character ID: %i, bandolier_id: %u, bandolier_slot: %u item_id: %u, icon:%u band_name:%s done", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterBandolier for character ID: %i, bandolier_id: %u, bandolier_slot: %u item_id: %u, icon:%u band_name:%s done", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name); if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } return true; } @@ -1267,7 +1265,7 @@ bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_i bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon) { std::string query = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%u, %u, %u, %u)", character_id, potion_id, item_id, icon); auto results = QueryDatabase(query); - ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterPotionBelt", query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterPotionBelt", query); if (!results.RowsAffected()){ std::cout << "ERROR Potionbelt Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } return true; } @@ -1577,7 +1575,7 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla ); auto results = database.QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "ZoneDatabase:SaveCharacterData", query); - LogFile->write(EQEMuLog::Status, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; } @@ -1619,7 +1617,7 @@ bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Stru pp->careerEbonCrystals); auto results = database.QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterCurrency", query); - LogFile->write(EQEMuLog::Status, "Saving Currency for character ID: %i, done", character_id); + LogFile->write(EQEMuLog::Debug, "Saving Currency for character ID: %i, done", character_id); return true; } @@ -1629,7 +1627,7 @@ bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 cur character_id, aa_id, current_level); auto results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterAA", rquery); - LogFile->write(EQEMuLog::Status, "Saving AA for character ID: %u, aa_id: %u current_level: %u", character_id, aa_id, current_level); + LogFile->write(EQEMuLog::Debug, "Saving AA for character ID: %u, aa_id: %u current_level: %u", character_id, aa_id, current_level); return true; } @@ -1657,6 +1655,10 @@ bool ZoneDatabase::DeleteCharacterLeadershipAAs(uint32 character_id){ std::string query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", character_id); QueryDatabase(query); return true; } +bool ZoneDatabase::DeleteCharacterAAs(uint32 character_id){ + std::string query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", character_id); QueryDatabase(query); return true; +} + bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ std::string query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); QueryDatabase(query); return true; } diff --git a/zone/zonedb.h b/zone/zonedb.h index 647e9bfbd..f476f543c 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -287,6 +287,7 @@ public: bool DeleteCharacterDisc(uint32 character_id, uint32 slot_id); bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id); bool DeleteCharacterLeadershipAAs(uint32 character_id); + bool DeleteCharacterAAs(uint32 character_id); /* Character Inventory */ bool NoRentExpired(const char* name); From fee8a1214ade02a7f3209014ed9cc39bdd0d4e05 Mon Sep 17 00:00:00 2001 From: akkadius Date: Mon, 22 Sep 2014 02:52:23 -0500 Subject: [PATCH 131/368] Putting back in demonstar's lowercase check --- world/client.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index cd028d409..ca90b1f46 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -489,10 +489,20 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) outapp->size = 1; bool valid = false; - if(!database.CheckNameFilter(char_name)) { valid = false; } - else if (char_name[0] < 'A' && char_name[0] > 'Z') { valid = false; } /* Name must begin with an upper-case letter. */ - else if (database.ReserveName(GetAccountID(), char_name)) { valid = true; } - else { valid = false; } + if(!database.CheckNameFilter(char_name)) { + valid = false; + } + /* Name must begin with an upper-case letter. */ + else if (islower(char_name[0])) { + 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); From 1170b57fd59e43caa215993bbf28c072220fc729 Mon Sep 17 00:00:00 2001 From: akkadius Date: Mon, 22 Sep 2014 16:23:43 -0500 Subject: [PATCH 132/368] Fix starting items for starting_item entries that use slot -1 to find a free slot --- common/database.cpp | 21 ++++++++++++++++----- common/shareddb.cpp | 6 +++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 7f7af3f2c..ed90b9a79 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -708,7 +708,9 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe /* This only for new Character creation storing */ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv) { - uint32 charid = 0; char zone[50]; float x, y, z; + uint32 charid = 0; + char zone[50]; + float x, y, z; charid = GetCharacterID(pp->name); if(!charid) { @@ -739,7 +741,7 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inven invquery = StringFormat("INSERT INTO `inventory` (charid, slotid, itemid, charges, color) VALUES (%u, %i, %u, %i, %u)", charid, i, newinv->GetItem()->ID, newinv->GetCharges(), newinv->GetColor()); - auto results = QueryDatabase(invquery); + auto results = QueryDatabase(invquery); if (!results.RowsAffected()) LogFile->write(EQEMuLog::Error, "StoreCharacter inventory failed. Query '%s' %s", invquery.c_str(), results.ErrorMessage().c_str()); @@ -749,9 +751,18 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inven #endif } - if (i == MainCursor) { i = EmuConstants::GENERAL_BAGS_BEGIN; continue; } - else if (i == EmuConstants::CURSOR_BAG_END) { i = EmuConstants::BANK_BEGIN; continue; } - else if (i == EmuConstants::BANK_END) { i = EmuConstants::BANK_BAGS_BEGIN; continue; } + if (i == MainCursor) { + i = EmuConstants::GENERAL_BAGS_BEGIN; + continue; + } + else if (i == EmuConstants::CURSOR_BAG_END) { + i = EmuConstants::BANK_BEGIN; + continue; + } + else if (i == EmuConstants::BANK_END) { + i = EmuConstants::BANK_BAGS_BEGIN; + continue; + } i++; } return true; diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 69c97b5f0..aecfa0488 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -347,8 +347,8 @@ bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) { bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) { const Item_Struct* myitem; uint32 itemid = 0; - uint32 charges = 0; - uint32 slot = 0; + int32 charges = 0; + int32 slot = 0; auto query = StringFormat( "SELECT `itemid`, `item_charges`, `slot` FROM `starting_items`" " WHERE (`race` = %i OR `race` = 0)" @@ -363,7 +363,7 @@ bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, charges = atoi(row[1]); slot = atoi(row[2]); myitem = GetItem(itemid); - if(!myitem) + if(!myitem) continue; ItemInst* myinst = CreateBaseItem(myitem, charges); if(slot < 0) From c160b8716f48bef524e3b87d278bb8e41bdf9179 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 22 Sep 2014 18:02:40 -0400 Subject: [PATCH 133/368] Kayen: Spell recourse effects / triggerable spell effects will now be applied AFTER the base spells effects have been applied (consistent with live). --- changelog.txt | 3 ++ zone/spells.cpp | 105 +++--------------------------------------------- 2 files changed, 8 insertions(+), 100 deletions(-) diff --git a/changelog.txt b/changelog.txt index b002c0524..8517f38fe 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,11 +1,14 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- + + == 09/22/2014 == Akkadius: #resetaa now covers the function of #resetaa and #refundaa - #resetaa will wipe all AA data, refund the spent points into the available points and send character to character select properly Akkadius: Removed #refundaa Akkadius: Removed a lot of debug code for blob conversion Akkadius: Changed status logging for loads/saves to Debug category +Kayen: Spell recourse effects / triggerable spell effects will now be applied AFTER the base spells effects have been applied (consistent with live) == 09/21/2014 == Akkadius: Player Profile Blob to Database Conversion diff --git a/zone/spells.cpp b/zone/spells.cpp index d400e46ce..4d5f3c86c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3606,108 +3606,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r spell_effectiveness = 100; } - // Recourse means there is a spell linked to that spell in that the recourse spell will - // be automatically casted on the casters group or the caster only depending on Targettype - // this is for things like dark empathy, shadow vortex - int recourse_spell=0; - recourse_spell = spells[spell_id].RecourseLink; - if(recourse_spell) - { - if(spells[recourse_spell].targettype == ST_Group || spells[recourse_spell].targettype == ST_GroupTeleport) - { - if(IsGrouped()) - { - Group *g = entity_list.GetGroupByMob(this); - if(g) - g->CastGroupSpell(this, recourse_spell); - else{ - SpellOnTarget(recourse_spell, this); -#ifdef GROUP_BUFF_PETS - if (GetPet()) - SpellOnTarget(recourse_spell, GetPet()); -#endif - } - } - else if(IsRaidGrouped() && IsClient()) - { - Raid *r = entity_list.GetRaidByClient(CastToClient()); - uint32 gid = 0xFFFFFFFF; - if(r) - gid = r->GetGroup(GetName()); - else - gid = 13; // Forces ungrouped spell casting - - if(gid < 12) - { - r->CastGroupSpell(this, recourse_spell, gid); - } - else{ - SpellOnTarget(recourse_spell, this); -#ifdef GROUP_BUFF_PETS - if (GetPet()) - SpellOnTarget(recourse_spell, GetPet()); -#endif - } - } - else if(HasOwner()) - { - if(GetOwner()->IsGrouped()) - { - Group *g = entity_list.GetGroupByMob(GetOwner()); - if(g) - g->CastGroupSpell(this, recourse_spell); - else{ - SpellOnTarget(recourse_spell, GetOwner()); - SpellOnTarget(recourse_spell, this); - } - } - else if(GetOwner()->IsRaidGrouped() && GetOwner()->IsClient()) - { - Raid *r = entity_list.GetRaidByClient(GetOwner()->CastToClient()); - uint32 gid = 0xFFFFFFFF; - if(r) - gid = r->GetGroup(GetOwner()->GetName()); - else - gid = 13; // Forces ungrouped spell casting - - if(gid < 12) - { - r->CastGroupSpell(this, recourse_spell, gid); - } - else - { - SpellOnTarget(recourse_spell, GetOwner()); - SpellOnTarget(recourse_spell, this); - } - } - else - { - SpellOnTarget(recourse_spell, GetOwner()); - SpellOnTarget(recourse_spell, this); - } - } - else - { - SpellOnTarget(recourse_spell, this); -#ifdef GROUP_BUFF_PETS - if (GetPet()) - SpellOnTarget(recourse_spell, GetPet()); -#endif - } - - } - else - { - SpellOnTarget(recourse_spell, this); - } - } - if(spelltar->spellbonuses.SpellDamageShield && IsDetrimentalSpell(spell_id)) spelltar->DamageShield(this, true); - TrySpellTrigger(spelltar, spell_id); - TryApplyEffect(spelltar, spell_id); - if (spelltar->IsAIControlled() && IsDetrimentalSpell(spell_id) && !IsHarmonySpell(spell_id)) { int32 aggro_amount = CheckAggroAmount(spell_id, isproc); mlog(SPELLS__CASTING, "Spell %d cast on %s generated %d hate", spell_id, spelltar->GetName(), aggro_amount); @@ -3746,7 +3647,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r return false; } - + TrySpellTrigger(spelltar, spell_id); + TryApplyEffect(spelltar, spell_id); + if (IsValidSpell(spells[spell_id].RecourseLink)) + SpellFinished(spells[spell_id].RecourseLink, this, 10, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff); + if (IsDetrimentalSpell(spell_id)) { CheckNumHitsRemaining(NUMHIT_OutgoingSpells); From 36a29dbb9fbcad03ed12603e1a8a2e73b69f5d0b Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 22 Sep 2014 15:37:11 -0700 Subject: [PATCH 134/368] Fix for creating characters failing at name verification --- world/worlddb.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index f26061a5f..32c4ada48 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -391,12 +391,12 @@ bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Stru in_pp->x = in_pp->y = in_pp->z = in_pp->heading = in_pp->zone_id = 0; in_pp->binds[0].x = in_pp->binds[0].y = in_pp->binds[0].z = in_pp->binds[0].zoneId = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "SELECT `x`, `y`, ``z, `heading`, `bind_id` " - " FROM `start_zones` " - " WHERE `zone_id` = %i " - " AND `player_class` = %i " - " AND player_deity=%i" - " AND player_race=%i", + if(!RunQuery(query, MakeAnyLenString(&query, "SELECT x, y, z, heading, bind_id " + " FROM start_zones " + " WHERE zone_id = %i " + " AND player_class = %i " + " AND player_deity = %i" + " AND player_race = %i", in_cc->start_zone, in_cc->class_, in_cc->deity, From 1f155690d879e5ff08dccc7055fc2e1a8a3849d2 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 22 Sep 2014 19:42:59 -0400 Subject: [PATCH 135/368] Fix to SE_ApplyEffect - Will now trigger spell from this effect at correct time. --- zone/mob.cpp | 20 -------------------- zone/mob.h | 1 - zone/spell_effects.cpp | 11 ++++++++++- zone/spells.cpp | 1 - 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 77ff4938a..638e680df 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3083,26 +3083,6 @@ void Mob::TrySpellTrigger(Mob *target, uint32 spell_id) } } -void Mob::TryApplyEffect(Mob *target, uint32 spell_id) -{ - if(target == nullptr || !IsValidSpell(spell_id)) - { - return; - } - - for(int i = 0; i < EFFECT_COUNT; i++) - { - if (spells[spell_id].effectid[i] == SE_ApplyEffect) - { - if(MakeRandomInt(0, 100) <= spells[spell_id].base[i]) - { - if(target) - SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff); - } - } - } -} - void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsPet) { /* diff --git a/zone/mob.h b/zone/mob.h index 4e72f0264..4a28d1883 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -580,7 +580,6 @@ public: void TryTriggerOnCast(uint32 spell_id, bool aa_trigger); void TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger); void TrySpellTrigger(Mob *target, uint32 spell_id); - void TryApplyEffect(Mob *target, uint32 spell_id); void TryTriggerOnValueAmount(bool IsHP = false, bool IsMana = false, bool IsEndur = false, bool IsPet = false); void TryTwincast(Mob *caster, Mob *target, uint32 spell_id); void TrySympatheticProc(Mob *target, uint32 spell_id); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 80a356e9f..e2061c6ee 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2729,6 +2729,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) Message(10, "The power of your next illusion spell will flow to your grouped target in your place."); break; } + + case SE_ApplyEffect: { + + if (caster && IsValidSpell(spells[spell_id].base2[i])){ + + if(MakeRandomInt(0, 100) <= spells[spell_id].base[i]) + caster->SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spell_id].ResistDiff); + } + break; + } // Handled Elsewhere case SE_ImmuneFleeing: @@ -2840,7 +2850,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_SkillDamageTaken: case SE_FcSpellVulnerability: case SE_SpellTrigger: - case SE_ApplyEffect: case SE_FcTwincast: case SE_DelayDeath: case SE_CastOnFadeEffect: diff --git a/zone/spells.cpp b/zone/spells.cpp index 4d5f3c86c..d3779f713 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3648,7 +3648,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r } TrySpellTrigger(spelltar, spell_id); - TryApplyEffect(spelltar, spell_id); if (IsValidSpell(spells[spell_id].RecourseLink)) SpellFinished(spells[spell_id].RecourseLink, this, 10, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff); From 11773208df9e762c90e257274fc3dc0353c08d55 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 22 Sep 2014 16:46:01 -0700 Subject: [PATCH 136/368] Style cleanup, clamp hunger and thirst values to correct ranges --- zone/client.cpp | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index fad7d1a80..7038df6fc 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -529,8 +529,12 @@ bool Client::Save(uint8 iCommitNow) { m_pp.heading = heading; /* Mana and HP */ - if (GetHP() <= 0) { m_pp.cur_hp = GetMaxHP(); } - else { m_pp.cur_hp = GetHP(); } + if (GetHP() <= 0) { + m_pp.cur_hp = GetMaxHP(); + } + else { + m_pp.cur_hp = GetHP(); + } m_pp.mana = cur_mana; m_pp.endurance = cur_end; @@ -551,10 +555,17 @@ bool Client::Save(uint8 iCommitNow) { m_pp.RestTimer = rest_timer.GetRemainingTime() / 1000; /* Save Mercs */ - if (GetMercInfo().MercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)){ GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); } - if (GetMercTimer()->Enabled()) { GetMercInfo().MercTimerRemaining = GetMercTimer()->GetRemainingTime(); } - if (GetMerc() && !dead) { } - else { memset(&m_mercinfo, 0, sizeof(struct MercInfo)); } + if (GetMercInfo().MercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)) { + GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); + } + + if (GetMercTimer()->Enabled()) { + GetMercInfo().MercTimerRemaining = GetMercTimer()->GetRemainingTime(); + } + + if (!(GetMerc() && !dead)) { + memset(&m_mercinfo, 0, sizeof(struct MercInfo)); + } m_pp.lastlogin = time(nullptr); @@ -571,13 +582,31 @@ bool Client::Save(uint8 iCommitNow) { } database.SavePetInfo(this); - if(tribute_timer.Enabled()) { m_pp.tribute_time_remaining = tribute_timer.GetRemainingTime(); } - else { m_pp.tribute_time_remaining = 0xFFFFFFFF; m_pp.tribute_active = 0; } + if(tribute_timer.Enabled()) { + m_pp.tribute_time_remaining = tribute_timer.GetRemainingTime(); + } + else { + m_pp.tribute_time_remaining = 0xFFFFFFFF; m_pp.tribute_active = 0; + } p_timers.Store(&database); database.SaveCharacterTribute(this->CharacterID(), &m_pp); SaveTaskState(); /* Save Character Task */ + + if(m_pp.hunger_level < 0) { + m_pp.hunger_level = 0; + } + else if(m_pp.hunger_level > 6000) { + m_pp.hunger_level = 6000; + } + + if(m_pp.thirst_level < 0) { + m_pp.thirst_level = 0; + } + else if(m_pp.thirst_level > 6000) { + m_pp.thirst_level = 6000; + } database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp, &m_epp); /* Save Character Data */ return true; From c03a70651c0c1de1fb04f2fc8eef909331da9139 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 23 Sep 2014 09:15:02 -0400 Subject: [PATCH 137/368] SE_ApplySpell and SE_TriggerSpell will now be applied based on which effect slot they are used in (instead of always before all spell effects are checked). Note: If a spell has multiple SE_TriggerSpell effects within it. Only one will be able to trigger. (If you want multiple spells use SE_ApplySpell) --- changelog.txt | 6 ++++-- zone/mob.cpp | 27 +++++++++++++-------------- zone/mob.h | 2 +- zone/spell_effects.cpp | 11 +++++++++++ zone/spells.cpp | 1 - 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/changelog.txt b/changelog.txt index 8517f38fe..4f6818041 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- - +== 09/23/2014 == +Kayen: Spell recourse effects will now be applied AFTER the base spells effects have been applied (consistent with live). +Kayen: SE_ApplySpell and SE_TriggerSpell will now be applied based on which effect slot they are used in (instead of always before all spell effects are checked). +Note: If a spell has multiple SE_TriggerSpell effects within it. Only one will be able to trigger. (If you want multiple spells use SE_ApplySpell) == 09/22/2014 == Akkadius: #resetaa now covers the function of #resetaa and #refundaa @@ -8,7 +11,6 @@ Akkadius: #resetaa now covers the function of #resetaa and #refundaa Akkadius: Removed #refundaa Akkadius: Removed a lot of debug code for blob conversion Akkadius: Changed status logging for loads/saves to Debug category -Kayen: Spell recourse effects / triggerable spell effects will now be applied AFTER the base spells effects have been applied (consistent with live) == 09/21/2014 == Akkadius: Player Profile Blob to Database Conversion diff --git a/zone/mob.cpp b/zone/mob.cpp index 638e680df..06e125aab 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3031,12 +3031,11 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger) } } -void Mob::TrySpellTrigger(Mob *target, uint32 spell_id) +bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) { - if(target == nullptr || !IsValidSpell(spell_id)) - { - return; - } + if(!target || !IsValidSpell(spell_id)) + return false; + int spell_trig = 0; // Count all the percentage chances to trigger for all effects for(int i = 0; i < EFFECT_COUNT; i++) @@ -3055,8 +3054,10 @@ void Mob::TrySpellTrigger(Mob *target, uint32 spell_id) if(MakeRandomInt(0, trig_chance) <= spells[spell_id].base[i]) { // If we trigger an effect then its over. - SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff); - break; + if (IsValidSpell(spells[spell_id].base2[i])){ + SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff); + return true; + } } else { @@ -3070,17 +3071,15 @@ void Mob::TrySpellTrigger(Mob *target, uint32 spell_id) // if the chances don't add to 100, then each effect gets a chance to fire, chance for no trigger as well. else { - for(int i = 0; i < EFFECT_COUNT; i++) + if(MakeRandomInt(0, 100) <= spells[spell_id].base[effect]) { - if (spells[spell_id].effectid[i] == SE_SpellTrigger) - { - if(MakeRandomInt(0, 100) <= spells[spell_id].base[i]) - { - SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff); - } + if (IsValidSpell(spells[spell_id].base2[effect])){ + SpellFinished(spells[spell_id].base2[effect], target, 10, 0, -1, spells[spell_id].ResistDiff); + return true; //Only trigger once of these per spell effect. } } } + return false; } void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsPet) diff --git a/zone/mob.h b/zone/mob.h index 4a28d1883..0d365fdac 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -579,7 +579,7 @@ public: void DoBuffWearOffEffect(uint32 index); void TryTriggerOnCast(uint32 spell_id, bool aa_trigger); void TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger); - void TrySpellTrigger(Mob *target, uint32 spell_id); + bool TrySpellTrigger(Mob *target, uint32 spell_id, int effect); void TryTriggerOnValueAmount(bool IsHP = false, bool IsMana = false, bool IsEndur = false, bool IsPet = false); void TryTwincast(Mob *caster, Mob *target, uint32 spell_id); void TrySympatheticProc(Mob *target, uint32 spell_id); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index e2061c6ee..b017a90b0 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -188,6 +188,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (!IsPowerDistModSpell(spell_id)) SetSpellPowerDistanceMod(0); + + bool SE_SpellTrigger_HasCast = false; // iterate through the effects in the spell for (i = 0; i < EFFECT_COUNT; i++) @@ -2739,6 +2741,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) } break; } + + case SE_SpellTrigger: { + + if (!SE_SpellTrigger_HasCast) { + if (caster && caster->TrySpellTrigger(this, spell_id, i)) + SE_SpellTrigger_HasCast = true; + } + break; + } // Handled Elsewhere case SE_ImmuneFleeing: diff --git a/zone/spells.cpp b/zone/spells.cpp index d3779f713..5d60b11a2 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3647,7 +3647,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r return false; } - TrySpellTrigger(spelltar, spell_id); if (IsValidSpell(spells[spell_id].RecourseLink)) SpellFinished(spells[spell_id].RecourseLink, this, 10, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff); From 413538f1b56e23d4845e2c52d4eb9acc6396269d Mon Sep 17 00:00:00 2001 From: akkadius Date: Tue, 23 Sep 2014 14:40:09 -0500 Subject: [PATCH 138/368] Sending the client a spell value that is more inline with what it expects when no spell is set. --- zone/zonedb.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 1327bd8ee..a7eaaa9a5 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -958,7 +958,12 @@ bool ZoneDatabase::LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_ "FROM " "`character_memmed_spells` " "WHERE `id` = %u ORDER BY `slot_id`", character_id); - auto results = database.QueryDatabase(query); int i = 0; + auto results = database.QueryDatabase(query); + int i = 0; + /* Initialize Spells */ + for (i = 0; i < MAX_PP_MEMSPELL; i++){ + pp->mem_spells[i] = 0xFFFFFFFF; + } for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); if (i < MAX_PP_MEMSPELL){ @@ -976,14 +981,15 @@ bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Str "FROM " "`character_spells` " "WHERE `id` = %u ORDER BY `slot_id`", character_id); - auto results = database.QueryDatabase(query); int i = 0; + auto results = database.QueryDatabase(query); + int i = 0; /* Initialize Spells */ for (i = 0; i < MAX_PP_SPELLBOOK; i++){ - pp->spell_book[i] = 0; + pp->spell_book[i] = 0xFFFFFFFF; } for (auto row = results.begin(); row != results.end(); ++row) { - i = atoi(row[0]); - if (i < MAX_PP_SPELLBOOK){ + i = atoi(row[0]); + if (i < MAX_PP_SPELLBOOK){ pp->spell_book[i] = atoi(row[1]); } } From 7b85b09f24eaf435ca783e0bf6b2d437637b4258 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 23 Sep 2014 13:04:18 -0700 Subject: [PATCH 139/368] isnan fix for vs2012 --- common/database.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index ed90b9a79..b3516e3ca 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -50,6 +50,16 @@ #include "extprofile.h" extern Client client; +#ifdef _WINDOWS +#if _MSC_VER > 1700 // greater than 2012 (2013+) +# define _ISNAN_(a) std::isnan(a) +#else +# include +# define _ISNAN_(a) _isnan(a) +#endif +#else +# define _ISNAN_(a) std::isnan(a) +#endif /* Establish a connection to a mysql database with the supplied parameters @@ -1756,7 +1766,7 @@ bool Database::CheckDatabaseConversions() { if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "AA Convert", rquery); } /* Run Bind Home Convert */ - if(pp->binds[4].zoneId < 999 && !std::isnan(pp->binds[4].x) && !std::isnan(pp->binds[4].y) && !std::isnan(pp->binds[4].z) && !std::isnan(pp->binds[4].heading)) { + if(pp->binds[4].zoneId < 999 && !_ISNAN_(pp->binds[4].x) && !_ISNAN_(pp->binds[4].y) && !_ISNAN_(pp->binds[4].z) && !_ISNAN_(pp->binds[4].heading)) { rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, 1)", character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); @@ -1764,7 +1774,7 @@ bool Database::CheckDatabaseConversions() { } /* Run Bind Convert */ - if(pp->binds[0].zoneId < 999 && !std::isnan(pp->binds[0].x) && !std::isnan(pp->binds[0].y) && !std::isnan(pp->binds[0].z) && !std::isnan(pp->binds[0].heading)) { + if(pp->binds[0].zoneId < 999 && !_ISNAN_(pp->binds[0].x) && !_ISNAN_(pp->binds[0].y) && !_ISNAN_(pp->binds[0].z) && !_ISNAN_(pp->binds[0].heading)) { rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, 0)", character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); From 7cef4d8fe9c8e885a03f81ec8975d19977ce41cf Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 23 Sep 2014 13:39:05 -0700 Subject: [PATCH 140/368] Fix for spell books on newer clients not playing well with the tricksy new deblob code --- common/patches/rof.cpp | 4 ++-- common/patches/underfoot.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index d5303ccfb..9e81e8878 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -1081,7 +1081,7 @@ ENCODE(OP_PlayerProfile) // zeroes for the rest of the spellbook slots for(uint32 r = 0; r < structs::MAX_PP_SPELLBOOK - MAX_PP_SPELLBOOK; r++) { - outapp->WriteUInt32(0); + outapp->WriteUInt32(0xFFFFFFFFU); } outapp->WriteUInt32(structs::MAX_PP_MEMSPELL); // Memorised spell slots @@ -1093,7 +1093,7 @@ ENCODE(OP_PlayerProfile) // zeroes for the rest of the slots for(uint32 r = 0; r < structs::MAX_PP_MEMSPELL - MAX_PP_MEMSPELL; r++) { - outapp->WriteUInt32(0); + outapp->WriteUInt32(0xFFFFFFFFU); } outapp->WriteUInt32(13); // Unknown count diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index 1eab127d5..524a4d23b 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -537,7 +537,8 @@ ENCODE(OP_PlayerProfile) { OUT(WIS); OUT(face); // OUT(unknown02264[47]); - OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); + memset(eq->spell_book, 0xFF, sizeof(uint32)* structs::MAX_PP_SPELLBOOK); + OUT_array(spell_book, 480U); // OUT(unknown4184[128]); OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); // OUT(unknown04396[32]); From e039f6d2c6f269d11b128c9b8ab77a90cd3450ed Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 23 Sep 2014 17:40:17 -0400 Subject: [PATCH 141/368] Fix AA issue --- zone/client_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 928eca1ad..a8057f822 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -720,7 +720,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); m_pp.aa_array[i].AA = atoi(row[1]); - m_pp.aa_array[i].value = atoi(row[1]); + m_pp.aa_array[i].value = atoi(row[2]); aa[i]->AA = atoi(row[1]); aa[i]->value = atoi(row[2]); } From f89add9f64f59aac3a66429e5ee6f7aa6c81e9c6 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 23 Sep 2014 15:09:33 -0700 Subject: [PATCH 142/368] Fix for AA conversion --- common/database.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index b3516e3ca..356c7f6a8 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1753,14 +1753,15 @@ bool Database::CheckDatabaseConversions() { */ /* Run AA Convert */ int first_entry = 0; rquery = ""; - for (i = 1; i < MAX_PP_AA_ARRAY; i++){ + for (i = 0; i < MAX_PP_AA_ARRAY; i++){ if (pp->aa_array[i].AA > 0 && pp->aa_array[i].value > 0){ if (first_entry != 1){ rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" " VALUES (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); first_entry = 1; + } else { + rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); } - rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); } } if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "AA Convert", rquery); } From e58d63bf35d952becb3d03fa02b4ed6cf6a1f0f7 Mon Sep 17 00:00:00 2001 From: akkadius Date: Tue, 23 Sep 2014 17:21:08 -0500 Subject: [PATCH 143/368] IncrementAA for scripting functions changed to use SaveAA instead of Save --- zone/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index 7038df6fc..516572c5e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8021,7 +8021,7 @@ void Client::IncrementAA(int aa_id) { SetAA(aa_id, GetAA(aa_id) + 1); - Save(); + SaveAA(); SendAA(aa_id); SendAATable(); From 15f57e4a4c1a9f27170a39eaa5a5ffbdcab11906 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 23 Sep 2014 15:42:20 -0700 Subject: [PATCH 144/368] Rob's broken skill up bug reported fixed --- zone/client.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 7038df6fc..70e92b764 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2324,12 +2324,14 @@ bool Client::CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int cha { // the higher your current skill level, the harder it is int16 Chance = 10 + chancemodi + ((252 - skillval) / 20); - if (Chance < 1) - Chance = 1; // Make it always possible + Chance = (Chance * RuleI(Character, SkillUpModifier) / 100); Chance = mod_increase_skill_chance(Chance, against_who); + if(Chance < 1) + Chance = 1; // Make it always possible + if(MakeRandomFloat(0, 99) < Chance) { SetSkill(skillid, GetRawSkill(skillid) + 1); From 3712d368679747a11d4b342dbe06acf498b02db0 Mon Sep 17 00:00:00 2001 From: akkadius Date: Tue, 23 Sep 2014 17:54:07 -0500 Subject: [PATCH 145/368] Fix for the undye command to properly purge character armor dye --- zone/command.cpp | 2 +- zone/zonedb.cpp | 38 ++++++++++++++++++++++++++++++-------- zone/zonedb.h | 1 + 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index fe8d58faa..2844e9daf 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -7681,7 +7681,7 @@ void Client::Undye() { SendWearChange(cur_slot); } - Save(0); + database.DeleteCharacterDye(this->CharacterID()); } void command_undye(Client *c, const Seperator *sep) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index a7eaaa9a5..9a7ac54aa 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1638,35 +1638,57 @@ bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 cur } bool ZoneDatabase::SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ - std::string query = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); QueryDatabase(query); return true; + std::string query = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); + QueryDatabase(query); + return true; } bool ZoneDatabase::SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ - std::string query = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); QueryDatabase(query); return true; + std::string query = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); + QueryDatabase(query); + return true; } bool ZoneDatabase::DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ - std::string query = StringFormat("DELETE FROM `character_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); QueryDatabase(query); return true; + std::string query = StringFormat("DELETE FROM `character_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); + QueryDatabase(query); + return true; } bool ZoneDatabase::DeleteCharacterDisc(uint32 character_id, uint32 slot_id){ - std::string query = StringFormat("DELETE FROM `character_disciplines` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); QueryDatabase(query); return true; + std::string query = StringFormat("DELETE FROM `character_disciplines` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); + QueryDatabase(query); + return true; } bool ZoneDatabase::DeleteCharacterBandolier(uint32 character_id, uint32 band_id){ - std::string query = StringFormat("DELETE FROM `character_bandolier` WHERE `bandolier_id` = %u AND `id` = %u", band_id, character_id); QueryDatabase(query); return true; + std::string query = StringFormat("DELETE FROM `character_bandolier` WHERE `bandolier_id` = %u AND `id` = %u", band_id, character_id); + QueryDatabase(query); + return true; } bool ZoneDatabase::DeleteCharacterLeadershipAAs(uint32 character_id){ - std::string query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", character_id); QueryDatabase(query); return true; + std::string query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", character_id); + QueryDatabase(query); + return true; } bool ZoneDatabase::DeleteCharacterAAs(uint32 character_id){ - std::string query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", character_id); QueryDatabase(query); return true; + std::string query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", character_id); + QueryDatabase(query); + return true; +} + +bool ZoneDatabase::DeleteCharacterDye(uint32 character_id){ + std::string query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", character_id); + QueryDatabase(query); + return true; } bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ - std::string query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); QueryDatabase(query); return true; + std::string query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); + QueryDatabase(query); + return true; } bool ZoneDatabase::NoRentExpired(const char* name){ diff --git a/zone/zonedb.h b/zone/zonedb.h index f476f543c..b6577cf42 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -288,6 +288,7 @@ public: bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id); bool DeleteCharacterLeadershipAAs(uint32 character_id); bool DeleteCharacterAAs(uint32 character_id); + bool DeleteCharacterDye(uint32 character_id); /* Character Inventory */ bool NoRentExpired(const char* name); From 4cbccfdce23dce9e685e810329d2d6dadcf16246 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 23 Sep 2014 15:59:58 -0700 Subject: [PATCH 146/368] Fix for MoveCharacterToZone using zoneid instead of zone_id --- common/database.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 356c7f6a8..6683e8ebd 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2467,7 +2467,7 @@ bool Database::MoveCharacterToZone(const char* charname, const char* zonename, u if(zonename == nullptr || strlen(zonename) == 0) return false; - std::string query = StringFormat("UPDATE `character_data` SET `zoneid` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `name` = '%s'", zoneid, charname); + std::string query = StringFormat("UPDATE `character_data` SET `zone_id` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `name` = '%s'", zoneid, charname); auto results = QueryDatabase(query); if (!results.Success()) { @@ -2486,7 +2486,7 @@ bool Database::MoveCharacterToZone(const char* charname, const char* zonename) { } bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { - std::string query = StringFormat("UPDATE `character_data` SET `zoneid` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `id` = %i", iZonename, GetZoneID(iZonename), iCharID); + std::string query = StringFormat("UPDATE `character_data` SET `zone_id` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `id` = %i", iZonename, GetZoneID(iZonename), iCharID); auto results = QueryDatabase(query); if (!results.Success()) { From 973aa94cb5837a8c3f560e84ef4c53b070c032a5 Mon Sep 17 00:00:00 2001 From: akkadius Date: Tue, 23 Sep 2014 18:28:17 -0500 Subject: [PATCH 147/368] Fix for database schema conversion where `character_` table has 0 characters present. This will allow the tables to still be created properly and the old one renamed. --- common/database.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 6683e8ebd..cc8236586 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -924,9 +924,9 @@ bool Database::CheckDatabaseConversions() { int runconvert = 0; /* Check For Legacy Storage Method */ - std::string rquery = StringFormat("SELECT `profile` FROM `character_` LIMIT 1"); + std::string rquery = StringFormat("SHOW TABLES LIKE 'character_'"); auto results = QueryDatabase(rquery); - for (auto row = results.begin(); row != results.end(); ++row) { + if (results.RowCount() == 1){ runconvert = 1; printf("\n\n::: Legacy Character Data Binary Blob Storage Detected... \n"); printf("----------------------------------------------------------\n\n"); From 16ba3eb11eceb9c1898d9006f3b32e229997694e Mon Sep 17 00:00:00 2001 From: akkadius Date: Tue, 23 Sep 2014 19:12:31 -0500 Subject: [PATCH 148/368] Restoring Disciplines to their original load order (Slot) Tribute uninitialized value database conversion fix Tribute load fix (Lost in translation) Changed Tribute 0 value to 0xFFFFFFFF instead of 0 Sanity check for unitialized tribute value on load --- common/database.cpp | 2 +- zone/client_packet.cpp | 1 + zone/zonedb.cpp | 17 +++++++++++------ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index cc8236586..1af4d05a0 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1857,7 +1857,7 @@ bool Database::CheckDatabaseConversions() { /* Run Tribute Convert */ first_entry = 0; rquery = ""; for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - if (pp->tributes[i].tribute > 0){ + if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 4294967295){ if (first_entry != 1){ rquery = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); first_entry = 1; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a8057f822..ba25cdd1a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -597,6 +597,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */ database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ database.LoadCharacterLeadershipAA(cid, &m_pp); /* Load Character Leadership AA's */ + database.LoadCharacterTribute(cid, &m_pp); /* Load CharacterTribute */ /* Set item material tint */ for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 9a7ac54aa..594d6492d 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1034,10 +1034,13 @@ bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_S "disc_id " "FROM " "`character_disciplines`" - "WHERE `id` = %u ORDER BY `disc_id`", character_id); - auto results = database.QueryDatabase(query); int i = 0; + "WHERE `id` = %u ORDER BY `slot_id`", character_id); + auto results = database.QueryDatabase(query); + int i = 0; + /* Initialize Disciplines */ + memset(pp->disciplines.values, 0, MAX_PP_DISCIPLINES); for (auto row = results.begin(); row != results.end(); ++row) { - if (i < MAX_PP_DISCIPLINES){ + if (i < MAX_PP_DISCIPLINES){ pp->disciplines.values[i] = atoi(row[0]); } i++; @@ -1150,12 +1153,14 @@ bool ZoneDatabase::LoadCharacterTribute(uint32 character_id, PlayerProfile_Struc std::string query = StringFormat("SELECT `tier`, `tribute` FROM `character_tribute` WHERE `id` = %u", character_id); auto results = database.QueryDatabase(query); int i = 0; for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - pp->tributes[i].tribute = 0; + pp->tributes[i].tribute = 0xFFFFFFFF; pp->tributes[i].tier = 0; } for (auto row = results.begin(); row != results.end(); ++row) { - pp->tributes[i].tier = atoi(row[0]); - pp->tributes[i].tribute = atoi(row[1]); + if(pp->tributes[i].tribute != TRIBUTE_NONE){ + pp->tributes[i].tier = atoi(row[0]); + pp->tributes[i].tribute = atoi(row[1]); + } } return true; } From 9be0d3b090f5a38077ca067a3c7f0d0e2a0eee48 Mon Sep 17 00:00:00 2001 From: akkadius Date: Tue, 23 Sep 2014 19:19:40 -0500 Subject: [PATCH 149/368] memset size fix to calc current size of 400 bytes for discipline initialization --- zone/zonedb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 594d6492d..6cae8e754 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1038,7 +1038,7 @@ bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_S auto results = database.QueryDatabase(query); int i = 0; /* Initialize Disciplines */ - memset(pp->disciplines.values, 0, MAX_PP_DISCIPLINES); + memset(pp->disciplines.values, 0, (sizeof(pp->disciplines.values[0]) * MAX_PP_DISCIPLINES)); for (auto row = results.begin(); row != results.end(); ++row) { if (i < MAX_PP_DISCIPLINES){ pp->disciplines.values[i] = atoi(row[0]); From 9878459049c986e1ca7912d0c737df317230893c Mon Sep 17 00:00:00 2001 From: akkadius Date: Tue, 23 Sep 2014 20:16:19 -0500 Subject: [PATCH 150/368] Fix tribute loading/saving for real --- zone/zonedb.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 6cae8e754..3728e3144 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1151,15 +1151,18 @@ bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Str bool ZoneDatabase::LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat("SELECT `tier`, `tribute` FROM `character_tribute` WHERE `id` = %u", character_id); - auto results = database.QueryDatabase(query); int i = 0; + auto results = database.QueryDatabase(query); + int i = 0; for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ pp->tributes[i].tribute = 0xFFFFFFFF; pp->tributes[i].tier = 0; } + i = 0; for (auto row = results.begin(); row != results.end(); ++row) { - if(pp->tributes[i].tribute != TRIBUTE_NONE){ + if(atoi(row[1]) != TRIBUTE_NONE){ pp->tributes[i].tier = atoi(row[0]); pp->tributes[i].tribute = atoi(row[1]); + i++; } } return true; @@ -1244,21 +1247,24 @@ bool ZoneDatabase::SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint } bool ZoneDatabase::SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id){ - std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); auto results = QueryDatabase(query); + std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); + auto results = QueryDatabase(query); LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterDisc for character ID: %i, slot:%u disc_id:%u done", character_id, slot_id, disc_id); ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterDisc", query); return true; } bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); QueryDatabase(query); + std::string query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); + QueryDatabase(query); /* Save Tributes only if we have values... */ for (int i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 0xffffffffu){ - std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); QueryDatabase(query); + if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != TRIBUTE_NONE){ + std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + QueryDatabase(query); LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); } - } + } return true; } From 0d12715d7701949b4d76003a0495359e37bb4526 Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 24 Sep 2014 03:30:38 -0700 Subject: [PATCH 151/368] Data verification utils, not in use yet. Also added ability for lua packet to bypass the translation layer (dangerous) if a writer so desires (useful for quickly trying packet stuff) --- common/data_verification.h | 43 ++++++++++++++++ common/eq_packet.h | 15 +++--- common/eq_stream.cpp | 54 +++---------------- common/struct_strategy.cpp | 5 ++ tests/CMakeLists.txt | 3 ++ tests/data_verification_test.h | 94 ++++++++++++++++++++++++++++++++++ tests/main.cpp | 2 + tests/string_util_test.h | 4 +- zone/client.cpp | 4 -- zone/lua_packet.cpp | 47 +++++++++++++++-- zone/lua_packet.h | 3 ++ 11 files changed, 209 insertions(+), 65 deletions(-) create mode 100644 common/data_verification.h create mode 100644 tests/data_verification_test.h diff --git a/common/data_verification.h b/common/data_verification.h new file mode 100644 index 000000000..d42f72aac --- /dev/null +++ b/common/data_verification.h @@ -0,0 +1,43 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef COMMON_DATA_VERIFICATION_H +#define COMMON_DATA_VERIFICATION_H + +#include + +namespace EQEmu +{ + +template +T Clamp(const T& value, const T& lower, const T& upper) { + return std::max(lower, std::min(value, upper)); +} + +template +T ClampLower(const T& value, const T& lower) { + return std::max(lower, value); +} + +template +T ClampUpper(const T& value, const T& upper) { + return std::min(value, upper); +} + +} + +#endif diff --git a/common/eq_packet.h b/common/eq_packet.h index 9e0bcec3e..04418f733 100644 --- a/common/eq_packet.h +++ b/common/eq_packet.h @@ -97,16 +97,15 @@ protected: }; class EQApplicationPacket : public EQPacket { -// friend class EQProtocolPacket; friend class EQStream; public: - EQApplicationPacket() : EQPacket(OP_Unknown,nullptr,0) + EQApplicationPacket() : EQPacket(OP_Unknown, nullptr, 0), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } - EQApplicationPacket(const EmuOpcode op) : EQPacket(op,nullptr,0) + EQApplicationPacket(const EmuOpcode op) : EQPacket(op, nullptr, 0), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } - EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(op,nullptr,len) + EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(op, nullptr, len), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } - EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(op,buf,len) + EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(op, buf, len), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } bool combine(const EQApplicationPacket *rhs); uint32 serialize (uint16 opcode, unsigned char *dest) const; @@ -119,12 +118,16 @@ public: virtual void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; + uint16 GetOpcodeBypass() { return opcode_bypass; } + void SetOpcodeBypass(uint16 v) { opcode_bypass = v; } + protected: uint8 app_opcode_size; + uint16 opcode_bypass; private: - EQApplicationPacket(const EQApplicationPacket &p) : EQPacket(p.emu_opcode, p.pBuffer, p.size) { app_opcode_size = p.app_opcode_size; } + EQApplicationPacket(const EQApplicationPacket &p) : EQPacket(p.emu_opcode, p.pBuffer, p.size), opcode_bypass(p.opcode_bypass) { app_opcode_size = p.app_opcode_size; } }; diff --git a/common/eq_stream.cpp b/common/eq_stream.cpp index 8cb67e0e0..7b69decc2 100644 --- a/common/eq_stream.cpp +++ b/common/eq_stream.cpp @@ -532,9 +532,12 @@ void EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req) return; } - uint16 opcode = (*OpMgr)->EmuToEQ(pack->emu_opcode); - - //_log(NET__APP_TRACE, "Queueing %sacked packet with opcode 0x%x (%s) and length %d", ack_req?"":"non-", opcode, OpcodeManager::EmuToName(pack->emu_opcode), pack->size); + uint16 opcode = 0; + if(pack->GetOpcodeBypass() != 0) { + opcode = pack->GetOpcodeBypass(); + } else { + opcode = (*OpMgr)->EmuToEQ(pack->emu_opcode); + } if (!ack_req) { NonSequencedPush(new EQProtocolPacket(opcode, pack->pBuffer, pack->size)); @@ -877,43 +880,6 @@ sockaddr_in address; AddBytesSent(length); } -/* -commented out since im not sure theres a lot of merit in it. -Really it was bitterness towards allocating a 2k buffer on the stack each call. -Im sure the thought was client side, but even then, they will -likely need a whole thread to call this method, in which case, they should -supply the buffer so we dont re-allocate it each time. -EQProtocolPacket *EQStream::Read(int eq_fd, sockaddr_in *from) -{ -int socklen; -int length=0; -EQProtocolPacket *p=nullptr; -char temp[15]; - - socklen=sizeof(sockaddr); -#ifdef _WINDOWS - length=recvfrom(eq_fd, (char *)_tempBuffer, 2048, 0, (struct sockaddr*)from, (int *)&socklen); -#else - length=recvfrom(eq_fd, _tempBuffer, 2048, 0, (struct sockaddr*)from, (socklen_t *)&socklen); -#endif - - if (length>=2) { - p=new EQProtocolPacket(_tempBuffer[1],&_tempBuffer[2],length-2); - - uint32 ip=from->sin_addr.s_addr; - sprintf(temp,"%d.%d.%d.%d:%d", - *(unsigned char *)&ip, - *((unsigned char *)&ip+1), - *((unsigned char *)&ip+2), - *((unsigned char *)&ip+3), - ntohs(from->sin_port)); - //std::cout << timestamp() << "Data from: " << temp << " OpCode 0x" << std::hex << std::setw(2) << std::setfill('0') << (int)p->opcode << std::dec << std::endl; - //dump_message(p->pBuffer,p->size,timestamp()); - - } - return p; -}*/ - void EQStream::SendSessionResponse() { EQProtocolPacket *out=new EQProtocolPacket(OP_SessionResponse,nullptr,sizeof(SessionResponse)); @@ -1101,14 +1067,6 @@ EQProtocolPacket *p=nullptr; SequencedQueue.clear(); } MOutboundQueue.unlock(); - -/*if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - _log(NET__ERROR, _L "Out-bound Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); -} -if(NextSequencedSend > SequencedQueue.size()) { - _log(NET__ERROR, _L "Out-bound Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); -}*/ - //NOTE: we prolly want to reset counters if we are stupposed to do anything after this. } void EQStream::PacketQueueClear() diff --git a/common/struct_strategy.cpp b/common/struct_strategy.cpp index 9507fd231..9b568b12e 100644 --- a/common/struct_strategy.cpp +++ b/common/struct_strategy.cpp @@ -17,6 +17,11 @@ StructStrategy::StructStrategy() { } void StructStrategy::Encode(EQApplicationPacket **p, EQStream *dest, bool ack_req) const { + if((*p)->GetOpcodeBypass() != 0) { + PassEncoder(p, dest, ack_req); + return; + } + EmuOpcode op = (*p)->GetOpcode(); Encoder proc = encoders[op]; proc(p, dest, ack_req); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f7972e98f..428e8b5b1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,7 @@ SET(tests_sources SET(tests_headers atobool_test.h + data_verification_test.h fixed_memory_test.h fixed_memory_variable_test.h hextoi_32_64_test.h @@ -20,6 +21,8 @@ ADD_EXECUTABLE(tests ${tests_sources} ${tests_headers}) TARGET_LINK_LIBRARIES(tests common cppunit) +INSTALL(TARGETS tests RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}) + IF(MSVC) SET_TARGET_PROPERTIES(tests PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") TARGET_LINK_LIBRARIES(tests "Ws2_32.lib") diff --git a/tests/data_verification_test.h b/tests/data_verification_test.h new file mode 100644 index 000000000..1a151c7fa --- /dev/null +++ b/tests/data_verification_test.h @@ -0,0 +1,94 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __EQEMU_TESTS_DATA_VERIFICATION_H +#define __EQEMU_TESTS_DATA_VERIFICATION_H + +#include "cppunit/cpptest.h" +#include "../common/data_verification.h" + +class DataVerificationTest : public Test::Suite { + typedef void(DataVerificationTest::*TestFunction)(void); +public: + DataVerificationTest() { + TEST_ADD(DataVerificationTest::Clamp); + TEST_ADD(DataVerificationTest::ClampUpper); + TEST_ADD(DataVerificationTest::ClampLower); + } + + ~DataVerificationTest() { + } + + private: + void Clamp() { + float value_f = 500.0f; + int value_i = 500; + + float vf1 = EQEmu::Clamp(value_f, 0.0f, 1000.0f); + float vf2 = EQEmu::Clamp(value_f, 0.0f, 250.0f); + float vf3 = EQEmu::Clamp(value_f, 750.0f, 1000.0f); + + int vi1 = EQEmu::Clamp(value_i, 0, 1000); + int vi2 = EQEmu::Clamp(value_i, 0, 250); + int vi3 = EQEmu::Clamp(value_i, 750, 1000); + + TEST_ASSERT_EQUALS(vf1, 500.0f); + TEST_ASSERT_EQUALS(vf2, 250.0f); + TEST_ASSERT_EQUALS(vf3, 750.0f); + + TEST_ASSERT_EQUALS(vi1, 500); + TEST_ASSERT_EQUALS(vi2, 250); + TEST_ASSERT_EQUALS(vi3, 750); + } + + void ClampUpper() { + float value_f = 500.0f; + int value_i = 500; + + float vf1 = EQEmu::ClampUpper(value_f, 1000.0f); + float vf2 = EQEmu::ClampUpper(value_f, 250.0f); + + int vi1 = EQEmu::ClampUpper(value_i, 1000); + int vi2 = EQEmu::ClampUpper(value_i, 250); + + TEST_ASSERT_EQUALS(vf1, 500.0f); + TEST_ASSERT_EQUALS(vf2, 250.0f); + + TEST_ASSERT_EQUALS(vi1, 500); + TEST_ASSERT_EQUALS(vi2, 250); + } + + void ClampLower() { + float value_f = 500.0f; + int value_i = 500; + + float vf1 = EQEmu::ClampLower(value_f, 0.0f); + float vf2 = EQEmu::ClampLower(value_f, 750.0f); + + int vi1 = EQEmu::ClampLower(value_i, 0); + int vi2 = EQEmu::ClampLower(value_i, 750); + + TEST_ASSERT_EQUALS(vf1, 500.0f); + TEST_ASSERT_EQUALS(vf2, 750.0f); + + TEST_ASSERT_EQUALS(vi1, 500); + TEST_ASSERT_EQUALS(vi2, 750); + } +}; + +#endif diff --git a/tests/main.cpp b/tests/main.cpp index 285460da4..9d9b658f9 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -27,6 +27,7 @@ #include "atobool_test.h" #include "hextoi_32_64_test.h" #include "string_util_test.h" +#include "data_verification_test.h" int main() { try { @@ -40,6 +41,7 @@ int main() { tests.add(new atoboolTest()); tests.add(new hextoi_32_64_Test()); tests.add(new StringUtilTest()); + tests.add(new DataVerificationTest()); tests.run(*output, true); } catch(...) { return -1; diff --git a/tests/string_util_test.h b/tests/string_util_test.h index 32efb5320..facb0cc72 100644 --- a/tests/string_util_test.h +++ b/tests/string_util_test.h @@ -23,7 +23,7 @@ #include "../common/string_util.h" class StringUtilTest : public Test::Suite { - typedef void(IPCMutexTest::*TestFunction)(void); + typedef void(StringUtilTest::*TestFunction)(void); public: StringUtilTest() { TEST_ADD(StringUtilTest::StringFormatTest); @@ -35,7 +35,7 @@ public: } private: - void StringFormatTest() { + void StringFormatTest() { const char* fmt = "Test: %c %d %4.2f"; char c = 'a'; int i = 2014; diff --git a/zone/client.cpp b/zone/client.cpp index 056f7eeb5..62b903802 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -701,14 +701,10 @@ void Client::QueuePacket(const EQApplicationPacket* app, bool ack_req, CLIENT_CO } void Client::FastQueuePacket(EQApplicationPacket** app, bool ack_req, CLIENT_CONN_STATUS required_state) { - - //std::cout << "Sending: 0x" << std::hex << std::setw(4) << std::setfill('0') << (*app)->GetOpcode() << std::dec << ", size=" << (*app)->size << std::endl; - // if the program doesnt care about the status or if the status isnt what we requested if (required_state != CLIENT_CONNECTINGALL && client_state != required_state) { // todo: save packets for later use AddPacket(app, ack_req); -// LogFile->write(EQEMuLog::Normal, "Adding Packet to list (%d) (%d)", (*app)->GetOpcode(), (int)required_state); return; } else { diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index ea761b9b2..640ee7015 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -14,14 +14,31 @@ Lua_Packet::Lua_Packet(int opcode, int size) { owned_ = true; } +Lua_Packet::Lua_Packet(int opcode, int size, bool raw) { + if(raw) { + SetLuaPtrData(new EQApplicationPacket(OP_Unknown, size)); + owned_ = true; + + EQApplicationPacket *self = reinterpret_cast(d_); + self->SetOpcodeBypass(opcode); + } else { + SetLuaPtrData(new EQApplicationPacket(static_cast(opcode), size)); + owned_ = true; + } +} + Lua_Packet& Lua_Packet::operator=(const Lua_Packet& o) { if(o.owned_) { owned_ = true; EQApplicationPacket *app = reinterpret_cast(o.d_); - if(app) + if(app) { d_ = new EQApplicationPacket(app->GetOpcode(), app->pBuffer, app->size); - else + + EQApplicationPacket *self = reinterpret_cast(d_); + self->SetOpcodeBypass(app->GetOpcodeBypass()); + } else { d_ = nullptr; + } } else { owned_ = false; d_ = o.d_; @@ -33,10 +50,14 @@ Lua_Packet::Lua_Packet(const Lua_Packet& o) { if(o.owned_) { owned_ = true; EQApplicationPacket *app = reinterpret_cast(o.d_); - if(app) + if(app) { d_ = new EQApplicationPacket(app->GetOpcode(), app->pBuffer, app->size); - else + + EQApplicationPacket *self = reinterpret_cast(d_); + self->SetOpcodeBypass(app->GetOpcodeBypass()); + } else { d_ = nullptr; + } } else { owned_ = false; d_ = o.d_; @@ -54,6 +75,16 @@ int Lua_Packet::GetOpcode() { } void Lua_Packet::SetOpcode(int op) { + Lua_Safe_Call_Void(); + self->SetOpcodeBypass(static_cast(op)); +} + +int Lua_Packet::GetRawOpcode() { + Lua_Safe_Call_Int(); + return static_cast(self->GetOpcodeBypass()); +} + +void Lua_Packet::SetRawOpcode(int op) { Lua_Safe_Call_Void(); self->SetOpcode(static_cast(op)); } @@ -244,11 +275,14 @@ luabind::scope lua_register_packet() { return luabind::class_("Packet") .def(luabind::constructor<>()) .def(luabind::constructor()) + .def(luabind::constructor()) .property("null", &Lua_Packet::Null) .property("valid", &Lua_Packet::Valid) .def("GetSize", &Lua_Packet::GetSize) .def("GetOpcode", &Lua_Packet::GetOpcode) .def("SetOpcode", &Lua_Packet::SetOpcode) + .def("GetRawOpcode", &Lua_Packet::GetRawOpcode) + .def("SetRawOpcode", &Lua_Packet::SetRawOpcode) .def("WriteInt8", &Lua_Packet::WriteInt8) .def("WriteInt16", &Lua_Packet::WriteInt16) .def("WriteInt32", &Lua_Packet::WriteInt32) @@ -809,7 +843,10 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("MercenaryDismiss", static_cast(OP_MercenaryDismiss)), luabind::value("MercenaryTimerRequest", static_cast(OP_MercenaryTimerRequest)), luabind::value("OpenInventory", static_cast(OP_OpenInventory)), - luabind::value("OpenContainer", static_cast(OP_OpenContainer)) + luabind::value("OpenContainer", static_cast(OP_OpenContainer)), + luabind::value("Marquee", static_cast(OP_Marquee)), + luabind::value("ClientTimeStamp", static_cast(OP_ClientTimeStamp)), + luabind::value("GuildPromote", static_cast(OP_GuildPromote)) ]; } diff --git a/zone/lua_packet.h b/zone/lua_packet.h index c036594d1..a3c5ac553 100644 --- a/zone/lua_packet.h +++ b/zone/lua_packet.h @@ -21,6 +21,7 @@ public: Lua_Packet() : Lua_Ptr(nullptr), owned_(false) { } Lua_Packet(EQApplicationPacket *d) : Lua_Ptr(d), owned_(false) { } Lua_Packet(int opcode, int size); + Lua_Packet(int opcode, int size, bool raw); Lua_Packet& operator=(const Lua_Packet& o); Lua_Packet(const Lua_Packet& o); virtual ~Lua_Packet() { if(owned_) { EQApplicationPacket *ptr = GetLuaPtrData(); if(ptr) { delete ptr; } } } @@ -28,6 +29,8 @@ public: int GetSize(); int GetOpcode(); void SetOpcode(int op); + int GetRawOpcode(); + void SetRawOpcode(int op); void WriteInt8(int offset, int value); void WriteInt16(int offset, int value); void WriteInt32(int offset, int value); From a445d7e39fe486f65742fa68598f36739eafe3bd Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 24 Sep 2014 12:22:06 -0400 Subject: [PATCH 152/368] Rename OP_BuffFadeMsg to OP_ColoredText since it is a generic opcode --- common/emu_oplist.h | 2 +- common/eq_packet_structs.h | 2 +- common/opcode_dispatch.h | 2 +- common/opcode_map.cpp | 2 +- common/patches/client62_structs.h | 2 +- common/patches/rof_structs.h | 2 +- common/patches/sod_structs.h | 2 +- common/patches/sof_opcode_list.h | 2 +- common/patches/sof_structs.h | 2 +- common/patches/titanium_structs.h | 2 +- common/patches/underfoot_structs.h | 2 +- utils/patches/opcodes.conf | 2 +- utils/patches/patch_6.2.conf | 2 +- utils/patches/patch_RoF.conf | 2 +- utils/patches/patch_SoD.conf | 4 ++-- utils/patches/patch_SoF.conf | 2 +- utils/patches/patch_Titanium.conf | 2 +- utils/patches/patch_Underfoot.conf | 2 +- zone/lua_packet.cpp | 2 +- zone/spells.cpp | 4 ++-- 20 files changed, 22 insertions(+), 22 deletions(-) diff --git a/common/emu_oplist.h b/common/emu_oplist.h index c1ceb3c80..4eb516d94 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -139,7 +139,7 @@ N(OP_Camp), N(OP_YellForHelp), N(OP_SafePoint), N(OP_Buff), -N(OP_BuffFadeMsg), +N(OP_ColoredText), N(OP_SpecialMesg), N(OP_Consent), N(OP_ConsentResponse), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index dab4bf0bb..ea0acaa90 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4067,7 +4067,7 @@ struct GroupInvite_Struct { // uint8 unknown128[65]; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; }; diff --git a/common/opcode_dispatch.h b/common/opcode_dispatch.h index 352fc62ef..3920dd3f7 100644 --- a/common/opcode_dispatch.h +++ b/common/opcode_dispatch.h @@ -305,7 +305,7 @@ OUTz(OP_FinishWindow2); //OUTv(OP_AdventureInfo, strlen(p)+1); //OUTv(OP_AdventureMerchantResponse, strlen(msg)+2); OUTv(OP_ItemPacket, ItemPacket_Struct); -OUTv(OP_BuffFadeMsg, BuffFadeMsg_Struct); +OUTv(OP_ColoredText, ColoredText_Struct); OUTv(OP_FormattedMessage, FormattedMessage_Struct); OUTv(OP_GuildMemberList, uint32); //variable length, but nasty OUTv(OP_InterruptCast, InterruptCast_Struct); diff --git a/common/opcode_map.cpp b/common/opcode_map.cpp index 10babaccb..edb6ba64c 100644 --- a/common/opcode_map.cpp +++ b/common/opcode_map.cpp @@ -160,7 +160,7 @@ void load_opcode_names() opcode_map[0x0192]="LiveOP_YellForHelp"; opcode_map[0x00ef]="LiveOP_SafePoint"; opcode_map[0x0157]="LiveOP_Buff"; - opcode_map[0x00c0]="LiveOP_BuffFadeMsg"; + opcode_map[0x00c0]="LiveOP_ColoredText"; opcode_map[0x0440]="LiveOP_MultiLineMsg"; opcode_map[0x021c]="LiveOP_SpecialMesg"; opcode_map[0x0013]="LiveOP_Consent"; diff --git a/common/patches/client62_structs.h b/common/patches/client62_structs.h index 27b808620..73726e165 100644 --- a/common/patches/client62_structs.h +++ b/common/patches/client62_structs.h @@ -2969,7 +2969,7 @@ struct GroupInvite_Struct { // uint8 unknown128[65]; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; }; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 2d1b5ef18..79b4f21b8 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4106,7 +4106,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 99a904b86..ffd6e9ecb 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -3693,7 +3693,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's diff --git a/common/patches/sof_opcode_list.h b/common/patches/sof_opcode_list.h index 28a1ce650..15236862f 100644 --- a/common/patches/sof_opcode_list.h +++ b/common/patches/sof_opcode_list.h @@ -187,7 +187,7 @@ 0x1ee9, 0x7f5d, OP_CastSpell 0x0659, OP_ManaChange -0x3bc7, OP_BuffFadeMsg +0x3bc7, OP_ColoredText 0x3209, 0x6a93, OP_MemorizeSpell 0x1237, diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index a31d60ef8..7d250a568 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -3556,7 +3556,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 1c058fb77..1fcd31b77 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -3046,7 +3046,7 @@ struct GroupInvite_Struct { // uint8 unknown128[65]; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; }; diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index 860fdd34a..8226e8fd7 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -3735,7 +3735,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's diff --git a/utils/patches/opcodes.conf b/utils/patches/opcodes.conf index ed2a685d7..562ba3347 100644 --- a/utils/patches/opcodes.conf +++ b/utils/patches/opcodes.conf @@ -147,7 +147,7 @@ OP_Begging=0x13e7 # ShowEQ 10/27/05 OP_InspectRequest=0x775d # ShowEQ 10/27/05 OP_Action2=0x0000 OP_BeginCast=0x3990 # ShowEQ 10/27/05 -OP_BuffFadeMsg=0x0b2d # ShowEQ 10/27/05 +OP_ColoredText=0x0b2d # ShowEQ 10/27/05 OP_Consent=0x1081 # ShowEQ 10/27/05 OP_LFGCommand=0x68ac # ShowEQ 10/27/05 OP_LFGGetMatchesRequest=0x022f # ShowEQ 10/27/05 diff --git a/utils/patches/patch_6.2.conf b/utils/patches/patch_6.2.conf index 25944a3b5..8eca3dd4e 100644 --- a/utils/patches/patch_6.2.conf +++ b/utils/patches/patch_6.2.conf @@ -150,7 +150,7 @@ OP_InspectRequest=0x2403 OP_Action2=0x0000 # ShowEQ 06/29/05 OP_BeginCast=0x3990 # ShowEQ 06/29/05 OP_WhoAllRequest=0x5cdd # ShowEQ 06/29/05 -OP_BuffFadeMsg=0x4bc6 # ShowEQ 06/29/05 +OP_ColoredText=0x4bc6 # ShowEQ 06/29/05 OP_Consent=0x1081 # ShowEQ 06/29/05 OP_LFGCommand=0x022f # ShowEQ 06/29/05 OP_LFGGetMatchesRequest=0x6f82 # ShowEQ 06/29/05 diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 50bfd5580..728676b2d 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -166,7 +166,7 @@ OP_InspectRequest=0x23f1 OP_InspectAnswer=0x5794 OP_InspectMessageUpdate=0x3064 OP_BeginCast=0x17ff -OP_BuffFadeMsg=0x41cb +OP_ColoredText=0x41cb OP_ConsentResponse=0x183d OP_MemorizeSpell=0x2fac OP_SwapSpell=0x4736 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 642e0f4ab..10df077f7 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -168,7 +168,7 @@ OP_GMLastName=0x3563 # C OP_InspectAnswer=0x4938 # C OP_Action2=0x7e4d # C OP_Damage? OP_BeginCast=0x0d5a # C -OP_BuffFadeMsg=0x569a # C +OP_ColoredText=0x569a # C OP_ConsentResponse=0x6e47 # C OP_MemorizeSpell=0x8543 # C OP_SwapSpell=0x3fd2 # C @@ -659,4 +659,4 @@ 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 # \ No newline at end of file +OP_InitialHPUpdate=0x0000 # diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index 819096ed4..5bffdfc93 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -164,7 +164,7 @@ OP_GMLastName=0x0375 #/lastname OP_InspectAnswer=0x084F #SEQ 12/04/08 OP_Action2=0x0EF2 #SEQ 12/04/08 OP_BeginCast=0x5A50 #SEQ 12/04/08 -OP_BuffFadeMsg=0x3BC7 #SEQ 12/04/08 +OP_ColoredText=0x3BC7 #SEQ 12/04/08 OP_ConsentResponse=0x4D30 #SEQ 12/04/08 OP_MemorizeSpell=0x6A93 #SEQ 12/04/08 OP_SwapSpell=0x1418 #SEQ 12/04/08 diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index 93f24e8db..50e426874 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -154,7 +154,7 @@ OP_InspectRequest=0x775d # ShowEQ 10/27/05 OP_InspectAnswer=0x2403 # ShowEQ 10/27/05 OP_Action2=0x0000 OP_BeginCast=0x3990 # ShowEQ 10/27/05 -OP_BuffFadeMsg=0x0b2d # ShowEQ 10/27/05 +OP_ColoredText=0x0b2d # ShowEQ 10/27/05 OP_Consent=0x1081 # ShowEQ 10/27/05 OP_ConsentDeny=0x4e8c # ShowEQ 10/27/05 OP_ConsentResponse=0x6380 # ShowEQ 10/27/05 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index cce73ce45..4621a74a9 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -168,7 +168,7 @@ OP_GMLastName=0x7bfb # C OP_InspectAnswer=0x0c2b # C OP_BeginCast=0x0d5a # C -OP_BuffFadeMsg=0x71bf # C +OP_ColoredText=0x71bf # C OP_ConsentResponse=0x0e87 # C OP_MemorizeSpell=0x3887 # C OP_SwapSpell=0x5805 # C diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index 640ee7015..deff0f0f2 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -446,7 +446,7 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("YellForHelp", static_cast(OP_YellForHelp)), luabind::value("SafePoint", static_cast(OP_SafePoint)), luabind::value("Buff", static_cast(OP_Buff)), - luabind::value("BuffFadeMsg", static_cast(OP_BuffFadeMsg)), + luabind::value("ColoredText", static_cast(OP_ColoredText)), luabind::value("SpecialMesg", static_cast(OP_SpecialMesg)), luabind::value("Consent", static_cast(OP_Consent)), luabind::value("ConsentResponse", static_cast(OP_ConsentResponse)), diff --git a/zone/spells.cpp b/zone/spells.cpp index d400e46ce..b007cd311 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4817,8 +4817,8 @@ void Client::MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message) if(send_message) { const char *fadetext = spells[spell_id].spell_fades; - outapp = new EQApplicationPacket(OP_BuffFadeMsg, sizeof(BuffFadeMsg_Struct) + strlen(fadetext)); - BuffFadeMsg_Struct *bfm = (BuffFadeMsg_Struct *) outapp->pBuffer; + outapp = new EQApplicationPacket(OP_ColoredText, sizeof(ColoredText_Struct) + strlen(fadetext)); + ColoredText_Struct *bfm = (ColoredText_Struct *) outapp->pBuffer; bfm->color = MT_Spells; memcpy(bfm->msg, fadetext, strlen(fadetext)); QueuePacket(outapp); From 34496c49b48488a629bd251b827acd7c50eee827 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 24 Sep 2014 13:14:20 -0400 Subject: [PATCH 153/368] Add Client::SendColoredText(uint32 color, std::string message) This will send a message (arbitrary size limit of 512) to the client of the provided color. The MT colors should work here. --- zone/client.cpp | 15 +++++++++++++++ zone/client.h | 1 + 2 files changed, 16 insertions(+) diff --git a/zone/client.cpp b/zone/client.cpp index 62b903802..964f228d1 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8313,3 +8313,18 @@ float Client::GetQuiverHaste() quiver_haste = 1.0f / (1.0f + static_cast(quiver_haste) / 100.0f); return quiver_haste; } + +void Client::SendColoredText(uint32 color, std::string message) +{ + // arbitrary size limit + if (message.size() > 512) // live does send this with empty strings sometimes ... + return; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ColoredText, + sizeof(ColoredText_Struct) + message.size()); + ColoredText_Struct *cts = (ColoredText_Struct *)outapp->pBuffer; + cts->color = color; + strcpy(cts->msg, message.c_str()); + QueuePacket(outapp); + safe_delete(outapp); +} + diff --git a/zone/client.h b/zone/client.h index 0ad8b043a..235861396 100644 --- a/zone/client.h +++ b/zone/client.h @@ -255,6 +255,7 @@ public: const char *message7 = nullptr, const char *message8 = nullptr, const char *message9 = nullptr); void Tell_StringID(uint32 string_id, const char *who, const char *message); + void SendColoredText(uint32 color, std::string message); void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice); void SendTraderItem(uint32 item_id,uint16 quantity); uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity); From 531e4b020783beb87b5a6d825c88063c935924eb Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 24 Sep 2014 13:50:18 -0400 Subject: [PATCH 154/368] Fix function signature of Mob::IsRaidTarget() so its actually a virtual --- zone/mob.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.h b/zone/mob.h index 4e72f0264..93682962c 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -526,7 +526,7 @@ public: //More stuff to sort: - virtual bool IsRaidTarget() { return false; }; + virtual bool IsRaidTarget() const { return false; }; virtual bool IsAttackAllowed(Mob *target, bool isSpellAttack = false); bool IsTargeted() const { return (targeted > 0); } inline void IsTargeted(int in_tar) { targeted += in_tar; if(targeted < 0) targeted = 0;} From e67423bba722e07e88a2b96898dd1a3c8af4c603 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 24 Sep 2014 14:04:17 -0400 Subject: [PATCH 155/368] Add raid mob message "This creature would take an army to defeat!" Colors may be a bit off due to differences in clients --- zone/client_packet.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ba25cdd1a..d8ce76a6a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3061,6 +3061,32 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) mod_consider(tmob, con); QueuePacket(outapp); + // only wanted to check raid target once + // and need con to still be around so, do it here! + if (tmob->IsRaidTarget()) { + uint32 color = 0; + switch (con->level) { + case CON_GREEN: + color = 2; + break; + case CON_LIGHTBLUE: + color = 10; + break; + case CON_BLUE: + color = 4; + break; + case CON_WHITE: + color = 10; + break; + case CON_YELLOW: + color = 15; + break; + case CON_RED: + color = 13; + break; + } + SendColoredText(color, std::string("This creature would take an army to defeat!")); + } safe_delete(outapp); return; } From 7feb531ff722c4d7a48e9195b448fb7f6ccb2a19 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 24 Sep 2014 20:23:01 -0400 Subject: [PATCH 156/368] Re-ordered server opcodes and handlers to facilitate the export of client patch structs and handlers --- changelog.txt | 3 + common/emu_oplist.h | 1006 +- common/mail_oplist.h | 18 +- zone/client_packet.cpp | 23325 ++++++++++++++++++++------------------- zone/client_packet.h | 558 +- 5 files changed, 12507 insertions(+), 12403 deletions(-) diff --git a/changelog.txt b/changelog.txt index b002c0524..f5ada28d5 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/24/2014 == +Uleat: Re-ordered server opcodes and handlers to give them some predictability of location (I need this for the inventory re-enumeration.) + == 09/22/2014 == Akkadius: #resetaa now covers the function of #resetaa and #refundaa - #resetaa will wipe all AA data, refund the spent points into the available points and send character to character select properly diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 4eb516d94..af871ec6a 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -1,543 +1,545 @@ +// system use N(OP_ExploreUnknown), -N(OP_Heartbeat), -N(OP_ReloadUI), -N(OP_IncreaseStats), -N(OP_ApproveZone), -N(OP_Dye), -N(OP_Stamina), -N(OP_ControlBoat), -N(OP_MobUpdate), //not used anymore, here for lecacy reasons (eqextractor) -N(OP_ClientUpdate), -N(OP_ChannelMessage), -N(OP_SimpleMessage), -N(OP_FormattedMessage), -N(OP_TGB), -N(OP_Bind_Wound), -N(OP_Charm), -N(OP_Begging), -N(OP_MoveCoin), -N(OP_SpawnDoor), -N(OP_Sneak), -N(OP_ExpUpdate), -N(OP_DumpName), -N(OP_RespondAA), -N(OP_UpdateAA), -N(OP_SendAAStats), -N(OP_SendAATable), +// start (please add new opcodes in descending order and re-order any name changes where applicable) +N(OP_0x0193), +N(OP_0x0347), N(OP_AAAction), -N(OP_BoardBoat), -N(OP_LeaveBoat), -N(OP_AdventureInfoRequest), -N(OP_AdventureInfo), -N(OP_AdventureRequest), -N(OP_AdventureDetails), -N(OP_LDoNButton), +N(OP_AAExpUpdate), +N(OP_AcceptNewTask), +N(OP_AckPacket), +N(OP_Action), +N(OP_Action2), N(OP_AdventureData), +N(OP_AdventureDetails), N(OP_AdventureFinish), -N(OP_LeaveAdventure), +N(OP_AdventureInfo), +N(OP_AdventureInfoRequest), +N(OP_AdventureLeaderboardReply), +N(OP_AdventureLeaderboardRequest), +N(OP_AdventureMerchantPurchase), +N(OP_AdventureMerchantRequest), +N(OP_AdventureMerchantResponse), +N(OP_AdventureMerchantSell), +N(OP_AdventurePointsUpdate), +N(OP_AdventureRequest), +N(OP_AdventureStatsReply), +N(OP_AdventureStatsRequest), N(OP_AdventureUpdate), -N(OP_SendExpZonein), -N(OP_RaidUpdate), -N(OP_GuildLeader), -N(OP_GuildPeace), -N(OP_GuildRemove), -N(OP_GuildMemberList), -N(OP_GuildMemberUpdate), -N(OP_GuildMemberLevelUpdate), -N(OP_GuildInvite), -N(OP_GuildMOTD), -N(OP_SetGuildMOTD), -N(OP_GuildPublicNote), -N(OP_GetGuildsList), -N(OP_GuildDemote), -N(OP_GuildInviteAccept), -N(OP_GuildWar), -N(OP_GuildDelete), -N(OP_GuildManageRemove), -N(OP_GuildManageAdd), -N(OP_GuildManageStatus), -N(OP_GuildManageBanker), -N(OP_GetGuildMOTD), -N(OP_Trader), -N(OP_Bazaar), -N(OP_BecomeTrader), -N(OP_TraderItemUpdate), -N(OP_TraderShop), -N(OP_TraderBuy), -N(OP_PetCommands), -N(OP_TradeSkillCombine), +N(OP_AltCurrency), +N(OP_AltCurrencyMerchantReply), +N(OP_AltCurrencyMerchantRequest), +N(OP_AltCurrencyPurchase), +N(OP_AltCurrencyReclaim), +N(OP_AltCurrencySell), +N(OP_AltCurrencySellSelection), +N(OP_Animation), +N(OP_AnnoyingZoneUnknown), +N(OP_ApplyPoison), +N(OP_ApproveName), +N(OP_ApproveWorld), +N(OP_ApproveZone), +N(OP_Assist), +N(OP_AssistGroup), +N(OP_AugmentInfo), N(OP_AugmentItem), -N(OP_ItemName), -N(OP_ShopItem), -N(OP_ShopPlayerBuy), -N(OP_ShopPlayerSell), -N(OP_ShopDelItem), -N(OP_ShopRequest), -N(OP_ShopEnd), -N(OP_LFGCommand), -N(OP_LFGAppearance), -N(OP_GroupUpdate), -N(OP_GroupInvite), +N(OP_AutoAttack), +N(OP_AutoAttack2), +N(OP_AutoFire), +N(OP_Bandolier), +N(OP_BankerChange), +N(OP_Barter), +N(OP_Bazaar), +N(OP_BazaarInspect), +N(OP_BazaarSearch), +N(OP_BecomeCorpse), +N(OP_BecomeTrader), +N(OP_Begging), +N(OP_BeginCast), +N(OP_Bind_Wound), +N(OP_BlockedBuffs), +N(OP_BoardBoat), +N(OP_Buff), +N(OP_BuffCreate), +N(OP_BuffRemoveRequest), +N(OP_Bug), +N(OP_CameraEffect), +N(OP_Camp), +N(OP_CancelTask), +N(OP_CancelTrade), +N(OP_CastSpell), +N(OP_ChangeSize), +N(OP_ChannelMessage), +N(OP_CharacterCreate), +N(OP_CharacterCreateRequest), +N(OP_CharInventory), +N(OP_Charm), +N(OP_ChatMessage), +N(OP_ClearBlockedBuffs), +N(OP_ClearNPCMarks), +N(OP_ClearObject), +N(OP_ClearSurname), +N(OP_ClickDoor), +N(OP_ClickObject), +N(OP_ClickObjectAction), +N(OP_ClientError), +N(OP_ClientReady), +N(OP_ClientTimeStamp), +N(OP_ClientUpdate), +N(OP_CloseContainer), +N(OP_CloseTributeMaster), +N(OP_ColoredText), +N(OP_CombatAbility), +N(OP_Command), +N(OP_CompletedTasks), +N(OP_ConfirmDelete), +N(OP_Consent), +N(OP_ConsentDeny), +N(OP_ConsentResponse), +N(OP_Consider), +N(OP_ConsiderCorpse), +N(OP_Consume), +N(OP_ControlBoat), +N(OP_CorpseDrag), +N(OP_CorpseDrop), +N(OP_CrashDump), +N(OP_CrystalCountUpdate), +N(OP_CrystalCreate), +N(OP_CrystalReclaim), +N(OP_CustomTitles), +N(OP_Damage), +N(OP_Death), +N(OP_DelegateAbility), +N(OP_DeleteCharacter), +N(OP_DeleteCharge), +N(OP_DeleteItem), +N(OP_DeletePetition), +N(OP_DeleteSpawn), +N(OP_DeleteSpell), +N(OP_DenyResponse), +N(OP_Disarm), +N(OP_DisarmTraps), +N(OP_DisciplineTimer), +N(OP_DisciplineUpdate), +N(OP_DiscordMerchantInventory), +N(OP_DoGroupLeadershipAbility), +N(OP_DuelResponse), +N(OP_DuelResponse2), +N(OP_DumpName), +N(OP_Dye), +N(OP_DynamicWall), +N(OP_DzAddPlayer), +N(OP_DzChooseZone), +N(OP_DzCompass), +N(OP_DzExpeditionEndsWarning), +N(OP_DzExpeditionInfo), +N(OP_DzExpeditionList), +N(OP_DzJoinExpeditionConfirm), +N(OP_DzJoinExpeditionReply), +N(OP_DzLeaderStatus), +N(OP_DzListTimers), +N(OP_DzMakeLeader), +N(OP_DzMemberList), +N(OP_DzMemberStatus), +N(OP_DzPlayerList), +N(OP_DzQuit), +N(OP_DzRemovePlayer), +N(OP_DzSwapPlayer), +N(OP_Emote), +N(OP_EndLootRequest), +N(OP_EnduranceUpdate), +N(OP_EnterChat), +N(OP_EnterWorld), +N(OP_EnvDamage), +N(OP_ExpansionInfo), +N(OP_ExpUpdate), +N(OP_FaceChange), +N(OP_Feedback), +N(OP_FeignDeath), +N(OP_FellowshipUpdate), +N(OP_FindPersonReply), +N(OP_FindPersonRequest), +N(OP_FinishTrade), +N(OP_FinishWindow), +N(OP_FinishWindow2), +N(OP_Fishing), +N(OP_FloatListThing), +N(OP_Forage), +N(OP_ForceFindPerson), +N(OP_FormattedMessage), +N(OP_FriendsWho), +N(OP_GetGuildMOTD), +N(OP_GetGuildMOTDReply), +N(OP_GetGuildsList), +N(OP_GiveMoney), +N(OP_GMApproval), +N(OP_GMBecomeNPC), +N(OP_GMDelCorpse), +N(OP_GMEmoteZone), +N(OP_GMEndTraining), +N(OP_GMEndTrainingResponse), +N(OP_GMFind), +N(OP_GMGoto), +N(OP_GMHideMe), +N(OP_GMKick), +N(OP_GMKill), +N(OP_GMLastName), +N(OP_GMNameChange), +N(OP_GMSearchCorpse), +N(OP_GMServers), +N(OP_GMSummon), +N(OP_GMToggle), +N(OP_GMTraining), +N(OP_GMTrainSkill), +N(OP_GMTrainSkillConfirm), +N(OP_GMZoneRequest), +N(OP_GMZoneRequest2), +N(OP_GroundSpawn), +N(OP_GroupAcknowledge), +N(OP_GroupCancelInvite), +N(OP_GroupDelete), N(OP_GroupDisband), -N(OP_GroupInvite2), +N(OP_GroupDisbandOther), +N(OP_GroupDisbandYou), N(OP_GroupFollow), N(OP_GroupFollow2), -N(OP_GroupCancelInvite), -N(OP_CustomTitles), -N(OP_Split), -N(OP_Jump), -N(OP_ConsiderCorpse), -N(OP_SkillUpdate), -N(OP_GMEndTrainingResponse), -N(OP_GMEndTraining), -N(OP_GMTrainSkill), -N(OP_GMTraining), -N(OP_DeleteItem), -N(OP_CombatAbility), -N(OP_TrackUnknown), -N(OP_TrackTarget), -N(OP_Track), +N(OP_GroupInvite), +N(OP_GroupInvite2), +N(OP_GroupLeaderChange), +N(OP_GroupLeadershipAAUpdate), +N(OP_GroupMakeLeader), +N(OP_GroupRoles), +N(OP_GroupUpdate), +N(OP_GroupUpdateB), +N(OP_GroupUpdateLeaderAA), +N(OP_GuildBank), +N(OP_GuildCreate), +N(OP_GuildDelete), +N(OP_GuildDemote), +N(OP_GuildInvite), +N(OP_GuildInviteAccept), +N(OP_GuildLeader), +N(OP_GuildManageAdd), +N(OP_GuildManageBanker), +N(OP_GuildManageRemove), +N(OP_GuildManageStatus), +N(OP_GuildMemberLevelUpdate), +N(OP_GuildMemberList), +N(OP_GuildMemberUpdate), +N(OP_GuildMOTD), +N(OP_GuildPeace), +N(OP_GuildPromote), +N(OP_GuildPublicNote), +N(OP_GuildRemove), +N(OP_GuildsList), +N(OP_GuildStatus), +N(OP_GuildTributeInfo), +N(OP_GuildUpdateURLAndChannel), +N(OP_GuildWar), +N(OP_Heartbeat), +N(OP_Hide), +N(OP_HideCorpse), +N(OP_HPUpdate), +N(OP_Illusion), +N(OP_IncreaseStats), +N(OP_InitialHPUpdate), +N(OP_InitialMobHealth), +N(OP_InspectAnswer), +N(OP_InspectMessageUpdate), +N(OP_InspectRequest), +N(OP_InstillDoubt), +N(OP_InterruptCast), N(OP_ItemLinkClick), N(OP_ItemLinkResponse), N(OP_ItemLinkText), -N(OP_RezzAnswer), -N(OP_RezzComplete), -N(OP_SendZonepoints), -N(OP_SetRunMode), -N(OP_InspectRequest), -N(OP_InspectAnswer), -N(OP_SenseTraps), -N(OP_DisarmTraps), -N(OP_Assist), -N(OP_AssistGroup), -N(OP_PickPocket), -N(OP_LootRequest), -N(OP_EndLootRequest), -N(OP_MoneyOnCorpse), -N(OP_LootComplete), -N(OP_LootItem), -N(OP_MoveItem), -N(OP_WhoAllRequest), -N(OP_WhoAllResponse), -N(OP_Consume), -N(OP_AutoAttack), -N(OP_AutoAttack2), -N(OP_TargetMouse), -N(OP_TargetCommand), -N(OP_TargetReject), -N(OP_TargetHoTT), -N(OP_Hide), -N(OP_Forage), -N(OP_Fishing), -N(OP_Bug), -N(OP_Emote), -N(OP_Consider), -N(OP_FaceChange), -N(OP_RandomReq), -N(OP_RandomReply), -N(OP_Camp), -N(OP_YellForHelp), -N(OP_SafePoint), -N(OP_Buff), -N(OP_ColoredText), -N(OP_SpecialMesg), -N(OP_Consent), -N(OP_ConsentResponse), -N(OP_Stun), -N(OP_BeginCast), -N(OP_CastSpell), -N(OP_InterruptCast), -N(OP_Death), -N(OP_FeignDeath), -N(OP_Illusion), -N(OP_LevelUpdate), -N(OP_LevelAppearance), -N(OP_MemorizeSpell), -N(OP_HPUpdate), -N(OP_Mend), -N(OP_Taunt), -N(OP_GMDelCorpse), -N(OP_GMFind), -N(OP_GMServers), -N(OP_GMGoto), -N(OP_GMSummon), -N(OP_GMKill), -N(OP_GMLastName), -N(OP_GMToggle), -N(OP_GMEmoteZone), -N(OP_GMBecomeNPC), -N(OP_GMHideMe), -N(OP_GMZoneRequest), -N(OP_GMZoneRequest2), -N(OP_Petition), -N(OP_PetitionRefresh), -N(OP_PDeletePetition), -N(OP_PetitionBug), -N(OP_PetitionUpdate), -N(OP_PetitionCheckout), -N(OP_PetitionCheckout2), -N(OP_PetitionDelete), -N(OP_PetitionResolve), -N(OP_PetitionCheckIn), -N(OP_PetitionUnCheckout), -N(OP_PetitionQue), -N(OP_SetServerFilter), -N(OP_NewSpawn), -N(OP_Animation), -N(OP_ZoneChange), -N(OP_DeleteSpawn), -N(OP_EnvDamage), -N(OP_Action), -N(OP_Damage), -N(OP_ManaChange), -N(OP_ClientError), -N(OP_Save), -N(OP_LocInfo), -N(OP_Surname), -N(OP_ClearSurname), -N(OP_SwapSpell), -N(OP_DeleteSpell), -N(OP_CloseContainer), -N(OP_ClickObjectAction), -N(OP_GroundSpawn), -N(OP_ClearObject), -N(OP_ZoneUnavail), +N(OP_ItemName), N(OP_ItemPacket), -N(OP_TradeRequest), -N(OP_TradeRequestAck), -N(OP_TradeAcceptClick), -N(OP_TradeMoneyUpdate), -N(OP_TradeCoins), -N(OP_CancelTrade), -N(OP_FinishTrade), -N(OP_SaveOnZoneReq), -N(OP_Logout), -N(OP_LogoutReply), -N(OP_PreLogoutReply), -N(OP_DuelResponse2), -N(OP_InstillDoubt), -N(OP_SafeFallSuccess), -N(OP_DisciplineUpdate), -N(OP_SendGuildTributes), -N(OP_SendTributes), -N(OP_TributeUpdate), -N(OP_TributeItem), -N(OP_TributePointUpdate), -N(OP_TributeInfo), -N(OP_GuildTributeInfo), -N(OP_OpenGuildTributeMaster), -N(OP_OpenTributeMaster), -N(OP_TributeTimer), -N(OP_SelectTribute), -N(OP_TributeNPC), -N(OP_TributeMoney), -N(OP_TributeToggle), -N(OP_CloseTributeMaster), -N(OP_RecipesFavorite), -N(OP_RecipesSearch), -N(OP_RecipeReply), -N(OP_RecipeDetails), -N(OP_RecipeAutoCombine), -N(OP_Shielding), -N(OP_FindPersonRequest), -N(OP_FindPersonReply), -N(OP_ZoneEntry), -N(OP_PlayerProfile), -N(OP_CharInventory), -N(OP_ZoneSpawns), -N(OP_Weather), -N(OP_ReqNewZone), -N(OP_NewZone), -N(OP_ReqClientSpawn), -N(OP_SpawnAppearance), -N(OP_ClientReady), -N(OP_ZoneComplete), -N(OP_ApproveWorld), -N(OP_LogServer), -N(OP_MOTD), -N(OP_SendLoginInfo), -N(OP_DeleteCharacter), -N(OP_SendCharInfo), -N(OP_ExpansionInfo), -N(OP_CharacterCreate), -N(OP_CharacterCreateRequest), -N(OP_RandomNameGenerator), -N(OP_GuildsList), -N(OP_ApproveName), -N(OP_EnterWorld), -N(OP_PostEnterWorld), //this is really OP_WorldAccessGranted -N(OP_SendSystemStats), -N(OP_World_Client_CRC1), -N(OP_World_Client_CRC2), -N(OP_SetChatServer), -N(OP_SetChatServer2), -N(OP_ZoneServerInfo), -N(OP_WorldClientReady), -N(OP_WorldUnknown001), -N(OP_AckPacket), -N(OP_WearChange), -N(OP_CrashDump), -N(OP_LoginComplete), -N(OP_GMNameChange), -N(OP_ReadBook), -N(OP_GMKick), -N(OP_RezzRequest), -N(OP_MultiLineMsg), -N(OP_TimeOfDay), -N(OP_CompletedTasks), -N(OP_MoneyUpdate), -N(OP_ClickObject), -N(OP_MoveDoor), -N(OP_TraderDelItem), -N(OP_AdventureMerchantPurchase), -N(OP_TestBuff), -N(OP_DuelResponse), -N(OP_RequestDuel), -N(OP_BazaarInspect), -N(OP_ClickDoor), -N(OP_GroupAcknowledge), -N(OP_GroupDelete), -N(OP_AdventureMerchantResponse), -N(OP_ShopEndConfirm), -N(OP_AdventureMerchantRequest), -N(OP_Sound), -N(OP_0x0193), -N(OP_0x0347), -N(OP_WorldComplete), -N(OP_MobRename), -N(OP_TaskDescription), -N(OP_TaskActivity), -N(OP_TaskMemberList), -N(OP_AnnoyingZoneUnknown), -N(OP_Some3ByteHPUpdate), -N(OP_FloatListThing), -N(OP_AAExpUpdate), -N(OP_ForceFindPerson), -N(OP_PlayMP3), -N(OP_RequestClientZoneChange), -N(OP_SomeItemPacketMaybe), -N(OP_QueryResponseThing), -N(OP_Some6ByteHPUpdate), -N(OP_BankerChange), -N(OP_BecomeCorpse), -N(OP_Action2), -N(OP_BazaarSearch), -N(OP_SetTitle), -N(OP_SetTitleReply), -N(OP_ConfirmDelete), -N(OP_ConsentDeny), -N(OP_CrystalCountUpdate), -N(OP_DeletePetition), -N(OP_DenyResponse), -N(OP_Disarm), -N(OP_Feedback), -N(OP_FriendsWho), -N(OP_GMApproval), -N(OP_GMSearchCorpse), -N(OP_GuildBank), -N(OP_InitialHPUpdate), -N(OP_InitialMobHealth), +N(OP_ItemPreview), +N(OP_ItemVerifyReply), +N(OP_ItemVerifyRequest), +N(OP_ItemViewUnknown), +N(OP_Jump), +N(OP_KeyRing), +N(OP_KnowledgeBase), +N(OP_LDoNButton), +N(OP_LDoNDisarmTraps), +N(OP_LDoNInspect), +N(OP_LDoNOpen), +N(OP_LDoNPickLock), +N(OP_LDoNSenseTraps), +N(OP_LeadershipExpToggle), +N(OP_LeadershipExpUpdate), +N(OP_LeaveAdventure), +N(OP_LeaveBoat), +N(OP_LevelAppearance), +N(OP_LevelUpdate), +N(OP_LFGAppearance), +N(OP_LFGCommand), N(OP_LFGGetMatchesRequest), N(OP_LFGGetMatchesResponse), N(OP_LFGResponse), +N(OP_LFGuild), N(OP_LFPCommand), N(OP_LFPGetMatchesRequest), N(OP_LFPGetMatchesResponse), -N(OP_LeadershipExpToggle), -N(OP_LeadershipExpUpdate), N(OP_LoadSpellSet), +N(OP_LocInfo), N(OP_LockoutTimerInfo), +N(OP_Login), +N(OP_LoginAccepted), +N(OP_LoginComplete), +N(OP_LoginUnknown1), +N(OP_LoginUnknown2), +N(OP_Logout), +N(OP_LogoutReply), +N(OP_LogServer), +N(OP_LootComplete), +N(OP_LootItem), +N(OP_LootRequest), +N(OP_ManaChange), +N(OP_ManaUpdate), +N(OP_MarkNPC), +N(OP_Marquee), +N(OP_MemorizeSpell), +N(OP_Mend), N(OP_MendHPUpdate), +N(OP_MercenaryAssign), +N(OP_MercenaryCommand), +N(OP_MercenaryDataRequest), +N(OP_MercenaryDataResponse), +N(OP_MercenaryDataUpdate), +N(OP_MercenaryDataUpdateRequest), +N(OP_MercenaryDismiss), +N(OP_MercenaryHire), +N(OP_MercenarySuspendRequest), +N(OP_MercenarySuspendResponse), +N(OP_MercenaryTimer), +N(OP_MercenaryTimerRequest), +N(OP_MercenaryUnknown1), +N(OP_MercenaryUnsuspendResponse), +N(OP_MobEnduranceUpdate), N(OP_MobHealth), +N(OP_MobManaUpdate), +N(OP_MobRename), +N(OP_MobUpdate), // not used anymore, here for lecacy reasons (eqextractor) +N(OP_MoneyOnCorpse), +N(OP_MoneyUpdate), +N(OP_MOTD), +N(OP_MoveCoin), +N(OP_MoveDoor), +N(OP_MoveItem), N(OP_MoveLogDisregard), N(OP_MoveLogRequest), +N(OP_MultiLineMsg), +N(OP_NewSpawn), +N(OP_NewTitlesAvailable), +N(OP_NewZone), +N(OP_OnLevelMessage), +N(OP_OpenContainer), +N(OP_OpenDiscordMerchant), +N(OP_OpenGuildTributeMaster), +N(OP_OpenInventory), +N(OP_OpenNewTasksWindow), +N(OP_OpenTributeMaster), +N(OP_PDeletePetition), +N(OP_PetBuffWindow), +N(OP_PetCommands), +N(OP_Petition), +N(OP_PetitionBug), +N(OP_PetitionCheckIn), +N(OP_PetitionCheckout), +N(OP_PetitionCheckout2), +N(OP_PetitionDelete), +N(OP_PetitionQue), +N(OP_PetitionRefresh), +N(OP_PetitionResolve), N(OP_PetitionSearch), N(OP_PetitionSearchResults), N(OP_PetitionSearchText), -N(OP_RaidInvite), -N(OP_ReclaimCrystals), -N(OP_Report), -N(OP_SenseHeading), -N(OP_LDoNOpen), -N(OP_LDoNSenseTraps), -N(OP_LDoNPickLock), -N(OP_LDoNDisarmTraps), -N(OP_LDoNInspect), -N(OP_DynamicWall), -N(OP_RequestTitles), -N(OP_PurchaseLeadershipAA), -N(OP_UpdateLeadershipAA), -N(OP_AdventurePointsUpdate), -N(OP_ZoneInUnknown), -N(OP_ZoneServerReady), //terrible name. -N(OP_ZoneGuildList), -N(OP_SendTitleList), -N(OP_NewTitlesAvailable), -N(OP_Bandolier), -N(OP_OpenDiscordMerchant), -N(OP_DiscordMerchantInventory), -N(OP_GiveMoney), -N(OP_OnLevelMessage), -N(OP_RequestKnowledgeBase), -N(OP_KnowledgeBase), -N(OP_VetRewardsAvaliable), -N(OP_VetClaimRequest), -N(OP_VetClaimReply), -N(OP_WeaponEquip1), -N(OP_WeaponEquip2), -N(OP_WeaponUnequip2), -N(OP_WorldLogout), -N(OP_SessionReady), -//Login -N(OP_Login), -N(OP_ServerListRequest), +N(OP_PetitionUnCheckout), +N(OP_PetitionUpdate), +N(OP_PickPocket), +N(OP_PlayerProfile), N(OP_PlayEverquestRequest), -N(OP_ChatMessage), -N(OP_LoginAccepted), -N(OP_ServerListResponse), -N(OP_Poll), N(OP_PlayEverquestResponse), -N(OP_EnterChat), +N(OP_PlayMP3), +N(OP_Poll), N(OP_PollResponse), -N(OP_Command), -N(OP_ZonePlayerToBind), -N(OP_AutoFire), -N(OP_Rewind), -N(OP_OpenNewTasksWindow), -N(OP_TaskActivityComplete), -N(OP_AcceptNewTask), -N(OP_CancelTask), -N(OP_TaskHistoryRequest), -N(OP_TaskHistoryReply), -N(OP_PetBuffWindow), -N(OP_RaidJoin), -N(OP_Translocate), -N(OP_Sacrifice), -N(OP_KeyRing), N(OP_PopupResponse), -N(OP_DeleteCharge), +N(OP_PostEnterWorld), //this is really OP_WorldAccessGranted N(OP_PotionBelt), -N(OP_Barter), -N(OP_VoiceMacroIn), -N(OP_VoiceMacroOut), -N(OP_WorldObjectsSent), -N(OP_BlockedBuffs), -N(OP_RemoveBlockedBuffs), -N(OP_ClearBlockedBuffs), -N(OP_GroupUpdateLeaderAA), -N(OP_MarkNPC), -N(OP_ClearNPCMarks), -N(OP_DoGroupLeadershipAbility), -N(OP_DelegateAbility), -N(OP_SetGroupTarget), -N(OP_ApplyPoison), -N(OP_FinishWindow), -N(OP_FinishWindow2), -N(OP_ItemVerifyRequest), -N(OP_ItemVerifyReply), -N(OP_GMTrainSkillConfirm), -N(OP_RestState), -N(OP_AugmentInfo), -N(OP_PVPStats), -N(OP_PVPLeaderBoardRequest), -N(OP_PVPLeaderBoardReply), -N(OP_PVPLeaderBoardDetailsRequest), +N(OP_PreLogoutReply), +N(OP_PurchaseLeadershipAA), N(OP_PVPLeaderBoardDetailsReply), -N(OP_DisciplineTimer), -N(OP_RespawnWindow), -N(OP_AdventureMerchantSell), -N(OP_AdventureStatsRequest), -N(OP_AdventureStatsReply), -N(OP_AdventureLeaderboardRequest), -N(OP_AdventureLeaderboardReply), -N(OP_SetStartCity), -N(OP_LoginUnknown1), -N(OP_LoginUnknown2), -N(OP_ItemViewUnknown), -N(OP_GetGuildMOTDReply), -N(OP_SetGuildRank), -N(OP_SpawnPositionUpdate), -N(OP_ManaUpdate), -N(OP_EnduranceUpdate), -N(OP_MobManaUpdate), -N(OP_MobEnduranceUpdate), -N(OP_GroupUpdateB), -N(OP_GroupDisbandYou), -N(OP_GroupDisbandOther), -N(OP_GroupLeaderChange), -N(OP_GroupLeadershipAAUpdate), -N(OP_GroupRoles), -N(OP_SendFindableNPCs), -N(OP_HideCorpse), -N(OP_TargetBuffs), -N(OP_TradeBusy), -N(OP_GuildUpdateURLAndChannel), -N(OP_CameraEffect), -N(OP_SpellEffect), -N(OP_DzQuit), -N(OP_DzListTimers), -N(OP_DzPlayerList), -N(OP_DzAddPlayer), -N(OP_DzRemovePlayer), -N(OP_DzSwapPlayer), -N(OP_DzMakeLeader), -N(OP_DzJoinExpeditionConfirm), -N(OP_DzJoinExpeditionReply), -N(OP_DzExpeditionInfo), -N(OP_DzMemberStatus), -N(OP_DzLeaderStatus), -N(OP_DzExpeditionEndsWarning), -N(OP_DzExpeditionList), -N(OP_DzMemberList), -N(OP_DzCompass), -N(OP_DzChooseZone), -N(OP_BuffCreate), -N(OP_GuildStatus), -N(OP_BuffRemoveRequest), -N(OP_CorpseDrag), -N(OP_CorpseDrop), -N(OP_ChangeSize), -N(OP_GroupMakeLeader), +N(OP_PVPLeaderBoardDetailsRequest), +N(OP_PVPLeaderBoardReply), +N(OP_PVPLeaderBoardRequest), +N(OP_PVPStats), +N(OP_QueryResponseThing), +N(OP_RaidInvite), +N(OP_RaidJoin), +N(OP_RaidUpdate), +N(OP_RandomNameGenerator), +N(OP_RandomReply), +N(OP_RandomReq), +N(OP_ReadBook), +N(OP_RecipeAutoCombine), +N(OP_RecipeDetails), +N(OP_RecipeReply), +N(OP_RecipesFavorite), +N(OP_RecipesSearch), +N(OP_ReclaimCrystals), +N(OP_ReloadUI), N(OP_RemoveAllDoors), +N(OP_RemoveBlockedBuffs), N(OP_RemoveNimbusEffect), -N(OP_GuildCreate), -N(OP_AltCurrency), -N(OP_FellowshipUpdate), -N(OP_AltCurrencyMerchantRequest), -N(OP_AltCurrencyMerchantReply), -N(OP_AltCurrencyPurchase), -N(OP_AltCurrencySellSelection), -N(OP_AltCurrencyReclaim), -N(OP_AltCurrencySell), -N(OP_Untargetable), -N(OP_CrystalReclaim), -N(OP_CrystalCreate), +N(OP_Report), +N(OP_ReqClientSpawn), +N(OP_ReqNewZone), +N(OP_RequestClientZoneChange), +N(OP_RequestDuel), +N(OP_RequestKnowledgeBase), +N(OP_RequestTitles), +N(OP_RespawnWindow), +N(OP_RespondAA), +N(OP_RestState), +N(OP_Rewind), +N(OP_RezzAnswer), +N(OP_RezzComplete), +N(OP_RezzRequest), +N(OP_Sacrifice), +N(OP_SafeFallSuccess), +N(OP_SafePoint), +N(OP_Save), +N(OP_SaveOnZoneReq), +N(OP_SelectTribute), +N(OP_SendAAStats), +N(OP_SendAATable), +N(OP_SendCharInfo), +N(OP_SendExpZonein), +N(OP_SendFindableNPCs), +N(OP_SendGuildTributes), +N(OP_SendLoginInfo), N(OP_SendMaxCharacters), N(OP_SendMembership), N(OP_SendMembershipDetails), -N(OP_LFGuild), +N(OP_SendSystemStats), +N(OP_SendTitleList), +N(OP_SendTributes), +N(OP_SendZonepoints), +N(OP_SenseHeading), +N(OP_SenseTraps), +N(OP_ServerListRequest), +N(OP_ServerListResponse), +N(OP_SessionReady), +N(OP_SetChatServer), +N(OP_SetChatServer2), +N(OP_SetGroupTarget), +N(OP_SetGuildMOTD), +N(OP_SetGuildRank), +N(OP_SetRunMode), +N(OP_SetServerFilter), +N(OP_SetStartCity), +N(OP_SetTitle), +N(OP_SetTitleReply), +N(OP_Shielding), +N(OP_ShopDelItem), +N(OP_ShopEnd), +N(OP_ShopEndConfirm), +N(OP_ShopItem), +N(OP_ShopPlayerBuy), +N(OP_ShopPlayerSell), +N(OP_ShopRequest), +N(OP_SimpleMessage), +N(OP_SkillUpdate), +N(OP_Sneak), +N(OP_Some3ByteHPUpdate), +N(OP_Some6ByteHPUpdate), +N(OP_SomeItemPacketMaybe), +N(OP_Sound), +N(OP_SpawnAppearance), +N(OP_SpawnDoor), +N(OP_SpawnPositionUpdate), +N(OP_SpecialMesg), +N(OP_SpellEffect), +N(OP_Split), +N(OP_Stamina), +N(OP_Stun), +N(OP_Surname), +N(OP_SwapSpell), +N(OP_TargetBuffs), +N(OP_TargetCommand), +N(OP_TargetHoTT), +N(OP_TargetMouse), +N(OP_TargetReject), +N(OP_TaskActivity), +N(OP_TaskActivityComplete), +N(OP_TaskDescription), +N(OP_TaskHistoryReply), +N(OP_TaskHistoryRequest), +N(OP_TaskMemberList), +N(OP_Taunt), +N(OP_TestBuff), +N(OP_TGB), +N(OP_TimeOfDay), +N(OP_Track), +N(OP_TrackTarget), +N(OP_TrackUnknown), +N(OP_TradeAcceptClick), +N(OP_TradeBusy), +N(OP_TradeCoins), +N(OP_TradeMoneyUpdate), +N(OP_Trader), +N(OP_TraderBuy), +N(OP_TraderDelItem), +N(OP_TradeRequest), +N(OP_TradeRequestAck), +N(OP_TraderItemUpdate), +N(OP_TraderShop), +N(OP_TradeSkillCombine), +N(OP_Translocate), +N(OP_TributeInfo), +N(OP_TributeItem), +N(OP_TributeMoney), +N(OP_TributeNPC), +N(OP_TributePointUpdate), +N(OP_TributeTimer), +N(OP_TributeToggle), +N(OP_TributeUpdate), +N(OP_Untargetable), +N(OP_UpdateAA), +N(OP_UpdateLeadershipAA), +N(OP_VetClaimReply), +N(OP_VetClaimRequest), +N(OP_VetRewardsAvaliable), +N(OP_VoiceMacroIn), +N(OP_VoiceMacroOut), +N(OP_WeaponEquip1), +N(OP_WeaponEquip2), +N(OP_WeaponUnequip2), +N(OP_WearChange), +N(OP_Weather), +N(OP_Weblink), +N(OP_WhoAllRequest), +N(OP_WhoAllResponse), +N(OP_World_Client_CRC1), +N(OP_World_Client_CRC2), +N(OP_WorldClientReady), +N(OP_WorldComplete), +N(OP_WorldLogout), +N(OP_WorldObjectsSent), +N(OP_WorldUnknown001), +N(OP_XTargetAutoAddHaters), N(OP_XTargetRequest), N(OP_XTargetResponse), -N(OP_XTargetAutoAddHaters), -N(OP_Weblink), -N(OP_InspectMessageUpdate), -N(OP_ItemPreview), -N(OP_MercenaryDataRequest), -N(OP_MercenaryDataResponse), -N(OP_MercenaryHire), -N(OP_MercenaryUnknown1), -N(OP_MercenaryTimer), -N(OP_MercenaryAssign), -N(OP_MercenaryDataUpdate), -N(OP_MercenaryCommand), -N(OP_MercenarySuspendRequest), -N(OP_MercenarySuspendResponse), -N(OP_MercenaryUnsuspendResponse), -N(OP_MercenaryDataUpdateRequest), -N(OP_MercenaryDismiss), -N(OP_MercenaryTimerRequest), -N(OP_OpenInventory), -N(OP_OpenContainer), -N(OP_Marquee), -N(OP_ClientTimeStamp), -N(OP_GuildPromote), +N(OP_YellForHelp), +N(OP_ZoneChange), +N(OP_ZoneComplete), +N(OP_ZoneEntry), +N(OP_ZoneGuildList), +N(OP_ZoneInUnknown), +N(OP_ZonePlayerToBind), +N(OP_ZoneServerInfo), +N(OP_ZoneServerReady), +N(OP_ZoneSpawns), +N(OP_ZoneUnavail), +// mail and chat opcodes located in ../mail_oplist.h diff --git a/common/mail_oplist.h b/common/mail_oplist.h index f10e62be0..29cb859e0 100644 --- a/common/mail_oplist.h +++ b/common/mail_oplist.h @@ -1,13 +1,13 @@ //Mail and Chat Channels -N(OP_MailLogin), -N(OP_Mail), +N(OP_Buddy), N(OP_ChannelAnnounceJoin), N(OP_ChannelAnnounceLeave), -N(OP_Buddy), -N(OP_MailHeaderCount), -N(OP_MailHeader), -N(OP_MailSendBody), -N(OP_MailNew), -N(OP_MailDeliveryStatus), -N(OP_MailboxChange), N(OP_Ignore), +N(OP_Mail), +N(OP_MailboxChange), +N(OP_MailDeliveryStatus), +N(OP_MailHeader), +N(OP_MailHeaderCount), +N(OP_MailLogin), +N(OP_MailNew), +N(OP_MailSendBody), diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d8ce76a6a..62e31c075 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -84,312 +84,313 @@ std::map ConnectingOpcodes; //Use a static array for connected, for speed ClientPacketProc ConnectedOpcodes[_maxEmuOpcode]; -void MapOpcodes() { +void MapOpcodes() +{ ConnectingOpcodes.clear(); memset(ConnectedOpcodes, 0, sizeof(ConnectedOpcodes)); - //Now put all the opcodes into their home... - //Begin Connecting opcodes: - ConnectingOpcodes[OP_ZoneEntry] = &Client::Handle_Connect_OP_ZoneEntry; - ConnectingOpcodes[OP_SetServerFilter] = &Client::Handle_Connect_OP_SetServerFilter; - ConnectingOpcodes[OP_SendAATable] = &Client::Handle_Connect_OP_SendAATable; - ConnectingOpcodes[OP_ReqClientSpawn] = &Client::Handle_Connect_OP_ReqClientSpawn; - ConnectingOpcodes[OP_SendExpZonein] = &Client::Handle_Connect_OP_SendExpZonein; - ConnectingOpcodes[OP_WorldObjectsSent] = &Client::Handle_Connect_OP_WorldObjectsSent; - ConnectingOpcodes[OP_ZoneComplete] = &Client::Handle_Connect_OP_ZoneComplete; - ConnectingOpcodes[OP_ReqNewZone] = &Client::Handle_Connect_OP_ReqNewZone; - ConnectingOpcodes[OP_SpawnAppearance] = &Client::Handle_Connect_OP_SpawnAppearance; - ConnectingOpcodes[OP_WearChange] = &Client::Handle_Connect_OP_WearChange; - ConnectingOpcodes[OP_ClientUpdate] = &Client::Handle_Connect_OP_ClientUpdate; - ConnectingOpcodes[OP_ClientError] = &Client::Handle_Connect_OP_ClientError; + // Now put all the opcodes into their home... + // connecting opcode handler assignments: ConnectingOpcodes[OP_ApproveZone] = &Client::Handle_Connect_OP_ApproveZone; - ConnectingOpcodes[OP_TGB] = &Client::Handle_Connect_OP_TGB; - ConnectingOpcodes[OP_SendTributes] = &Client::Handle_Connect_OP_SendTributes; - ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; - ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; - ConnectingOpcodes[OP_SendAAStats] = &Client::Handle_Connect_OP_SendAAStats; - ConnectingOpcodes[OP_ClientReady] = &Client::Handle_Connect_OP_ClientReady; - ConnectingOpcodes[OP_UpdateAA] = &Client::Handle_Connect_OP_UpdateAA; ConnectingOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; - ConnectingOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; + ConnectingOpcodes[OP_ClientError] = &Client::Handle_Connect_OP_ClientError; + ConnectingOpcodes[OP_ClientReady] = &Client::Handle_Connect_OP_ClientReady; + ConnectingOpcodes[OP_ClientUpdate] = &Client::Handle_Connect_OP_ClientUpdate; + ConnectingOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; // temporary hack + ConnectingOpcodes[OP_ReqClientSpawn] = &Client::Handle_Connect_OP_ReqClientSpawn; + ConnectingOpcodes[OP_ReqNewZone] = &Client::Handle_Connect_OP_ReqNewZone; + ConnectingOpcodes[OP_SendAAStats] = &Client::Handle_Connect_OP_SendAAStats; + ConnectingOpcodes[OP_SendAATable] = &Client::Handle_Connect_OP_SendAATable; + ConnectingOpcodes[OP_SendExpZonein] = &Client::Handle_Connect_OP_SendExpZonein; + ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; + ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; // I guess it didn't believe us with the first assignment? + ConnectingOpcodes[OP_SendTributes] = &Client::Handle_Connect_OP_SendTributes; + ConnectingOpcodes[OP_SetServerFilter] = &Client::Handle_Connect_OP_SetServerFilter; + ConnectingOpcodes[OP_SpawnAppearance] = &Client::Handle_Connect_OP_SpawnAppearance; + ConnectingOpcodes[OP_TGB] = &Client::Handle_Connect_OP_TGB; + ConnectingOpcodes[OP_UpdateAA] = &Client::Handle_Connect_OP_UpdateAA; + ConnectingOpcodes[OP_WearChange] = &Client::Handle_Connect_OP_WearChange; + ConnectingOpcodes[OP_WorldObjectsSent] = &Client::Handle_Connect_OP_WorldObjectsSent; ConnectingOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; -//temporary hack: - ConnectingOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; + ConnectingOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; + ConnectingOpcodes[OP_ZoneComplete] = &Client::Handle_Connect_OP_ZoneComplete; + ConnectingOpcodes[OP_ZoneEntry] = &Client::Handle_Connect_OP_ZoneEntry; - //Begin Connected opcodes: - ConnectedOpcodes[OP_ClientUpdate] = &Client::Handle_OP_ClientUpdate; - ConnectedOpcodes[OP_AutoAttack] = &Client::Handle_OP_AutoAttack; - ConnectedOpcodes[OP_AutoAttack2] = &Client::Handle_OP_AutoAttack2; - ConnectedOpcodes[OP_Consent] = &Client::Handle_OP_Consent; - ConnectedOpcodes[OP_ConsentDeny] = &Client::Handle_OP_ConsentDeny; - ConnectedOpcodes[OP_TargetMouse] = &Client::Handle_OP_TargetMouse; - ConnectedOpcodes[OP_TargetCommand] = &Client::Handle_OP_TargetCommand; - ConnectedOpcodes[OP_Shielding] = &Client::Handle_OP_Shielding; - ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump; - ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest; - ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest; - ConnectedOpcodes[OP_LDoNButton] = &Client::Handle_OP_LDoNButton; - ConnectedOpcodes[OP_LeaveAdventure] = &Client::Handle_OP_LeaveAdventure; - ConnectedOpcodes[OP_Consume] = &Client::Handle_OP_Consume; - ConnectedOpcodes[OP_ItemVerifyRequest] = &Client::Handle_OP_ItemVerifyRequest; - ConnectedOpcodes[OP_AdventureMerchantRequest] = &Client::Handle_OP_AdventureMerchantRequest; - ConnectedOpcodes[OP_AdventureMerchantPurchase] = &Client::Handle_OP_AdventureMerchantPurchase; - ConnectedOpcodes[OP_ConsiderCorpse] = &Client::Handle_OP_ConsiderCorpse; - ConnectedOpcodes[OP_Consider] = &Client::Handle_OP_Consider; - ConnectedOpcodes[OP_Begging] = &Client::Handle_OP_Begging; - ConnectedOpcodes[OP_TestBuff] = &Client::Handle_OP_TestBuff; - ConnectedOpcodes[OP_Surname] = &Client::Handle_OP_Surname; - ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname; - ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp; - ConnectedOpcodes[OP_Assist] = &Client::Handle_OP_Assist; - ConnectedOpcodes[OP_AssistGroup] = &Client::Handle_OP_AssistGroup; - ConnectedOpcodes[OP_GMTraining] = &Client::Handle_OP_GMTraining; - ConnectedOpcodes[OP_GMEndTraining] = &Client::Handle_OP_GMEndTraining; - ConnectedOpcodes[OP_GMTrainSkill] = &Client::Handle_OP_GMTrainSkill; - ConnectedOpcodes[OP_DuelResponse] = &Client::Handle_OP_DuelResponse; - ConnectedOpcodes[OP_DuelResponse2] = &Client::Handle_OP_DuelResponse2; - ConnectedOpcodes[OP_RequestDuel] = &Client::Handle_OP_RequestDuel; - ConnectedOpcodes[OP_SpawnAppearance] = &Client::Handle_OP_SpawnAppearance; - ConnectedOpcodes[OP_BazaarInspect] = &Client::Handle_OP_BazaarInspect; - ConnectedOpcodes[OP_Death] = &Client::Handle_OP_Death; - ConnectedOpcodes[OP_MoveCoin] = &Client::Handle_OP_MoveCoin; - ConnectedOpcodes[OP_ItemLinkClick] = &Client::Handle_OP_ItemLinkClick; - ConnectedOpcodes[OP_ItemLinkResponse] = &Client::Handle_OP_ItemLinkResponse; - ConnectedOpcodes[OP_MoveItem] = &Client::Handle_OP_MoveItem; - ConnectedOpcodes[OP_Camp] = &Client::Handle_OP_Camp; - ConnectedOpcodes[OP_Logout] = &Client::Handle_OP_Logout; - ConnectedOpcodes[OP_LDoNOpen] = &Client::Handle_OP_LDoNOpen; - ConnectedOpcodes[OP_LDoNSenseTraps] = &Client::Handle_OP_LDoNSenseTraps; - ConnectedOpcodes[OP_LDoNDisarmTraps] = &Client::Handle_OP_LDoNDisarmTraps; - ConnectedOpcodes[OP_LDoNInspect] = &Client::Handle_OP_LDoNInspect; - ConnectedOpcodes[OP_LDoNPickLock] = &Client::Handle_OP_LDoNPickLock; - ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath; - ConnectedOpcodes[OP_Sneak] = &Client::Handle_OP_Sneak; - ConnectedOpcodes[OP_Hide] = &Client::Handle_OP_Hide; - ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage; - ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; - ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange; - ConnectedOpcodes[OP_DeleteSpawn] = &Client::Handle_OP_DeleteSpawn; - ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq; - ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save; - ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; - ConnectedOpcodes[OP_GMZoneRequest] = &Client::Handle_OP_GMZoneRequest; - ConnectedOpcodes[OP_GMZoneRequest2] = &Client::Handle_OP_GMZoneRequest2; - ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest; - ConnectedOpcodes[OP_LootRequest] = &Client::Handle_OP_LootRequest; - ConnectedOpcodes[OP_Dye] = &Client::Handle_OP_Dye; - ConnectedOpcodes[OP_LootItem] = &Client::Handle_OP_LootItem; - ConnectedOpcodes[OP_GuildDelete] = &Client::Handle_OP_GuildDelete; - ConnectedOpcodes[OP_GuildPublicNote] = &Client::Handle_OP_GuildPublicNote; - ConnectedOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; - ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD; - ConnectedOpcodes[OP_GuildPeace] = &Client::Handle_OP_GuildPeace; - ConnectedOpcodes[OP_GuildWar] = &Client::Handle_OP_GuildWar; - ConnectedOpcodes[OP_GuildLeader] = &Client::Handle_OP_GuildLeader; - ConnectedOpcodes[OP_GuildDemote] = &Client::Handle_OP_GuildDemote; - ConnectedOpcodes[OP_GuildPromote] = &Client::Handle_OP_GuildPromote; - ConnectedOpcodes[OP_GuildInvite] = &Client::Handle_OP_GuildInvite; - ConnectedOpcodes[OP_GuildRemove] = &Client::Handle_OP_GuildRemove; - ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD; - ConnectedOpcodes[OP_GuildManageBanker] = &Client::Handle_OP_GuildManageBanker; - ConnectedOpcodes[OP_GuildInviteAccept] = &Client::Handle_OP_GuildInviteAccept; - ConnectedOpcodes[OP_ManaChange] = &Client::Handle_OP_ManaChange; - ConnectedOpcodes[OP_MemorizeSpell] = &Client::Handle_OP_MemorizeSpell; - ConnectedOpcodes[OP_SwapSpell] = &Client::Handle_OP_SwapSpell; - ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell; - ConnectedOpcodes[OP_DeleteItem] = &Client::Handle_OP_DeleteItem; - ConnectedOpcodes[OP_CombatAbility] = &Client::Handle_OP_CombatAbility; - ConnectedOpcodes[OP_Taunt] = &Client::Handle_OP_Taunt; - ConnectedOpcodes[OP_InstillDoubt] = &Client::Handle_OP_InstillDoubt; - ConnectedOpcodes[OP_RezzAnswer] = &Client::Handle_OP_RezzAnswer; - ConnectedOpcodes[OP_GMSummon] = &Client::Handle_OP_GMSummon; - ConnectedOpcodes[OP_TradeRequest] = &Client::Handle_OP_TradeRequest; - ConnectedOpcodes[OP_TradeRequestAck] = &Client::Handle_OP_TradeRequestAck; - ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade; - ConnectedOpcodes[OP_TradeAcceptClick] = &Client::Handle_OP_TradeAcceptClick; - ConnectedOpcodes[OP_BoardBoat] = &Client::Handle_OP_BoardBoat; - ConnectedOpcodes[OP_LeaveBoat] = &Client::Handle_OP_LeaveBoat; - ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq; - ConnectedOpcodes[OP_Buff] = &Client::Handle_OP_Buff; - ConnectedOpcodes[OP_GMHideMe] = &Client::Handle_OP_GMHideMe; - ConnectedOpcodes[OP_GMNameChange] = &Client::Handle_OP_GMNameChange; - ConnectedOpcodes[OP_GMKill] = &Client::Handle_OP_GMKill; - ConnectedOpcodes[OP_GMLastName] = &Client::Handle_OP_GMLastName; - ConnectedOpcodes[OP_GMToggle] = &Client::Handle_OP_GMToggle; - ConnectedOpcodes[OP_LFGCommand] = &Client::Handle_OP_LFGCommand; - ConnectedOpcodes[OP_GMGoto] = &Client::Handle_OP_GMGoto; - ConnectedOpcodes[OP_Trader] = &Client::Handle_OP_Trader; - ConnectedOpcodes[OP_TraderShop] = &Client::Handle_OP_TraderShop; - ConnectedOpcodes[OP_ShopRequest] = &Client::Handle_OP_ShopRequest; - ConnectedOpcodes[OP_BazaarSearch] = &Client::Handle_OP_BazaarSearch; - ConnectedOpcodes[OP_ShopPlayerBuy] = &Client::Handle_OP_ShopPlayerBuy; - ConnectedOpcodes[OP_ShopPlayerSell] = &Client::Handle_OP_ShopPlayerSell; - ConnectedOpcodes[OP_ShopEnd] = &Client::Handle_OP_ShopEnd; - ConnectedOpcodes[OP_ClickObjectAction] = &Client::Handle_OP_ClickObjectAction; - ConnectedOpcodes[OP_ClickObject] = &Client::Handle_OP_ClickObject; - ConnectedOpcodes[OP_RecipesFavorite] = &Client::Handle_OP_RecipesFavorite; - ConnectedOpcodes[OP_RecipesSearch] = &Client::Handle_OP_RecipesSearch; - ConnectedOpcodes[OP_RecipeDetails] = &Client::Handle_OP_RecipeDetails; - ConnectedOpcodes[OP_RecipeAutoCombine] = &Client::Handle_OP_RecipeAutoCombine; - ConnectedOpcodes[OP_TradeSkillCombine] = &Client::Handle_OP_TradeSkillCombine; - ConnectedOpcodes[OP_ItemName] = &Client::Handle_OP_ItemName; - ConnectedOpcodes[OP_AugmentItem] = &Client::Handle_OP_AugmentItem; - ConnectedOpcodes[OP_ClickDoor] = &Client::Handle_OP_ClickDoor; - ConnectedOpcodes[OP_GroundSpawn] = &Client::Handle_OP_CreateObject; - ConnectedOpcodes[OP_FaceChange] = &Client::Handle_OP_FaceChange; - ConnectedOpcodes[OP_GroupInvite] = &Client::Handle_OP_GroupInvite; - ConnectedOpcodes[OP_GroupInvite2] = &Client::Handle_OP_GroupInvite2; - ConnectedOpcodes[OP_GroupAcknowledge] = &Client::Handle_OP_GroupAcknowledge; - ConnectedOpcodes[OP_GroupCancelInvite] = &Client::Handle_OP_GroupCancelInvite; - ConnectedOpcodes[OP_GroupFollow] = &Client::Handle_OP_GroupFollow; - ConnectedOpcodes[OP_GroupFollow2] = &Client::Handle_OP_GroupFollow2; - ConnectedOpcodes[OP_GroupDisband] = &Client::Handle_OP_GroupDisband; - ConnectedOpcodes[OP_GroupDelete] = &Client::Handle_OP_GroupDelete; - ConnectedOpcodes[OP_GMEmoteZone] = &Client::Handle_OP_GMEmoteZone; - ConnectedOpcodes[OP_InspectRequest] = &Client::Handle_OP_InspectRequest; - ConnectedOpcodes[OP_InspectAnswer] = &Client::Handle_OP_InspectAnswer; - ConnectedOpcodes[OP_InspectMessageUpdate] = &Client::Handle_OP_InspectMessageUpdate; - ConnectedOpcodes[OP_DeleteSpell] = &Client::Handle_OP_DeleteSpell; - ConnectedOpcodes[OP_PetitionBug] = &Client::Handle_OP_PetitionBug; - ConnectedOpcodes[OP_Bug] = &Client::Handle_OP_Bug; - ConnectedOpcodes[OP_Petition] = &Client::Handle_OP_Petition; - ConnectedOpcodes[OP_PetitionCheckIn] = &Client::Handle_OP_PetitionCheckIn; - ConnectedOpcodes[OP_PetitionResolve] = &Client::Handle_OP_PetitionResolve; - ConnectedOpcodes[OP_PetitionDelete] = &Client::Handle_OP_PetitionDelete; - ConnectedOpcodes[OP_PetCommands] = &Client::Handle_OP_PetCommands; - ConnectedOpcodes[OP_PetitionUnCheckout] = &Client::Handle_OP_PetitionUnCheckout; - ConnectedOpcodes[OP_PetitionQue] = &Client::Handle_OP_PetitionQue; - ConnectedOpcodes[OP_PDeletePetition] = &Client::Handle_OP_PDeletePetition; - ConnectedOpcodes[OP_PetitionCheckout] = &Client::Handle_OP_PetitionCheckout; - ConnectedOpcodes[OP_PetitionRefresh] = &Client::Handle_OP_PetitionRefresh; - ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook; - ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote; - ConnectedOpcodes[OP_Animation] = &Client::Handle_OP_Animation; - ConnectedOpcodes[OP_SetServerFilter] = &Client::Handle_OP_SetServerFilter; - ConnectedOpcodes[OP_GMDelCorpse] = &Client::Handle_OP_GMDelCorpse; - ConnectedOpcodes[OP_GMKick] = &Client::Handle_OP_GMKick; - ConnectedOpcodes[OP_GMServers] = &Client::Handle_OP_GMServers; - ConnectedOpcodes[OP_Illusion] = &Client::Handle_OP_Illusion; - ConnectedOpcodes[OP_GMBecomeNPC] = &Client::Handle_OP_GMBecomeNPC; - ConnectedOpcodes[OP_Fishing] = &Client::Handle_OP_Fishing; - ConnectedOpcodes[OP_Forage] = &Client::Handle_OP_Forage; - ConnectedOpcodes[OP_Mend] = &Client::Handle_OP_Mend; - ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage; - ConnectedOpcodes[OP_Damage] = &Client::Handle_OP_Damage; - ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction; - ConnectedOpcodes[OP_TraderBuy] = &Client::Handle_OP_TraderBuy; - ConnectedOpcodes[OP_GMFind] = &Client::Handle_OP_GMFind; - ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket; - ConnectedOpcodes[OP_Bind_Wound] = &Client::Handle_OP_Bind_Wound; - ConnectedOpcodes[OP_TrackTarget] = &Client::Handle_OP_TrackTarget; - ConnectedOpcodes[OP_Track] = &Client::Handle_OP_Track; - ConnectedOpcodes[OP_TrackUnknown] = &Client::Handle_OP_TrackUnknown; + // connected opcode handler assignments: ConnectedOpcodes[OP_0x0193] = &Client::Handle_0x0193; - ConnectedOpcodes[OP_ClientError] = &Client::Handle_OP_ClientError; - ConnectedOpcodes[OP_ReloadUI] = &Client::Handle_OP_ReloadUI; - ConnectedOpcodes[OP_TGB] = &Client::Handle_OP_TGB; - ConnectedOpcodes[OP_Split] = &Client::Handle_OP_Split; - ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps; - ConnectedOpcodes[OP_DisarmTraps] = &Client::Handle_OP_DisarmTraps; - ConnectedOpcodes[OP_OpenTributeMaster] = &Client::Handle_OP_OpenTributeMaster; - ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster; - ConnectedOpcodes[OP_TributeItem] = &Client::Handle_OP_TributeItem; - ConnectedOpcodes[OP_TributeMoney] = &Client::Handle_OP_TributeMoney; - ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute; - ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate; - ConnectedOpcodes[OP_TributeToggle] = &Client::Handle_OP_TributeToggle; - ConnectedOpcodes[OP_TributeNPC] = &Client::Handle_OP_TributeNPC; - ConnectedOpcodes[OP_ConfirmDelete] = &Client::Handle_OP_ConfirmDelete; - ConnectedOpcodes[OP_CrashDump] = &Client::Handle_OP_CrashDump; - ConnectedOpcodes[OP_ControlBoat] = &Client::Handle_OP_ControlBoat; - ConnectedOpcodes[OP_DumpName] = &Client::Handle_OP_DumpName; - ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode; - ConnectedOpcodes[OP_SafeFallSuccess] = &Client::Handle_OP_SafeFallSuccess; - ConnectedOpcodes[OP_Heartbeat] = &Client::Handle_OP_Heartbeat; - ConnectedOpcodes[OP_SafePoint] = &Client::Handle_OP_SafePoint; - ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest; - ConnectedOpcodes[OP_BankerChange] = &Client::Handle_OP_BankerChange; - ConnectedOpcodes[OP_LeadershipExpToggle] = &Client::Handle_OP_LeadershipExpToggle; - ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA; - ConnectedOpcodes[OP_RequestTitles] = &Client::Handle_OP_RequestTitles; - ConnectedOpcodes[OP_SetTitle] = &Client::Handle_OP_SetTitle; - ConnectedOpcodes[OP_SenseHeading] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_FloatListThing] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_LoadSpellSet] = &Client::Handle_OP_LoadSpellSet; - ConnectedOpcodes[OP_AutoFire] = &Client::Handle_OP_AutoFire; - ConnectedOpcodes[OP_Rewind] = &Client::Handle_OP_Rewind; - ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand; - ConnectedOpcodes[OP_Translocate] = &Client::Handle_OP_Translocate; - ConnectedOpcodes[OP_Sacrifice] = &Client::Handle_OP_Sacrifice; + ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction; ConnectedOpcodes[OP_AcceptNewTask] = &Client::Handle_OP_AcceptNewTask; - ConnectedOpcodes[OP_CancelTask] = &Client::Handle_OP_CancelTask; - ConnectedOpcodes[OP_TaskHistoryRequest] = &Client::Handle_OP_TaskHistoryRequest; - ConnectedOpcodes[OP_KeyRing] = &Client::Handle_OP_KeyRing; - ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho; - ConnectedOpcodes[OP_Bandolier] = &Client::Handle_OP_Bandolier; - ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse; - ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt; - ConnectedOpcodes[OP_LFGGetMatchesRequest] = &Client::Handle_OP_LFGGetMatchesRequest; - ConnectedOpcodes[OP_LFPCommand] = &Client::Handle_OP_LFPCommand; - ConnectedOpcodes[OP_LFPGetMatchesRequest] = &Client::Handle_OP_LFPGetMatchesRequest; - ConnectedOpcodes[OP_Barter] = &Client::Handle_OP_Barter; - ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; - ConnectedOpcodes[OP_DoGroupLeadershipAbility] = &Client::Handle_OP_DoGroupLeadershipAbility; - ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks; - ConnectedOpcodes[OP_DelegateAbility] = &Client::Handle_OP_DelegateAbility; - ConnectedOpcodes[OP_ApplyPoison] = &Client::Handle_OP_ApplyPoison; - ConnectedOpcodes[OP_AugmentInfo] = &Client::Handle_OP_AugmentInfo; - ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest; - ConnectedOpcodes[OP_PVPLeaderBoardDetailsRequest] = &Client::Handle_OP_PVPLeaderBoardDetailsRequest; - ConnectedOpcodes[OP_RespawnWindow] = &Client::Handle_OP_RespawnWindow; - ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell; - ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest; + ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest; ConnectedOpcodes[OP_AdventureLeaderboardRequest] = &Client::Handle_OP_AdventureLeaderboardRequest; - ConnectedOpcodes[OP_GroupUpdate] = &Client::Handle_OP_GroupUpdate; - ConnectedOpcodes[OP_SetStartCity] = &Client::Handle_OP_SetStartCity; - ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_Report] = &Client::Handle_OP_Report; - ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; - ConnectedOpcodes[OP_GMSearchCorpse] = &Client::Handle_OP_GMSearchCorpse; - ConnectedOpcodes[OP_GuildBank] = &Client::Handle_OP_GuildBank; - ConnectedOpcodes[OP_GroupRoles] = &Client::Handle_OP_GroupRoles; - ConnectedOpcodes[OP_HideCorpse] = &Client::Handle_OP_HideCorpse; - ConnectedOpcodes[OP_TradeBusy] = &Client::Handle_OP_TradeBusy; - ConnectedOpcodes[OP_GuildUpdateURLAndChannel] = &Client::Handle_OP_GuildUpdateURLAndChannel; - ConnectedOpcodes[OP_GuildStatus] = &Client::Handle_OP_GuildStatus; - ConnectedOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; - ConnectedOpcodes[OP_RemoveBlockedBuffs] = &Client::Handle_OP_RemoveBlockedBuffs; - ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs; - ConnectedOpcodes[OP_BuffRemoveRequest] = &Client::Handle_OP_BuffRemoveRequest; - ConnectedOpcodes[OP_CorpseDrag] = &Client::Handle_OP_CorpseDrag; - ConnectedOpcodes[OP_CorpseDrop] = &Client::Handle_OP_CorpseDrop; - ConnectedOpcodes[OP_GroupMakeLeader] = &Client::Handle_OP_GroupMakeLeader; - ConnectedOpcodes[OP_GuildCreate] = &Client::Handle_OP_GuildCreate; + ConnectedOpcodes[OP_AdventureMerchantPurchase] = &Client::Handle_OP_AdventureMerchantPurchase; + ConnectedOpcodes[OP_AdventureMerchantRequest] = &Client::Handle_OP_AdventureMerchantRequest; + ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell; + ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest; + ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest; ConnectedOpcodes[OP_AltCurrencyMerchantRequest] = &Client::Handle_OP_AltCurrencyMerchantRequest; - ConnectedOpcodes[OP_AltCurrencySellSelection] = &Client::Handle_OP_AltCurrencySellSelection; ConnectedOpcodes[OP_AltCurrencyPurchase] = &Client::Handle_OP_AltCurrencyPurchase; ConnectedOpcodes[OP_AltCurrencyReclaim] = &Client::Handle_OP_AltCurrencyReclaim; ConnectedOpcodes[OP_AltCurrencySell] = &Client::Handle_OP_AltCurrencySell; - ConnectedOpcodes[OP_CrystalReclaim] = &Client::Handle_OP_CrystalReclaim; - ConnectedOpcodes[OP_CrystalCreate] = &Client::Handle_OP_CrystalCreate; - ConnectedOpcodes[OP_LFGuild] = &Client::Handle_OP_LFGuild; - ConnectedOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; - ConnectedOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; - ConnectedOpcodes[OP_ItemPreview] = &Client::Handle_OP_ItemPreview; - ConnectedOpcodes[OP_MercenaryDataRequest] = &Client::Handle_OP_MercenaryDataRequest; - ConnectedOpcodes[OP_MercenaryHire] = &Client::Handle_OP_MercenaryHire; - ConnectedOpcodes[OP_MercenaryCommand] = &Client::Handle_OP_MercenaryCommand; - ConnectedOpcodes[OP_MercenaryDataUpdateRequest] = &Client::Handle_OP_MercenaryDataUpdateRequest; - ConnectedOpcodes[OP_MercenarySuspendRequest] = &Client::Handle_OP_MercenarySuspendRequest; - ConnectedOpcodes[OP_MercenaryDismiss] = &Client::Handle_OP_MercenaryDismiss; - ConnectedOpcodes[OP_MercenaryTimerRequest] = &Client::Handle_OP_MercenaryTimerRequest; - ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory; - ConnectedOpcodes[OP_OpenContainer] = &Client::Handle_OP_OpenContainer; + ConnectedOpcodes[OP_AltCurrencySellSelection] = &Client::Handle_OP_AltCurrencySellSelection; + ConnectedOpcodes[OP_Animation] = &Client::Handle_OP_Animation; + ConnectedOpcodes[OP_ApplyPoison] = &Client::Handle_OP_ApplyPoison; + ConnectedOpcodes[OP_Assist] = &Client::Handle_OP_Assist; + ConnectedOpcodes[OP_AssistGroup] = &Client::Handle_OP_AssistGroup; + ConnectedOpcodes[OP_AugmentInfo] = &Client::Handle_OP_AugmentInfo; + ConnectedOpcodes[OP_AugmentItem] = &Client::Handle_OP_AugmentItem; + ConnectedOpcodes[OP_AutoAttack] = &Client::Handle_OP_AutoAttack; + ConnectedOpcodes[OP_AutoAttack2] = &Client::Handle_OP_AutoAttack2; + ConnectedOpcodes[OP_AutoFire] = &Client::Handle_OP_AutoFire; + ConnectedOpcodes[OP_Bandolier] = &Client::Handle_OP_Bandolier; + ConnectedOpcodes[OP_BankerChange] = &Client::Handle_OP_BankerChange; + ConnectedOpcodes[OP_Barter] = &Client::Handle_OP_Barter; + ConnectedOpcodes[OP_BazaarInspect] = &Client::Handle_OP_BazaarInspect; + ConnectedOpcodes[OP_BazaarSearch] = &Client::Handle_OP_BazaarSearch; + ConnectedOpcodes[OP_Begging] = &Client::Handle_OP_Begging; + ConnectedOpcodes[OP_Bind_Wound] = &Client::Handle_OP_Bind_Wound; + ConnectedOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; + ConnectedOpcodes[OP_BoardBoat] = &Client::Handle_OP_BoardBoat; + ConnectedOpcodes[OP_Buff] = &Client::Handle_OP_Buff; + ConnectedOpcodes[OP_BuffRemoveRequest] = &Client::Handle_OP_BuffRemoveRequest; + ConnectedOpcodes[OP_Bug] = &Client::Handle_OP_Bug; + ConnectedOpcodes[OP_Camp] = &Client::Handle_OP_Camp; + ConnectedOpcodes[OP_CancelTask] = &Client::Handle_OP_CancelTask; + ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade; + ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell; + ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage; + ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs; + ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks; + ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname; + ConnectedOpcodes[OP_ClickDoor] = &Client::Handle_OP_ClickDoor; + ConnectedOpcodes[OP_ClickObject] = &Client::Handle_OP_ClickObject; + ConnectedOpcodes[OP_ClickObjectAction] = &Client::Handle_OP_ClickObjectAction; + ConnectedOpcodes[OP_ClientError] = &Client::Handle_OP_ClientError; ConnectedOpcodes[OP_ClientTimeStamp] = &Client::Handle_OP_ClientTimeStamp; + ConnectedOpcodes[OP_ClientUpdate] = &Client::Handle_OP_ClientUpdate; + ConnectedOpcodes[OP_CombatAbility] = &Client::Handle_OP_CombatAbility; + ConnectedOpcodes[OP_ConfirmDelete] = &Client::Handle_OP_ConfirmDelete; + ConnectedOpcodes[OP_Consent] = &Client::Handle_OP_Consent; + ConnectedOpcodes[OP_ConsentDeny] = &Client::Handle_OP_ConsentDeny; + ConnectedOpcodes[OP_Consider] = &Client::Handle_OP_Consider; + ConnectedOpcodes[OP_ConsiderCorpse] = &Client::Handle_OP_ConsiderCorpse; + ConnectedOpcodes[OP_Consume] = &Client::Handle_OP_Consume; + ConnectedOpcodes[OP_ControlBoat] = &Client::Handle_OP_ControlBoat; + ConnectedOpcodes[OP_CorpseDrag] = &Client::Handle_OP_CorpseDrag; + ConnectedOpcodes[OP_CorpseDrop] = &Client::Handle_OP_CorpseDrop; + ConnectedOpcodes[OP_CrashDump] = &Client::Handle_OP_CrashDump; + ConnectedOpcodes[OP_CrystalCreate] = &Client::Handle_OP_CrystalCreate; + ConnectedOpcodes[OP_CrystalReclaim] = &Client::Handle_OP_CrystalReclaim; + ConnectedOpcodes[OP_Damage] = &Client::Handle_OP_Damage; + ConnectedOpcodes[OP_Death] = &Client::Handle_OP_Death; + ConnectedOpcodes[OP_DelegateAbility] = &Client::Handle_OP_DelegateAbility; + ConnectedOpcodes[OP_DeleteItem] = &Client::Handle_OP_DeleteItem; + ConnectedOpcodes[OP_DeleteSpawn] = &Client::Handle_OP_DeleteSpawn; + ConnectedOpcodes[OP_DeleteSpell] = &Client::Handle_OP_DeleteSpell; + ConnectedOpcodes[OP_DisarmTraps] = &Client::Handle_OP_DisarmTraps; + ConnectedOpcodes[OP_DoGroupLeadershipAbility] = &Client::Handle_OP_DoGroupLeadershipAbility; + ConnectedOpcodes[OP_DuelResponse] = &Client::Handle_OP_DuelResponse; + ConnectedOpcodes[OP_DuelResponse2] = &Client::Handle_OP_DuelResponse2; + ConnectedOpcodes[OP_DumpName] = &Client::Handle_OP_DumpName; + ConnectedOpcodes[OP_Dye] = &Client::Handle_OP_Dye; + ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote; + ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest; + ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage; + ConnectedOpcodes[OP_FaceChange] = &Client::Handle_OP_FaceChange; + ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath; + ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest; + ConnectedOpcodes[OP_Fishing] = &Client::Handle_OP_Fishing; + ConnectedOpcodes[OP_FloatListThing] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_Forage] = &Client::Handle_OP_Forage; + ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho; + ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD; + ConnectedOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; + ConnectedOpcodes[OP_GMBecomeNPC] = &Client::Handle_OP_GMBecomeNPC; + ConnectedOpcodes[OP_GMDelCorpse] = &Client::Handle_OP_GMDelCorpse; + ConnectedOpcodes[OP_GMEmoteZone] = &Client::Handle_OP_GMEmoteZone; + ConnectedOpcodes[OP_GMEndTraining] = &Client::Handle_OP_GMEndTraining; + ConnectedOpcodes[OP_GMFind] = &Client::Handle_OP_GMFind; + ConnectedOpcodes[OP_GMGoto] = &Client::Handle_OP_GMGoto; + ConnectedOpcodes[OP_GMHideMe] = &Client::Handle_OP_GMHideMe; + ConnectedOpcodes[OP_GMKick] = &Client::Handle_OP_GMKick; + ConnectedOpcodes[OP_GMKill] = &Client::Handle_OP_GMKill; + ConnectedOpcodes[OP_GMLastName] = &Client::Handle_OP_GMLastName; + ConnectedOpcodes[OP_GMNameChange] = &Client::Handle_OP_GMNameChange; + ConnectedOpcodes[OP_GMSearchCorpse] = &Client::Handle_OP_GMSearchCorpse; + ConnectedOpcodes[OP_GMServers] = &Client::Handle_OP_GMServers; + ConnectedOpcodes[OP_GMSummon] = &Client::Handle_OP_GMSummon; + ConnectedOpcodes[OP_GMToggle] = &Client::Handle_OP_GMToggle; + ConnectedOpcodes[OP_GMTraining] = &Client::Handle_OP_GMTraining; + ConnectedOpcodes[OP_GMTrainSkill] = &Client::Handle_OP_GMTrainSkill; + ConnectedOpcodes[OP_GMZoneRequest] = &Client::Handle_OP_GMZoneRequest; + ConnectedOpcodes[OP_GMZoneRequest2] = &Client::Handle_OP_GMZoneRequest2; + ConnectedOpcodes[OP_GroundSpawn] = &Client::Handle_OP_CreateObject; + ConnectedOpcodes[OP_GroupAcknowledge] = &Client::Handle_OP_GroupAcknowledge; + ConnectedOpcodes[OP_GroupCancelInvite] = &Client::Handle_OP_GroupCancelInvite; + ConnectedOpcodes[OP_GroupDelete] = &Client::Handle_OP_GroupDelete; + ConnectedOpcodes[OP_GroupDisband] = &Client::Handle_OP_GroupDisband; + ConnectedOpcodes[OP_GroupFollow] = &Client::Handle_OP_GroupFollow; + ConnectedOpcodes[OP_GroupFollow2] = &Client::Handle_OP_GroupFollow2; + ConnectedOpcodes[OP_GroupInvite] = &Client::Handle_OP_GroupInvite; + ConnectedOpcodes[OP_GroupInvite2] = &Client::Handle_OP_GroupInvite2; + ConnectedOpcodes[OP_GroupMakeLeader] = &Client::Handle_OP_GroupMakeLeader; + ConnectedOpcodes[OP_GroupRoles] = &Client::Handle_OP_GroupRoles; + ConnectedOpcodes[OP_GroupUpdate] = &Client::Handle_OP_GroupUpdate; + ConnectedOpcodes[OP_GuildBank] = &Client::Handle_OP_GuildBank; + ConnectedOpcodes[OP_GuildCreate] = &Client::Handle_OP_GuildCreate; + ConnectedOpcodes[OP_GuildDelete] = &Client::Handle_OP_GuildDelete; + ConnectedOpcodes[OP_GuildDemote] = &Client::Handle_OP_GuildDemote; + ConnectedOpcodes[OP_GuildInvite] = &Client::Handle_OP_GuildInvite; + ConnectedOpcodes[OP_GuildInviteAccept] = &Client::Handle_OP_GuildInviteAccept; + ConnectedOpcodes[OP_GuildLeader] = &Client::Handle_OP_GuildLeader; + ConnectedOpcodes[OP_GuildManageBanker] = &Client::Handle_OP_GuildManageBanker; + ConnectedOpcodes[OP_GuildPeace] = &Client::Handle_OP_GuildPeace; + ConnectedOpcodes[OP_GuildPromote] = &Client::Handle_OP_GuildPromote; + ConnectedOpcodes[OP_GuildPublicNote] = &Client::Handle_OP_GuildPublicNote; + ConnectedOpcodes[OP_GuildRemove] = &Client::Handle_OP_GuildRemove; + ConnectedOpcodes[OP_GuildStatus] = &Client::Handle_OP_GuildStatus; + ConnectedOpcodes[OP_GuildUpdateURLAndChannel] = &Client::Handle_OP_GuildUpdateURLAndChannel; + ConnectedOpcodes[OP_GuildWar] = &Client::Handle_OP_GuildWar; + ConnectedOpcodes[OP_Heartbeat] = &Client::Handle_OP_Heartbeat; + ConnectedOpcodes[OP_Hide] = &Client::Handle_OP_Hide; + ConnectedOpcodes[OP_HideCorpse] = &Client::Handle_OP_HideCorpse; + ConnectedOpcodes[OP_Illusion] = &Client::Handle_OP_Illusion; + ConnectedOpcodes[OP_InspectAnswer] = &Client::Handle_OP_InspectAnswer; + ConnectedOpcodes[OP_InspectMessageUpdate] = &Client::Handle_OP_InspectMessageUpdate; + ConnectedOpcodes[OP_InspectRequest] = &Client::Handle_OP_InspectRequest; + ConnectedOpcodes[OP_InstillDoubt] = &Client::Handle_OP_InstillDoubt; + ConnectedOpcodes[OP_ItemLinkClick] = &Client::Handle_OP_ItemLinkClick; + ConnectedOpcodes[OP_ItemLinkResponse] = &Client::Handle_OP_ItemLinkResponse; + ConnectedOpcodes[OP_ItemName] = &Client::Handle_OP_ItemName; + ConnectedOpcodes[OP_ItemPreview] = &Client::Handle_OP_ItemPreview; + ConnectedOpcodes[OP_ItemVerifyRequest] = &Client::Handle_OP_ItemVerifyRequest; + ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump; + ConnectedOpcodes[OP_KeyRing] = &Client::Handle_OP_KeyRing; + ConnectedOpcodes[OP_LDoNButton] = &Client::Handle_OP_LDoNButton; + ConnectedOpcodes[OP_LDoNDisarmTraps] = &Client::Handle_OP_LDoNDisarmTraps; + ConnectedOpcodes[OP_LDoNInspect] = &Client::Handle_OP_LDoNInspect; + ConnectedOpcodes[OP_LDoNOpen] = &Client::Handle_OP_LDoNOpen; + ConnectedOpcodes[OP_LDoNPickLock] = &Client::Handle_OP_LDoNPickLock; + ConnectedOpcodes[OP_LDoNSenseTraps] = &Client::Handle_OP_LDoNSenseTraps; + ConnectedOpcodes[OP_LeadershipExpToggle] = &Client::Handle_OP_LeadershipExpToggle; + ConnectedOpcodes[OP_LeaveAdventure] = &Client::Handle_OP_LeaveAdventure; + ConnectedOpcodes[OP_LeaveBoat] = &Client::Handle_OP_LeaveBoat; + ConnectedOpcodes[OP_LFGCommand] = &Client::Handle_OP_LFGCommand; + ConnectedOpcodes[OP_LFGGetMatchesRequest] = &Client::Handle_OP_LFGGetMatchesRequest; + ConnectedOpcodes[OP_LFGuild] = &Client::Handle_OP_LFGuild; + ConnectedOpcodes[OP_LFPCommand] = &Client::Handle_OP_LFPCommand; + ConnectedOpcodes[OP_LFPGetMatchesRequest] = &Client::Handle_OP_LFPGetMatchesRequest; + ConnectedOpcodes[OP_LoadSpellSet] = &Client::Handle_OP_LoadSpellSet; + ConnectedOpcodes[OP_Logout] = &Client::Handle_OP_Logout; + ConnectedOpcodes[OP_LootItem] = &Client::Handle_OP_LootItem; + ConnectedOpcodes[OP_LootRequest] = &Client::Handle_OP_LootRequest; + ConnectedOpcodes[OP_ManaChange] = &Client::Handle_OP_ManaChange; + ConnectedOpcodes[OP_MemorizeSpell] = &Client::Handle_OP_MemorizeSpell; + ConnectedOpcodes[OP_Mend] = &Client::Handle_OP_Mend; + ConnectedOpcodes[OP_MercenaryCommand] = &Client::Handle_OP_MercenaryCommand; + ConnectedOpcodes[OP_MercenaryDataRequest] = &Client::Handle_OP_MercenaryDataRequest; + ConnectedOpcodes[OP_MercenaryDataUpdateRequest] = &Client::Handle_OP_MercenaryDataUpdateRequest; + ConnectedOpcodes[OP_MercenaryDismiss] = &Client::Handle_OP_MercenaryDismiss; + ConnectedOpcodes[OP_MercenaryHire] = &Client::Handle_OP_MercenaryHire; + ConnectedOpcodes[OP_MercenarySuspendRequest] = &Client::Handle_OP_MercenarySuspendRequest; + ConnectedOpcodes[OP_MercenaryTimerRequest] = &Client::Handle_OP_MercenaryTimerRequest; + ConnectedOpcodes[OP_MoveCoin] = &Client::Handle_OP_MoveCoin; + ConnectedOpcodes[OP_MoveItem] = &Client::Handle_OP_MoveItem; + ConnectedOpcodes[OP_OpenContainer] = &Client::Handle_OP_OpenContainer; + ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster; + ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory; + ConnectedOpcodes[OP_OpenTributeMaster] = &Client::Handle_OP_OpenTributeMaster; + ConnectedOpcodes[OP_PDeletePetition] = &Client::Handle_OP_PDeletePetition; + ConnectedOpcodes[OP_PetCommands] = &Client::Handle_OP_PetCommands; + ConnectedOpcodes[OP_Petition] = &Client::Handle_OP_Petition; + ConnectedOpcodes[OP_PetitionBug] = &Client::Handle_OP_PetitionBug; + ConnectedOpcodes[OP_PetitionCheckIn] = &Client::Handle_OP_PetitionCheckIn; + ConnectedOpcodes[OP_PetitionCheckout] = &Client::Handle_OP_PetitionCheckout; + ConnectedOpcodes[OP_PetitionDelete] = &Client::Handle_OP_PetitionDelete; + ConnectedOpcodes[OP_PetitionQue] = &Client::Handle_OP_PetitionQue; + ConnectedOpcodes[OP_PetitionRefresh] = &Client::Handle_OP_PetitionRefresh; + ConnectedOpcodes[OP_PetitionResolve] = &Client::Handle_OP_PetitionResolve; + ConnectedOpcodes[OP_PetitionUnCheckout] = &Client::Handle_OP_PetitionUnCheckout; + ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket; + ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse; + ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt; + ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA; + ConnectedOpcodes[OP_PVPLeaderBoardDetailsRequest] = &Client::Handle_OP_PVPLeaderBoardDetailsRequest; + ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest; + ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand; + ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq; + ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook; + ConnectedOpcodes[OP_RecipeAutoCombine] = &Client::Handle_OP_RecipeAutoCombine; + ConnectedOpcodes[OP_RecipeDetails] = &Client::Handle_OP_RecipeDetails; + ConnectedOpcodes[OP_RecipesFavorite] = &Client::Handle_OP_RecipesFavorite; + ConnectedOpcodes[OP_RecipesSearch] = &Client::Handle_OP_RecipesSearch; + ConnectedOpcodes[OP_ReloadUI] = &Client::Handle_OP_ReloadUI; + ConnectedOpcodes[OP_RemoveBlockedBuffs] = &Client::Handle_OP_RemoveBlockedBuffs; + ConnectedOpcodes[OP_Report] = &Client::Handle_OP_Report; + ConnectedOpcodes[OP_RequestDuel] = &Client::Handle_OP_RequestDuel; + ConnectedOpcodes[OP_RequestTitles] = &Client::Handle_OP_RequestTitles; + ConnectedOpcodes[OP_RespawnWindow] = &Client::Handle_OP_RespawnWindow; + ConnectedOpcodes[OP_Rewind] = &Client::Handle_OP_Rewind; + ConnectedOpcodes[OP_RezzAnswer] = &Client::Handle_OP_RezzAnswer; + ConnectedOpcodes[OP_Sacrifice] = &Client::Handle_OP_Sacrifice; + ConnectedOpcodes[OP_SafeFallSuccess] = &Client::Handle_OP_SafeFallSuccess; + ConnectedOpcodes[OP_SafePoint] = &Client::Handle_OP_SafePoint; + ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save; + ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq; + ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute; + ConnectedOpcodes[OP_SenseHeading] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps; + ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD; + ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode; + ConnectedOpcodes[OP_SetServerFilter] = &Client::Handle_OP_SetServerFilter; + ConnectedOpcodes[OP_SetStartCity] = &Client::Handle_OP_SetStartCity; + ConnectedOpcodes[OP_SetTitle] = &Client::Handle_OP_SetTitle; + ConnectedOpcodes[OP_Shielding] = &Client::Handle_OP_Shielding; + ConnectedOpcodes[OP_ShopEnd] = &Client::Handle_OP_ShopEnd; + ConnectedOpcodes[OP_ShopPlayerBuy] = &Client::Handle_OP_ShopPlayerBuy; + ConnectedOpcodes[OP_ShopPlayerSell] = &Client::Handle_OP_ShopPlayerSell; + ConnectedOpcodes[OP_ShopRequest] = &Client::Handle_OP_ShopRequest; + ConnectedOpcodes[OP_Sneak] = &Client::Handle_OP_Sneak; + ConnectedOpcodes[OP_SpawnAppearance] = &Client::Handle_OP_SpawnAppearance; + ConnectedOpcodes[OP_Split] = &Client::Handle_OP_Split; + ConnectedOpcodes[OP_Surname] = &Client::Handle_OP_Surname; + ConnectedOpcodes[OP_SwapSpell] = &Client::Handle_OP_SwapSpell; + ConnectedOpcodes[OP_TargetCommand] = &Client::Handle_OP_TargetCommand; + ConnectedOpcodes[OP_TargetMouse] = &Client::Handle_OP_TargetMouse; + ConnectedOpcodes[OP_TaskHistoryRequest] = &Client::Handle_OP_TaskHistoryRequest; + ConnectedOpcodes[OP_Taunt] = &Client::Handle_OP_Taunt; + ConnectedOpcodes[OP_TestBuff] = &Client::Handle_OP_TestBuff; + ConnectedOpcodes[OP_TGB] = &Client::Handle_OP_TGB; + ConnectedOpcodes[OP_Track] = &Client::Handle_OP_Track; + ConnectedOpcodes[OP_TrackTarget] = &Client::Handle_OP_TrackTarget; + ConnectedOpcodes[OP_TrackUnknown] = &Client::Handle_OP_TrackUnknown; + ConnectedOpcodes[OP_TradeAcceptClick] = &Client::Handle_OP_TradeAcceptClick; + ConnectedOpcodes[OP_TradeBusy] = &Client::Handle_OP_TradeBusy; + ConnectedOpcodes[OP_Trader] = &Client::Handle_OP_Trader; + ConnectedOpcodes[OP_TraderBuy] = &Client::Handle_OP_TraderBuy; + ConnectedOpcodes[OP_TradeRequest] = &Client::Handle_OP_TradeRequest; + ConnectedOpcodes[OP_TradeRequestAck] = &Client::Handle_OP_TradeRequestAck; + ConnectedOpcodes[OP_TraderShop] = &Client::Handle_OP_TraderShop; + ConnectedOpcodes[OP_TradeSkillCombine] = &Client::Handle_OP_TradeSkillCombine; + ConnectedOpcodes[OP_Translocate] = &Client::Handle_OP_Translocate; + ConnectedOpcodes[OP_TributeItem] = &Client::Handle_OP_TributeItem; + ConnectedOpcodes[OP_TributeMoney] = &Client::Handle_OP_TributeMoney; + ConnectedOpcodes[OP_TributeNPC] = &Client::Handle_OP_TributeNPC; + ConnectedOpcodes[OP_TributeToggle] = &Client::Handle_OP_TributeToggle; + ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate; + ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; + ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; + ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; + ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; + ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; + ConnectedOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; + ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp; + ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange; } -void ClearMappedOpcode(EmuOpcode op) { +void ClearMappedOpcode(EmuOpcode op) +{ if(op >= _maxEmuOpcode) return; @@ -400,6 +401,7 @@ void ClearMappedOpcode(EmuOpcode op) { } } +// client methods int Client::HandlePacket(const EQApplicationPacket *app) { if(is_log_enabled(CLIENT__NET_IN_TRACE)) { @@ -495,7 +497,805 @@ int Client::HandlePacket(const EQApplicationPacket *app) return(true); } -void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { +// Finish client connecting state +void Client::CompleteConnect() +{ + UpdateWho(); + client_state = CLIENT_CONNECTED; + + hpupdate_timer.Start(); + position_timer.Start(); + autosave_timer.Start(); + SetDuelTarget(0); + SetDueling(false); + + EnteringMessages(this); + LoadZoneFlags(); + + /* Sets GM Flag if needed & Sends Petition Queue */ + UpdateAdmin(false); + + if (IsInAGuild()){ + uint8 rank = GuildRank(); + if (GetClientVersion() >= EQClientRoF) + { + switch (rank) { + case 0: { rank = 5; break; } // GUILD_MEMBER 0 + case 1: { rank = 3; break; } // GUILD_OFFICER 1 + case 2: { rank = 1; break; } // GUILD_LEADER 2 + default: { break; } // GUILD_NONE + } + } + SendAppearancePacket(AT_GuildID, GuildID(), false); + SendAppearancePacket(AT_GuildRank, rank, false); + } + for (uint32 spellInt = 0; spellInt < MAX_PP_REF_SPELLBOOK; spellInt++) { + if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000) + m_pp.spell_book[spellInt] = 0xFFFFFFFF; + } + //SendAATable(); + + if (GetHideMe()) Message(13, "[GM] You are currently hidden to all clients"); + + uint32 raidid = database.GetRaidID(GetName()); + Raid *raid = nullptr; + if (raidid > 0){ + raid = entity_list.GetRaidByID(raidid); + if (!raid){ + raid = new Raid(raidid); + if (raid->GetID() != 0){ + entity_list.AddRaid(raid, raidid); + } + else + raid = nullptr; + } + if (raid){ + SetRaidGrouped(true); + raid->LearnMembers(); + raid->VerifyRaid(); + raid->GetRaidDetails(); + /* + Only leader should get this; send to all for now till + I figure out correct creation; can probably also send a no longer leader packet for non leaders + but not important for now. + */ + raid->SendRaidCreate(this); + raid->SendMakeLeaderPacketTo(raid->leadername, this); + raid->SendRaidAdd(GetName(), this); + raid->SendBulkRaid(this); + raid->SendGroupUpdate(this); + uint32 grpID = raid->GetGroup(GetName()); + if (grpID < 12){ + raid->SendRaidGroupRemove(GetName(), grpID); + raid->SendRaidGroupAdd(GetName(), grpID); + } + if (raid->IsLocked()) + raid->SendRaidLockTo(this); + } + } + + //bulk raid send in here eventually + + //reapply some buffs + uint32 buff_count = GetMaxTotalSlots(); + for (uint32 j1 = 0; j1 < buff_count; j1++) { + if (!IsValidSpell(buffs[j1].spellid)) + continue; + + const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; + + int NimbusEffect = GetNimbusEffect(buffs[j1].spellid); + if (NimbusEffect) { + if (!IsNimbusEffectActive(NimbusEffect)) + SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true); + } + + for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { + switch (spell.effectid[x1]) { + case SE_IllusionCopy: + case SE_Illusion: { + if (spell.base[x1] == -1) { + if (gender == 1) + gender = 0; + else if (gender == 0) + gender = 1; + SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); + } + else if (spell.base[x1] == -2) + { + if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) + SendIllusionPacket(GetRace(), GetGender(), spell.max[x1], spell.max[x1]); + } + else if (spell.max[x1] > 0) + { + SendIllusionPacket(spell.base[x1], 0xFF, spell.max[x1], spell.max[x1]); + } + else + { + SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); + } + switch (spell.base[x1]){ + case OGRE: + SendAppearancePacket(AT_Size, 9); + break; + case TROLL: + SendAppearancePacket(AT_Size, 8); + break; + case VAHSHIR: + case BARBARIAN: + SendAppearancePacket(AT_Size, 7); + break; + case HALF_ELF: + case WOOD_ELF: + case DARK_ELF: + case FROGLOK: + SendAppearancePacket(AT_Size, 5); + break; + case DWARF: + SendAppearancePacket(AT_Size, 4); + break; + case HALFLING: + case GNOME: + SendAppearancePacket(AT_Size, 3); + break; + default: + SendAppearancePacket(AT_Size, 6); + break; + } + break; + } + case SE_SummonHorse: { + SummonHorse(buffs[j1].spellid); + //hasmount = true; //this was false, is that the correct thing? + break; + } + case SE_Silence: + { + Silence(true); + break; + } + case SE_Amnesia: + { + Amnesia(true); + break; + } + case SE_DivineAura: + { + invulnerable = true; + break; + } + case SE_Invisibility2: + case SE_Invisibility: + { + invisible = true; + SendAppearancePacket(AT_Invis, 1); + break; + } + case SE_Levitate: + { + if (!zone->CanLevitate()) + { + if (!GetGM()) + { + SendAppearancePacket(AT_Levitate, 0); + BuffFadeByEffect(SE_Levitate); + Message(13, "You can't levitate in this zone."); + } + } + else{ + SendAppearancePacket(AT_Levitate, 2); + } + break; + } + case SE_InvisVsUndead2: + case SE_InvisVsUndead: + { + invisible_undead = true; + break; + } + case SE_InvisVsAnimals: + { + invisible_animals = true; + break; + } + case SE_AddMeleeProc: + case SE_WeaponProc: + { + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + case SE_DefensiveProc: + { + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + case SE_RangedProc: + { + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + } + } + } + + /* Sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead */ + entity_list.SendZoneAppearance(this); + /* Sends the Nimbus particle effects (up to 3) for any mob using them */ + entity_list.SendNimbusEffects(this); + + entity_list.SendUntargetable(this); + + int x; + for (x = 0; x < 8; x++) + SendWearChange(x); + Mob *pet = GetPet(); + if (pet != nullptr) { + for (x = 0; x < 8; x++) + pet->SendWearChange(x); + } + + entity_list.SendTraders(this); + + zoneinpacket_timer.Start(); + + if (GetPet()){ + GetPet()->SendPetBuffsToClient(); + } + + if (GetGroup()) + database.RefreshGroupFromDB(this); + + if (RuleB(TaskSystem, EnableTaskSystem)) + TaskPeriodic_Timer.Start(); + else + TaskPeriodic_Timer.Disable(); + + conn_state = ClientConnectFinished; + + //enforce some rules.. + if (!CanBeInZone()) { + _log(CLIENT__ERROR, "Kicking char from zone, not allowed here"); + GoToSafeCoords(database.GetZoneID("arena"), 0); + return; + } + + if (zone) + zone->weatherSend(); + + TotalKarma = database.GetKarma(AccountID()); + SendDisciplineTimers(); + + parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); + + /* This sub event is for if a player logs in for the first time since entering world. */ + if (firstlogon == 1){ + parse->EventPlayer(EVENT_CONNECT, this, "", 0); + /* QS: PlayerLogConnectDisconnect */ + if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ + std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); + } + } + + if (zone) { + if (zone->GetInstanceTimer()) { + uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime(); + uint32 day = (ttime / 86400000); + uint32 hour = (ttime / 3600000) % 24; + uint32 minute = (ttime / 60000) % 60; + uint32 second = (ttime / 1000) % 60; + if (day) { + Message(15, "%s(%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second); + } + else if (hour) { + Message(15, "%s(%u) will expire in %u hours, %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), hour, minute, second); + } + else if (minute) { + Message(15, "%s(%u) will expire in %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), minute, second); + } + else { + Message(15, "%s(%u) will expire in in %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), second); + } + } + } + + SendRewards(); + SendAltCurrencies(); + database.LoadAltCurrencyValues(CharacterID(), alternate_currency); + SendAlternateCurrencyValues(); + alternate_currency_loaded = true; + ProcessAlternateCurrencyQueue(); + + /* This needs to be set, this determines whether or not data was loaded properly before a save */ + client_data_loaded = true; + + CalcItemScale(); + DoItemEnterZone(); + + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) + GuildBanks->SendGuildBank(this); + + if (GetClientVersion() >= EQClientSoD) + entity_list.SendFindableNPCList(this); + + if (IsInAGuild()) { + SendGuildRanks(); + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); + guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID()); + } + + /** Request adventure info **/ + ServerPacket *pack = new ServerPacket(ServerOP_AdventureDataRequest, 64); + strcpy((char*)pack->pBuffer, GetName()); + worldserver.SendPacket(pack); + delete pack; + + if (IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { + EQApplicationPacket *outapp = MakeBuffsPacket(false); + CastToClient()->FastQueuePacket(&outapp); + } + + entity_list.RefreshClientXTargets(this); + + worldserver.RequestTellQueue(GetName()); +} + +void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z) +{ + //ToDo: Break warp down for special zones. Some zones have special teleportation pads or bad .map files which can trigger the detector without a legit zone request. + + switch (CheatType) + { + case MQWarp: //Some zones may still have issues. Database updates will eliminate most if not all problems. + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + Message(13, "Large warp detected."); + char hString[250]; + sprintf(hString, "/MQWarp with location %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + } + break; + case MQWarpShadowStep: + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + char *hString = nullptr; + MakeAnyLenString(&hString, "/MQWarp(SS) with location %.2f, %.2f, %.2f, the target was shadow step exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + } + break; + case MQWarpKnockBack: + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + char *hString = nullptr; + MakeAnyLenString(&hString, "/MQWarp(KB) with location %.2f, %.2f, %.2f, the target was Knock Back exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + } + break; + + case MQWarpLight: + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + if (RuleB(Zone, MarkMQWarpLT)) + { + char *hString = nullptr; + MakeAnyLenString(&hString, "/MQWarp(LT) with location %.2f, %.2f, %.2f, running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + } + } + break; + + case MQZone: + if (RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + { + char hString[250]; + sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f to %.2f %.2f %.2f", GetX(), GetY(), GetZ(), x, y, z); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + } + break; + case MQZoneUnknownDest: + if (RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + { + char hString[250]; + sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + } + break; + case MQGate: + if (RuleB(Zone, EnableMQGateDetector) && ((this->Admin() < RuleI(Zone, MQGateExemptStatus) || (RuleI(Zone, MQGateExemptStatus)) == -1))) { + Message(13, "Illegal gate request."); + char hString[250]; + sprintf(hString, "/MQGate used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + if (zone) + { + this->SetZone(this->GetZoneID(), zone->GetInstanceID()); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. + } + else + { + this->SetZone(this->GetZoneID(), 0); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. + + } + } + break; + case MQGhost: //Not currently implemented, but the framework is in place - just needs detection scenarios identified + if (RuleB(Zone, EnableMQGhostDetector) && ((this->Admin() < RuleI(Zone, MQGhostExemptStatus) || (RuleI(Zone, MQGhostExemptStatus)) == -1))) { + database.SetMQDetectionFlag(this->account_name, this->name, "/MQGhost", zone->GetShortName()); + } + break; + default: + char *hString = nullptr; + MakeAnyLenString(&hString, "Unhandled HackerDetection flag with location %.2f, %.2f, %.2f.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + break; + } +} + +// connecting opcode handlers +/* +void Client::Handle_Connect_0x3e33(const EQApplicationPacket *app) +{ +//OP_0x0380 = 0x642c +EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0380, sizeof(uint32)); // Dunno +QueuePacket(outapp); +safe_delete(outapp); +return; +} +*/ + +void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ApproveZone_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ApproveZone: Expected %i, Got %i", + sizeof(ApproveZone_Struct), app->size); + return; + } + ApproveZone_Struct* azone = (ApproveZone_Struct*)app->pBuffer; + azone->approve = 1; + QueuePacket(app); + return; +} + +void Client::Handle_Connect_OP_ClientError(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClientError_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClientError: Expected %i, Got %i", + sizeof(ClientError_Struct), app->size); + return; + } + // Client reporting error to server + ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; + LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); + LogFile->write(EQEMuLog::Error, "Error message: %s", error->message); + Message(13, error->message); +#if (EQDEBUG>=5) + DumpPacket(app); +#endif + return; +} + +void Client::Handle_Connect_OP_ClientReady(const EQApplicationPacket *app) +{ + conn_state = ClientReadyReceived; + + CompleteConnect(); + SendHPUpdate(); +} + +void Client::Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app) +{ + //Once we get this, the client thinks it is connected + //So give it the benefit of the doubt and move to connected + + Handle_Connect_OP_ClientReady(app); +} + +void Client::Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app) +{ + conn_state = ClientSpawnRequested; + + EQApplicationPacket* outapp = new EQApplicationPacket; + + // Send Zone Doors + if (entity_list.MakeDoorSpawnPacket(outapp, this)) + { + QueuePacket(outapp); + } + safe_delete(outapp); + + // Send Zone Objects + entity_list.SendZoneObjects(this); + SendZonePoints(); + // Live does this + outapp = new EQApplicationPacket(OP_SendAAStats, 0); + FastQueuePacket(&outapp); + + // Tell client they can continue we're done + outapp = new EQApplicationPacket(OP_ZoneServerReady, 0); + FastQueuePacket(&outapp); + outapp = new EQApplicationPacket(OP_SendExpZonein, 0); + FastQueuePacket(&outapp); + + if (GetClientVersion() >= EQClientRoF) + { + outapp = new EQApplicationPacket(OP_ClientReady, 0); + FastQueuePacket(&outapp); + } + + // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein + outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); + QueuePacket(outapp); + safe_delete(outapp); + + if (strncasecmp(zone->GetShortName(), "bazaar", 6) == 0) + SendBazaarWelcome(); + + conn_state = ZoneContentsSent; + + return; +} + +void Client::Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app) +{ + conn_state = NewZoneRequested; + + EQApplicationPacket* outapp; + + ///////////////////////////////////// + // New Zone Packet + outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + NewZone_Struct* nz = (NewZone_Struct*)outapp->pBuffer; + memcpy(outapp->pBuffer, &zone->newzone_data, sizeof(NewZone_Struct)); + strcpy(nz->char_name, m_pp.name); + + FastQueuePacket(&outapp); + + return; +} + +void Client::Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app) +{ + SendAATimers(); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAAStats, 0); + QueuePacket(outapp); + safe_delete(outapp); + return; +} + +void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app) +{ + SendAAList(); + return; +} + +void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) +{ + ////////////////////////////////////////////////////// + // Spawn Appearance Packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; + sa->type = AT_SpawnID; // Is 0x10 used to set the player id? + sa->parameter = GetID(); // Four bytes for this parameter... + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + // Inform the world about the client + outapp = new EQApplicationPacket(); + + CreateSpawnPacket(outapp); + outapp->priority = 6; + if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + if (GetPVP()) //force a PVP update until we fix the spawn struct + SendAppearancePacket(AT_PVP, GetPVP(), true, false); + + //Send AA Exp packet: + if (GetLevel() >= 51) + SendAAStats(); + + // Send exp packets + outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); + ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; + uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); + uint32 tmpxp2 = GetEXPForLevel(GetLevel()); + + // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) + if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { + float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); + eu->exp = (uint32)(330.0f * tmpxp); + outapp->priority = 6; + QueuePacket(outapp); + } + safe_delete(outapp); + + SendAATimers(); + + outapp = new EQApplicationPacket(OP_SendExpZonein, 0); + QueuePacket(outapp); + safe_delete(outapp); + + outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); + ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; + strcpy(zonesendname->name, m_pp.name); + strcpy(zonesendname->name2, m_pp.name); + zonesendname->unknown0 = 0x0A; + QueuePacket(outapp); + safe_delete(outapp); + + /* this is actually the guild MOTD + outapp = new EQApplicationPacket(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2)); + ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer; + strcpy(zonesendname2->name,m_pp.name); + QueuePacket(outapp); + safe_delete(outapp);*/ + + if (IsInAGuild()) { + SendGuildMembers(); + SendGuildURL(); + SendGuildChannel(); + SendGuildLFGuildStatus(); + } + SendLFGuildStatus(); + + //No idea why live sends this if even were not in a guild + SendGuildMOTD(); + SpawnMercOnZone(); + + return; +} + +void Client::Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app) +{ + SendGuildTributes(); + return; +} + +void Client::Handle_Connect_OP_SendTributes(const EQApplicationPacket *app) +{ + SendTributes(); + return; +} + +void Client::Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SetServerFilter_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_SetServerFilter"); + DumpPacket(app); + return; + } + SetServerFilter_Struct* filter = (SetServerFilter_Struct*)app->pBuffer; + ServerFilter(filter); + return; +} + +void Client::Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_Connect_OP_TGB(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_TGB: Expected %i, Got %i", + sizeof(uint32), app->size); + return; + } + OPTGB(app); + return; +} + +void Client::Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app) +{ + SendAATable(); +} + +void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) +{ + //not sure what these are supposed to mean to us. + return; +} + +void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) +{ + //This is a copy of SendExpZonein created for SoF due to packet order change + //This does not affect clients other than SoF + + ////////////////////////////////////////////////////// + // Spawn Appearance Packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; + sa->type = AT_SpawnID; // Is 0x10 used to set the player id? + sa->parameter = GetID(); // Four bytes for this parameter... + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + // Inform the world about the client + outapp = new EQApplicationPacket(); + + CreateSpawnPacket(outapp); + outapp->priority = 6; + if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + if (GetPVP()) //force a PVP update until we fix the spawn struct + SendAppearancePacket(AT_PVP, GetPVP(), true, false); + + //Send AA Exp packet: + if (GetLevel() >= 51) + SendAAStats(); + + // Send exp packets + outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); + ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; + uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); + uint32 tmpxp2 = GetEXPForLevel(GetLevel()); + + // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) + if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { + float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); + eu->exp = (uint32)(330.0f * tmpxp); + outapp->priority = 6; + QueuePacket(outapp); + } + safe_delete(outapp); + + SendAATimers(); + + // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein + outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); + QueuePacket(outapp); + safe_delete(outapp); + + outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); + ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; + strcpy(zonesendname->name, m_pp.name); + strcpy(zonesendname->name2, m_pp.name); + zonesendname->unknown0 = 0x0A; + QueuePacket(outapp); + safe_delete(outapp); + + if (IsInAGuild()) { + SendGuildMembers(); + SendGuildURL(); + SendGuildChannel(); + SendGuildLFGuildStatus(); + } + SendLFGuildStatus(); + + //No idea why live sends this if even were not in a guild + SendGuildMOTD(); + + SpawnMercOnZone(); + + return; +} + +void Client::Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0347, 0); + QueuePacket(outapp); + safe_delete(outapp); + return; +} + +void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) +{ if(app->size != sizeof(ClientZoneEntry_Struct)) return; ClientZoneEntry_Struct *cze = (ClientZoneEntry_Struct *) app->pBuffer; @@ -1029,448 +1829,2481 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { return; } -void Client::Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app) +// connected opcode handlers +void Client::Handle_0x0193(const EQApplicationPacket *app) { - if(app->size != sizeof(SetServerFilter_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_SetServerFilter"); + // Not sure what this opcode does. It started being sent when OP_ClientUpdate was + // changed to pump OP_ClientUpdate back out instead of OP_MobUpdate + // 2 bytes: 00 00 + + return; +} + +void Client::Handle_OP_AAAction(const EQApplicationPacket *app) +{ + mlog(AA__IN, "Received OP_AAAction"); + mpkt(AA__IN, app); + + if (app->size != sizeof(AA_Action)){ + printf("Error! OP_AAAction size didnt match!\n"); + return; + } + AA_Action* action = (AA_Action*)app->pBuffer; + + if (action->action == aaActionActivate) {//AA Hotkey + mlog(AA__MESSAGE, "Activating AA %d", action->ability); + ActivateAA((aaID)action->ability); + } + else if (action->action == aaActionBuy) { + BuyAA(action); + } + else if (action->action == aaActionDisableEXP){ //Turn Off AA Exp + if (m_epp.perAA > 0) + Message_StringID(0, AA_OFF); + m_epp.perAA = 0; + SendAAStats(); + } + else if (action->action == aaActionSetEXP) { + if (m_epp.perAA == 0) + Message_StringID(0, AA_ON); + m_epp.perAA = action->exp_value; + if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA = 0; // stop exploit with sanity check + // send an update + SendAAStats(); + SendAATable(); + } + else { + printf("Unknown AA action: %u %u 0x%x %d\n", action->action, action->ability, action->unknown08, action->exp_value); + } + + return; +} + +void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(AcceptNewTask_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AcceptNewTask expected %i got %i", + sizeof(AcceptNewTask_Struct), app->size); DumpPacket(app); return; } - SetServerFilter_Struct* filter=(SetServerFilter_Struct*)app->pBuffer; - ServerFilter(filter); - return; + AcceptNewTask_Struct *ant = (AcceptNewTask_Struct*)app->pBuffer; + + if (ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && taskstate) + taskstate->AcceptNewTask(this, ant->task_id, ant->task_master_id); } -void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app) +void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app) { - SendAAList(); - return; -} - -void Client::Handle_Connect_OP_SendTributes(const EQApplicationPacket *app) -{ - SendTributes(); - return; -} - -void Client::Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app) -{ - SendGuildTributes(); - return; -} - -void Client::Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app) -{ - SendAATimers(); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAAStats, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -//void Client::Handle_Connect_0x3e33(const EQApplicationPacket *app) -//{ -/*OP_0x0380 = 0x642c*/ - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0380, sizeof(uint32)); // Dunno - //QueuePacket(outapp); - //safe_delete(outapp); - //return; -//} - -void Client::Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app) -{ - conn_state = ClientSpawnRequested; - - EQApplicationPacket* outapp = new EQApplicationPacket; - - // Send Zone Doors - if(entity_list.MakeDoorSpawnPacket(outapp, this)) + if (app->size < sizeof(EntityId_Struct)) { - QueuePacket(outapp); + LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureInfoRequest had a packet that was too small."); + return; } - safe_delete(outapp); - - // Send Zone Objects - entity_list.SendZoneObjects(this); - SendZonePoints(); - // Live does this - outapp = new EQApplicationPacket(OP_SendAAStats, 0); - FastQueuePacket(&outapp); - - // Tell client they can continue we're done - outapp = new EQApplicationPacket(OP_ZoneServerReady, 0); - FastQueuePacket(&outapp); - outapp = new EQApplicationPacket(OP_SendExpZonein, 0); - FastQueuePacket(&outapp); - - if(GetClientVersion() >= EQClientRoF) + EntityId_Struct* ent = (EntityId_Struct*)app->pBuffer; + Mob * m = entity_list.GetMob(ent->entity_id); + if (m && m->IsNPC()) { - outapp = new EQApplicationPacket(OP_ClientReady, 0); + std::map::iterator it; + it = zone->adventure_entry_list_flavor.find(m->CastToNPC()->GetAdventureTemplate()); + if (it != zone->adventure_entry_list_flavor.end()) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (it->second.size() + 2)); + strn0cpy((char*)outapp->pBuffer, it->second.c_str(), it->second.size()); + FastQueuePacket(&outapp); + } + else + { + if (m->CastToNPC()->GetAdventureTemplate() != 0) + { + std::string text = "Choose your difficulty and preferred adventure type."; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (text.size() + 2)); + strn0cpy((char*)outapp->pBuffer, text.c_str(), text.size()); + FastQueuePacket(&outapp); + } + } + } +} + +void Client::Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app) +{ + if (app->size < sizeof(AdventureLeaderboardRequest_Struct)) + { + return; + } + + if (adventure_leaderboard_timer) + { + return; + } + + adventure_leaderboard_timer = new Timer(4000); + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, sizeof(ServerLeaderboardRequest_Struct)); + ServerLeaderboardRequest_Struct *lr = (ServerLeaderboardRequest_Struct*)pack->pBuffer; + strcpy(lr->player, GetName()); + + AdventureLeaderboardRequest_Struct *lrs = (AdventureLeaderboardRequest_Struct*)app->pBuffer; + lr->type = 1 + (lrs->theme * 2) + lrs->type; + worldserver.SendPacket(pack); + delete pack; +} + +void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Adventure_Purchase_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantPurchase expected:%i got:%i", sizeof(Adventure_Purchase_Struct), app->size); + return; + } + + Adventure_Purchase_Struct* aps = (Adventure_Purchase_Struct*)app->pBuffer; + /* + Get item apc->itemid (can check NPC if thats necessary), ldon point theme check only if theme is not 0 (I am not sure what 1-5 are though for themes) + if(ldon_points_available >= item ldonpointcost) + { + give item (67 00 00 00 for the packettype using opcode 0x02c5) + ldon_points_available -= ldonpointcost; + } + */ + uint32 merchantid = 0; + Mob* tmp = entity_list.GetMob(aps->npcid); + if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && + (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + + const Item_Struct* item = nullptr; + bool found = false; + std::list merlist = zone->merchanttable[merchantid]; + std::list::const_iterator itr; + + for (itr = merlist.begin(); itr != merlist.end(); ++itr){ + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tmp->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + if (item->ID == aps->itemid) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... + found = true; + break; + } + } + if (!item || !found) { + Message(13, "Error: The item you purchased does not exist!"); + return; + } + + if (aps->Type == LDoNMerchant) + { + if (m_pp.ldon_points_available < int32(item->LDoNPrice)) { + Message(13, "You cannot afford that item."); + return; + } + + if (item->LDoNTheme <= 16) + { + if (item->LDoNTheme & 16) + { + if (m_pp.ldon_points_tak < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in tak to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 8) + { + if (m_pp.ldon_points_ruj < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in ruj to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 4) + { + if (m_pp.ldon_points_mmc < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in mmc to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 2) + { + if (m_pp.ldon_points_mir < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in mir to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 1) + { + if (m_pp.ldon_points_guk < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in guk to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + } + } + else if (aps->Type == DiscordMerchant) + { + if (GetPVPPoints() < item->LDoNPrice) + { + Message(13, "You need at least %u PVP points to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (aps->Type == NorrathsKeepersMerchant) + { + if (GetRadiantCrystals() < item->LDoNPrice) + { + Message(13, "You need at least %u Radiant Crystals to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (aps->Type == DarkReignMerchant) + { + if (GetEbonCrystals() < item->LDoNPrice) + { + Message(13, "You need at least %u Ebon Crystals to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else + { + Message(13, "Unknown Adventure Merchant type."); + return; + } + + + if (CheckLoreConflict(item)) + { + Message(15, "You can only have one of a lore item."); + return; + } + + if (aps->Type == LDoNMerchant) + { + int32 requiredpts = (int32)item->LDoNPrice*-1; + + if (!UpdateLDoNPoints(requiredpts, 6)) + return; + } + else if (aps->Type == DiscordMerchant) + { + SetPVPPoints(GetPVPPoints() - (int32)item->LDoNPrice); + SendPVPStats(); + } + else if (aps->Type == NorrathsKeepersMerchant) + { + SetRadiantCrystals(GetRadiantCrystals() - (int32)item->LDoNPrice); + SendCrystalCounts(); + } + else if (aps->Type == DarkReignMerchant) + { + SetEbonCrystals(GetEbonCrystals() - (int32)item->LDoNPrice); + SendCrystalCounts(); + } + int16 charges = 1; + if (item->MaxCharges != 0) + charges = item->MaxCharges; + + ItemInst *inst = database.CreateItem(item, charges); + if (!AutoPutLootInInventory(*inst, true, true)) + { + PutLootInInventory(MainCursor, *inst); + } + Save(1); +} + +void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(AdventureMerchant_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantRequest expected:%i got:%i", sizeof(AdventureMerchant_Struct), app->size); + return; + } + std::stringstream ss(std::stringstream::in | std::stringstream::out); + + uint8 count = 0; + AdventureMerchant_Struct* eid = (AdventureMerchant_Struct*)app->pBuffer; + uint32 merchantid = 0; + + Mob* tmp = entity_list.GetMob(eid->entity_id); + if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && + (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + tmp->CastToNPC()->FaceTarget(this->CastToMob()); + + const Item_Struct *item = 0; + std::list merlist = zone->merchanttable[merchantid]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end() && count<255; ++itr){ + const MerchantList &ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tmp->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (item) + { + uint32 theme; + if (item->LDoNTheme > 16) + { + theme = 0; + } + else if (item->LDoNTheme & 16) + { + theme = 5; + } + else if (item->LDoNTheme & 8) + { + theme = 4; + } + else if (item->LDoNTheme & 4) + { + theme = 3; + } + else if (item->LDoNTheme & 2) + { + theme = 2; + } + else if (item->LDoNTheme & 1) + { + theme = 1; + } + else + { + theme = 0; + } + ss << "^" << item->Name << "|"; + ss << item->ID << "|"; + ss << item->LDoNPrice << "|"; + ss << theme << "|"; + ss << "0|"; + ss << "1|"; + ss << item->Races << "|"; + ss << item->Classes; + count++; + } + } + //Count + //^Item Name,Item ID,Cost in Points,Theme (0=none),0,1,races bit map,classes bitmap + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantResponse, ss.str().size() + 2); + outapp->pBuffer[0] = count; + strn0cpy((char*)&outapp->pBuffer[1], ss.str().c_str(), ss.str().size()); + FastQueuePacket(&outapp); +} + +void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Adventure_Sell_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_AdventureMerchantSell: got %u expected %u", + app->size, sizeof(Adventure_Sell_Struct)); + DumpPacket(app); + return; + } + + Adventure_Sell_Struct *ams_in = (Adventure_Sell_Struct*)app->pBuffer; + + Mob* vendor = entity_list.GetMob(ams_in->npcid); + if (vendor == 0 || !vendor->IsNPC() || ((vendor->GetClass() != ADVENTUREMERCHANT) && + (vendor->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (vendor->GetClass() != DARK_REIGN_MERCHANT))) + { + Message(13, "Vendor was not found."); + return; + } + + if (DistNoRoot(*vendor) > USE_NPC_RANGE2) + { + Message(13, "Vendor is out of range."); + return; + } + + uint32 itemid = GetItemIDAt(ams_in->slot); + + if (itemid == 0) + { + Message(13, "Found no item at that slot."); + return; + } + + const Item_Struct* item = database.GetItem(itemid); + ItemInst* inst = GetInv().GetItem(ams_in->slot); + if (!item || !inst){ + Message(13, "You seemed to have misplaced that item..."); + return; + } + + // Note that Lucy has ldonsold values of 4 and 5 for items sold by Norrath's Keepers and Dark Reign, whereas 13th Floor + // has ldonsold = 0 for these items, so some manual editing of the items DB will be required to support sell back of the + // items. + // + // The Merchant seems to have some other way of knowing whether he will accept the item, other than the ldonsold field, + // e.g. if you summon items 76036 and 76053 (good and evil versions of Spell: Ward Of Vengeance), if you are interacting + // with a Norrath's Keeper merchant and click on 76036 in your inventory, he says he will give you radiant crystals for + // it, but he will refuse for item 76053. + // + // Similarly, just giving a cloth cap an ldonsold value of 4 will not make the Merchant buy it. + // + // Note that the the Client will not allow you to sell anything back to a Discord merchant, so there is no need to handle + // that case here. + if (item->LDoNSold == 0) + { + Message(13, "The merchant does not want that item."); + return; + } + + if (item->LDoNPrice == 0) + { + Message(13, "The merchant does not want that item."); + return; + } + + int32 price = item->LDoNPrice * 70 / 100; + + if (price == 0) + { + Message(13, "The merchant does not want that item."); + return; + } + + if (RuleB(EventLog, RecordSellToMerchant)) + LogMerchant(this, vendor, ams_in->charges, price, item, false); + + if (!inst->IsStackable()) + { + DeleteItemInInventory(ams_in->slot, 0, false); + } + else + { + if (inst->GetCharges() < ams_in->charges) + { + ams_in->charges = inst->GetCharges(); + } + + if (ams_in->charges == 0) + { + Message(13, "Charge mismatch error."); + return; + } + + DeleteItemInInventory(ams_in->slot, ams_in->charges, false); + price *= ams_in->charges; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantSell, sizeof(Adventure_Sell_Struct)); + Adventure_Sell_Struct *ams = (Adventure_Sell_Struct*)outapp->pBuffer; + ams->slot = ams_in->slot; + ams->unknown000 = 1; + ams->npcid = ams->npcid; + ams->charges = ams_in->charges; + ams->sell_price = price; + FastQueuePacket(&outapp); + + switch (vendor->GetClass()) + { + case ADVENTUREMERCHANT: + { + UpdateLDoNPoints(price, 6); + break; + } + case NORRATHS_KEEPERS_MERCHANT: + { + SetRadiantCrystals(GetRadiantCrystals() + price); + break; + } + case DARK_REIGN_MERCHANT: + { + SetEbonCrystals(GetEbonCrystals() + price); + break; + } + + default: + break; + } + + Save(1); +} + +void Client::Handle_OP_AdventureRequest(const EQApplicationPacket *app) +{ + if (app->size < sizeof(AdventureRequest_Struct)) + { + LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureRequest had a packet that was too small."); + return; + } + + if (IsOnAdventure()) + { + return; + } + + if (!p_timers.Expired(&database, pTimerStartAdventureTimer, false)) + { + return; + } + + if (GetPendingAdventureRequest()) + { + return; + } + + AdventureRequest_Struct* ars = (AdventureRequest_Struct*)app->pBuffer; + uint8 group_members = 0; + Raid *r = nullptr; + Group *g = nullptr; + + if (IsRaidGrouped()) + { + r = GetRaid(); + group_members = r->RaidCount(); + } + else if (IsGrouped()) + { + g = GetGroup(); + group_members = g->GroupCount(); + } + else + { + return; + } + + if (group_members < RuleI(Adventure, MinNumberForGroup) || group_members > RuleI(Adventure, MaxNumberForGroup)) + { + return; + } + + Mob* m = entity_list.GetMob(ars->entity_id); + uint32 template_id = 0; + if (m && m->IsNPC()) + { + template_id = m->CastToNPC()->GetAdventureTemplate(); + } + else + { + return; + } + + ServerPacket *packet = new ServerPacket(ServerOP_AdventureRequest, sizeof(ServerAdventureRequest_Struct)+(64 * group_members)); + ServerAdventureRequest_Struct *sar = (ServerAdventureRequest_Struct*)packet->pBuffer; + sar->member_count = group_members; + sar->risk = ars->risk; + sar->type = ars->type; + sar->template_id = template_id; + strcpy(sar->leader, GetName()); + + if (IsRaidGrouped()) + { + int i = 0; + for (int x = 0; x < 72; ++x) + { + if (i == group_members) + { + break; + } + + const char *c_name = nullptr; + c_name = r->GetClientNameByIndex(x); + if (c_name) + { + memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct)+(64 * i)), c_name, strlen(c_name)); + ++i; + } + } + } + else + { + int i = 0; + for (int x = 0; x < 6; ++x) + { + if (i == group_members) + { + break; + } + + const char *c_name = nullptr; + c_name = g->GetClientNameByIndex(x); + if (c_name) + { + memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct)+(64 * i)), c_name, strlen(c_name)); + ++i; + } + } + } + + packet->Deflate(); + worldserver.SendPacket(packet); + delete packet; + p_timers.Start(pTimerStartAdventureTimer, 5); +} + +void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app) +{ + if (adventure_stats_timer) + { + return; + } + + adventure_stats_timer = new Timer(8000); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureStatsReply, sizeof(AdventureStats_Struct)); + AdventureStats_Struct *as = (AdventureStats_Struct*)outapp->pBuffer; + + if (database.GetAdventureStats(CharacterID(), as->success.guk, as->success.mir, as->success.mmc, as->success.ruj, + as->success.tak, as->failure.guk, as->failure.mir, as->failure.mmc, as->failure.ruj, as->failure.tak)) + { + as->failure.total = as->failure.guk + as->failure.mir + as->failure.mmc + as->failure.ruj + as->failure.tak; + as->success.total = as->success.guk + as->success.mir + as->success.mmc + as->success.ruj + as->success.tak; + m_pp.ldon_wins_guk = as->success.guk; + m_pp.ldon_wins_mir = as->success.mir; + m_pp.ldon_wins_mmc = as->success.mmc; + m_pp.ldon_wins_ruj = as->success.ruj; + m_pp.ldon_wins_tak = as->success.tak; + m_pp.ldon_losses_guk = as->failure.guk; + m_pp.ldon_losses_mir = as->failure.mir; + m_pp.ldon_losses_mmc = as->failure.mmc; + m_pp.ldon_losses_ruj = as->failure.ruj; + m_pp.ldon_losses_tak = as->failure.tak; + } + + FastQueuePacket(&outapp); +} + +void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32); + + NPC* tar = entity_list.GetNPCByID(*((uint32*)app->pBuffer)); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + std::list::iterator altc_iter = zone->AlternateCurrencies.begin(); + bool found = false; + while (altc_iter != zone->AlternateCurrencies.end()) { + if ((*altc_iter).id == alt_cur_id) { + found = true; + break; + } + ++altc_iter; + } + + if (!found) { + return; + } + + std::stringstream ss(std::stringstream::in | std::stringstream::out); + std::stringstream item_ss(std::stringstream::in | std::stringstream::out); + ss << alt_cur_id << "|1|" << alt_cur_id; + uint32 count = 0; + uint32 merchant_id = tar->MerchantType; + const Item_Struct *item = nullptr; + + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end() && count < 255; ++itr){ + const MerchantList &ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (item) + { + item_ss << "^" << item->Name << "|"; + item_ss << item->ID << "|"; + item_ss << ml.alt_currency_cost << "|"; + item_ss << "0|"; + item_ss << "1|"; + item_ss << item->Races << "|"; + item_ss << item->Classes; + count++; + } + } + + if (count > 0) { + ss << "|" << count << item_ss.str(); + } + else { + ss << "|0"; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencyMerchantReply, ss.str().length() + 1); + memcpy(outapp->pBuffer, ss.str().c_str(), ss.str().length()); FastQueuePacket(&outapp); } +} - // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein - outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); - QueuePacket(outapp); - safe_delete(outapp); +void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencyPurchase, app, AltCurrencyPurchaseItem_Struct); + AltCurrencyPurchaseItem_Struct *purchase = (AltCurrencyPurchaseItem_Struct*)app->pBuffer; + NPC* tar = entity_list.GetNPCByID(purchase->merchant_entity_id); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; - if(strncasecmp(zone->GetShortName(), "bazaar", 6) == 0) - SendBazaarWelcome(); + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } - conn_state = ZoneContentsSent; + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + const Item_Struct* item = nullptr; + uint32 cost = 0; + uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); + uint32 merchant_id = tar->MerchantType; + bool found = false; + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr) { + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + + if (item->ID == purchase->item_id) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... + cost = ml.alt_currency_cost; + found = true; + break; + } + } + + if (!item || !found) { + Message(13, "Error: The item you purchased does not exist!"); + return; + } + + if (cost > current_currency) { + Message(13, "You cannot afford that item right now."); + return; + } + + if (CheckLoreConflict(item)) + { + Message(15, "You can only have one of a lore item."); + return; + } + + /* QS: PlayerLogAlternateCurrencyTransactions :: Merchant Purchase */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Merchant Purchase :: Spent alt_currency_id:%i cost:%i for itemid:%i in zoneid:%i instid:%i", alt_cur_id, cost, item->ID, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + + AddAlternateCurrencyValue(alt_cur_id, -((int32)cost)); + int16 charges = 1; + if (item->MaxCharges != 0) + charges = item->MaxCharges; + + ItemInst *inst = database.CreateItem(item, charges); + if (!AutoPutLootInInventory(*inst, true, true)) + { + PutLootInInventory(MainCursor, *inst); + } + + Save(1); + } +} + +void Client::Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencyReclaim, app, AltCurrencyReclaim_Struct); + AltCurrencyReclaim_Struct *reclaim = (AltCurrencyReclaim_Struct*)app->pBuffer; + uint32 item_id = 0; + std::list::iterator iter = zone->AlternateCurrencies.begin(); + while (iter != zone->AlternateCurrencies.end()) { + if ((*iter).id == reclaim->currency_id) { + item_id = (*iter).item_id; + } + ++iter; + } + + if (item_id == 0) { + return; + } + + /* Item to Currency Storage */ + if (reclaim->reclaim_flag == 1) { + uint32 removed = NukeItem(item_id, invWhereWorn | invWherePersonal | invWhereCursor); + if (removed > 0) { + AddAlternateCurrencyValue(reclaim->currency_id, removed); + + /* QS: PlayerLogAlternateCurrencyTransactions :: Item to Currency */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Reclaim :: Item to Currency :: alt_currency_id:%i amount:%i to currency tab in zoneid:%i instid:%i", reclaim->currency_id, removed, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + } + } + /* Cursor to Item storage */ + else { + uint32 max_currency = GetAlternateCurrencyValue(reclaim->currency_id); + + /* If you input more than you have currency wise, just give the max of the currency you currently have */ + if (reclaim->count > max_currency) { + SummonItem(item_id, max_currency); + SetAlternateCurrencyValue(reclaim->currency_id, 0); + } + else { + SummonItem(item_id, reclaim->count, 0, 0, 0, 0, 0, false, MainCursor); + AddAlternateCurrencyValue(reclaim->currency_id, -((int32)reclaim->count)); + } + /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Reclaim :: Cursor to Item :: alt_currency_id:%i amount:-%i in zoneid:%i instid:%i", reclaim->currency_id, reclaim->count, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + } +} + +void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencySell, app, AltCurrencySellItem_Struct); + EQApplicationPacket *outapp = app->Copy(); + AltCurrencySellItem_Struct *sell = (AltCurrencySellItem_Struct*)outapp->pBuffer; + + NPC* tar = entity_list.GetNPCByID(sell->merchant_entity_id); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + ItemInst* inst = GetInv().GetItem(sell->slot_id); + if (!inst) { + return; + } + + if (!RuleB(Merchant, EnableAltCurrencySell)) { + return; + } + + const Item_Struct* item = nullptr; + uint32 cost = 0; + uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); + uint32 merchant_id = tar->MerchantType; + uint32 npc_id = tar->GetNPCTypeID(); + bool found = false; + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr) { + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + + if (item->ID == inst->GetItem()->ID) { + cost = ml.alt_currency_cost; + found = true; + break; + } + } + + if (!found) { + return; + } + + if (!inst->IsStackable()) + { + DeleteItemInInventory(sell->slot_id, 0, false); + } + else + { + if (inst->GetCharges() < sell->charges) + { + sell->charges = inst->GetCharges(); + } + + if (sell->charges == 0) + { + Message(13, "Charge mismatch error."); + return; + } + + DeleteItemInInventory(sell->slot_id, sell->charges, false); + cost *= sell->charges; + } + + sell->cost = cost; + + /* QS: PlayerLogAlternateCurrencyTransactions :: Sold to Merchant*/ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Sold to Merchant :: itemid:%u npcid:%u alt_currency_id:%u cost:%u in zoneid:%u instid:%i", item->ID, npc_id, alt_cur_id, cost, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + + FastQueuePacket(&outapp); + AddAlternateCurrencyValue(alt_cur_id, cost); + Save(1); + } +} + +void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencySellSelection, app, AltCurrencySelectItem_Struct); + + AltCurrencySelectItem_Struct *select = (AltCurrencySelectItem_Struct*)app->pBuffer; + NPC* tar = entity_list.GetNPCByID(select->merchant_entity_id); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + ItemInst *inst = m_inv.GetItem(select->slot_id); + if (!inst) { + return; + } + + const Item_Struct* item = nullptr; + uint32 cost = 0; + uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); + uint32 merchant_id = tar->MerchantType; + + if (RuleB(Merchant, EnableAltCurrencySell)) { + bool found = false; + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr) { + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + + if (item->ID == inst->GetItem()->ID) { + cost = ml.alt_currency_cost; + found = true; + break; + } + } + + if (!found) { + cost = 0; + } + } + else { + cost = 0; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencySellSelection, sizeof(AltCurrencySelectItemReply_Struct)); + AltCurrencySelectItemReply_Struct *reply = (AltCurrencySelectItemReply_Struct*)outapp->pBuffer; + reply->unknown004 = 0xFF; + reply->unknown005 = 0xFF; + reply->unknown006 = 0xFF; + reply->unknown007 = 0xFF; + strcpy(reply->item_name, inst->GetItem()->Name); + reply->cost = cost; + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_Animation(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Animation_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized " + "OP_Animation: got %d, expected %d", app->size, + sizeof(Animation_Struct)); + DumpPacket(app); + return; + } + + Animation_Struct *s = (Animation_Struct *)app->pBuffer; + + //might verify spawn ID, but it wouldent affect anything + + DoAnim(s->action, s->value); return; } -void Client::Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app) +void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) { - conn_state = NewZoneRequested; + if (app->size != sizeof(ApplyPoison_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ApplyPoison, size=%i, expected %i", app->size, sizeof(ApplyPoison_Struct)); + DumpPacket(app); + return; + } + uint32 ApplyPoisonSuccessResult = 0; + ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer; + const ItemInst* PrimaryWeapon = GetInv().GetItem(MainPrimary); + const ItemInst* SecondaryWeapon = GetInv().GetItem(MainSecondary); + const ItemInst* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot]; - EQApplicationPacket* outapp; + bool IsPoison = PoisonItemInstance && (PoisonItemInstance->GetItem()->ItemType == ItemTypePoison); - ///////////////////////////////////// - // New Zone Packet - outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - NewZone_Struct* nz = (NewZone_Struct*)outapp->pBuffer; - memcpy(outapp->pBuffer, &zone->newzone_data, sizeof(NewZone_Struct)); - strcpy(nz->char_name, m_pp.name); + if (!IsPoison) + { + mlog(SPELLS__CASTING_ERR, "Item used to cast spell effect from a poison item was missing from inventory slot %d " + "after casting, or is not a poison!", ApplyPoisonData->inventorySlot); + + Message(0, "Error: item not found for inventory slot #%i or is not a poison", ApplyPoisonData->inventorySlot); + } + else if (GetClass() == ROGUE) + { + if ((PrimaryWeapon && PrimaryWeapon->GetItem()->ItemType == ItemType1HPiercing) || + (SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == ItemType1HPiercing)) { + float SuccessChance = (GetSkill(SkillApplyPoison) + GetLevel()) / 400.0f; + double ChanceRoll = MakeRandomFloat(0, 1); + + CheckIncreaseSkill(SkillApplyPoison, nullptr, 10); + + if (ChanceRoll < SuccessChance) { + ApplyPoisonSuccessResult = 1; + // NOTE: Someone may want to tweak the chance to proc the poison effect that is added to the weapon here. + // My thinking was that DEX should be apart of the calculation. + AddProcToWeapon(PoisonItemInstance->GetItem()->Proc.Effect, false, (GetDEX() / 100) + 103); + } + + DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); + + LogFile->write(EQEMuLog::Debug, "Chance to Apply Poison was %f. Roll was %f. Result is %u.", SuccessChance, ChanceRoll, ApplyPoisonSuccessResult); + } + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApplyPoison, nullptr, sizeof(ApplyPoison_Struct)); + ApplyPoison_Struct* ApplyPoisonResult = (ApplyPoison_Struct*)outapp->pBuffer; + ApplyPoisonResult->success = ApplyPoisonSuccessResult; + ApplyPoisonResult->inventorySlot = ApplyPoisonData->inventorySlot; FastQueuePacket(&outapp); - - return; } -void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) +void Client::Handle_OP_Assist(const EQApplicationPacket *app) { - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if(GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if(GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float) ( (float) m_pp.exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 ); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); - } - safe_delete(outapp); - - SendAATimers(); - - outapp = new EQApplicationPacket(OP_SendExpZonein, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname=(ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name,m_pp.name); - strcpy(zonesendname->name2,m_pp.name); - zonesendname->unknown0=0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - /* this is actually the guild MOTD - outapp = new EQApplicationPacket(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2)); - ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer; - strcpy(zonesendname2->name,m_pp.name); - QueuePacket(outapp); - safe_delete(outapp);*/ - - if(IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); - SpawnMercOnZone(); - - return; -} - -void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) -{ - //This is a copy of SendExpZonein created for SoF due to packet order change - //This does not affect clients other than SoF - - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if(GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if(GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float) ( (float) m_pp.exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 ); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); - } - safe_delete(outapp); - - SendAATimers(); - - // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein - outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname=(ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name,m_pp.name); - strcpy(zonesendname->name2,m_pp.name); - zonesendname->unknown0=0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - if(IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); - - SpawnMercOnZone(); - - return; -} - -void Client::Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app) -{ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0347, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -void Client::Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) -{ - //not sure what these are supposed to mean to us. - return; -} - -void Client::Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app) -{ - //Once we get this, the client thinks it is connected - //So give it the benefit of the doubt and move to connected - - Handle_Connect_OP_ClientReady(app); -} - -void Client::Handle_Connect_OP_ClientReady(const EQApplicationPacket *app) -{ - conn_state = ClientReadyReceived; - - CompleteConnect(); - SendHPUpdate(); -} - -void Client::Handle_Connect_OP_ClientError(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientError_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClientError: Expected %i, Got %i", - sizeof(ClientError_Struct), app->size); + if (app->size != sizeof(EntityId_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Assist expected %i got %i", sizeof(EntityId_Struct), app->size); return; } - // Client reporting error to server - ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; - LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); - LogFile->write(EQEMuLog::Error, "Error message: %s", error->message); - Message(13, error->message); -#if (EQDEBUG>=5) - DumpPacket(app); -#endif + + EntityId_Struct* eid = (EntityId_Struct*)app->pBuffer; + Entity* entity = entity_list.GetID(eid->entity_id); + + EQApplicationPacket* outapp = app->Copy(); + eid = (EntityId_Struct*)outapp->pBuffer; + if (RuleB(Combat, AssistNoTargetSelf)) + eid->entity_id = GetID(); + if (entity && entity->IsMob()) { + Mob *assistee = entity->CastToMob(); + if (assistee->GetTarget()) { + Mob *new_target = assistee->GetTarget(); + if (new_target && (GetGM() || + Dist(*assistee) <= TARGETING_RANGE)) { + SetAssistExemption(true); + eid->entity_id = new_target->GetID(); + } + } + } + + FastQueuePacket(&outapp); return; } -void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app) +void Client::Handle_OP_AssistGroup(const EQApplicationPacket *app) { - if (app->size != sizeof(ApproveZone_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ApproveZone: Expected %i, Got %i", - sizeof(ApproveZone_Struct), app->size); + if (app->size != sizeof(EntityId_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AssistGroup expected %i got %i", sizeof(EntityId_Struct), app->size); return; } - ApproveZone_Struct* azone =(ApproveZone_Struct*)app->pBuffer; - azone->approve=1; QueuePacket(app); return; } -void Client::Handle_Connect_OP_TGB(const EQApplicationPacket *app) +void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) { - if (app->size != sizeof(uint32)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TGB: Expected %i, Got %i", - sizeof(uint32), app->size); + + // This packet is sent by the client when an Augment item information window is opened. + // We respond with an OP_ReadBook containing the type of distiller required to remove the augment. + // The OP_Augment packet includes a window parameter to determine which Item window in the UI the + // text is to be displayed in. out->type = 2 indicates the BookText_Struct contains item information. + // + + if (app->size != sizeof(AugmentInfo_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AugmentInfo expected %i got %i", + sizeof(AugmentInfo_Struct), app->size); + + DumpPacket(app); + return; } - OPTGB(app); - return; -} + AugmentInfo_Struct* AugInfo = (AugmentInfo_Struct*)app->pBuffer; -void Client::Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app) { - SendAATable(); -} + char *outstring = nullptr; -void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z) -{ //ToDo: Break warp down for special zones. Some zones have special teleportation pads or bad .map files which can trigger the detector without a legit zone request. - switch (CheatType) + const Item_Struct * item = database.GetItem(AugInfo->itemid); + + if (item) { - case MQWarp: //Some zones may still have issues. Database updates will eliminate most if not all problems. - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - Message(13, "Large warp detected."); - char hString[250]; - sprintf(hString, "/MQWarp with location %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - } - break; - case MQWarpShadowStep: - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(SS) with location %.2f, %.2f, %.2f, the target was shadow step exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - break; - case MQWarpKnockBack: - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(KB) with location %.2f, %.2f, %.2f, the target was Knock Back exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - break; + MakeAnyLenString(&outstring, "You must use the solvent %s to remove this augment safely.", item->Name); - case MQWarpLight: - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - if(RuleB(Zone, MarkMQWarpLT)) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(LT) with location %.2f, %.2f, %.2f, running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - } - break; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, strlen(outstring) + sizeof(BookText_Struct)); - case MQZone: - if(RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + BookText_Struct *out = (BookText_Struct *)outapp->pBuffer; + + out->window = AugInfo->window; + + out->type = 2; + + out->invslot = 0; + + strcpy(out->booktext, outstring); + + safe_delete_array(outstring); + + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(AugmentItem_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for AugmentItem_Struct: Expected: %i, Got: %i", + sizeof(AugmentItem_Struct), app->size); + return; + } + + // Delegate to tradeskill object to perform combine + AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; + bool deleteItems = false; + if (GetClientVersion() >= EQClientRoF) + { + ItemInst *itemOneToPush = nullptr, *itemTwoToPush = nullptr; + + //Message(15, "%i %i %i %i %i %i", in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); + + // Adding augment + if (in_augment->augment_action == 0) + { + ItemInst *tobe_auged, *auged_with = nullptr; + int8 slot = -1; + Inventory& user_inv = GetInv(); + + uint16 slot_id = in_augment->container_slot; + uint16 aug_slot_id = in_augment->augment_slot; + //Message(13, "%i AugSlot", aug_slot_id); + if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) { - char hString[250]; - sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f to %.2f %.2f %.2f", GetX(), GetY(), GetZ(), x, y, z); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + Message(13, "Error: Invalid Aug Index."); + return; } - break; - case MQZoneUnknownDest: - if(RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + + tobe_auged = user_inv.GetItem(slot_id); + auged_with = user_inv.GetItem(MainCursor); + + if (tobe_auged && auged_with) { - char hString[250]; - sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - } - break; - case MQGate: - if (RuleB(Zone, EnableMQGateDetector)&& ((this->Admin() < RuleI(Zone, MQGateExemptStatus) || (RuleI(Zone, MQGateExemptStatus)) == -1))) { - Message(13, "Illegal gate request."); - char hString[250]; - sprintf(hString, "/MQGate used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - if(zone) + if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) && + (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) { - this->SetZone(this->GetZoneID(), zone->GetInstanceID()); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. + tobe_auged->PutAugment(in_augment->augment_index, *auged_with); + + ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); + if (aug) { + std::vector args; + args.push_back(aug); + parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); + + args.assign(1, tobe_auged); + parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args); + } + else + { + Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index); + return; + } + + itemOneToPush = tobe_auged->Clone(); + // Must push items after the items in inventory are deleted - necessary due to lore items... + if (itemOneToPush) + { + DeleteItemInInventory(slot_id, 0, true); + DeleteItemInInventory(MainCursor, 0, true); + if (PutItemInInventory(slot_id, *itemOneToPush, true)) + { + //Message(13, "Sucessfully added an augment to your item!"); + return; + } + else + { + Message(13, "Error: No available slot for end result. Please free up some bag space."); + } + } + else + { + Message(13, "Error in cloning item for augment. Aborted."); + } + } else { - this->SetZone(this->GetZoneID(), 0); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. - + Message(13, "Error: No available slot for augment in that item."); } } - break; - case MQGhost: //Not currently implemented, but the framework is in place - just needs detection scenarios identified - if (RuleB(Zone, EnableMQGhostDetector) && ((this->Admin() < RuleI(Zone, MQGhostExemptStatus) || (RuleI(Zone, MQGhostExemptStatus)) == -1))) { - database.SetMQDetectionFlag(this->account_name, this->name, "/MQGhost", zone->GetShortName()); + } + else if (in_augment->augment_action == 1) + { + ItemInst *tobe_auged, *auged_with = nullptr; + int8 slot = -1; + Inventory& user_inv = GetInv(); + + uint16 slot_id = in_augment->container_slot; + uint16 aug_slot_id = in_augment->augment_slot; //it's actually solvent slot + if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) + { + Message(13, "Error: Invalid Aug Index."); + return; } - break; - default: - char *hString = nullptr; - MakeAnyLenString(&hString, "Unhandled HackerDetection flag with location %.2f, %.2f, %.2f.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - break; + + tobe_auged = user_inv.GetItem(slot_id); + auged_with = user_inv.GetItem(aug_slot_id); + + ItemInst *old_aug = nullptr; + if (!auged_with) + return; + const uint32 id = auged_with->GetID(); + ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); + if (aug) { + std::vector args; + args.push_back(aug); + parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); + + args.assign(1, tobe_auged); + + args.push_back(false); + + parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); + } + else + { + Message(13, "Error: Could not find augmentation at index %i. Aborting."); + return; + } + old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); + + itemOneToPush = tobe_auged->Clone(); + if (old_aug) + itemTwoToPush = old_aug->Clone(); + if (itemOneToPush && itemTwoToPush && auged_with) + { + DeleteItemInInventory(slot_id, 0, true); + DeleteItemInInventory(aug_slot_id, auged_with->IsStackable() ? 1 : 0, true); + if (!PutItemInInventory(slot_id, *itemOneToPush, true)) + { + Message(15, "Shouldn't happen, contact an admin!"); + } + + if (PutItemInInventory(MainCursor, *itemTwoToPush, true)) + { + //Message(15, "Successfully removed an augmentation!"); + } + } + } } + else + { + Object::HandleAugmentation(this, in_augment, m_tradeskill_object); + } + return; +} + +void Client::Handle_OP_AutoAttack(const EQApplicationPacket *app) +{ + if (app->size != 4) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_AutoAttack expected:4 got:%i", app->size); + return; + } + + if (app->pBuffer[0] == 0) + { + auto_attack = false; + if (IsAIControlled()) + return; + attack_timer.Disable(); + ranged_timer.Disable(); + attack_dw_timer.Disable(); + + aa_los_me.x = 0; + aa_los_me.y = 0; + aa_los_me.z = 0; + aa_los_me_heading = 0; + aa_los_them.x = 0; + aa_los_them.y = 0; + aa_los_them.z = 0; + aa_los_them_mob = nullptr; + } + else if (app->pBuffer[0] == 1) + { + auto_attack = true; + auto_fire = false; + if (IsAIControlled()) + return; + SetAttackTimer(); + + if (GetTarget()) + { + aa_los_them_mob = GetTarget(); + aa_los_me.x = GetX(); + aa_los_me.y = GetY(); + aa_los_me.z = GetZ(); + aa_los_me_heading = GetHeading(); + aa_los_them.x = aa_los_them_mob->GetX(); + aa_los_them.y = aa_los_them_mob->GetY(); + aa_los_them.z = aa_los_them_mob->GetZ(); + los_status = CheckLosFN(aa_los_them_mob); + los_status_facing = IsFacingMob(aa_los_them_mob); + } + else + { + aa_los_me.x = GetX(); + aa_los_me.y = GetY(); + aa_los_me.z = GetZ(); + aa_los_me_heading = GetHeading(); + aa_los_them.x = 0; + aa_los_them.y = 0; + aa_los_them.z = 0; + aa_los_them_mob = nullptr; + los_status = false; + los_status_facing = false; + } + } +} + +void Client::Handle_OP_AutoAttack2(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) +{ + if (app->size != sizeof(bool)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AutoFire expected %i got %i", sizeof(bool), app->size); + DumpPacket(app); + return; + } + bool *af = (bool*)app->pBuffer; + auto_fire = *af; + auto_attack = false; + SetAttackTimer(); +} + +void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) +{ + + // Although there are three different structs for OP_Bandolier, they are all the same size. + // + if (app->size != sizeof(BandolierCreate_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Bandolier expected %i got %i", + sizeof(BandolierCreate_Struct), app->size); + DumpPacket(app); + return; + } + + BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; + + switch (bs->action) { + case BandolierCreate: + CreateBandolier(app); + break; + case BandolierRemove: + RemoveBandolier(app); + break; + case BandolierSet: + SetBandolier(app); + break; + default: + LogFile->write(EQEMuLog::Debug, "Uknown Bandolier action %i", bs->action); + + } +} + +void Client::Handle_OP_BankerChange(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BankerChange_Struct) && app->size != 4) //Titanium only sends 4 Bytes for this + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BankerChange expected %i got %i", sizeof(BankerChange_Struct), app->size); + DumpPacket(app); + return; + } + + uint32 distance = 0; + NPC *banker = entity_list.GetClosestBanker(this, distance); + + if (!banker || distance > USE_NPC_RANGE2) + { + char *hacked_string = nullptr; + MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(money) but %s is non-existant or too far away (%u units).", + banker ? banker->GetName() : "UNKNOWN NPC", distance); + database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); + safe_delete_array(hacked_string); + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_BankerChange, nullptr, sizeof(BankerChange_Struct)); + BankerChange_Struct *bc = (BankerChange_Struct *)outapp->pBuffer; + + if (m_pp.platinum < 0) + m_pp.platinum = 0; + if (m_pp.gold < 0) + m_pp.gold = 0; + if (m_pp.silver < 0) + m_pp.silver = 0; + if (m_pp.copper < 0) + m_pp.copper = 0; + + if (m_pp.platinum_bank < 0) + m_pp.platinum_bank = 0; + if (m_pp.gold_bank < 0) + m_pp.gold_bank = 0; + if (m_pp.silver_bank < 0) + m_pp.silver_bank = 0; + if (m_pp.copper_bank < 0) + m_pp.copper_bank = 0; + + uint64 cp = static_cast(m_pp.copper) + + (static_cast(m_pp.silver) * 10) + + (static_cast(m_pp.gold) * 100) + + (static_cast(m_pp.platinum) * 1000); + + m_pp.copper = cp % 10; + cp /= 10; + m_pp.silver = cp % 10; + cp /= 10; + m_pp.gold = cp % 10; + cp /= 10; + m_pp.platinum = cp; + + cp = static_cast(m_pp.copper_bank) + + (static_cast(m_pp.silver_bank) * 10) + + (static_cast(m_pp.gold_bank) * 100) + + (static_cast(m_pp.platinum_bank) * 1000); + + m_pp.copper_bank = cp % 10; + cp /= 10; + m_pp.silver_bank = cp % 10; + cp /= 10; + m_pp.gold_bank = cp % 10; + cp /= 10; + m_pp.platinum_bank = cp; + + bc->copper = m_pp.copper; + bc->silver = m_pp.silver; + bc->gold = m_pp.gold; + bc->platinum = m_pp.platinum; + + bc->copper_bank = m_pp.copper_bank; + bc->silver_bank = m_pp.silver_bank; + bc->gold_bank = m_pp.gold_bank; + bc->platinum_bank = m_pp.platinum_bank; + + FastQueuePacket(&outapp); + + return; +} + +void Client::Handle_OP_Barter(const EQApplicationPacket *app) +{ + + if (app->size < 4) + { + LogFile->write(EQEMuLog::Debug, "OP_Barter packet below minimum expected size. The packet was %i bytes.", app->size); + DumpPacket(app); + return; + } + + char* Buf = (char *)app->pBuffer; + + // The first 4 bytes of the packet determine the action. A lot of Barter packets require the + // packet the client sent, sent back to it as an acknowledgement. + // + uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf); + + _pkt(TRADING__BARTER, app); + + switch (Action) + { + + case Barter_BuyerSearch: + { + BuyerItemSearch(app); + break; + } + + case Barter_SellerSearch: + { + BarterSearchRequest_Struct *bsr = (BarterSearchRequest_Struct*)app->pBuffer; + SendBuyerResults(bsr->SearchString, bsr->SearchID); + break; + } + + case Barter_BuyerModeOn: + { + if (!Trader) { + ToggleBuyerMode(true); + } + else { + Buf = (char *)app->pBuffer; + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerModeOff); + Message(13, "You cannot be a Trader and Buyer at the same time."); + } + QueuePacket(app); + break; + } + + case Barter_BuyerModeOff: + { + QueuePacket(app); + ToggleBuyerMode(false); + break; + } + + case Barter_BuyerItemUpdate: + { + UpdateBuyLine(app); + break; + } + + case Barter_BuyerItemRemove: + { + BuyerRemoveItem_Struct* bris = (BuyerRemoveItem_Struct*)app->pBuffer; + database.RemoveBuyLine(CharacterID(), bris->BuySlot); + QueuePacket(app); + break; + } + + case Barter_SellItem: + { + SellToBuyer(app); + break; + } + + case Barter_BuyerInspectBegin: + { + ShowBuyLines(app); + break; + } + + case Barter_BuyerInspectEnd: + { + BuyerInspectRequest_Struct* bir = (BuyerInspectRequest_Struct*)app->pBuffer; + Client *Buyer = entity_list.GetClientByID(bir->BuyerID); + if (Buyer) + Buyer->WithCustomer(0); + + break; + } + + case Barter_BarterItemInspect: + { + BarterItemSearchLinkRequest_Struct* bislr = (BarterItemSearchLinkRequest_Struct*)app->pBuffer; + + const Item_Struct* item = database.GetItem(bislr->ItemID); + + if (!item) + Message(13, "Error: This item does not exist!"); + else + { + ItemInst* inst = database.CreateItem(item); + if (inst) + { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + } + break; + } + + case Barter_Welcome: + { + SendBazaarWelcome(); + break; + } + + case Barter_WelcomeMessageUpdate: + { + BuyerWelcomeMessageUpdate_Struct* bwmu = (BuyerWelcomeMessageUpdate_Struct*)app->pBuffer; + SetBuyerWelcomeMessage(bwmu->WelcomeMessage); + break; + } + + case Barter_BuyerItemInspect: + { + BuyerItemSearchLinkRequest_Struct* bislr = (BuyerItemSearchLinkRequest_Struct*)app->pBuffer; + + const Item_Struct* item = database.GetItem(bislr->ItemID); + + if (!item) + Message(13, "Error: This item does not exist!"); + else + { + ItemInst* inst = database.CreateItem(item); + if (inst) + { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + } + break; + } + + case Barter_Unknown23: + { + // Sent by SoD client for no discernible reason. + break; + } + + default: + Message(13, "Unrecognised Barter action."); + _log(TRADING__BARTER, "Unrecognised Barter Action %i", Action); + + } +} + +void Client::Handle_OP_BazaarInspect(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BazaarInspect_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for BazaarInspect_Struct: Expected %i, Got %i", + sizeof(BazaarInspect_Struct), app->size); + return; + } + + BazaarInspect_Struct* bis = (BazaarInspect_Struct*)app->pBuffer; + + const Item_Struct* item = database.GetItem(bis->ItemID); + + if (!item) { + Message(13, "Error: This item does not exist!"); + return; + } + + ItemInst* inst = database.CreateItem(item); + + if (inst) { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + + return; +} + +void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app) +{ + _pkt(TRADING__PACKETS, app); + + if (app->size == sizeof(BazaarSearch_Struct)) { + + BazaarSearch_Struct* bss = (BazaarSearch_Struct*)app->pBuffer; + + this->SendBazaarResults(bss->TraderID, bss->Class_, bss->Race, bss->ItemStat, bss->Slot, bss->Type, + bss->Name, bss->MinPrice * 1000, bss->MaxPrice * 1000); + } + else if (app->size == sizeof(BazaarWelcome_Struct)) { + + BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)app->pBuffer; + + if (bws->Beginning.Action == BazaarWelcome) + SendBazaarWelcome(); + } + else if (app->size == sizeof(NewBazaarInspect_Struct)) { + + NewBazaarInspect_Struct *nbis = (NewBazaarInspect_Struct*)app->pBuffer; + + Client *c = entity_list.GetClientByName(nbis->Name); + if (c) { + ItemInst* inst = c->FindTraderItemBySerialNumber(nbis->SerialNumber); + if (inst) + SendItemPacket(0, inst, ItemPacketViewLink); + } + return; + } + else { + _log(TRADING__CLIENT, "Malformed BazaarSearch_Struct packe, Action %it received, ignoring..."); + LogFile->write(EQEMuLog::Error, "Malformed BazaarSearch_Struct packet received, ignoring...\n"); + } + + return; +} + +void Client::Handle_OP_Begging(const EQApplicationPacket *app) +{ + if (!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) + { + Message(13, "Ability recovery time not yet met."); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); + BeggingResponse_Struct *brs = (BeggingResponse_Struct*)outapp->pBuffer; + brs->Result = 0; + FastQueuePacket(&outapp); + return; + } + + if (!HasSkill(SkillBegging) || !GetTarget()) + return; + + if (GetTarget()->GetClass() == LDON_TREASURE) + return; + + p_timers.Start(pTimerBeggingPickPocket, 8); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); + BeggingResponse_Struct *brs = (BeggingResponse_Struct*)outapp->pBuffer; + + brs->Result = 0; // Default, Fail. + if (GetTarget() == this) + { + FastQueuePacket(&outapp); + return; + } + + int RandomChance = MakeRandomInt(0, 100); + + int ChanceToAttack = 0; + + if (GetLevel() > GetTarget()->GetLevel()) + ChanceToAttack = MakeRandomInt(0, 15); + else + ChanceToAttack = MakeRandomInt(((this->GetTarget()->GetLevel() - this->GetLevel()) * 10) - 5, ((this->GetTarget()->GetLevel() - this->GetLevel()) * 10)); + + if (ChanceToAttack < 0) + ChanceToAttack = -ChanceToAttack; + + if (RandomChance < ChanceToAttack) + { + GetTarget()->Attack(this); + QueuePacket(outapp); + safe_delete(outapp); + return; + } + + uint16 CurrentSkill = GetSkill(SkillBegging); + + float ChanceToBeg = ((float)(CurrentSkill / 700.0f) + 0.15f) * 100; + + if (RandomChance < ChanceToBeg) + { + brs->Amount = MakeRandomInt(1, 10); + // This needs some work to determine how much money they can beg, based on skill level etc. + if (CurrentSkill < 50) + { + brs->Result = 4; // Copper + AddMoneyToPP(brs->Amount, false); + } + else + { + brs->Result = 3; // Silver + AddMoneyToPP(brs->Amount * 10, false); + } + + } + QueuePacket(outapp); + safe_delete(outapp); + CheckIncreaseSkill(SkillBegging, nullptr, -10); +} + +void Client::Handle_OP_Bind_Wound(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BindWound_Struct)){ + LogFile->write(EQEMuLog::Error, "Size mismatch for Bind wound packet"); + DumpPacket(app); + } + BindWound_Struct* bind_in = (BindWound_Struct*)app->pBuffer; + Mob* bindmob = entity_list.GetMob(bind_in->to); + if (!bindmob){ + LogFile->write(EQEMuLog::Error, "Bindwound on non-exsistant mob from %s", this->GetName()); + } + else { + LogFile->write(EQEMuLog::Debug, "BindWound in: to:\'%s\' from=\'%s\'", bindmob->GetName(), GetName()); + BindWound(bindmob, true); + } + return; +} + +void Client::Handle_OP_BlockedBuffs(const EQApplicationPacket *app) +{ + if (!RuleB(Spells, EnableBlockedBuffs)) + return; + + if (app->size != sizeof(BlockedBuffs_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BlockedBuffs expected %i got %i", + sizeof(BlockedBuffs_Struct), app->size); + + DumpPacket(app); + + return; + } + + std::set::iterator Iterator; + + BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; + + std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; + + if (bbs->Initialise == 1) + { + BlockedBuffs->clear(); + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + { + if ((bbs->SpellID[i] > 0) && IsBeneficialSpell(bbs->SpellID[i])) + { + if (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end()) + BlockedBuffs->insert(bbs->SpellID[i]); + } + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); + + BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + obbs->SpellID[i] = -1; + + obbs->Pet = bbs->Pet; + obbs->Initialise = 1; + obbs->Flags = 0x54; + obbs->Count = BlockedBuffs->size(); + + unsigned int Element = 0; + + Iterator = BlockedBuffs->begin(); + + while (Iterator != BlockedBuffs->end()) + { + obbs->SpellID[Element++] = (*Iterator); + ++Iterator; + } + + FastQueuePacket(&outapp); + return; + } + + if ((bbs->Initialise == 0) && (bbs->Count > 0)) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); + + BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + obbs->SpellID[i] = -1; + + obbs->Pet = bbs->Pet; + obbs->Initialise = 0; + obbs->Flags = 0x54; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + { + if (!IsBeneficialSpell(bbs->SpellID[i])) + continue; + + if ((BlockedBuffs->size() < BLOCKED_BUFF_COUNT) && (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end())) + BlockedBuffs->insert(bbs->SpellID[i]); + } + obbs->Count = BlockedBuffs->size(); + + Iterator = BlockedBuffs->begin(); + + unsigned int Element = 0; + + while (Iterator != BlockedBuffs->end()) + { + obbs->SpellID[Element++] = (*Iterator); + ++Iterator; + } + + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_BoardBoat(const EQApplicationPacket *app) +{ + + if (app->size <= 5) + return; + + char *boatname; + boatname = new char[app->size - 3]; + memset(boatname, 0, app->size - 3); + memcpy(boatname, app->pBuffer, app->size - 4); + + Mob* boat = entity_list.GetMob(boatname); + if (boat) + this->BoatID = boat->GetID(); // set the client's BoatID to show that it's on this boat + safe_delete_array(boatname); + return; +} + +void Client::Handle_OP_Buff(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SpellBuffFade_Struct)) + { + LogFile->write(EQEMuLog::Error, "Size mismatch in OP_Buff. expected %i got %i", sizeof(SpellBuffFade_Struct), app->size); + DumpPacket(app); + return; + } + + SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*)app->pBuffer; + uint32 spid = sbf->spellid; + mlog(SPELLS__BUFFS, "Client requested that buff with spell id %d be canceled.", spid); + + //something about IsDetrimentalSpell() crashes this portion of code.. + //tbh we shouldn't use it anyway since this is a simple red vs blue buff check and + //isdetrimentalspell() is much more complex + if (spid == 0xFFFF || (IsValidSpell(spid) && (spells[spid].goodEffect == 0))) + QueuePacket(app); + else + BuffFadeBySpellID(spid); + + return; +} + +void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) +{ + // In SoD, this is used for clicking off Pet Buffs only. In Underfoot, it is used both for Client and Pets + // The payload contains buffslot and EntityID only, so we must check if the EntityID is ours or our pets. + // + VERIFY_PACKET_LENGTH(OP_BuffRemoveRequest, app, BuffRemoveRequest_Struct); + + BuffRemoveRequest_Struct *brrs = (BuffRemoveRequest_Struct*)app->pBuffer; + + Mob *m = nullptr; + + if (brrs->EntityID == GetID()) + m = this; + else if (brrs->EntityID == GetPetID()) + m = GetPet(); + + if (!m) + return; + + if (brrs->SlotID > (uint32)m->GetMaxTotalSlots()) + return; + + uint16 SpellID = m->GetSpellIDFromSlot(brrs->SlotID); + + if (SpellID && IsBeneficialSpell(SpellID)) + m->BuffFadeBySlot(brrs->SlotID, true); +} + +void Client::Handle_OP_Bug(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BugStruct)) + printf("Wrong size of BugStruct got %d expected %zu!\n", app->size, sizeof(BugStruct)); + else{ + BugStruct* bug = (BugStruct*)app->pBuffer; + database.UpdateBug(bug); + } + return; +} + +void Client::Handle_OP_Camp(const EQApplicationPacket *app) +{ +#ifdef BOTS + // This block is necessary to clean up any bot objects owned by a Client + Bot::BotHealRotationsClear(this); + Bot::BotOrderCampAll(this); +#endif + if (IsLFP()) + worldserver.StopLFP(CharacterID()); + + if (GetGM()) + { + OnDisconnect(true); + return; + } + camp_timer.Start(29000, true); + return; +} + +void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(CancelTask_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_CancelTask expected %i got %i", + sizeof(CancelTask_Struct), app->size); + DumpPacket(app); + return; + } + CancelTask_Struct *cts = (CancelTask_Struct*)app->pBuffer; + + if (RuleB(TaskSystem, EnableTaskSystem) && taskstate) + taskstate->CancelTask(this, cts->SequenceNumber); +} + +void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CancelTrade_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_CancelTrade, size=%i, expected %i", app->size, sizeof(CancelTrade_Struct)); + return; + } + Mob* with = trade->With(); + if (with && with->IsClient()) { + CancelTrade_Struct* msg = (CancelTrade_Struct*)app->pBuffer; + + // Forward cancel packet to other client + msg->fromid = with->GetID(); + //msg->action = 1; + + with->CastToClient()->QueuePacket(app); + + // Put trade items/cash back into inventory + FinishTrade(this); + trade->Reset(); + } + else if (with){ + CancelTrade_Struct* msg = (CancelTrade_Struct*)app->pBuffer; + msg->fromid = with->GetID(); + QueuePacket(app); + FinishTrade(this); + trade->Reset(); + } + EQApplicationPacket end_trade1(OP_FinishWindow, 0); + QueuePacket(&end_trade1); + + EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + QueuePacket(&end_trade2); + return; +} + +void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CastSpell_Struct)) { + std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl; + return; + } + if (IsAIControlled()) { + this->Message_StringID(13, NOT_IN_CONTROL); + //Message(13, "You cant cast right now, you arent in control of yourself!"); + return; + } + + CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer; + +#ifdef _EQDEBUG + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[0], castspell->cs_unknown[0]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[1], castspell->cs_unknown[1]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[2], castspell->cs_unknown[2]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[3], castspell->cs_unknown[3]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %u", &castspell->cs_unknown, *(uint32*)castspell->cs_unknown); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %i", &castspell->cs_unknown, *(uint32*)castspell->cs_unknown); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %u %u", &castspell->cs_unknown, *(uint16*)castspell->cs_unknown, *(uint16*)castspell->cs_unknown + sizeof(uint16)); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %i %i", &castspell->cs_unknown, *(uint16*)castspell->cs_unknown, *(uint16*)castspell->cs_unknown + sizeof(uint16)); +#endif + LogFile->write(EQEMuLog::Debug, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot); + + std::cout << "OP_CastSpell " << castspell->slot << " spell " << castspell->spell_id << " inventory slot " << castspell->inventoryslot << "\n" << std::endl; + + /* Memorized Spell */ + if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id){ + + uint16 spell_to_cast = 0; + if (castspell->slot < MAX_PP_MEMSPELL) { + spell_to_cast = m_pp.mem_spells[castspell->slot]; + if (spell_to_cast != castspell->spell_id) { + InterruptSpell(castspell->spell_id); //CHEATER!!! + return; + } + } + else if (castspell->slot >= MAX_PP_MEMSPELL) { + InterruptSpell(); + return; + } + + CastSpell(spell_to_cast, castspell->target_id, castspell->slot); + } + /* Spell Slot or Potion Belt Slot */ + else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast + { + //discipline, using the item spell slot + if (castspell->inventoryslot == 0xFFFFFFFF) { + if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { + LogFile->write(EQEMuLog::Debug, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); + InterruptSpell(castspell->spell_id); + } + return; + } + else if ((castspell->inventoryslot <= EmuConstants::GENERAL_END) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check + { + const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field + //bool cancast = true; + if (inst && inst->IsType(ItemClassCommon)) + { + const Item_Struct* item = inst->GetItem(); + if (item->Click.Effect != (uint32)castspell->spell_id) + { + database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, tried to cast a different spell.", zone->GetShortName()); + InterruptSpell(castspell->spell_id); //CHEATER!! + return; + } + + if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) + { + if (item->Click.Level2 > 0) + { + if (GetLevel() >= item->Click.Level2) + { + ItemInst* p_inst = (ItemInst*)inst; + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); + + if (i == 0) { + CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); + } + else { + InterruptSpell(castspell->spell_id); + return; + } + } + else + { + database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, did not meet req level.", zone->GetShortName()); + Message(0, "Error: level not high enough.", castspell->inventoryslot); + InterruptSpell(castspell->spell_id); + } + } + else + { + ItemInst* p_inst = (ItemInst*)inst; + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); + + if (i == 0) { + CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); + } + else { + InterruptSpell(castspell->spell_id); + return; + } + } + } + else + { + Message(0, "Error: unknown item->Click.Type (0x%02x)", item->Click.Type); + } + } + else + { + Message(0, "Error: item not found in inventory slot #%i", castspell->inventoryslot); + InterruptSpell(castspell->spell_id); + } + } + else + { + Message(0, "Error: castspell->inventoryslot >= %i (0x%04x)", MainCursor, castspell->inventoryslot); + InterruptSpell(castspell->spell_id); + } + } + /* Discipline */ + else if (castspell->slot == DISCIPLINE_SPELL_SLOT) { + if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { + printf("Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); + InterruptSpell(castspell->spell_id); + return; + } + } + /* ABILITY cast (LoH and Harm Touch) */ + else if (castspell->slot == ABILITY_SPELL_SLOT) { + uint16 spell_to_cast = 0; + + if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) { + if (!p_timers.Expired(&database, pTimerLayHands)) { + Message(13, "Ability recovery time not yet met."); + InterruptSpell(castspell->spell_id); + return; + } + spell_to_cast = SPELL_LAY_ON_HANDS; + p_timers.Start(pTimerLayHands, LayOnHandsReuseTime); + } + else if ((castspell->spell_id == SPELL_HARM_TOUCH + || castspell->spell_id == SPELL_HARM_TOUCH2) && GetClass() == SHADOWKNIGHT) { + if (!p_timers.Expired(&database, pTimerHarmTouch)) { + Message(13, "Ability recovery time not yet met."); + InterruptSpell(castspell->spell_id); + return; + } + + // determine which version of HT we are casting based on level + if (GetLevel() < 40) + spell_to_cast = SPELL_HARM_TOUCH; + else + spell_to_cast = SPELL_HARM_TOUCH2; + + p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); + } + + if (spell_to_cast > 0) // if we've matched LoH or HT, cast now + CastSpell(spell_to_cast, castspell->target_id, castspell->slot); + } + return; +} + +void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) +{ + ChannelMessage_Struct* cm = (ChannelMessage_Struct*)app->pBuffer; + + if (app->size < sizeof(ChannelMessage_Struct)) { + std::cout << "Wrong size " << app->size << ", should be " << sizeof(ChannelMessage_Struct) << "+ on 0x" << std::hex << std::setfill('0') << std::setw(4) << app->GetOpcode() << std::dec << std::endl; + return; + } + if (IsAIControlled()) { + Message(13, "You try to speak but cant move your mouth!"); + return; + } + + ChannelMessageReceived(cm->chan_num, cm->language, cm->skill_in_language, cm->message, cm->targetname); + return; +} + +void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app) +{ + if (!RuleB(Spells, EnableBlockedBuffs)) + return; + + if (app->size != 1) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearBlockedBuffs expected 1 got %i", app->size); + + DumpPacket(app); + + return; + } + + bool Pet = app->pBuffer[0]; + + if (Pet) + PetBlockedBuffs.clear(); + else + PlayerBlockedBuffs.clear(); + + QueuePacket(app); +} + +void Client::Handle_OP_ClearNPCMarks(const EQApplicationPacket *app) +{ + + if (app->size != 0) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearNPCMarks expected 0 got %i", + app->size); + + DumpPacket(app); + + return; + } + + Group *g = GetGroup(); + + if (g) + g->ClearAllNPCMarks(); +} + +void Client::Handle_OP_ClearSurname(const EQApplicationPacket *app) +{ + ChangeLastName(""); +} + +void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClickDoor_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ClickDoor, size=%i, expected %i", app->size, sizeof(ClickDoor_Struct)); + return; + } + ClickDoor_Struct* cd = (ClickDoor_Struct*)app->pBuffer; + Doors* currentdoor = entity_list.FindDoor(cd->doorid); + if (!currentdoor) + { + Message(0, "Unable to find door, please notify a GM (DoorID: %i).", cd->doorid); + return; + } + + char buf[20]; + snprintf(buf, 19, "%u", cd->doorid); + buf[19] = '\0'; + std::vector args; + args.push_back(currentdoor); + parse->EventPlayer(EVENT_CLICK_DOOR, this, buf, 0, &args); + + currentdoor->HandleClick(this, 0); + return; +} + +void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClickObject_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on ClickObject_Struct: Expected %i, Got %i", + sizeof(ClickObject_Struct), app->size); + return; + } + + ClickObject_Struct* click_object = (ClickObject_Struct*)app->pBuffer; + Entity* entity = entity_list.GetID(click_object->drop_id); + if (entity && entity->IsObject()) { + Object* object = entity->CastToObject(); + + object->HandleClick(this, click_object); + + std::vector args; + args.push_back(object); + + char buf[10]; + snprintf(buf, 9, "%u", click_object->drop_id); + buf[9] = '\0'; + parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, 0, &args); + } + + // Observed in RoF after OP_ClickObjectAction: + //EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + //QueuePacket(&end_trade2); + return; +} + +void Client::Handle_OP_ClickObjectAction(const EQApplicationPacket *app) +{ + if (app->size == 0) { + // RoF sends this packet 0 sized when switching from auto-combine to experiment windows. + // Not completely sure if 0 sized is for this or for closing objects as commented out below + EQApplicationPacket end_trade1(OP_FinishWindow, 0); + QueuePacket(&end_trade1); + + EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + QueuePacket(&end_trade2); + + return; + + // RoF sends a 0 sized packet for closing objects + /* + Object* object = GetTradeskillObject(); + if (object) { + object->CastToObject()->Close(); + } + */ + } + else + { + if (app->size != sizeof(ClickObjectAction_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClickObjectAction: Expected %i, Got %i", + sizeof(ClickObjectAction_Struct), app->size); + return; + } + + ClickObjectAction_Struct* oos = (ClickObjectAction_Struct*)app->pBuffer; + Entity* entity = entity_list.GetEntityObject(oos->drop_id); + if (entity && entity->IsObject()) { + Object* object = entity->CastToObject(); + if (oos->open == 0) { + object->Close(); + } + else { + LogFile->write(EQEMuLog::Error, "Unsupported action %d in OP_ClickObjectAction", oos->open); + } + } + else { + LogFile->write(EQEMuLog::Error, "Invalid object %d in OP_ClickObjectAction", oos->drop_id); + } + } + + SetTradeskillObject(nullptr); + + EQApplicationPacket end_trade1(OP_FinishWindow, 0); + QueuePacket(&end_trade1); + + EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + QueuePacket(&end_trade2); + return; +} + +void Client::Handle_OP_ClientError(const EQApplicationPacket *app) +{ + ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; + LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); + LogFile->write(EQEMuLog::Error, "Error message:%s", error->message); + return; +} + +void Client::Handle_OP_ClientTimeStamp(const EQApplicationPacket *app) +{ + return; } void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) @@ -1754,69 +4587,38 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) return; } -void Client::Handle_OP_AutoAttack(const EQApplicationPacket *app) +/* +void Client::Handle_OP_CloseContainer(const EQApplicationPacket *app) { - if (app->size != 4) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AutoAttack expected:4 got:%i", app->size); - return; - } - - if (app->pBuffer[0] == 0) - { - auto_attack = false; - if (IsAIControlled()) - return; - attack_timer.Disable(); - ranged_timer.Disable(); - attack_dw_timer.Disable(); - - aa_los_me.x = 0; - aa_los_me.y = 0; - aa_los_me.z = 0; - aa_los_me_heading = 0; - aa_los_them.x = 0; - aa_los_them.y = 0; - aa_los_them.z = 0; - aa_los_them_mob = nullptr; - } - else if (app->pBuffer[0] == 1) - { - auto_attack = true; - auto_fire = false; - if (IsAIControlled()) - return; - SetAttackTimer(); - - if(GetTarget()) - { - aa_los_them_mob = GetTarget(); - aa_los_me.x = GetX(); - aa_los_me.y = GetY(); - aa_los_me.z = GetZ(); - aa_los_me_heading = GetHeading(); - aa_los_them.x = aa_los_them_mob->GetX(); - aa_los_them.y = aa_los_them_mob->GetY(); - aa_los_them.z = aa_los_them_mob->GetZ(); - los_status = CheckLosFN(aa_los_them_mob); - los_status_facing = IsFacingMob(aa_los_them_mob); - } - else - { - aa_los_me.x = GetX(); - aa_los_me.y = GetY(); - aa_los_me.z = GetZ(); - aa_los_me_heading = GetHeading(); - aa_los_them.x = 0; - aa_los_them.y = 0; - aa_los_them.z = 0; - aa_los_them_mob = nullptr; - los_status = false; - los_status_facing = false; - } - } +if (app->size != sizeof(CloseContainer_Struct)) { +LogFile->write(EQEMuLog::Error, "Invalid size on CloseContainer_Struct: Expected %i, Got %i", +sizeof(CloseContainer_Struct), app->size); +return; } -void Client::Handle_OP_AutoAttack2(const EQApplicationPacket *app) +SetTradeskillObject(nullptr); + +ClickObjectAck_Struct* oos = (ClickObjectAck_Struct*)app->pBuffer; +Entity* entity = entity_list.GetEntityObject(oos->drop_id); +if (entity && entity->IsObject()) { +Object* object = entity->CastToObject(); +object->Close(); +} +return; +} +*/ + +void Client::Handle_OP_CombatAbility(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CombatAbility_Struct)) { + std::cout << "Wrong size on OP_CombatAbility. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << std::endl; + return; + } + OPCombatAbility(app); + return; +} + +void Client::Handle_OP_ConfirmDelete(const EQApplicationPacket* app) { return; } @@ -1864,1142 +4666,6 @@ void Client::Handle_OP_ConsentDeny(const EQApplicationPacket *app) return; } -void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app) -{ - Handle_OP_TargetCommand(app); -} - -void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientTarget_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_TargetMouse expected:%i got:%i", sizeof(ClientTarget_Struct), app->size); - return; - } - - if(GetTarget()) - { - GetTarget()->IsTargeted(-1); - } - - // Locate and cache new target - ClientTarget_Struct* ct=(ClientTarget_Struct*)app->pBuffer; - pClientSideTarget = ct->new_target; - if(!IsAIControlled()) - { - Mob *nt = entity_list.GetMob(ct->new_target); - if(nt) - { - SetTarget(nt); - if ((nt->IsClient() && !nt->CastToClient()->GetPVP()) || - (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || - (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) - nt->SendBuffsToClient(this); - } - else - { - SetTarget(nullptr); - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); - - Group *g = GetGroup(); - - if(g && g->HasRole(this, RoleAssist)) - g->SetGroupAssistTarget(0); - - if(g && g->HasRole(this, RoleTank)) - g->SetGroupTankTarget(0); - - if(g && g->HasRole(this, RolePuller)) - g->SetGroupPullerTarget(0); - - return; - } - } - else - { - SetTarget(nullptr); - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); - return; - } - - // HoTT - if (GetTarget() && GetTarget()->GetTarget()) - { - SetHoTT(GetTarget()->GetTarget()->GetID()); - UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); - } - else - { - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); - } - - Group *g = GetGroup(); - - if(g && g->HasRole(this, RoleAssist)) - g->SetGroupAssistTarget(GetTarget()); - - if(g && g->HasRole(this, RoleTank)) - g->SetGroupTankTarget(GetTarget()); - - if(g && g->HasRole(this, RolePuller)) - g->SetGroupPullerTarget(GetTarget()); - - // For /target, send reject or success packet - if (app->GetOpcode() == OP_TargetCommand) { - if (GetTarget() && !GetTarget()->CastToMob()->IsInvisible(this) && (DistNoRoot(*GetTarget()) <= TARGETING_RANGE*TARGETING_RANGE || GetGM())) { - if(GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special - || GetTarget()->GetBodyType() == BT_NoTarget) - { - //Targeting something we shouldn't with /target - //but the client allows this without MQ so you don't flag it - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); - outapp->pBuffer[0] = 0x2f; - outapp->pBuffer[1] = 0x01; - outapp->pBuffer[4] = 0x0d; - if(GetTarget()) - { - SetTarget(nullptr); - } - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - QueuePacket(app); - EQApplicationPacket hp_app; - GetTarget()->IsTargeted(1); - GetTarget()->CreateHPPacket(&hp_app); - QueuePacket(&hp_app, false); - } - else - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); - outapp->pBuffer[0] = 0x2f; - outapp->pBuffer[1] = 0x01; - outapp->pBuffer[4] = 0x0d; - if(GetTarget()) - { - SetTarget(nullptr); - } - QueuePacket(outapp); - safe_delete(outapp); - } - } - else - { - if(GetTarget()) - { - if(GetGM()) - { - GetTarget()->IsTargeted(1); - return; - } - else if(IsAssistExempted()) - { - GetTarget()->IsTargeted(1); - SetAssistExemption(false); - return; - } - else if(GetTarget()->IsClient()) - { - //make sure this client is in our raid/group - GetTarget()->IsTargeted(1); - return; - } - else if(GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special - || GetTarget()->GetBodyType() == BT_NoTarget) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something untargetable, %s bodytype: %i\n", - GetName(), GetTarget()->GetName(), (int)GetTarget()->GetBodyType()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget((Mob*)nullptr); - return; - } - else if(IsPortExempted()) - { - GetTarget()->IsTargeted(1); - return; - } - else if(IsSenseExempted()) - { - GetTarget()->IsTargeted(1); - SetSenseExemption(false); - return; - } - else if(IsXTarget(GetTarget())) - { - GetTarget()->IsTargeted(1); - return; - } - else if(GetBindSightTarget()) - { - if(GetBindSightTarget()->DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - if(DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," - " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), - (zone->newzone_data.maxclip*zone->newzone_data.maxclip), - GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget(nullptr); - return; - } - } - } - else if(DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," - " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), - (zone->newzone_data.maxclip*zone->newzone_data.maxclip), - GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget(nullptr); - return; - } - - GetTarget()->IsTargeted(1); - } - } - return; -} - -void Client::Handle_OP_Shielding(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Shielding_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_Shielding expected:%i got:%i", sizeof(Shielding_Struct), app->size); - return; - } - if(GetClass() != WARRIOR) - { - return; - } - - if (shield_target) - { - entity_list.MessageClose_StringID(this, false, 100, 0, - END_SHIELDING, GetName(), shield_target->GetName()); - for (int y = 0; y < 2; y++) - { - if (shield_target->shielder[y].shielder_id == GetID()) - { - shield_target->shielder[y].shielder_id = 0; - shield_target->shielder[y].shielder_bonus = 0; - } - } - } - Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer; - shield_target = entity_list.GetMob(shield->target_id); - bool ack = false; - ItemInst* inst = GetInv().GetItem(MainSecondary); - if (!shield_target) - return; - if (inst) - { - const Item_Struct* shield = inst->GetItem(); - if (shield && shield->ItemType == ItemTypeShield) - { - for (int x = 0; x < 2; x++) - { - if (shield_target->shielder[x].shielder_id == 0) - { - entity_list.MessageClose_StringID(this ,false, 100, 0, - START_SHIELDING, GetName(), shield_target->GetName()); - shield_target->shielder[x].shielder_id = GetID(); - int shieldbonus = shield->AC*2; - switch (GetAA(197)) - { - case 1: - shieldbonus = shieldbonus * 115 / 100; - break; - case 2: - shieldbonus = shieldbonus * 125 / 100; - break; - case 3: - shieldbonus = shieldbonus * 150 / 100; - break; - } - shield_target->shielder[x].shielder_bonus = shieldbonus; - shield_timer.Start(); - ack = true; - break; - } - } - } - else - { - Message(0,"You must have a shield equipped to shield a target!"); - shield_target = 0; - return; - } - } - else - { - Message(0,"You must have a shield equipped to shield a target!"); - shield_target = 0; - return; - } - if (!ack) - { - Message_StringID(0, ALREADY_SHIELDED); - shield_target = 0; - return; - } - return; -} - -void Client::Handle_OP_Jump(const EQApplicationPacket *app) -{ - SetEndurance(GetEndurance() - (GetLevel()<20?(225*GetLevel()/100):50)); - return; -} - -void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(EntityId_Struct)) - { - LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureInfoRequest had a packet that was too small."); - return; - } - EntityId_Struct* ent = (EntityId_Struct*)app->pBuffer; - Mob * m = entity_list.GetMob(ent->entity_id); - if(m && m->IsNPC()) - { - std::map::iterator it; - it = zone->adventure_entry_list_flavor.find(m->CastToNPC()->GetAdventureTemplate()); - if(it != zone->adventure_entry_list_flavor.end()) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (it->second.size() + 2)); - strn0cpy((char*)outapp->pBuffer, it->second.c_str(), it->second.size()); - FastQueuePacket(&outapp); - } - else - { - if(m->CastToNPC()->GetAdventureTemplate() != 0) - { - std::string text = "Choose your difficulty and preferred adventure type."; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (text.size() + 2)); - strn0cpy((char*)outapp->pBuffer, text.c_str(), text.size()); - FastQueuePacket(&outapp); - } - } - } -} - -void Client::Handle_OP_AdventureRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(AdventureRequest_Struct)) - { - LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureRequest had a packet that was too small."); - return; - } - - if(IsOnAdventure()) - { - return; - } - - if(!p_timers.Expired(&database, pTimerStartAdventureTimer, false)) - { - return; - } - - if(GetPendingAdventureRequest()) - { - return; - } - - AdventureRequest_Struct* ars = (AdventureRequest_Struct*)app->pBuffer; - uint8 group_members = 0; - Raid *r = nullptr; - Group *g = nullptr; - - if(IsRaidGrouped()) - { - r = GetRaid(); - group_members = r->RaidCount(); - } - else if(IsGrouped()) - { - g = GetGroup(); - group_members = g->GroupCount(); - } - else - { - return; - } - - if(group_members < RuleI(Adventure, MinNumberForGroup) || group_members > RuleI(Adventure, MaxNumberForGroup)) - { - return; - } - - Mob* m = entity_list.GetMob(ars->entity_id); - uint32 template_id = 0; - if(m && m->IsNPC()) - { - template_id = m->CastToNPC()->GetAdventureTemplate(); - } - else - { - return; - } - - ServerPacket *packet = new ServerPacket(ServerOP_AdventureRequest, sizeof(ServerAdventureRequest_Struct) + (64 * group_members)); - ServerAdventureRequest_Struct *sar = (ServerAdventureRequest_Struct*)packet->pBuffer; - sar->member_count = group_members; - sar->risk = ars->risk; - sar->type = ars->type; - sar->template_id = template_id; - strcpy(sar->leader, GetName()); - - if(IsRaidGrouped()) - { - int i = 0; - for(int x = 0; x < 72; ++x) - { - if(i == group_members) - { - break; - } - - const char *c_name = nullptr; - c_name = r->GetClientNameByIndex(x); - if(c_name) - { - memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct) + (64 * i)), c_name, strlen(c_name)); - ++i; - } - } - } - else - { - int i = 0; - for(int x = 0; x < 6; ++x) - { - if(i == group_members) - { - break; - } - - const char *c_name = nullptr; - c_name = g->GetClientNameByIndex(x); - if(c_name) - { - memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct) + (64 * i)), c_name, strlen(c_name)); - ++i; - } - } - } - - packet->Deflate(); - worldserver.SendPacket(packet); - delete packet; - p_timers.Start(pTimerStartAdventureTimer, 5); -} - -void Client::Handle_OP_LDoNButton(const EQApplicationPacket *app) -{ - if(app->size < sizeof(bool)) - { - return; - } - - if(GetPendingAdventureCreate()) - { - return; - } - - if(IsOnAdventure()) - { - return; - } - - bool* p = (bool*)app->pBuffer; - if(*p == true) - { - ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestCreate, sizeof(ServerAdventureRequestCreate_Struct) + (64 * adv_requested_member_count)); - ServerAdventureRequestCreate_Struct *sac = (ServerAdventureRequestCreate_Struct*)pack->pBuffer; - strcpy(sac->leader, GetName()); - sac->id = adv_requested_id; - sac->theme = adv_requested_theme; - sac->member_count = adv_requested_member_count; - memcpy((pack->pBuffer + sizeof(ServerAdventureRequestCreate_Struct)), adv_requested_data, (64 * adv_requested_member_count)); - pack->Deflate(); - worldserver.SendPacket(pack); - delete pack; - PendingAdventureCreate(); - ClearPendingAdventureData(); - } - else - { - ClearPendingAdventureData(); - } -} - -void Client::Handle_OP_LeaveAdventure(const EQApplicationPacket *app) -{ - if(!IsOnAdventure()) - { - return; - } - LeaveAdventure(); -} - -void Client::Handle_OP_Consume(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Consume_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_Consume expected:%i got:%i", sizeof(Consume_Struct), app->size); - return; - } - Consume_Struct* pcs = (Consume_Struct*)app->pBuffer; - if(pcs->type == 0x01) - { - if(m_pp.hunger_level > 6000) - { - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; - sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; - sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; - - QueuePacket(outapp); - safe_delete(outapp); - return; - } - } - else if(pcs->type == 0x02) - { - if(m_pp.thirst_level > 6000) - { - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; - sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; - sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; - - QueuePacket(outapp); - safe_delete(outapp); - return; - } - } - - ItemInst *myitem = GetInv().GetItem(pcs->slot); - if(myitem == nullptr) { - LogFile->write(EQEMuLog::Error, "Consuming from empty slot %d", pcs->slot); - return; - } - - const Item_Struct* eat_item = myitem->GetItem(); - if (pcs->type == 0x01) { - Consume(eat_item, ItemTypeFood, pcs->slot, (pcs->auto_consumed == 0xffffffff)); - } - else if (pcs->type == 0x02) { - Consume(eat_item, ItemTypeDrink, pcs->slot, (pcs->auto_consumed == 0xffffffff)); - } - else { - LogFile->write(EQEMuLog::Error, "OP_Consume: unknown type, type:%i", (int)pcs->type); - return; - } - if (m_pp.hunger_level > 50000) - m_pp.hunger_level = 50000; - if (m_pp.thirst_level > 50000) - m_pp.thirst_level = 50000; - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; - sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; - sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; - - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ItemVerifyRequest_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size); - return; - } - - ItemVerifyRequest_Struct* request = (ItemVerifyRequest_Struct*)app->pBuffer; - int32 slot_id; - int32 target_id; - int32 spell_id = 0; - slot_id = request->slot; - target_id = request->target; - - - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct)); - ItemVerifyReply_Struct* reply = (ItemVerifyReply_Struct*)outapp->pBuffer; - reply->slot = slot_id; - reply->target = target_id; - - QueuePacket(outapp); - safe_delete(outapp); - - - if (IsAIControlled()) { - this->Message_StringID(13,NOT_IN_CONTROL); - return; - } - - if(slot_id < 0) { - LogFile->write(EQEMuLog::Debug, "Unknown slot being used by %s, slot being used is: %i",GetName(),request->slot); - return; - } - - const ItemInst* inst = m_inv[slot_id]; - if (!inst) { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - DeleteItemInInventory(slot_id,0,true); - return; - } - - const Item_Struct* item = inst->GetItem(); - if (!item) { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - DeleteItemInInventory(slot_id,0,true); - return; - } - - spell_id = item->Click.Effect; - - if - ( - spell_id > 0 && - ( - !IsValidSpell(spell_id) || - casting_spell_id || - delaytimer || - spellend_timer.Enabled() || - IsStunned() || - IsFeared() || - IsMezzed() || - DivineAura() || - (IsSilenced() && !IsDiscipline(spell_id)) || - (IsAmnesiad() && IsDiscipline(spell_id)) || - (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) - ) - ) - { - SendSpellBarEnable(spell_id); - return; - } - - LogFile->write(EQEMuLog::Debug, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id); - - if ((slot_id < MainCursor) || (slot_id == MainPowerSource) || (slot_id > 250 && slot_id < 331 && ((item->ItemType == ItemTypePotion) || item->PotionBelt))) // sanity check - { - ItemInst* p_inst = (ItemInst*)inst; - - parse->EventItem(EVENT_ITEM_CLICK, this, p_inst, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if(!inst) - { - return; - } - - int r; - bool tryaug = false; - ItemInst* clickaug = 0; - Item_Struct* augitem = 0; - - for (r = 0; r < EmuConstants::ITEM_COMMON_SIZE; r++) { - const ItemInst* aug_i = inst->GetAugment(r); - if(!aug_i) - continue; - const Item_Struct* aug = aug_i->GetItem(); - if(!aug) - continue; - - if ( (aug->Click.Type == ET_ClickEffect) || (aug->Click.Type == ET_Expendable) || (aug->Click.Type == ET_EquipClick) || (aug->Click.Type == ET_ClickEffect2) ) - { - tryaug = true; - clickaug = (ItemInst*)aug_i; - augitem = (Item_Struct*)aug; - spell_id = aug->Click.Effect; - break; - } - } - - if((spell_id <= 0) && (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol && item->ItemType != ItemTypeSpell)) - { - LogFile->write(EQEMuLog::Debug, "Item with no effect right clicked by %s",GetName()); - } - else if (inst->IsType(ItemClassCommon)) - { - if(item->ItemType == ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) - { - DeleteItemInInventory(slot_id, 1, true); - TrainDiscipline(item->ID); - } - else if(item->ItemType == ItemTypeSpell) - { - return; - } - else if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) - { - if (inst->GetCharges() == 0) - { - //Message(0, "This item is out of charges."); - Message_StringID(13, ITEM_OUT_OF_CHARGES); - return; - } - if(GetLevel() >= item->Click.Level2) - { - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if(!inst) - { - return; - } - - if(i == 0) { - CastSpell(item->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, item->CastTime, 0, 0, slot_id); - } - } - else - { - Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); - return; - } - } - else if (tryaug) - { - if (clickaug->GetCharges() == 0) - { - //Message(0, "This item is out of charges."); - Message_StringID(13, ITEM_OUT_OF_CHARGES); - return; - } - if(GetLevel() >= augitem->Click.Level2) - { - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, clickaug, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if(!inst) - { - return; - } - - if(i == 0) { - CastSpell(augitem->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, augitem->CastTime, 0, 0, slot_id); - } - } - else - { - Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); - return; - } - } - else - { - if(GetClientVersion() >= EQClientSoD && !inst->IsEquipable(GetBaseRace(),GetClass())) - { - if(item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol) - { - LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); - } - else - { - //This is food/drink - consume it - if (item->ItemType == ItemTypeFood && m_pp.hunger_level < 5000) - { - Consume(item, item->ItemType, slot_id, false); - } - else if (item->ItemType == ItemTypeDrink && m_pp.thirst_level < 5000) - { - Consume(item, item->ItemType, slot_id, false); - } - else if (item->ItemType == ItemTypeAlcohol) - { -#if EQDEBUG >= 1 - LogFile->write(EQEMuLog::Debug, "Drinking Alcohol from slot:%i", slot_id); -#endif - // This Seems to be handled in OP_DeleteItem handling - //DeleteItemInInventory(slot_id, 1, false); - //entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); - //Should add intoxication level to the PP at some point - //CheckIncreaseSkill(ALCOHOL_TOLERANCE, nullptr, 25); - } - - if (m_pp.hunger_level > 6000) - m_pp.hunger_level = 6000; - if (m_pp.thirst_level > 6000) - m_pp.thirst_level = 6000; - - EQApplicationPacket *outapp2; - outapp2 = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp2->pBuffer; - sta->food = m_pp.hunger_level; - sta->water = m_pp.thirst_level; - - QueuePacket(outapp2); - safe_delete(outapp2); - } - - } - else - { - LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); - } - } - } - else - { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - } - } - else - { - Message(0, "Error: Invalid inventory slot for using effects (inventory slot #%i)", slot_id); - } - - return; -} - -void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(AdventureMerchant_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantRequest expected:%i got:%i", sizeof(AdventureMerchant_Struct), app->size); - return; - } - std::stringstream ss(std::stringstream::in | std::stringstream::out); - - uint8 count = 0; - AdventureMerchant_Struct* eid = (AdventureMerchant_Struct*)app->pBuffer; - uint32 merchantid = 0; - - Mob* tmp = entity_list.GetMob(eid->entity_id); - if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && - (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid=tmp->CastToNPC()->MerchantType; - tmp->CastToNPC()->FaceTarget(this->CastToMob()); - - const Item_Struct *item = 0; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - for(itr = merlist.begin();itr != merlist.end() && count<255;++itr){ - const MerchantList &ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(item) - { - uint32 theme; - if(item->LDoNTheme > 16) - { - theme = 0; - } - else if(item->LDoNTheme & 16) - { - theme = 5; - } - else if(item->LDoNTheme & 8) - { - theme = 4; - } - else if(item->LDoNTheme & 4) - { - theme = 3; - } - else if(item->LDoNTheme & 2) - { - theme = 2; - } - else if(item->LDoNTheme & 1) - { - theme = 1; - } - else - { - theme = 0; - } - ss << "^" << item->Name << "|"; - ss << item->ID << "|"; - ss << item->LDoNPrice << "|"; - ss << theme << "|"; - ss << "0|"; - ss << "1|"; - ss << item->Races << "|"; - ss << item->Classes; - count++; - } - } - //Count - //^Item Name,Item ID,Cost in Points,Theme (0=none),0,1,races bit map,classes bitmap - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantResponse,ss.str().size()+2); - outapp->pBuffer[0] = count; - strn0cpy((char*)&outapp->pBuffer[1],ss.str().c_str(),ss.str().size()); - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Adventure_Purchase_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantPurchase expected:%i got:%i", sizeof(Adventure_Purchase_Struct), app->size); - return; - } - - Adventure_Purchase_Struct* aps = (Adventure_Purchase_Struct*)app->pBuffer; -/* - Get item apc->itemid (can check NPC if thats necessary), ldon point theme check only if theme is not 0 (I am not sure what 1-5 are though for themes) - if(ldon_points_available >= item ldonpointcost) - { - give item (67 00 00 00 for the packettype using opcode 0x02c5) - ldon_points_available -= ldonpointcost; - } -*/ - uint32 merchantid = 0; - Mob* tmp = entity_list.GetMob(aps->npcid); - if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && - (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid = tmp->CastToNPC()->MerchantType; - - const Item_Struct* item = nullptr; - bool found = false; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - - for(itr = merlist.begin();itr != merlist.end();++itr){ - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(!item) - continue; - if(item->ID == aps->itemid) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... - found = true; - break; - } - } - if (!item || !found) { - Message(13, "Error: The item you purchased does not exist!"); - return; - } - - if(aps->Type == LDoNMerchant) - { - if(m_pp.ldon_points_available < int32(item->LDoNPrice)) { - Message(13, "You cannot afford that item."); - return; - } - - if(item->LDoNTheme <= 16) - { - if(item->LDoNTheme & 16) - { - if(m_pp.ldon_points_tak < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in tak to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 8) - { - if(m_pp.ldon_points_ruj < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in ruj to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 4) - { - if(m_pp.ldon_points_mmc < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in mmc to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 2) - { - if(m_pp.ldon_points_mir < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in mir to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 1) - { - if(m_pp.ldon_points_guk < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in guk to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - } - } - else if(aps->Type == DiscordMerchant) - { - if(GetPVPPoints() < item->LDoNPrice) - { - Message(13, "You need at least %u PVP points to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(aps->Type == NorrathsKeepersMerchant) - { - if(GetRadiantCrystals() < item->LDoNPrice) - { - Message(13, "You need at least %u Radiant Crystals to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(aps->Type == DarkReignMerchant) - { - if(GetEbonCrystals() < item->LDoNPrice) - { - Message(13, "You need at least %u Ebon Crystals to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else - { - Message(13, "Unknown Adventure Merchant type."); - return; - } - - - if(CheckLoreConflict(item)) - { - Message(15,"You can only have one of a lore item."); - return; - } - - if(aps->Type == LDoNMerchant) - { - int32 requiredpts = (int32)item->LDoNPrice*-1; - - if(!UpdateLDoNPoints(requiredpts, 6)) - return; - } - else if(aps->Type == DiscordMerchant) - { - SetPVPPoints(GetPVPPoints() - (int32)item->LDoNPrice); - SendPVPStats(); - } - else if(aps->Type == NorrathsKeepersMerchant) - { - SetRadiantCrystals(GetRadiantCrystals() - (int32)item->LDoNPrice); - SendCrystalCounts(); - } - else if(aps->Type == DarkReignMerchant) - { - SetEbonCrystals(GetEbonCrystals() - (int32)item->LDoNPrice); - SendCrystalCounts(); - } - int16 charges = 1; - if(item->MaxCharges != 0) - charges = item->MaxCharges; - - ItemInst *inst = database.CreateItem(item, charges); - if(!AutoPutLootInInventory(*inst, true, true)) - { - PutLootInInventory(MainCursor, *inst); - } - Save(1); -} - -void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Consider_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider corpse expected %i got %i", sizeof(Consider_Struct), app->size); - return; - } - Consider_Struct* conin = (Consider_Struct*)app->pBuffer; - Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); - if (tcorpse && tcorpse->IsNPCCorpse()) { - uint32 min; uint32 sec; uint32 ttime; - if ((ttime = tcorpse->GetDecayTime()) != 0) { - sec = (ttime/1000)%60; // Total seconds - min = (ttime/60000)%60; // Total seconds / 60 drop .00 - char val1[20]={0}; - char val2[20]={0}; - Message_StringID(10,CORPSE_DECAY1,ConvertArray(min,val1),ConvertArray(sec,val2)); - } - else { - Message_StringID(10,CORPSE_DECAY_NOW); - } - } - else if (tcorpse && tcorpse->IsPlayerCorpse()) { - uint32 day, hour, min, sec, ttime; - if ((ttime = tcorpse->GetDecayTime()) != 0) { - sec = (ttime/1000)%60; // Total seconds - min = (ttime/60000)%60; // Total seconds - hour = (ttime/3600000)%24; // Total hours - day = ttime/86400000; // Total Days - if(day) - Message(0, "This corpse will decay in %i days, %i hours, %i minutes and %i seconds.", day, hour, min, sec); - else if(hour) - Message(0, "This corpse will decay in %i hours, %i minutes and %i seconds.", hour, min, sec); - else - Message(0, "This corpse will decay in %i minutes and %i seconds.", min, sec); - - Message(0, "This corpse %s be resurrected.", tcorpse->Rezzed()?"cannot":"can"); - /* - hour = 0; - - if((ttime = tcorpse->GetResTime()) != 0) { - sec = (ttime/1000)%60; // Total seconds - min = (ttime/60000)%60; // Total seconds - hour = (ttime/3600000)%24; // Total hours - if(hour) - Message(0, "This corpse can be resurrected for %i hours, %i minutes and %i seconds.", hour, min, sec); - else - Message(0, "This corpse can be resurrected for %i minutes and %i seconds.", min, sec); - } - else { - Message_StringID(0, CORPSE_TOO_OLD); - } - */ - } - else { - Message_StringID(10,CORPSE_DECAY_NOW); - } - } -} - void Client::Handle_OP_Consider(const EQApplicationPacket *app) { if (app->size != sizeof(Consider_Struct)) @@ -3012,7 +4678,7 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) if (tmob == 0) return; - if(tmob->GetClass() == LDON_TREASURE) + if (tmob->GetClass() == LDON_TREASURE) { Message(15, "%s", tmob->GetCleanName()); return; @@ -3022,13 +4688,13 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) Consider_Struct* con = (Consider_Struct*)outapp->pBuffer; con->playerid = GetID(); con->targetid = conin->targetid; - if(tmob->IsNPC()) - con->faction = GetFactionLevel(character_id, tmob->GetNPCTypeID(), race, class_, deity,(tmob->IsNPC()) ? tmob->CastToNPC()->GetPrimaryFaction():0, tmob); // rembrant, Dec. 20, 2001; TODO: Send the players proper deity + if (tmob->IsNPC()) + con->faction = GetFactionLevel(character_id, tmob->GetNPCTypeID(), race, class_, deity, (tmob->IsNPC()) ? tmob->CastToNPC()->GetPrimaryFaction() : 0, tmob); // rembrant, Dec. 20, 2001; TODO: Send the players proper deity else con->faction = 1; con->level = GetLevelCon(tmob->GetLevel()); - if(zone->IsPVPZone()) { - if (!tmob->IsNPC() ) + if (zone->IsPVPZone()) { + if (!tmob->IsNPC()) con->pvpcon = tmob->CastToClient()->GetPVP(); } @@ -3039,22 +4705,25 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) con->faction = FACTION_INDIFFERENT; } - if(!(con->faction == FACTION_SCOWLS)) + if (!(con->faction == FACTION_SCOWLS)) { - if(tmob->IsNPC()) + if (tmob->IsNPC()) { - if(tmob->CastToNPC()->IsOnHatelist(this)) + if (tmob->CastToNPC()->IsOnHatelist(this)) con->faction = FACTION_THREATENLY; } } - if(con->faction == FACTION_APPREHENSIVE) { + if (con->faction == FACTION_APPREHENSIVE) { con->faction = FACTION_SCOWLS; - } else if(con->faction == FACTION_DUBIOUS) { + } + else if (con->faction == FACTION_DUBIOUS) { con->faction = FACTION_THREATENLY; - } else if(con->faction == FACTION_SCOWLS) { + } + else if (con->faction == FACTION_SCOWLS) { con->faction = FACTION_APPREHENSIVE; - } else if(con->faction == FACTION_THREATENLY) { + } + else if (con->faction == FACTION_THREATENLY) { con->faction = FACTION_DUBIOUS; } @@ -3091,265 +4760,588 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) return; } -void Client::Handle_OP_Begging(const EQApplicationPacket *app) +void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) { - if(!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) + if (app->size != sizeof(Consider_Struct)) { - Message(13,"Ability recovery time not yet met."); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); - BeggingResponse_Struct *brs = (BeggingResponse_Struct*) outapp->pBuffer; - brs->Result = 0; - FastQueuePacket(&outapp); + LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider corpse expected %i got %i", sizeof(Consider_Struct), app->size); return; } - - if(!HasSkill(SkillBegging) || !GetTarget()) - return; - - if(GetTarget()->GetClass() == LDON_TREASURE) - return; - - p_timers.Start(pTimerBeggingPickPocket, 8); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); - BeggingResponse_Struct *brs = (BeggingResponse_Struct*) outapp->pBuffer; - - brs->Result = 0; // Default, Fail. - if(GetTarget() == this) - { - FastQueuePacket(&outapp); - return; - } - - int RandomChance = MakeRandomInt(0 ,100); - - int ChanceToAttack = 0; - - if(GetLevel() > GetTarget()->GetLevel()) - ChanceToAttack = MakeRandomInt(0, 15); - else - ChanceToAttack = MakeRandomInt(((this->GetTarget()->GetLevel() - this->GetLevel())*10)-5,((this->GetTarget()->GetLevel() - this->GetLevel())*10)); - - if(ChanceToAttack < 0) - ChanceToAttack = -ChanceToAttack; - - if(RandomChance < ChanceToAttack) - { - GetTarget()->Attack(this); - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - uint16 CurrentSkill = GetSkill(SkillBegging); - - float ChanceToBeg=((float)(CurrentSkill/700.0f) + 0.15f) * 100; - - if(RandomChance < ChanceToBeg) - { - brs->Amount = MakeRandomInt(1, 10); - // This needs some work to determine how much money they can beg, based on skill level etc. - if(CurrentSkill < 50) - { - brs->Result = 4; // Copper - AddMoneyToPP(brs->Amount, false); + Consider_Struct* conin = (Consider_Struct*)app->pBuffer; + Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); + if (tcorpse && tcorpse->IsNPCCorpse()) { + uint32 min; uint32 sec; uint32 ttime; + if ((ttime = tcorpse->GetDecayTime()) != 0) { + sec = (ttime / 1000) % 60; // Total seconds + min = (ttime / 60000) % 60; // Total seconds / 60 drop .00 + char val1[20] = { 0 }; + char val2[20] = { 0 }; + Message_StringID(10, CORPSE_DECAY1, ConvertArray(min, val1), ConvertArray(sec, val2)); } - else - { - brs->Result = 3; // Silver - AddMoneyToPP(brs->Amount * 10, false); + else { + Message_StringID(10, CORPSE_DECAY_NOW); } - } + else if (tcorpse && tcorpse->IsPlayerCorpse()) { + uint32 day, hour, min, sec, ttime; + if ((ttime = tcorpse->GetDecayTime()) != 0) { + sec = (ttime / 1000) % 60; // Total seconds + min = (ttime / 60000) % 60; // Total seconds + hour = (ttime / 3600000) % 24; // Total hours + day = ttime / 86400000; // Total Days + if (day) + Message(0, "This corpse will decay in %i days, %i hours, %i minutes and %i seconds.", day, hour, min, sec); + else if (hour) + Message(0, "This corpse will decay in %i hours, %i minutes and %i seconds.", hour, min, sec); + else + Message(0, "This corpse will decay in %i minutes and %i seconds.", min, sec); + + Message(0, "This corpse %s be resurrected.", tcorpse->Rezzed() ? "cannot" : "can"); + /* + hour = 0; + + if((ttime = tcorpse->GetResTime()) != 0) { + sec = (ttime/1000)%60; // Total seconds + min = (ttime/60000)%60; // Total seconds + hour = (ttime/3600000)%24; // Total hours + if(hour) + Message(0, "This corpse can be resurrected for %i hours, %i minutes and %i seconds.", hour, min, sec); + else + Message(0, "This corpse can be resurrected for %i minutes and %i seconds.", min, sec); + } + else { + Message_StringID(0, CORPSE_TOO_OLD); + } + */ + } + else { + Message_StringID(10, CORPSE_DECAY_NOW); + } + } +} + +void Client::Handle_OP_Consume(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Consume_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_Consume expected:%i got:%i", sizeof(Consume_Struct), app->size); + return; + } + Consume_Struct* pcs = (Consume_Struct*)app->pBuffer; + if (pcs->type == 0x01) + { + if (m_pp.hunger_level > 6000) + { + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); + Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; + sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; + sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; + + QueuePacket(outapp); + safe_delete(outapp); + return; + } + } + else if (pcs->type == 0x02) + { + if (m_pp.thirst_level > 6000) + { + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); + Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; + sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; + sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; + + QueuePacket(outapp); + safe_delete(outapp); + return; + } + } + + ItemInst *myitem = GetInv().GetItem(pcs->slot); + if (myitem == nullptr) { + LogFile->write(EQEMuLog::Error, "Consuming from empty slot %d", pcs->slot); + return; + } + + const Item_Struct* eat_item = myitem->GetItem(); + if (pcs->type == 0x01) { + Consume(eat_item, ItemTypeFood, pcs->slot, (pcs->auto_consumed == 0xffffffff)); + } + else if (pcs->type == 0x02) { + Consume(eat_item, ItemTypeDrink, pcs->slot, (pcs->auto_consumed == 0xffffffff)); + } + else { + LogFile->write(EQEMuLog::Error, "OP_Consume: unknown type, type:%i", (int)pcs->type); + return; + } + if (m_pp.hunger_level > 50000) + m_pp.hunger_level = 50000; + if (m_pp.thirst_level > 50000) + m_pp.thirst_level = 50000; + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); + Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; + sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; + sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; + QueuePacket(outapp); safe_delete(outapp); - CheckIncreaseSkill(SkillBegging, nullptr, -10); + return; } -void Client::Handle_OP_TestBuff(const EQApplicationPacket *app) +void Client::Handle_OP_ControlBoat(const EQApplicationPacket *app) { + if (app->size != sizeof(ControlBoat_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ControlBoat, size=%i, expected %i", app->size, sizeof(ControlBoat_Struct)); + return; + } + ControlBoat_Struct* cbs = (ControlBoat_Struct*)app->pBuffer; + Mob* boat = entity_list.GetMob(cbs->boatId); + if (boat == 0) + return; // do nothing if the boat isn't valid + + if (!boat->IsNPC() || (boat->GetRace() != CONTROLLED_BOAT && boat->GetRace() != 502)) + { + char *hacked_string = nullptr; + MakeAnyLenString(&hacked_string, "OP_Control Boat was sent against %s which is of race %u", boat->GetName(), boat->GetRace()); + database.SetMQDetectionFlag(this->AccountName(), this->GetName(), hacked_string, zone->GetShortName()); + safe_delete_array(hacked_string); + return; + } + + if (cbs->TakeControl) { + // this uses the boat's target to indicate who has control of it. It has to check hate to make sure the boat isn't actually attacking anyone. + if ((boat->GetTarget() == 0) || (boat->GetTarget() == this && boat->GetHateAmount(this) == 0)) { + boat->SetTarget(this); + } + else { + this->Message_StringID(13, IN_USE); + return; + } + } + else + boat->SetTarget(0); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ControlBoat, 0); + FastQueuePacket(&outapp); + safe_delete(outapp); + // have the boat signal itself, so quests can be triggered by boat use + boat->CastToNPC()->SignalNPC(0); } -void Client::Handle_OP_Surname(const EQApplicationPacket *app) +void Client::Handle_OP_CorpseDrag(const EQApplicationPacket *app) { - if (app->size != sizeof(Surname_Struct)) + if (DraggedCorpses.size() >= (unsigned int)RuleI(Character, MaxDraggedCorpses)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Surname expected %i got %i", sizeof(Surname_Struct), app->size); + Message_StringID(13, CORPSEDRAG_LIMIT); return; } - if(!p_timers.Expired(&database, pTimerSurnameChange, false) && !GetGM()) + VERIFY_PACKET_LENGTH(OP_CorpseDrag, app, CorpseDrag_Struct); + + CorpseDrag_Struct *cds = (CorpseDrag_Struct*)app->pBuffer; + + Mob* corpse = entity_list.GetMob(cds->CorpseName); + + if (!corpse || !corpse->IsPlayerCorpse() || corpse->CastToCorpse()->IsBeingLooted()) + return; + + Client *c = entity_list.FindCorpseDragger(corpse->GetID()); + + if (c) { - Message(15, "You may only change surnames once every 7 days, your /surname is currently on cooldown."); + if (c == this) + Message_StringID(MT_DefaultText, CORPSEDRAG_ALREADY, corpse->GetCleanName()); + else + Message_StringID(MT_DefaultText, CORPSEDRAG_SOMEONE_ELSE, corpse->GetCleanName()); + return; } - if(GetLevel() < 20) + if (!corpse->CastToCorpse()->Summon(this, false, true)) + return; + + DraggedCorpses.push_back(std::pair(cds->CorpseName, corpse->GetID())); + + Message_StringID(MT_DefaultText, CORPSEDRAG_BEGIN, cds->CorpseName); +} + +void Client::Handle_OP_CorpseDrop(const EQApplicationPacket *app) +{ + if (app->size == 1) { - Message_StringID(15, SURNAME_LEVEL); + Message_StringID(MT_DefaultText, CORPSEDRAG_STOPALL); + ClearDraggedCorpses(); return; } - Surname_Struct* surname = (Surname_Struct*) app->pBuffer; - - char *c = nullptr; - bool first = true; - for(c = surname->lastname; *c; c++) + for (auto Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator) { - if(first) + if (!strcasecmp(Iterator->first.c_str(), (const char *)app->pBuffer)) { - *c = toupper(*c); - first = false; + Message_StringID(MT_DefaultText, CORPSEDRAG_STOP); + Iterator = DraggedCorpses.erase(Iterator); + return; + } + } +} + +void Client::Handle_OP_CrashDump(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_CreateObject(const EQApplicationPacket *app) +{ + DropItem(MainCursor); + return; +} + +void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_CrystalCreate, app, CrystalReclaim_Struct); + CrystalReclaim_Struct *cr = (CrystalReclaim_Struct*)app->pBuffer; + + if (cr->type == 5) { + if (cr->amount > GetEbonCrystals()) { + SummonItem(RuleI(Zone, EbonCrystalItemID), GetEbonCrystals()); + m_pp.currentEbonCrystals = 0; + m_pp.careerEbonCrystals = 0; + SaveCurrency(); + SendCrystalCounts(); + } + else { + SummonItem(RuleI(Zone, EbonCrystalItemID), cr->amount); + m_pp.currentEbonCrystals -= cr->amount; + m_pp.careerEbonCrystals -= cr->amount; + SaveCurrency(); + SendCrystalCounts(); + } + } + else if (cr->type == 4) { + if (cr->amount > GetRadiantCrystals()) { + SummonItem(RuleI(Zone, RadiantCrystalItemID), GetRadiantCrystals()); + m_pp.currentRadCrystals = 0; + m_pp.careerRadCrystals = 0; + SaveCurrency(); + SendCrystalCounts(); + } + else { + SummonItem(RuleI(Zone, RadiantCrystalItemID), cr->amount); + m_pp.currentRadCrystals -= cr->amount; + m_pp.careerRadCrystals -= cr->amount; + SaveCurrency(); + SendCrystalCounts(); + } + } +} + +void Client::Handle_OP_CrystalReclaim(const EQApplicationPacket *app) +{ + uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); + uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); + if ((ebon + radiant) > 0) { + AddCrystals(radiant, ebon); + } +} + +void Client::Handle_OP_Damage(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CombatDamage_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Damage: got %d, expected %d", app->size, + sizeof(CombatDamage_Struct)); + DumpPacket(app); + return; + } + + // Broadcast to other clients + CombatDamage_Struct* damage = (CombatDamage_Struct*)app->pBuffer; + //dont send to originator of falling damage packets + entity_list.QueueClients(this, app, (damage->type == DamageTypeFalling)); + return; +} + +void Client::Handle_OP_Death(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Death_Struct)) + return; + + Death_Struct* ds = (Death_Struct*)app->pBuffer; + + //I think this attack_skill value is really a value from SkillDamageTypes... + if (ds->attack_skill > HIGHEST_SKILL) { + mlog(CLIENT__ERROR, "Invalid skill in OP_Death: %d"); + return; + } + + if (GetHP() > 0) + return; + + Mob* killer = entity_list.GetMob(ds->killer_id); + Death(killer, ds->damage, ds->spell_id, (SkillUseTypes)ds->attack_skill); + return; +} + +void Client::Handle_OP_DelegateAbility(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(DelegateAbility_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DelegateAbility expected %i got %i", + sizeof(DelegateAbility_Struct), app->size); + + DumpPacket(app); + + return; + } + + DelegateAbility_Struct* das = (DelegateAbility_Struct*)app->pBuffer; + + Group *g = GetGroup(); + + if (!g) return; + + switch (das->DelegateAbility) + { + case 0: + { + g->DelegateMainAssist(das->Name); + break; + } + case 1: + { + g->DelegateMarkNPC(das->Name); + break; + } + case 2: + { + g->DelegateMainTank(das->Name); + break; + } + case 3: + { + g->DelegatePuller(das->Name); + break; + } + default: + break; + } +} + +void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(DeleteItem_Struct)) { + std::cout << "Wrong size on OP_DeleteItem. Got: " << app->size << ", Expected: " << sizeof(DeleteItem_Struct) << std::endl; + return; + } + + DeleteItem_Struct* alc = (DeleteItem_Struct*)app->pBuffer; + const ItemInst *inst = GetInv().GetItem(alc->from_slot); + if (inst && inst->GetItem()->ItemType == ItemTypeAlcohol) { + entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), inst->GetItem()->Name); + CheckIncreaseSkill(SkillAlcoholTolerance, nullptr, 25); + + int16 AlcoholTolerance = GetSkill(SkillAlcoholTolerance); + int16 IntoxicationIncrease; + + if (GetClientVersion() < EQClientSoD) + IntoxicationIncrease = (200 - AlcoholTolerance) * 30 / 200 + 10; + else + IntoxicationIncrease = (270 - AlcoholTolerance) * 0.111111108 + 10; + + if (IntoxicationIncrease < 0) + IntoxicationIncrease = 1; + + m_pp.intoxication += IntoxicationIncrease; + + if (m_pp.intoxication > 200) + m_pp.intoxication = 200; + } + DeleteItemInInventory(alc->from_slot, 1); + + return; +} + +void Client::Handle_OP_DeleteSpawn(const EQApplicationPacket *app) +{ + // The client will send this with his id when he zones, maybe when he disconnects too? + //eqs->RemoveData(); // Flushing the queue of packet data to allow for proper zoning + + //just make sure this gets out + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); + FastQueuePacket(&outapp); + + outapp = new EQApplicationPacket(OP_DeleteSpawn, sizeof(EntityId_Struct)); + EntityId_Struct* eid = (EntityId_Struct*)outapp->pBuffer; + eid->entity_id = GetID(); + + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); + + hate_list.RemoveEnt(this->CastToMob()); + + Disconnect(); + return; +} + +void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(DeleteSpell_Struct)) + return; + + EQApplicationPacket* outapp = app->Copy(); + DeleteSpell_Struct* dss = (DeleteSpell_Struct*)outapp->pBuffer; + + if (dss->spell_slot < 0 || dss->spell_slot > int(MAX_PP_SPELLBOOK)) + return; + + if (m_pp.spell_book[dss->spell_slot] != SPELLBOOK_UNKNOWN) { + m_pp.spell_book[dss->spell_slot] = SPELLBOOK_UNKNOWN; + dss->success = 1; + } + else + dss->success = 0; + + FastQueuePacket(&outapp); + return; +} + +void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillDisarmTraps)) + return; + + if (!p_timers.Expired(&database, pTimerDisarmTraps, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + int reuse = DisarmTrapsReuseTime; + switch (GetAA(aaAdvTrapNegotiation)) { + case 1: + reuse -= 1; + break; + case 2: + reuse -= 3; + break; + case 3: + reuse -= 5; + break; + } + p_timers.Start(pTimerDisarmTraps, reuse - 1); + + Trap* trap = entity_list.FindNearbyTrap(this, 60); + if (trap && trap->detected) + { + int uskill = GetSkill(SkillDisarmTraps); + if ((MakeRandomInt(0, 49) + uskill) >= (MakeRandomInt(0, 49) + trap->skill)) + { + Message(MT_Skills, "You disarm a trap."); + trap->disarmed = true; + trap->chkarea_timer.Disable(); + trap->respawn_timer.Start((trap->respawn_time + MakeRandomInt(0, trap->respawn_var)) * 1000); } else { - *c = tolower(*c); - } - } - - if (strlen(surname->lastname) >= 20) { - Message_StringID(15, SURNAME_TOO_LONG); - return; - } - - if(!database.CheckNameFilter(surname->lastname, true)) - { - Message_StringID(15, SURNAME_REJECTED); - return; - } - - ChangeLastName(surname->lastname); - p_timers.Start(pTimerSurnameChange, 604800); - - EQApplicationPacket* outapp = app->Copy(); - outapp = app->Copy(); - surname = (Surname_Struct*) outapp->pBuffer; - surname->unknown0064=1; - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_ClearSurname(const EQApplicationPacket *app) -{ - ChangeLastName(""); -} - -void Client::Handle_OP_YellForHelp(const EQApplicationPacket *app) -{ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_YellForHelp, 4); - *(uint32 *)outapp->pBuffer = GetID(); - entity_list.QueueCloseClients(this, outapp, true, 100.0); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_Assist(const EQApplicationPacket *app) -{ - if (app->size != sizeof(EntityId_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Assist expected %i got %i", sizeof(EntityId_Struct), app->size); - return; - } - - EntityId_Struct* eid = (EntityId_Struct*)app->pBuffer; - Entity* entity = entity_list.GetID(eid->entity_id); - - EQApplicationPacket* outapp = app->Copy(); - eid = (EntityId_Struct*)outapp->pBuffer; - if (RuleB(Combat, AssistNoTargetSelf)) - eid->entity_id = GetID(); - if (entity && entity->IsMob()) { - Mob *assistee = entity->CastToMob(); - if (assistee->GetTarget()) { - Mob *new_target = assistee->GetTarget(); - if (new_target && (GetGM() || - Dist(*assistee) <= TARGETING_RANGE)) { - SetAssistExemption(true); - eid->entity_id = new_target->GetID(); + if (MakeRandomInt(0, 99) < 25){ + Message(MT_Skills, "You set off the trap while trying to disarm it!"); + trap->Trigger(this); + } + else{ + Message(MT_Skills, "You failed to disarm a trap."); } } - } - - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_AssistGroup(const EQApplicationPacket *app) -{ - if (app->size != sizeof(EntityId_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AssistGroup expected %i got %i", sizeof(EntityId_Struct), app->size); + CheckIncreaseSkill(SkillDisarmTraps, nullptr); return; } - QueuePacket(app); + Message(MT_Skills, "You did not find any traps close enough to disarm."); return; } -void Client::Handle_OP_GMTraining(const EQApplicationPacket *app) +void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) { - if (app->size != sizeof(GMTrainee_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTraining expected %i got %i", sizeof(GMTrainee_Struct), app->size); + + if (app->size != sizeof(DoGroupLeadershipAbility_Struct)) { + + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DoGroupLeadershipAbility expected %i got %i", + sizeof(DoGroupLeadershipAbility_Struct), app->size); + DumpPacket(app); - return; - } - OPGMTraining(app); - return; -} -void Client::Handle_OP_GMEndTraining(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMTrainEnd_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMEndTraining expected %i got %i", sizeof(GMTrainEnd_Struct), app->size); - DumpPacket(app); return; } - OPGMEndTraining(app); - return; -} -void Client::Handle_OP_GMTrainSkill(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSkillChange_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTrainSkill expected %i got %i", sizeof(GMSkillChange_Struct), app->size); - DumpPacket(app); - return; + DoGroupLeadershipAbility_Struct* dglas = (DoGroupLeadershipAbility_Struct*)app->pBuffer; + + switch (dglas->Ability) + { + case GroupLeadershipAbility_MarkNPC: + { + if (GetTarget()) + { + Group* g = GetGroup(); + if (g) + g->MarkNPC(GetTarget(), dglas->Parameter); + } + break; + } + + case groupAAInspectBuffs: + { + Mob *Target = GetTarget(); + + if (!Target || !Target->IsClient()) + return; + + Group *g = GetGroup(); + + if (!g || (g->GroupCount() < 3)) + return; + + Target->CastToClient()->InspectBuffs(this, g->GetLeadershipAA(groupAAInspectBuffs)); + + break; + } + + default: + break; } - OPGMTrainSkill(app); - return; } void Client::Handle_OP_DuelResponse(const EQApplicationPacket *app) { - if(app->size != sizeof(DuelResponse_Struct)) + if (app->size != sizeof(DuelResponse_Struct)) return; - DuelResponse_Struct* ds = (DuelResponse_Struct*) app->pBuffer; + DuelResponse_Struct* ds = (DuelResponse_Struct*)app->pBuffer; Entity* entity = entity_list.GetID(ds->target_id); Entity* initiator = entity_list.GetID(ds->entity_id); - if(!entity->IsClient() || !initiator->IsClient()) + if (!entity->IsClient() || !initiator->IsClient()) return; entity->CastToClient()->SetDuelTarget(0); entity->CastToClient()->SetDueling(false); initiator->CastToClient()->SetDuelTarget(0); initiator->CastToClient()->SetDueling(false); - if(GetID() == initiator->GetID()) - entity->CastToClient()->Message_StringID(10,DUEL_DECLINE,initiator->GetName()); + if (GetID() == initiator->GetID()) + entity->CastToClient()->Message_StringID(10, DUEL_DECLINE, initiator->GetName()); else - initiator->CastToClient()->Message_StringID(10,DUEL_DECLINE,entity->GetName()); + initiator->CastToClient()->Message_StringID(10, DUEL_DECLINE, entity->GetName()); return; } void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) { - if(app->size != sizeof(Duel_Struct)) + if (app->size != sizeof(Duel_Struct)) return; - Duel_Struct* ds = (Duel_Struct*) app->pBuffer; + Duel_Struct* ds = (Duel_Struct*)app->pBuffer; Entity* entity = entity_list.GetID(ds->duel_target); Entity* initiator = entity_list.GetID(ds->duel_initiator); if (entity && initiator && entity == this && initiator->IsClient()) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_RequestDuel, sizeof(Duel_Struct)); - Duel_Struct* ds2 = (Duel_Struct*) outapp->pBuffer; + Duel_Struct* ds2 = (Duel_Struct*)outapp->pBuffer; ds2->duel_initiator = entity->GetID(); ds2->duel_target = entity->GetID(); @@ -3374,485 +5366,218 @@ void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) return; } -void Client::Handle_OP_RequestDuel(const EQApplicationPacket *app) +void Client::Handle_OP_DumpName(const EQApplicationPacket *app) { - if(app->size != sizeof(Duel_Struct)) - return; + return; +} - EQApplicationPacket* outapp = app->Copy(); - Duel_Struct* ds = (Duel_Struct*) outapp->pBuffer; - uint32 duel = ds->duel_initiator; - ds->duel_initiator = ds->duel_target; - ds->duel_target = duel; - Entity* entity = entity_list.GetID(ds->duel_target); - if(GetID() != ds->duel_target && entity->IsClient() && (entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() != 0)) { - Message_StringID(10,DUEL_CONSIDERING,entity->GetName()); - return; +void Client::Handle_OP_Dye(const EQApplicationPacket *app) +{ + if (app->size != sizeof(DyeStruct)) + printf("Wrong size of DyeStruct, Got: %i, Expected: %zu\n", app->size, sizeof(DyeStruct)); + else{ + DyeStruct* dye = (DyeStruct*)app->pBuffer; + DyeArmor(dye); } - if(IsDueling()) { - Message_StringID(10,DUEL_INPROGRESS); + return; +} + +void Client::Handle_OP_Emote(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Emote_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized " + "OP_Emote: got %d, expected %d", app->size, + sizeof(Emote_Struct)); + DumpPacket(app); return; } - if(GetID() != ds->duel_target && entity->IsClient() && GetDuelTarget() == 0 && !IsDueling() && !entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() == 0) { - SetDuelTarget(ds->duel_target); - entity->CastToClient()->SetDuelTarget(GetID()); - ds->duel_target = ds->duel_initiator; - entity->CastToClient()->FastQueuePacket(&outapp); - entity->CastToClient()->SetDueling(false); - SetDueling(false); + // Calculate new packet dimensions + Emote_Struct* in = (Emote_Struct*)app->pBuffer; + in->message[1023] = '\0'; + + const char* name = GetName(); + uint32 len_name = strlen(name); + uint32 len_msg = strlen(in->message); + // crash protection -- cheater + if (len_msg > 512) { + in->message[512] = '\0'; + len_msg = 512; + } + uint32 len_packet = sizeof(in->unknown01) + len_name + + len_msg + 1; + + // Construct outgoing packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Emote, len_packet); + Emote_Struct* out = (Emote_Struct*)outapp->pBuffer; + out->unknown01 = in->unknown01; + memcpy(out->message, name, len_name); + memcpy(&out->message[len_name], in->message, len_msg); + + /* + if (target && target->IsClient()) { + entity_list.QueueCloseClients(this, outapp, false, 100, target); + + cptr = outapp->pBuffer + 2; + + // not sure if live does this or not. thought it was a nice feature, but would take a lot to + // clean up grammatical and other errors. Maybe with a regex parser... + replacestr((char *)cptr, target->GetName(), "you"); + replacestr((char *)cptr, " he", " you"); + replacestr((char *)cptr, " she", " you"); + replacestr((char *)cptr, " him", " you"); + replacestr((char *)cptr, " her", " you"); + target->CastToClient()->QueuePacket(outapp); + } else - safe_delete(outapp); + */ + entity_list.QueueCloseClients(this, outapp, true, 100, 0, true, FilterSocials); + + safe_delete(outapp); return; } -void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) +void Client::Handle_OP_EndLootRequest(const EQApplicationPacket *app) { - if (app->size != sizeof(SpawnAppearance_Struct)) { - std::cout << "Wrong size on OP_SpawnAppearance. Got: " << app->size << ", Expected: " << sizeof(SpawnAppearance_Struct) << std::endl; + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_EndLootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; return; } - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; - if(sa->spawn_id != GetID()) - return; + SetLooting(false); - if (sa->type == AT_Invis) { - if(sa->parameter != 0) - { - if(!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) - { - if(GetClientVersion() < EQClientSoF) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Invis: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - } - return; - } - invisible = false; - hidden = false; - improved_hidden = false; - entity_list.QueueClients(this, app, true); + Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); + if (entity == 0) { + Message(13, "Error: OP_EndLootRequest: Corpse not found (ent = 0)"); + if (GetClientVersion() >= EQClientSoD) + Corpse::SendEndLootErrorPacket(this); + else + Corpse::SendLootReqErrorPacket(this); return; } - else if (sa->type == AT_Anim) { - if (IsAIControlled()) - return; - if (sa->parameter == ANIM_STAND) { - SetAppearance(eaStanding); - playeraction = 0; - SetFeigned(false); - BindWound(this, false, true); - camp_timer.Disable(); - } - else if (sa->parameter == ANIM_SIT) { - SetAppearance(eaSitting); - playeraction = 1; - if(!UseBardSpellLogic()) - InterruptSpell(); - SetFeigned(false); - BindWound(this, false, true); - } - else if (sa->parameter == ANIM_CROUCH) { - if(!UseBardSpellLogic()) - InterruptSpell(); - SetAppearance(eaCrouching); - playeraction = 2; - SetFeigned(false); - } - else if (sa->parameter == ANIM_DEATH) { // feign death too - SetAppearance(eaDead); - playeraction = 3; - InterruptSpell(); - } - else if (sa->parameter == ANIM_LOOT) { - SetAppearance(eaLooting); - playeraction = 4; - SetFeigned(false); - } - - // This is from old code - // I have no clue what it's for - /* - else if (sa->parameter == 0x05) { - // Illusion - std::cout << "Illusion packet recv'd:" << std::endl; - DumpPacket(app); - } - */ - else { - std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl; - return; - } - - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Anon) { - // For Anon/Roleplay - if (sa->parameter == 1) { // Anon - m_pp.anon = 1; - } - else if ((sa->parameter == 2) || (sa->parameter == 3)) { // This is Roleplay, or anon+rp - m_pp.anon = 2; - } - else if (sa->parameter == 0) { // This is Non-Anon - m_pp.anon = 0; - } - else { - std::cerr << "Client " << name << " unknown Anon/Roleplay Switch " << (int)sa->parameter << std::endl; - return; - } - entity_list.QueueClients(this, app, true); - UpdateWho(); - } - else if ((sa->type == AT_HP) && (dead == 0)) { + else if (!entity->IsCorpse()) { + Message(13, "Error: OP_EndLootRequest: Corpse not found (!entity->IsCorpse())"); + Corpse::SendLootReqErrorPacket(this); return; } - else if (sa->type == AT_AFK) { - this->AFK = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Split) { - m_pp.autosplit = (sa->parameter == 1); - } - else if (sa->type == AT_Sneak) { - if(sa->parameter != 0) - { - if(!HasSkill(SkillSneak)) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Sneak: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - return; - } - this->sneaking = 0; - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Size) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Size: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield) - { - entity_list.QueueClients(this, app, false); - } - else if (sa->type == AT_Levitate) - { - // don't do anything with this, we tell the client when it's - // levitating, not the other way around - } - else if (sa->type == AT_ShowHelm) - { - m_pp.showhelm = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); - } else { - std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec - << " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl; + entity->CastToCorpse()->EndLoot(this, app); } return; } -void Client::Handle_OP_BazaarInspect(const EQApplicationPacket *app) +void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) { - if (app->size != sizeof(BazaarInspect_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for BazaarInspect_Struct: Expected %i, Got %i", - sizeof(BazaarInspect_Struct), app->size); + if (!ClientFinishedLoading()) + { + SetHP(GetHP() - 1); return; } - BazaarInspect_Struct* bis = (BazaarInspect_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bis->ItemID); - - if (!item) { - Message(13, "Error: This item does not exist!"); - return; - } - - ItemInst* inst = database.CreateItem(item); - - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - - return; -} - -void Client::Handle_OP_Death(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Death_Struct)) - return; - - Death_Struct* ds = (Death_Struct*)app->pBuffer; - - //I think this attack_skill value is really a value from SkillDamageTypes... - if(ds->attack_skill > HIGHEST_SKILL) { - mlog(CLIENT__ERROR, "Invalid skill in OP_Death: %d"); - return; - } - - if(GetHP() > 0) - return; - - Mob* killer = entity_list.GetMob(ds->killer_id); - Death(killer, ds->damage, ds->spell_id, (SkillUseTypes)ds->attack_skill); - return; -} - -void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) -{ - if(app->size != sizeof(MoveCoin_Struct)){ - LogFile->write(EQEMuLog::Error, "Wrong size on OP_MoveCoin. Got: %i, Expected: %i", app->size, sizeof(MoveCoin_Struct)); + if (app->size != sizeof(EnvDamage2_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_EnvDamage: got %d, expected %d", app->size, + sizeof(EnvDamage2_Struct)); DumpPacket(app); return; } - OPMoveCoin(app); - return; -} - -void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) -{ - if(app->size != sizeof(ItemViewRequest_Struct)){ - LogFile->write(EQEMuLog::Error, "Wrong size on OP_ItemLinkClick. Got: %i, Expected: %i", app->size, sizeof(ItemViewRequest_Struct)); - DumpPacket(app); + EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer; + if (admin >= minStatusToAvoidFalling && GetGM()){ + Message(13, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); + SetHP(GetHP() - 1);//needed or else the client wont acknowledge + return; + } + else if (GetInvul()) { + Message(13, "Your invuln status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); + SetHP(GetHP() - 1);//needed or else the client wont acknowledge return; } - DumpPacket(app); - ItemViewRequest_Struct* ivrs = (ItemViewRequest_Struct*)app->pBuffer; + int damage = ed->damage; - //todo: verify ivrs->link_hash based on a rule, in case we don't care about people being able to sniff data from the item DB + if (ed->dmgtype == 252) { - const Item_Struct* item = database.GetItem(ivrs->item_id); - if (!item) { - if (ivrs->item_id > 500000) - { - std::string response = ""; - int sayid = ivrs->item_id - 500000; - bool silentsaylink = false; - - if (sayid > 250000) //Silent Saylink - { - sayid = sayid - 250000; - silentsaylink = true; - } - - if (sayid > 0) - { - - std::string query = StringFormat("SELECT `phrase` FROM saylink WHERE `id` = '%i'", sayid); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); - return; - } - - if (results.RowCount() != 1) { - Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); - return; - } - - auto row = results.begin(); - response = row[0]; - - } - - if((response).size() > 0) - { - if( !mod_saylink(response, silentsaylink) ) { return; } - - if(GetTarget() && GetTarget()->IsNPC()) - { - if(silentsaylink) - { - parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); - } - else - { - Message(7, "You say, '%s'", response.c_str()); - ChannelMessageReceived(8, 0, 100, response.c_str()); - } - return; - } - else - { - if(silentsaylink) - { - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); - } - else - { - Message(7, "You say, '%s'", response.c_str()); - ChannelMessageReceived(8, 0, 100, response.c_str()); - } - return; - } - } - else - { - Message(13, "Error: Say Link not found or is too long."); - return; - } + switch (GetAA(aaAcrobatics)) { //Don't know what acrobatics effect is yet but it should be done client side via aa effect.. till then + case 1: + damage = damage * 95 / 100; + break; + case 2: + damage = damage * 90 / 100; + break; + case 3: + damage = damage * 80 / 100; + break; } - else { - Message(13, "Error: The item for the link you have clicked on does not exist!"); - return; - } - } - ItemInst* inst = database.CreateItem(item, item->MaxCharges, ivrs->augments[0], ivrs->augments[1], ivrs->augments[2], ivrs->augments[3], ivrs->augments[4]); - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - return; -} + if (damage < 0) + damage = 31337; -void Client::Handle_OP_ItemLinkResponse(const EQApplicationPacket *app) { - if (app->size != sizeof(LDONItemViewRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemLinkResponse expected:%i got:%i", sizeof(LDONItemViewRequest_Struct), app->size); + else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) return; - } - LDONItemViewRequest_Struct* item = (LDONItemViewRequest_Struct*)app->pBuffer; - ItemInst* inst = database.CreateItem(item->item_id); - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - return; -} + else + SetHP(GetHP() - damage); -void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) -{ - if(!CharacterID()) + if (GetHP() <= 0) { - return; + mod_client_death_env(); + + Death(0, 32000, SPELL_UNKNOWN, SkillHandtoHand); } - - if (app->size != sizeof(MoveItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_MoveItem, size=%i, expected %i", app->size, sizeof(MoveItem_Struct)); - return; - } - - MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer; - if(spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) - { - if(mi->from_slot != mi->to_slot && (mi->from_slot <= EmuConstants::GENERAL_END || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) - { - char *detect = nullptr; - const ItemInst *itm_from = GetInv().GetItem(mi->from_slot); - const ItemInst *itm_to = GetInv().GetItem(mi->to_slot); - MakeAnyLenString(&detect, "Player issued a move item from %u(item id %u) to %u(item id %u) while casting %u.", - mi->from_slot, - itm_from ? itm_from->GetID() : 0, - mi->to_slot, - itm_to ? itm_to->GetID() : 0, - casting_spell_id); - database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName()); - safe_delete_array(detect); - Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots - return; - } - } - - // Illegal bagslot useage checks. Currently, user only receives a message if this check is triggered. - bool mi_hack = false; - - if(mi->from_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->from_slot <= EmuConstants::CURSOR_BAG_END) { - if(mi->from_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 from_parent = m_inv.CalcSlotId(mi->from_slot); - if(!m_inv[from_parent]) { mi_hack = true; } - else if(!m_inv[from_parent]->IsType(ItemClassContainer)) { mi_hack = true; } - else if(m_inv.CalcBagIdx(mi->from_slot) >= m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if(mi->to_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->to_slot <= EmuConstants::CURSOR_BAG_END) { - if(mi->to_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 to_parent = m_inv.CalcSlotId(mi->to_slot); - if(!m_inv[to_parent]) { mi_hack = true; } - else if(!m_inv[to_parent]->IsType(ItemClassContainer)) { mi_hack = true; } - else if(m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if(mi_hack) { Message(15, "Caution: Illegal use of inaccessable bag slots!"); } - - if(!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { SwapItemResync(mi); } - + SendHPUpdate(); return; } -void Client::Handle_OP_Camp(const EQApplicationPacket *app) { -#ifdef BOTS - // This block is necessary to clean up any bot objects owned by a Client - Bot::BotHealRotationsClear(this); - Bot::BotOrderCampAll(this); -#endif - if(IsLFP()) - worldserver.StopLFP(CharacterID()); - - if (GetGM()) - { - OnDisconnect(true); - return; - } - camp_timer.Start(29000,true); - return; -} - -void Client::Handle_OP_Logout(const EQApplicationPacket *app) +void Client::Handle_OP_FaceChange(const EQApplicationPacket *app) { - //LogFile->write(EQEMuLog::Debug, "%s sent a logout packet.", GetName()); - //we will save when we get destroyed soon anyhow - //Save(); + if (app->size != sizeof(FaceChange_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_FaceChange: Expected: %i, Got: %i", + sizeof(FaceChange_Struct), app->size); + return; + } - SendLogoutPackets(); + // Notify other clients in zone + entity_list.QueueClients(this, app, false); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); - FastQueuePacket(&outapp); - - Disconnect(); + FaceChange_Struct* fc = (FaceChange_Struct*)app->pBuffer; + m_pp.haircolor = fc->haircolor; + m_pp.beardcolor = fc->beardcolor; + m_pp.eyecolor1 = fc->eyecolor1; + m_pp.eyecolor2 = fc->eyecolor2; + m_pp.hairstyle = fc->hairstyle; + m_pp.face = fc->face; + m_pp.beard = fc->beard; + m_pp.drakkin_heritage = fc->drakkin_heritage; + m_pp.drakkin_tattoo = fc->drakkin_tattoo; + m_pp.drakkin_details = fc->drakkin_details; + Save(); + Message_StringID(13, FACE_ACCEPTED); + //Message(13, "Facial features updated."); return; } void Client::Handle_OP_FeignDeath(const EQApplicationPacket *app) { - if(GetClass() != MONK) + if (GetClass() != MONK) return; - if(!p_timers.Expired(&database, pTimerFeignDeath, false)) { - Message(13,"Ability recovery time not yet met."); + if (!p_timers.Expired(&database, pTimerFeignDeath, false)) { + Message(13, "Ability recovery time not yet met."); return; } int reuse = FeignDeathReuseTime; switch (GetAA(aaRapidFeign)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 2; - break; - case 3: - reuse -= 5; - break; + case 1: + reuse -= 1; + break; + case 2: + reuse -= 2; + break; + case 3: + reuse -= 5; + break; } - p_timers.Start(pTimerFeignDeath, reuse-1); + p_timers.Start(pTimerFeignDeath, reuse - 1); //BreakInvis(); @@ -3879,213 +5604,663 @@ void Client::Handle_OP_FeignDeath(const EQApplicationPacket *app) return; } -void Client::Handle_OP_Sneak(const EQApplicationPacket *app) +void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) { - if(!HasSkill(SkillSneak) && GetSkill(SkillSneak) == 0) { - return; //You cannot sneak if you do not have sneak - } - - if(!p_timers.Expired(&database, pTimerSneak, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerSneak, SneakReuseTime-1); - - bool was = sneaking; - if (sneaking){ - sneaking = false; - hidden = false; - improved_hidden = false; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } + if (app->size != sizeof(FindPersonRequest_Struct)) + printf("Error in FindPersonRequest_Struct. Expected size of: %zu, but got: %i\n", sizeof(FindPersonRequest_Struct), app->size); else { - CheckIncreaseSkill(SkillSneak, nullptr, 5); - } - float hidechance = ((GetSkill(SkillSneak)/300.0f) + .25) * 100; - float random = MakeRandomFloat(0, 99); - if(!was && random < hidechance) { - sneaking = true; - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x0F; - sa_out->parameter = sneaking; - QueuePacket(outapp); - safe_delete(outapp); - if(GetClass() == ROGUE){ - outapp = new EQApplicationPacket(OP_SimpleMessage,12); - SimpleMessage_Struct *msg=(SimpleMessage_Struct *)outapp->pBuffer; - msg->color=0x010E; - if (sneaking){ - msg->string_id=347; + FindPersonRequest_Struct* t = (FindPersonRequest_Struct*)app->pBuffer; + + std::vector points; + Mob* target = entity_list.GetMob(t->npc_id); + + if (target == nullptr) { + //empty length packet == not found. + EQApplicationPacket outapp(OP_FindPersonReply, 0); + QueuePacket(&outapp); + return; } - else { - msg->string_id=348; + + if (!RuleB(Pathing, Find) && RuleB(Bazaar, EnableWarpToTrader) && target->IsClient() && (target->CastToClient()->Trader || + target->CastToClient()->Buyer)) { + Message(15, "Moving you to Trader %s", target->GetName()); + MovePC(zone->GetZoneID(), zone->GetInstanceID(), target->GetX(), target->GetY(), target->GetZ(), 0.0f); } - FastQueuePacket(&outapp); - } - return; -} -void Client::Handle_OP_Hide(const EQApplicationPacket *app) -{ - if(!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) - { - //Can not be able to train hide but still have it from racial though - return; //You cannot hide if you do not have hide - } - - if(!p_timers.Expired(&database, pTimerHide, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - int reuse = HideReuseTime - GetAA(209); - p_timers.Start(pTimerHide, reuse-1); - - float hidechance = ((GetSkill(SkillHide)/250.0f) + .25) * 100; - float random = MakeRandomFloat(0, 100); - CheckIncreaseSkill(SkillHide, nullptr, 5); - if (random < hidechance) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 1; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if(GetAA(aaShroudofStealth)){ - improved_hidden = true; - hidden = true; + if (!RuleB(Pathing, Find) || !zone->pathing) + { + //fill in the path array... + // + points.resize(2); + points[0].x = GetX(); + points[0].y = GetY(); + points[0].z = GetZ(); + points[1].x = target->GetX(); + points[1].y = target->GetY(); + points[1].z = target->GetZ(); } else - hidden = true; - } - if(GetClass() == ROGUE){ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage,sizeof(SimpleMessage_Struct)); - SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; - msg->color = 0x010E; - Mob *evadetar = GetTarget(); - if (!auto_attack && (evadetar && evadetar->CheckAggro(this) - && evadetar->IsNPC())) { - if (MakeRandomInt(0, 260) < (int)GetSkill(SkillHide)) { - msg->string_id = EVADE_SUCCESS; - RogueEvade(evadetar); - } else { - msg->string_id = EVADE_FAIL; + { + Map::Vertex Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); + Map::Vertex End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION); + + if (!zone->zonemap->LineIntersectsZone(Start, End, 1.0f, nullptr) && zone->pathing->NoHazards(Start, End)) + { + points.resize(2); + points[0].x = Start.x; + points[0].y = Start.y; + points[0].z = Start.z; + + points[1].x = End.x; + points[1].y = End.y; + points[1].z = End.z; + } - } else { - if (hidden){ - msg->string_id = HIDE_SUCCESS; - } - else { - msg->string_id = HIDE_FAIL; + else + { + std::list pathlist = zone->pathing->FindRoute(Start, End); + + if (pathlist.size() == 0) + { + EQApplicationPacket outapp(OP_FindPersonReply, 0); + QueuePacket(&outapp); + return; + } + + //the client seems to have issues with packets larger than this + if (pathlist.size() > 36) + { + EQApplicationPacket outapp(OP_FindPersonReply, 0); + QueuePacket(&outapp); + return; + } + + // Live appears to send the points in this order: + // Final destination. + // Current Position. + // rest of the points. + FindPerson_Point p; + + int PointNumber = 0; + + bool LeadsToTeleporter = false; + + Map::Vertex v = zone->pathing->GetPathNodeCoordinates(pathlist.back()); + + p.x = v.x; + p.y = v.y; + p.z = v.z; + points.push_back(p); + + p.x = GetX(); + p.y = GetY(); + p.z = GetZ(); + points.push_back(p); + + for (std::list::iterator Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) + { + if ((*Iterator) == -1) // Teleporter + { + LeadsToTeleporter = true; + break; + } + + Map::Vertex v = zone->pathing->GetPathNodeCoordinates((*Iterator), false); + p.x = v.x; + p.y = v.y; + p.z = v.z; + points.push_back(p); + ++PointNumber; + } + + if (!LeadsToTeleporter) + { + p.x = target->GetX(); + p.y = target->GetY(); + p.z = target->GetZ(); + + points.push_back(p); + } + } } - FastQueuePacket(&outapp); + + SendPathPacket(points); } return; } -void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) +void Client::Handle_OP_Fishing(const EQApplicationPacket *app) { - ChannelMessage_Struct* cm=(ChannelMessage_Struct*)app->pBuffer; - - if (app->size < sizeof(ChannelMessage_Struct)) { - std::cout << "Wrong size " << app->size << ", should be " << sizeof(ChannelMessage_Struct) << "+ on 0x" << std::hex << std::setfill('0') << std::setw(4) << app->GetOpcode() << std::dec << std::endl; - return; - } - if (IsAIControlled()) { - Message(13, "You try to speak but cant move your mouth!"); + if (!p_timers.Expired(&database, pTimerFishing, false)) { + Message(13, "Ability recovery time not yet met."); return; } - ChannelMessageReceived(cm->chan_num, cm->language, cm->skill_in_language, cm->message, cm->targetname); + if (CanFish()) { + parse->EventPlayer(EVENT_FISH_START, this, "", 0); + + //these will trigger GoFish() after a delay if we're able to actually fish, and if not, we won't stop the client from trying again immediately (although we may need to tell it to repop the button) + p_timers.Start(pTimerFishing, FishingReuseTime - 1); + fishing_timer.Start(); + } return; + // Changes made based on Bobs work on foraging. Now can set items in the forage database table to + // forage for. } -void Client::Handle_OP_WearChange(const EQApplicationPacket *app) +void Client::Handle_OP_Forage(const EQApplicationPacket *app) { - if (app->size != sizeof(WearChange_Struct)) { - std::cout << "Wrong size: OP_WearChange, size=" << app->size << ", expected " << sizeof(WearChange_Struct) << std::endl; + + if (!p_timers.Expired(&database, pTimerForaging, false)) { + Message(13, "Ability recovery time not yet met."); return; } + p_timers.Start(pTimerForaging, ForagingReuseTime - 1); - WearChange_Struct* wc=(WearChange_Struct*)app->pBuffer; - if(wc->spawn_id != GetID()) - return; + ForageItem(); - // we could maybe ignore this and just send our own from moveitem - entity_list.QueueClients(this, app, true); - return; -} - -//in zoning.cpp -//void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { -//} - -void Client::Handle_OP_DeleteSpawn(const EQApplicationPacket *app) -{ - // The client will send this with his id when he zones, maybe when he disconnects too? - //eqs->RemoveData(); // Flushing the queue of packet data to allow for proper zoning - - //just make sure this gets out - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); - FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_DeleteSpawn, sizeof(EntityId_Struct)); - EntityId_Struct* eid = (EntityId_Struct*)outapp->pBuffer; - eid->entity_id = GetID(); - - entity_list.QueueClients(this, outapp, false); - safe_delete(outapp); - - hate_list.RemoveEnt(this->CastToMob()); - - Disconnect(); - return; -} - -void Client::Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app) -{ - Handle_OP_Save(app); -} - -void Client::Handle_OP_Save(const EQApplicationPacket *app) -{ - // The payload is 192 bytes - Not sure what is contained in payload - Save(); - return; -} - -void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Who_All_Struct)) { - std::cout << "Wrong size on OP_WhoAll. Got: " << app->size << ", Expected: " << sizeof(Who_All_Struct) << std::endl; - return; - } - Who_All_Struct* whoall = (Who_All_Struct*) app->pBuffer; - - if(whoall->type == 0) // SoF only, for regular /who - entity_list.ZoneWho(this, whoall); - else - WhoAll(whoall); return; } void Client::Handle_OP_FriendsWho(const EQApplicationPacket *app) { - char *FriendsString = (char*) app->pBuffer; + char *FriendsString = (char*)app->pBuffer; FriendsWho(FriendsString); return; } +void Client::Handle_OP_GetGuildMOTD(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildMOTD"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + SendGuildMOTD(true); + + if (IsInAGuild()) + { + SendGuildURL(); + SendGuildChannel(); + } +} + +void Client::Handle_OP_GetGuildsList(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildsList"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + SendGuildList(); +} + +void Client::Handle_OP_GMBecomeNPC(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/becomenpc"); + return; + } + if (app->size != sizeof(BecomeNPC_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMBecomeNPC, size=%i, expected %i", app->size, sizeof(BecomeNPC_Struct)); + return; + } + //entity_list.QueueClients(this, app, false); + BecomeNPC_Struct* bnpc = (BecomeNPC_Struct*)app->pBuffer; + + Mob* cli = (Mob*)entity_list.GetMob(bnpc->id); + if (cli == 0) + return; + + if (cli->IsClient()) + cli->CastToClient()->QueuePacket(app); + cli->SendAppearancePacket(AT_NPCName, 1, true); + cli->CastToClient()->SetBecomeNPC(true); + cli->CastToClient()->SetBecomeNPCLevel(bnpc->maxlevel); + cli->Message_StringID(0, TOGGLE_OFF); + cli->CastToClient()->tellsoff = true; + //TODO: Make this toggle a BecomeNPC flag so that it gets updated when people zone in as well; Make combat work with this. + return; +} + +void Client::Handle_OP_GMDelCorpse(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMDelCorpse_Struct)) + return; + if (this->Admin() < commandEditPlayerCorpses) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/delcorpse"); + return; + } + GMDelCorpse_Struct* dc = (GMDelCorpse_Struct *)app->pBuffer; + Mob* corpse = entity_list.GetMob(dc->corpsename); + if (corpse == 0) { + return; + } + if (corpse->IsCorpse() != true) { + return; + } + corpse->CastToCorpse()->Delete(); + std::cout << name << " deleted corpse " << dc->corpsename << std::endl; + Message(13, "Corpse %s deleted.", dc->corpsename); + return; +} + +void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/emote"); + return; + } + if (app->size != sizeof(GMEmoteZone_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMEmoteZone, size=%i, expected %i", app->size, sizeof(GMEmoteZone_Struct)); + return; + } + GMEmoteZone_Struct* gmez = (GMEmoteZone_Struct*)app->pBuffer; + char* newmessage = 0; + if (strstr(gmez->text, "^") == 0) + entity_list.Message(0, 15, gmez->text); + else{ + for (newmessage = strtok((char*)gmez->text, "^"); newmessage != nullptr; newmessage = strtok(nullptr, "^")) + entity_list.Message(0, 15, newmessage); + } + return; +} + +void Client::Handle_OP_GMEndTraining(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMTrainEnd_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMEndTraining expected %i got %i", sizeof(GMTrainEnd_Struct), app->size); + DumpPacket(app); + return; + } + OPGMEndTraining(app); + return; +} + +void Client::Handle_OP_GMFind(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/find"); + return; + } + if (app->size != sizeof(GMSummon_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMFind, size=%i, expected %i", app->size, sizeof(GMSummon_Struct)); + return; + } + //Break down incoming + GMSummon_Struct* request = (GMSummon_Struct*)app->pBuffer; + //Create a new outgoing + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GMFind, sizeof(GMSummon_Struct)); + GMSummon_Struct* foundplayer = (GMSummon_Struct*)outapp->pBuffer; + //Copy the constants + strcpy(foundplayer->charname, request->charname); + strcpy(foundplayer->gmname, request->gmname); + //Check if the NPC exits intrazone... + Mob* gt = entity_list.GetMob(request->charname); + if (gt != 0) { + foundplayer->success = 1; + foundplayer->x = (int32)gt->GetX(); + foundplayer->y = (int32)gt->GetY(); + + foundplayer->z = (int32)gt->GetZ(); + foundplayer->zoneID = zone->GetZoneID(); + } + //Send the packet... + FastQueuePacket(&outapp); + return; +} + +void Client::Handle_OP_GMGoto(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMSummon_Struct)) { + std::cout << "Wrong size on OP_GMGoto. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; + return; + } + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/goto"); + return; + } + GMSummon_Struct* gmg = (GMSummon_Struct*)app->pBuffer; + Mob* gt = entity_list.GetMob(gmg->charname); + if (gt != nullptr) { + this->MovePC(zone->GetZoneID(), zone->GetInstanceID(), gt->GetX(), gt->GetY(), gt->GetZ(), gt->GetHeading()); + } + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected."); + else { + ServerPacket* pack = new ServerPacket(ServerOP_GMGoto, sizeof(ServerGMGoto_Struct)); + memset(pack->pBuffer, 0, pack->size); + ServerGMGoto_Struct* wsgmg = (ServerGMGoto_Struct*)pack->pBuffer; + strcpy(wsgmg->myname, this->GetName()); + strcpy(wsgmg->gotoname, gmg->charname); + wsgmg->admin = admin; + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; +} + +void Client::Handle_OP_GMHideMe(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/hideme"); + return; + } + if (app->size != sizeof(SpawnAppearance_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMHideMe, size=%i, expected %i", app->size, sizeof(SpawnAppearance_Struct)); + return; + } + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; + Message(13, "#: %i, %i", sa->type, sa->parameter); + SetHideMe(!sa->parameter); + return; + +} + +void Client::Handle_OP_GMKick(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMKick_Struct)) + return; + if (this->Admin() < minStatusToKick) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/kick"); + return; + } + GMKick_Struct* gmk = (GMKick_Struct *)app->pBuffer; + + Client* client = entity_list.GetClientByName(gmk->name); + if (client == 0) { + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)pack->pBuffer; + strcpy(skp->adminname, gmk->gmname); + strcpy(skp->name, gmk->name); + skp->adminrank = this->Admin(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } + else { + entity_list.QueueClients(this, app); + //client->Kick(); + } + return; +} + +void Client::Handle_OP_GMKill(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/kill"); + return; + } + if (app->size != sizeof(GMKill_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMKill, size=%i, expected %i", app->size, sizeof(GMKill_Struct)); + return; + } + GMKill_Struct* gmk = (GMKill_Struct *)app->pBuffer; + Mob* obj = entity_list.GetMob(gmk->name); + Client* client = entity_list.GetClientByName(gmk->name); + if (obj != 0) { + if (client != 0) { + entity_list.QueueClients(this, app); + } + else { + obj->Kill(); + } + } + else { + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_KillPlayer, sizeof(ServerKillPlayer_Struct)); + ServerKillPlayer_Struct* skp = (ServerKillPlayer_Struct*)pack->pBuffer; + strcpy(skp->gmname, gmk->gmname); + strcpy(skp->target, gmk->name); + skp->admin = this->Admin(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } + return; +} + +void Client::Handle_OP_GMLastName(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMLastName_Struct)) { + std::cout << "Wrong size on OP_GMLastName. Got: " << app->size << ", Expected: " << sizeof(GMLastName_Struct) << std::endl; + return; + } + GMLastName_Struct* gmln = (GMLastName_Struct*)app->pBuffer; + if (strlen(gmln->lastname) >= 64) { + Message(13, "/LastName: New last name too long. (max=63)"); + } + else { + Client* client = entity_list.GetClientByName(gmln->name); + if (client == 0) { + Message(13, "/LastName: %s not found", gmln->name); + } + else { + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(client->account_name, client->name, "/lastname"); + return; + } + else + + client->ChangeLastName(gmln->lastname); + } + gmln->unknown[0] = 1; + gmln->unknown[1] = 1; + gmln->unknown[2] = 1; + gmln->unknown[3] = 1; + entity_list.QueueClients(this, app, false); + } + return; +} + +void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMName_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMNameChange, size=%i, expected %i", app->size, sizeof(GMName_Struct)); + return; + } + const GMName_Struct* gmn = (const GMName_Struct *)app->pBuffer; + if (this->Admin() < minStatusToUseGMCommands){ + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/name"); + return; + } + Client* client = entity_list.GetClientByName(gmn->oldname); + LogFile->write(EQEMuLog::Status, "GM(%s) changeing players name. Old:%s New:%s", GetName(), gmn->oldname, gmn->newname); + bool usedname = database.CheckUsedName((const char*)gmn->newname); + if (client == 0) { + Message(13, "%s not found for name change. Operation failed!", gmn->oldname); + return; + } + if ((strlen(gmn->newname) > 63) || (strlen(gmn->newname) == 0)) { + Message(13, "Invalid number of characters in new name (%s).", gmn->newname); + return; + } + if (!usedname) { + Message(13, "%s is already in use. Operation failed!", gmn->newname); + return; + + } + database.UpdateName(gmn->oldname, gmn->newname); + strcpy(client->name, gmn->newname); + client->Save(); + + if (gmn->badname == 1) { + database.AddToNameFilter(gmn->oldname); + } + EQApplicationPacket* outapp = app->Copy(); + GMName_Struct* gmn2 = (GMName_Struct*)outapp->pBuffer; + gmn2->unknown[0] = 1; + gmn2->unknown[1] = 1; + gmn2->unknown[2] = 1; + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); + UpdateWho(); + return; +} + +void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app) +{ + // Could make this into a rule, although there is a hard limit since we are using a popup, of 4096 bytes that can + // be displayed in the window, including all the HTML formatting tags. + // + const int maxResults = 10; + + if (app->size < sizeof(GMSearchCorpse_Struct)) + { + LogFile->write(EQEMuLog::Debug, "OP_GMSearchCorpse size lower than expected: got %u expected at least %u", + app->size, sizeof(GMSearchCorpse_Struct)); + DumpPacket(app); + return; + } + + GMSearchCorpse_Struct *gmscs = (GMSearchCorpse_Struct *)app->pBuffer; + gmscs->Name[63] = '\0'; + + char *escSearchString = new char[129]; + database.DoEscapeString(escSearchString, gmscs->Name, strlen(gmscs->Name)); + + std::string query = StringFormat("SELECT charname, zoneid, x, y, z, timeofdeath, rezzed, IsBurried " + "FROM player_corpses WheRE charname LIKE '%%%s%%' ORDER BY charname LIMIT %i", + escSearchString, maxResults); + safe_delete_array(escSearchString); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + Message(0, "Query failed: %s.", results.ErrorMessage().c_str()); + return; + } + + if (results.RowCount() == 0) + return; + + if (results.RowCount() == maxResults) + Message(clientMessageError, "Your search found too many results; some are not displayed."); + else + Message(clientMessageYellow, "There are %i corpse(s) that match the search string '%s'.", results.RowCount(), gmscs->Name); + + char charName[64], timeOfDeath[20]; + + std::string popupText = ""; + + for (auto row = results.begin(); row != results.end(); ++row) { + + strn0cpy(charName, row[0], sizeof(charName)); + + uint32 ZoneID = atoi(row[1]); + float CorpseX = atof(row[2]); + float CorpseY = atof(row[3]); + float CorpseZ = atof(row[4]); + + strn0cpy(timeOfDeath, row[5], sizeof(timeOfDeath)); + + bool corpseRezzed = atoi(row[6]); + bool corpseBuried = atoi(row[7]); + + popupText += StringFormat("", + charName, StaticGetZoneName(ZoneID), CorpseX, CorpseY, CorpseZ, timeOfDeath, + corpseRezzed ? "Yes" : "No", corpseBuried ? "Yes" : "No"); + + if (popupText.size() > 4000) { + Message(clientMessageError, "Unable to display all the results."); + break; + } + + } + + popupText += "
NameZoneXYZDate" + "RezzedBuried
 " + "
%s%s%8.0f%8.0f%8.0f%s%s%s
"; + + SendPopupToClient("Corpses", popupText.c_str()); + +} + +void Client::Handle_OP_GMServers(const EQApplicationPacket *app) +{ + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_ZoneStatus, strlen(this->GetName()) + 2); + memset(pack->pBuffer, (uint8)admin, 1); + strcpy((char *)&pack->pBuffer[1], this->GetName()); + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; +} + +void Client::Handle_OP_GMSummon(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMSummon_Struct)) { + std::cout << "Wrong size on OP_GMSummon. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; + return; + } + OPGMSummon(app); + return; +} + +void Client::Handle_OP_GMToggle(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMToggle_Struct)) { + std::cout << "Wrong size on OP_GMToggle. Got: " << app->size << ", Expected: " << sizeof(GMToggle_Struct) << std::endl; + return; + } + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/toggle"); + return; + } + GMToggle_Struct *ts = (GMToggle_Struct *)app->pBuffer; + if (ts->toggle == 0) { + this->Message_StringID(0, TOGGLE_OFF); + //Message(0, "Turning tells OFF"); + tellsoff = true; + } + else if (ts->toggle == 1) { + //Message(0, "Turning tells ON"); + this->Message_StringID(0, TOGGLE_ON); + tellsoff = false; + } + else { + Message(0, "Unkown value in /toggle packet"); + } + UpdateWho(); + return; +} + +void Client::Handle_OP_GMTraining(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMTrainee_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTraining expected %i got %i", sizeof(GMTrainee_Struct), app->size); + DumpPacket(app); + return; + } + OPGMTraining(app); + return; +} + +void Client::Handle_OP_GMTrainSkill(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMSkillChange_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTrainSkill expected %i got %i", sizeof(GMSkillChange_Struct), app->size); + DumpPacket(app); + return; + } + OPGMTrainSkill(app); + return; +} + void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) { if (app->size != sizeof(GMZoneRequest_Struct)) { @@ -4108,7 +6283,7 @@ void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) if (gmzr->zone_id == 0) zid = zonesummon_id; const char * zname = database.GetZoneName(zid); - if(zname == nullptr) + if (zname == nullptr) tarzone[0] = 0; else strcpy(tarzone, zname); @@ -4119,7 +6294,7 @@ void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) } EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMZoneRequest, sizeof(GMZoneRequest_Struct)); - GMZoneRequest_Struct* gmzr2 = (GMZoneRequest_Struct*) outapp->pBuffer; + GMZoneRequest_Struct* gmzr2 = (GMZoneRequest_Struct*)outapp->pBuffer; strcpy(gmzr2->charname, this->GetName()); gmzr2->zone_id = gmzr->zone_id; gmzr2->x = tarx; @@ -4155,2895 +6330,6 @@ void Client::Handle_OP_GMZoneRequest2(const EQApplicationPacket *app) return; } -void Client::Handle_OP_EndLootRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_EndLootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - - SetLooting(false); - - Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); - if (entity == 0) { - Message(13, "Error: OP_EndLootRequest: Corpse not found (ent = 0)"); - if(GetClientVersion() >= EQClientSoD) - Corpse::SendEndLootErrorPacket(this); - else - Corpse::SendLootReqErrorPacket(this); - return; - } - else if (!entity->IsCorpse()) { - Message(13, "Error: OP_EndLootRequest: Corpse not found (!entity->IsCorpse())"); - Corpse::SendLootReqErrorPacket(this); - return; - } - else { - entity->CastToCorpse()->EndLoot(this, app); - } - return; -} - -void Client::Handle_OP_LootRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_LootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - - SetLooting(true); - - Entity* ent = entity_list.GetID(*((uint32*)app->pBuffer)); - if (ent == 0) { - Message(13, "Error: OP_LootRequest: Corpse not found (ent = 0)"); - Corpse::SendLootReqErrorPacket(this); - return; - } - if (ent->IsCorpse()) - { - Corpse *ent_corpse = ent->CastToCorpse(); - if(DistNoRootNoZ(ent_corpse->GetX(), ent_corpse->GetY()) > 625) - { - Message(13, "Corpse too far away."); - Corpse::SendLootReqErrorPacket(this); - return; - } - - if(invisible) { - BuffFadeByEffect(SE_Invisibility); - BuffFadeByEffect(SE_Invisibility2); - invisible = false; - } - if(invisible_undead) { - BuffFadeByEffect(SE_InvisVsUndead); - BuffFadeByEffect(SE_InvisVsUndead2); - invisible_undead = false; - } - if(invisible_animals){ - BuffFadeByEffect(SE_InvisVsAnimals); - invisible_animals = false; - } - if(hidden || improved_hidden){ - hidden = false; - improved_hidden = false; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } - ent->CastToCorpse()->MakeLootRequestPackets(this, app); - return; - } - else { - std::cout << "npc == 0 LOOTING FOOKED3" << std::endl; - Message(13, "Error: OP_LootRequest: Corpse not a corpse?"); - Corpse::SendLootReqErrorPacket(this); - } - return; -} - -void Client::Handle_OP_LDoNOpen(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target && target->IsNPC()) - HandleLDoNOpen(target->CastToNPC()); -} - -void Client::Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target->IsNPC()) - { - if(HasSkill(SkillSenseTraps)) - { - if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNSenseTraps(target->CastToNPC(), GetSkill(SkillSenseTraps), LDoNTypeMechanical); - } - else - Message(13, "You do not have the sense traps skill."); - } -} - -void Client::Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target->IsNPC()) - { - if(HasSkill(SkillDisarmTraps)) - { - if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNDisarm(target->CastToNPC(), GetSkill(SkillDisarmTraps), LDoNTypeMechanical); - } - else - Message(13, "You do not have the disarm trap skill."); - } -} - -void Client::Handle_OP_LDoNPickLock(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target->IsNPC()) - { - if(HasSkill(SkillPickLock)) - { - if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNPickLock(target->CastToNPC(), GetSkill(SkillPickLock), LDoNTypeMechanical); - } - else - Message(13, "You do not have the pick locks skill."); - } -} - -void Client::Handle_OP_LDoNInspect(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target && target->GetClass() == LDON_TREASURE) - Message(15, "%s", target->GetCleanName()); -} - -void Client::Handle_OP_Dye(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(DyeStruct)) - printf("Wrong size of DyeStruct, Got: %i, Expected: %zu\n",app->size,sizeof(DyeStruct)); - else{ - DyeStruct* dye = (DyeStruct*)app->pBuffer; - DyeArmor(dye); - } - return; -} - -void Client::Handle_OP_ConfirmDelete(const EQApplicationPacket* app){ - return; -} - -void Client::Handle_OP_LootItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(LootingItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LootItem, size=%i, expected %i", app->size, sizeof(LootingItem_Struct)); - return; - } - /* - ** fixed the looting code so that it sends the correct opcodes - ** and now correctly removes the looted item the player selected - ** as well as gives the player the proper item. - ** Also fixed a few UI lock ups that would occur. - */ - - EQApplicationPacket* outapp = 0; - Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); - if (entity == 0) { - Message(13, "Error: OP_LootItem: Corpse not found (ent = 0)"); - outapp = new EQApplicationPacket(OP_LootComplete, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - if (entity->IsCorpse()) { - entity->CastToCorpse()->LootItem(this, app); - return; - } - else { - Message(13, "Error: Corpse not found! (!ent->IsCorpse())"); - Corpse::SendEndLootErrorPacket(this); - } - - return; -} - -void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildDelete"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if(!IsInAGuild() || !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - Message(0,"You are not a guild leader or not in a guild."); - else { - mlog(GUILDS__ACTIONS, "Deleting guild %s (%d)", guild_mgr.GetGuildName(GuildID()), GuildID()); - if (!guild_mgr.DeleteGuild(GuildID())) - Message(0, "Guild delete failed."); - else { - Message(0, "Guild successfully deleted."); - } - } -} - -void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildPublicNote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size < sizeof(GuildUpdate_PublicNote)) { - // client calls for a motd on login even if they arent in a guild - printf("Error: app size of %i < size of OP_GuildPublicNote of %zu\n",app->size,sizeof(GuildUpdate_PublicNote)); - return; - } - GuildUpdate_PublicNote* gpn=(GuildUpdate_PublicNote*)app->pBuffer; - - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(gpn->target, gci)) { - Message(0, "Unable to find '%s'", gpn->target); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - mlog(GUILDS__ACTIONS, "Setting public note on %s (%d) in guild %s (%d) to: %s", - gpn->target, gci.char_id, - guild_mgr.GetGuildName(GuildID()), GuildID(), - gpn->note); - - if(!guild_mgr.SetPublicNote(gci.char_id, gpn->note)) { - Message(13, "Failed to set public note on %s", gpn->target); - } else { - Message(0, "Successfully changed public note on %s", gpn->target); - } -// SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GetGuildMOTD(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildMOTD"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SendGuildMOTD(true); - - if(IsInAGuild()) - { - SendGuildURL(); - SendGuildChannel(); - } -} - -void Client::Handle_OP_GetGuildsList(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildsList"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SendGuildList(); -} - -void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_SetGuildMOTD"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildMOTD_Struct)) { - // client calls for a motd on login even if they arent in a guild - printf("Error: app size of %i != size of GuildMOTD_Struct of %zu\n",app->size,sizeof(GuildMOTD_Struct)); - return; - } - if(!IsInAGuild()) { - Message(13, "You are not in a guild!"); - return; - } - if(!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_MOTD)) { - Message(13, "You do not have permissions to edit your guild's MOTD."); - return; - } - - GuildMOTD_Struct* gmotd=(GuildMOTD_Struct*)app->pBuffer; - - mlog(GUILDS__ACTIONS, "Setting MOTD for %s (%d) to: %s - %s", - guild_mgr.GetGuildName(GuildID()), GuildID(), GetName(), gmotd->motd); - - if (!guild_mgr.SetGuildMOTD(GuildID(), gmotd->motd, GetName())) { - Message(0, "Motd update failed."); - } - - return; -} - -void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) -{ - - mlog(GUILDS__IN_PACKETS, "Got OP_GuildManageBanker of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - if(app->size != sizeof(GuildManageBanker_Struct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of OP_GuildManageBanker of %i\n", app->size, sizeof(GuildManageBanker_Struct)); - return; - } - GuildManageBanker_Struct* gmb = (GuildManageBanker_Struct*) app->pBuffer; - - if(!IsInAGuild()) { - Message(13, "Your not in a guild!"); - return; - } - - CharGuildInfo gci; - - if(!guild_mgr.GetCharInfo(gmb->member, gci)) - { - Message(0, "Unable to find '%s'", gmb->member); - return; - } - bool IsCurrentlyABanker = guild_mgr.GetBankerFlag(gci.char_id); - - bool IsCurrentlyAnAlt = guild_mgr.GetAltFlag(gci.char_id); - - bool NewBankerStatus = gmb->enabled & 0x01; - - bool NewAltStatus = gmb->enabled & 0x02; - - if((IsCurrentlyABanker != NewBankerStatus) && !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - { - Message(13, "Only the guild leader can assign guild bankers!"); - return; - } - - if(IsCurrentlyAnAlt != NewAltStatus) - { - bool IsAllowed = !strncasecmp(GetName(), gmb->member, strlen(GetName())) || (GuildRank() >= GUILD_OFFICER); - - if(!IsAllowed) - { - Message(13, "You are not allowed to change the alt status of %s", gmb->member); - return; - } - } - - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - if(IsCurrentlyABanker != NewBankerStatus) - { - if(!guild_mgr.SetBankerFlag(gci.char_id, NewBankerStatus)) { - Message(13, "Error setting guild banker flag."); - return; - } - - if(NewBankerStatus) - Message(0, "%s has been made a guild banker.", gmb->member); - else - Message(0, "%s is no longer a guild banker.", gmb->member); - } - if(IsCurrentlyAnAlt != NewAltStatus) - { - if(!guild_mgr.SetAltFlag(gci.char_id, NewAltStatus)) { - Message(13, "Error setting guild alt flag."); - return; - } - - if(NewAltStatus) - Message(0, "%s has been marked as an alt.", gmb->member); - else - Message(0, "%s is no longer marked as an alt.", gmb->member); - } -} - -void Client::Handle_OP_GuildPeace(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Got OP_GuildPeace of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - return; -} - -void Client::Handle_OP_GuildWar(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Got OP_GuildWar of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - return; -} - -void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildLeader"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size < 2) { - mlog(GUILDS__ERROR, "Invalid length %d on OP_GuildLeader", app->size); - return; - } - - app->pBuffer[app->size-1] = 0; - GuildMakeLeader* gml=(GuildMakeLeader*)app->pBuffer; - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (GuildRank() != GUILD_LEADER) - Message(0, "Error: You arent the guild leader!"); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - - //NOTE: we could do cross-zone lookups here... - - Client* newleader = entity_list.GetClientByName(gml->target); - if(newleader) { - - mlog(GUILDS__ACTIONS, "Transfering leadership of %s (%d) to %s (%d)", - guild_mgr.GetGuildName(GuildID()), GuildID(), - newleader->GetName(), newleader->CharacterID()); - - if(guild_mgr.SetGuildLeader(GuildID(), newleader->CharacterID())){ - Message(0,"Successfully Transfered Leadership to %s.",gml->target); - newleader->Message(15,"%s has transfered the guild leadership into your hands.",GetName()); - } - else - Message(0,"Could not change leadership at this time."); - } - else - Message(0,"Failed to change leader, could not find target."); - } -// SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildDemote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if(app->size != sizeof(GuildDemoteStruct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n",app->size,sizeof(GuildDemoteStruct)); - return; - } - - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) - Message(0, "You dont have permission to invite."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - GuildDemoteStruct* demote = (GuildDemoteStruct*)app->pBuffer; - - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(demote->target, gci)) { - Message(0, "Unable to find '%s'", demote->target); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - if(gci.rank < 1) { - Message(0, "%s cannot be demoted any further!", demote->target); - return; - } - uint8 rank = gci.rank - 1; - - - mlog(GUILDS__ACTIONS, "Demoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", - demote->target, gci.char_id, - guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, - guild_mgr.GetRankName(GuildID(), rank), rank, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(gci.char_id, rank)) { - Message(13, "Error while setting rank %d on '%s'.", rank, demote->target); - return; - } - Message(0, "Successfully demoted %s to rank %d", demote->target, rank); - } -// SendGuildMembers(GuildID(), true); - return; -} - - -void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildPromote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if(app->size != sizeof(GuildPromoteStruct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n",app->size,sizeof(GuildPromoteStruct)); - return; - } - - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) - Message(0, "You dont have permission to invite."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - GuildPromoteStruct* promote = (GuildPromoteStruct*)app->pBuffer; - - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(promote->target, gci)) { - Message(0, "Unable to find '%s'", promote->target); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - uint8 rank = gci.rank + 1; - - if(rank > GUILD_OFFICER) - return; - - - mlog(GUILDS__ACTIONS, "Promoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", - promote->target, gci.char_id, - guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, - guild_mgr.GetRankName(GuildID(), rank), rank, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(gci.char_id, rank)) { - Message(13, "Error while setting rank %d on '%s'.", rank, promote->target); - return; - } - Message(0, "Successfully promoted %s to rank %d", promote->target, rank); - } - return; -} - -void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildInvite"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildCommand_Struct)) { - std::cout << "Wrong size: OP_GuildInvite, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; - return; - } - - GuildCommand_Struct* gc = (GuildCommand_Struct*) app->pBuffer; - - if (!IsInAGuild()) - Message(0, "Error: You are not in a guild!"); - else if(gc->officer > GUILD_MAX_RANK) - Message(13, "Invalid rank."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - - //ok, the invite is also used for changing rank as well. - Mob* invitee = entity_list.GetMob(gc->othername); - - if(!invitee) { - Message(13, "Prospective guild member %s must be in zone to preform guild operations on them.", gc->othername); - return; - } - - if(invitee->IsClient()) { - Client* client = invitee->CastToClient(); - - //ok, figure out what they are trying to do. - if(client->GuildID() == GuildID()) { - //they are already in this guild, must be a promotion or demotion - if(gc->officer < client->GuildRank()) { - //demotion - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) { - Message(13, "You dont have permission to demote."); - return; - } - - //we could send this to the member and prompt them to see if they want to - //be demoted (I guess), but I dont see a point in that. - - mlog(GUILDS__ACTIONS, "%s (%d) is demoting %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - client->GetName(), client->CharacterID(), - gc->officer, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(client->CharacterID(), gc->officer)) { - Message(13, "There was an error during the demotion, DB may now be inconsistent."); - return; - } - - } else if(gc->officer > client->GuildRank()) { - //promotion - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) { - Message(13, "You dont have permission to demote."); - return; - } - - mlog(GUILDS__ACTIONS, "%s (%d) is asking to promote %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - client->GetName(), client->CharacterID(), - gc->officer, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - //record the promotion with guild manager so we know its valid when we get the reply - guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); - - if(gc->guildeqid == 0) - gc->guildeqid = GuildID(); - - mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for promotion to %s, length %d", client->GetName(), app->size); - mpkt(GUILDS__OUT_PACKET_TRACE, app); - client->QueuePacket(app); - - } else { - Message(13, "That member is already that rank."); - return; - } - } else if(!client->IsInAGuild()) { - //they are not in this or any other guild, this is an invite - // - if(client->GetPendingGuildInvitation()) - { - Message(13, "That person is already considering a guild invitation."); - return; - } - - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_INVITE)) { - Message(13, "You dont have permission to invite."); - return; - } - - mlog(GUILDS__ACTIONS, "Inviting %s (%d) into guild %s (%d)", - client->GetName(), client->CharacterID(), - guild_mgr.GetGuildName(GuildID()), GuildID()); - - //record the invite with guild manager so we know its valid when we get the reply - guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); - - if(gc->guildeqid == 0) - gc->guildeqid = GuildID(); - - mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size); - mpkt(GUILDS__OUT_PACKET_TRACE, app); - client->SetPendingGuildInvitation(true); - client->QueuePacket(app); - - } else { - //they are in some other guild - Message(13,"Player is in a guild."); - return; - } - } -#ifdef BOTS - else if (invitee->IsBot()) { - // The guild system is too tightly coupled with the character_data table so we have to avoid using much of the system - Bot::ProcessGuildInvite(this, invitee->CastToBot()); - return; - } -#endif - } -} - -void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildRemove"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildCommand_Struct)) { - std::cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; - return; - } - GuildCommand_Struct* gc = (GuildCommand_Struct*) app->pBuffer; - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - // we can always remove ourself, otherwise, our rank needs remove permissions - else if (strcasecmp(gc->othername,GetName()) != 0 && - !guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_REMOVE)) - Message(0, "You dont have permission to remove guild members."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { -#ifdef BOTS - if(Bot::ProcessGuildRemoval(this, gc->othername)) - return; -#endif - uint32 char_id; - Client* client = entity_list.GetClientByName(gc->othername); - - if(client) { - if(!client->IsInGuild(GuildID())) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - char_id = client->CharacterID(); - - mlog(GUILDS__ACTIONS, "Removing %s (%d) from guild %s (%d)", - client->GetName(), client->CharacterID(), - guild_mgr.GetGuildName(GuildID()), GuildID()); - } else { - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(gc->othername, gci)) { - Message(0, "Unable to find '%s'", gc->othername); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - char_id = gci.char_id; - - mlog(GUILDS__ACTIONS, "Removing remote/offline %s (%d) into guild %s (%d)", - gci.char_name.c_str(), gci.char_id, - guild_mgr.GetGuildName(GuildID()), GuildID()); - } - - if(!guild_mgr.SetGuild(char_id, GUILD_NONE, 0)) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildManageRemove, sizeof(GuildManageRemove_Struct)); - GuildManageRemove_Struct* gm = (GuildManageRemove_Struct*) outapp->pBuffer; - gm->guildeqid = GuildID(); - strcpy(gm->member, gc->othername); - Message(0,"%s successfully removed from your guild.",gc->othername); - entity_list.QueueClientsGuild(this, outapp, false, GuildID()); - safe_delete(outapp); - } - else - Message(0,"Unable to remove %s from your guild.",gc->othername); - } -// SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildInviteAccept"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SetPendingGuildInvitation(false); - - if (app->size != sizeof(GuildInviteAccept_Struct)) { - std::cout << "Wrong size: OP_GuildInviteAccept, size=" << app->size << ", expected " << sizeof(GuildJoin_Struct) << std::endl; - return; - } - - GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*) app->pBuffer; - - if(GetClientVersion() >= EQClientRoF) - { - if(gj->response > 9) - { - //dont care if the check fails (since we dont know the rank), just want to clear the entry. - guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); - return; - } - } - if (gj->response == 5 || gj->response == 4) { - //dont care if the check fails (since we dont know the rank), just want to clear the entry. - guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); - - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); - - return; - } - - //uint32 tmpeq = gj->guildeqid; - if (IsInAGuild() && gj->response==GuildRank()) - Message(0, "Error: You're already in a guild!"); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - mlog(GUILDS__ACTIONS, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s", - gj->guildeqid, gj->response, gj->inviter, gj->newmember); - - //we dont really care a lot about what this packet means, as long as - //it has been authorized with the guild manager - if(!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) { - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName()); - Message(13, "Invalid invite response packet!"); - return; - } - - if(gj->guildeqid == GuildID()) { - //only need to change rank. - - mlog(GUILDS__ACTIONS, "Changing guild rank of %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - gj->response, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(CharacterID(), gj->response)) { - Message(13, "There was an error during the rank change, DB may now be inconsistent."); - return; - } - } else { - - mlog(GUILDS__ACTIONS, "Adding %s (%d) to guild %s (%d) at rank %d", - GetName(), CharacterID(), - guild_mgr.GetGuildName(gj->guildeqid), gj->guildeqid, - gj->response); - - //change guild and rank - - uint32 guildrank = gj->response; - - if(GetClientVersion() == EQClientRoF) - { - if(gj->response == 8) - { - guildrank = 0; - } - } - - if(!guild_mgr.SetGuild(CharacterID(), gj->guildeqid, guildrank)) { - Message(13, "There was an error during the invite, DB may now be inconsistent."); - return; - } - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) - GuildBanks->SendGuildBank(this); - } - } -} - -void Client::Handle_OP_ManaChange(const EQApplicationPacket *app) -{ - if(app->size == 0) { - // i think thats the sign to stop the songs - if(IsBardSong(casting_spell_id) || bardsong != 0) - InterruptSpell(SONG_ENDS, 0x121); - else - InterruptSpell(INTERRUPT_SPELL, 0x121); - - return; - } - else // I don't think the client sends proper manachanges - { // with a length, just the 0 len ones for stopping songs - //ManaChange_Struct* p = (ManaChange_Struct*)app->pBuffer; - printf("OP_ManaChange from client:\n"); - DumpPacket(app); - } - return; -} - -void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app) -{ - OPMemorizeSpell(app); - return; -} - -void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(SwapSpell_Struct)) { - std::cout << "Wrong size on OP_SwapSpell. Got: " << app->size << ", Expected: " << sizeof(SwapSpell_Struct) << std::endl; - return; - } - const SwapSpell_Struct* swapspell = (const SwapSpell_Struct*) app->pBuffer; - int swapspelltemp; - - if(swapspell->from_slot < 0 || swapspell->from_slot > MAX_PP_SPELLBOOK || swapspell->to_slot < 0 || swapspell->to_slot > MAX_PP_SPELLBOOK) - return; - - swapspelltemp = m_pp.spell_book[swapspell->from_slot]; - if (swapspelltemp < 0){ - return; - } - m_pp.spell_book[swapspell->from_slot] = m_pp.spell_book[swapspell->to_slot]; - m_pp.spell_book[swapspell->to_slot] = swapspelltemp; - - database.SaveCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot); - database.SaveCharacterSpell(this->CharacterID(), swapspelltemp, swapspell->to_slot); - - QueuePacket(app); - return; -} - -void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CastSpell_Struct)) { - std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl; - return; - } - if (IsAIControlled()) { - this->Message_StringID(13, NOT_IN_CONTROL); - //Message(13, "You cant cast right now, you arent in control of yourself!"); - return; - } - - CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer; - -#ifdef _EQDEBUG - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[0], castspell->cs_unknown[0]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[1], castspell->cs_unknown[1]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[2], castspell->cs_unknown[2]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[3], castspell->cs_unknown[3]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %u", &castspell->cs_unknown, *(uint32*) castspell->cs_unknown ); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %i", &castspell->cs_unknown, *(uint32*) castspell->cs_unknown ); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %u %u", &castspell->cs_unknown, *(uint16*) castspell->cs_unknown, *(uint16*) castspell->cs_unknown+sizeof(uint16) ); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %i %i", &castspell->cs_unknown, *(uint16*) castspell->cs_unknown, *(uint16*) castspell->cs_unknown+sizeof(uint16) ); -#endif - LogFile->write(EQEMuLog::Debug, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot); - - std::cout << "OP_CastSpell " << castspell->slot << " spell " << castspell->spell_id << " inventory slot " << castspell->inventoryslot << "\n" << std::endl; - - /* Memorized Spell */ - if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id){ - - uint16 spell_to_cast = 0; - if (castspell->slot < MAX_PP_MEMSPELL) { - spell_to_cast = m_pp.mem_spells[castspell->slot]; - if (spell_to_cast != castspell->spell_id) { - InterruptSpell(castspell->spell_id); //CHEATER!!! - return; - } - } - else if (castspell->slot >= MAX_PP_MEMSPELL) { - InterruptSpell(); - return; - } - - CastSpell(spell_to_cast, castspell->target_id, castspell->slot); - } - /* Spell Slot or Potion Belt Slot */ - else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast - { - //discipline, using the item spell slot - if (castspell->inventoryslot == 0xFFFFFFFF) { - if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { - LogFile->write(EQEMuLog::Debug, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); - InterruptSpell(castspell->spell_id); - } - return; - } - else if ((castspell->inventoryslot <= EmuConstants::GENERAL_END) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check - { - const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field - //bool cancast = true; - if (inst && inst->IsType(ItemClassCommon)) - { - const Item_Struct* item = inst->GetItem(); - if (item->Click.Effect != (uint32)castspell->spell_id) - { - database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, tried to cast a different spell.", zone->GetShortName()); - InterruptSpell(castspell->spell_id); //CHEATER!! - return; - } - - if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) - { - if (item->Click.Level2 > 0) - { - if (GetLevel() >= item->Click.Level2) - { - ItemInst* p_inst = (ItemInst*)inst; - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); - - if (i == 0) { - CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); - } - else { - InterruptSpell(castspell->spell_id); - return; - } - } - else - { - database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, did not meet req level.", zone->GetShortName()); - Message(0, "Error: level not high enough.", castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - else - { - ItemInst* p_inst = (ItemInst*)inst; - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); - - if (i == 0) { - CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); - } - else { - InterruptSpell(castspell->spell_id); - return; - } - } - } - else - { - Message(0, "Error: unknown item->Click.Type (0x%02x)", item->Click.Type); - } - } - else - { - Message(0, "Error: item not found in inventory slot #%i", castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - else - { - Message(0, "Error: castspell->inventoryslot >= %i (0x%04x)", MainCursor, castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - /* Discipline */ - else if (castspell->slot == DISCIPLINE_SPELL_SLOT) { - if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { - printf("Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); - InterruptSpell(castspell->spell_id); - return; - } - } - /* ABILITY cast (LoH and Harm Touch) */ - else if (castspell->slot == ABILITY_SPELL_SLOT) { - uint16 spell_to_cast = 0; - - if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) { - if (!p_timers.Expired(&database, pTimerLayHands)) { - Message(13, "Ability recovery time not yet met."); - InterruptSpell(castspell->spell_id); - return; - } - spell_to_cast = SPELL_LAY_ON_HANDS; - p_timers.Start(pTimerLayHands, LayOnHandsReuseTime); - } - else if ((castspell->spell_id == SPELL_HARM_TOUCH - || castspell->spell_id == SPELL_HARM_TOUCH2) && GetClass() == SHADOWKNIGHT) { - if (!p_timers.Expired(&database, pTimerHarmTouch)) { - Message(13, "Ability recovery time not yet met."); - InterruptSpell(castspell->spell_id); - return; - } - - // determine which version of HT we are casting based on level - if (GetLevel() < 40) - spell_to_cast = SPELL_HARM_TOUCH; - else - spell_to_cast = SPELL_HARM_TOUCH2; - - p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); - } - - if (spell_to_cast > 0) // if we've matched LoH or HT, cast now - CastSpell(spell_to_cast, castspell->target_id, castspell->slot); - } - return; -} - -void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(DeleteItem_Struct)) { - std::cout << "Wrong size on OP_DeleteItem. Got: " << app->size << ", Expected: " << sizeof(DeleteItem_Struct) << std::endl; - return; - } - - DeleteItem_Struct* alc = (DeleteItem_Struct*) app->pBuffer; - const ItemInst *inst = GetInv().GetItem(alc->from_slot); - if (inst && inst->GetItem()->ItemType == ItemTypeAlcohol) { - entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), inst->GetItem()->Name); - CheckIncreaseSkill(SkillAlcoholTolerance, nullptr, 25); - - int16 AlcoholTolerance = GetSkill(SkillAlcoholTolerance); - int16 IntoxicationIncrease; - - if(GetClientVersion() < EQClientSoD) - IntoxicationIncrease = (200 - AlcoholTolerance) * 30 / 200 + 10; - else - IntoxicationIncrease = (270 - AlcoholTolerance) * 0.111111108 + 10; - - if(IntoxicationIncrease < 0) - IntoxicationIncrease = 1; - - m_pp.intoxication += IntoxicationIncrease; - - if(m_pp.intoxication > 200) - m_pp.intoxication = 200; - } - DeleteItemInInventory(alc->from_slot, 1); - - return; -} - -void Client::Handle_OP_CombatAbility(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CombatAbility_Struct)) { - std::cout << "Wrong size on OP_CombatAbility. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << std::endl; - return; - } - OPCombatAbility(app); - return; -} - -void Client::Handle_OP_Taunt(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientTarget_Struct)) { - std::cout << "Wrong size on OP_Taunt. Got: " << app->size << ", Expected: "<< sizeof(ClientTarget_Struct) << std::endl; - return; - } - - if(!p_timers.Expired(&database, pTimerTaunt, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerTaunt, TauntReuseTime-1); - - if(GetTarget() == nullptr || !GetTarget()->IsNPC()) - return; - - Taunt(GetTarget()->CastToNPC(), false); - return; -} - -void Client::Handle_OP_InstillDoubt(const EQApplicationPacket *app) -{ - //packet is empty as of 12/14/04 - - if(!p_timers.Expired(&database, pTimerInstillDoubt, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerInstillDoubt, InstillDoubtReuseTime-1); - - InstillDoubt(GetTarget()); - return; -} - -void Client::Handle_OP_RezzAnswer(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_RezzAnswer, app, Resurrect_Struct); - - const Resurrect_Struct* ra = (const Resurrect_Struct*) app->pBuffer; - - _log(SPELLS__REZ, "Received OP_RezzAnswer from client. Pendingrezzexp is %i, action is %s", - PendingRezzXP, ra->action ? "ACCEPT" : "DECLINE"); - - _pkt(SPELLS__REZ, app); - - OPRezzAnswer(ra->action, ra->spellid, ra->zone_id, ra->instance_id, ra->x, ra->y, ra->z); - - if(ra->action == 1) - { - EQApplicationPacket* outapp = app->Copy(); - // Send the OP_RezzComplete to the world server. This finds it's way to the zone that - // the rezzed corpse is in to mark the corpse as rezzed. - outapp->SetOpcode(OP_RezzComplete); - worldserver.RezzPlayer(outapp, 0, 0, OP_RezzComplete); - safe_delete(outapp); - } - return; -} - -void Client::Handle_OP_GMSummon(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSummon_Struct)) { - std::cout << "Wrong size on OP_GMSummon. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; - return; - } - OPGMSummon(app); - return; -} - -void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequest, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); - return; - } - // Client requesting a trade session from an npc/client - // Trade session not started until OP_TradeRequestAck is sent - - BreakInvis(); - - // Pass trade request on to recipient - TradeRequest_Struct* msg = (TradeRequest_Struct*) app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - tradee->CastToClient()->QueuePacket(app); - } -#ifndef BOTS - else if (tradee && tradee->IsNPC()) { -#else - else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { -#endif - //npcs always accept - trade->Start(msg->to_mob_id); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); - TradeRequest_Struct* acc = (TradeRequest_Struct*) outapp->pBuffer; - acc->from_mob_id = msg->to_mob_id; - acc->to_mob_id = msg->from_mob_id; - FastQueuePacket(&outapp); - safe_delete(outapp); - } - return; -} - -void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequestAck, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); - return; - } - // Trade request recipient is acknowledging they are able to trade - // After this, the trade session has officially started - // Send ack on to trade initiator if client - TradeRequest_Struct* msg = (TradeRequest_Struct*) app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - trade->Start(msg->to_mob_id); - tradee->CastToClient()->QueuePacket(app); - } - return; -} - -void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CancelTrade_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_CancelTrade, size=%i, expected %i", app->size, sizeof(CancelTrade_Struct)); - return; - } - Mob* with = trade->With(); - if (with && with->IsClient()) { - CancelTrade_Struct* msg = (CancelTrade_Struct*) app->pBuffer; - - // Forward cancel packet to other client - msg->fromid = with->GetID(); - //msg->action = 1; - - with->CastToClient()->QueuePacket(app); - - // Put trade items/cash back into inventory - FinishTrade(this); - trade->Reset(); - } - else if(with){ - CancelTrade_Struct* msg = (CancelTrade_Struct*) app->pBuffer; - msg->fromid = with->GetID(); - QueuePacket(app); - FinishTrade(this); - trade->Reset(); - } - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app) -{ - Mob* with = trade->With(); - trade->state = TradeAccepted; - - if (with && with->IsClient()) { - //finish trade... - // Have both accepted? - Client* other = with->CastToClient(); - other->QueuePacket(app); - - if (other->trade->state == trade->state) { - other->trade->state = TradeCompleting; - trade->state = TradeCompleting; - - // should we do this for NoDrop items as well? - if (CheckTradeLoreConflict(other) || other->CheckTradeLoreConflict(this)) { - Message_StringID(13, TRADE_CANCEL_LORE); - other->Message_StringID(13, TRADE_CANCEL_LORE); - this->FinishTrade(this); - other->FinishTrade(other); - other->trade->Reset(); - trade->Reset(); - } - else { - // Audit trade to database for both trade streams - other->trade->LogTrade(); - trade->LogTrade(); - - // start QS code - if(RuleB(QueryServ, PlayerLogTrades)) { - QSPlayerLogTrade_Struct event_entry; - std::list event_details; - - memset(&event_entry, 0, sizeof(QSPlayerLogTrade_Struct)); - - // Perform actual trade - this->FinishTrade(other, true, &event_entry, &event_details); - other->FinishTrade(this, false, &event_entry, &event_details); - - event_entry._detail_count = event_details.size(); - - ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogTrades, sizeof(QSPlayerLogTrade_Struct)+(sizeof(QSTradeItems_Struct)* event_entry._detail_count)); - QSPlayerLogTrade_Struct* qs_buf = (QSPlayerLogTrade_Struct*)qs_pack->pBuffer; - - memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogTrade_Struct)); - - int offset = 0; - - for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { - QSTradeItems_Struct* detail = reinterpret_cast(*iter); - qs_buf->items[offset] = *detail; - safe_delete(detail); - } - - event_details.clear(); - - qs_pack->Deflate(); - - if(worldserver.Connected()) - worldserver.SendPacket(qs_pack); - - safe_delete(qs_pack); - // end QS code - } - else { - this->FinishTrade(other); - other->FinishTrade(this); - } - - other->trade->Reset(); - trade->Reset(); - } - // All done - EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); - other->QueuePacket(outapp); - this->FastQueuePacket(&outapp); - } - } - // Trading with a Mob object that is not a Client. - else if(with) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); - QueuePacket(outapp); - safe_delete(outapp); - if(with->IsNPC()) { - // Audit trade to database for player trade stream - if(RuleB(QueryServ, PlayerLogHandins)) { - QSPlayerLogHandin_Struct event_entry; - std::list event_details; - - memset(&event_entry, 0, sizeof(QSPlayerLogHandin_Struct)); - - FinishTrade(with->CastToNPC(), false, &event_entry, &event_details); - - event_entry._detail_count = event_details.size(); - - ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogHandins, sizeof(QSPlayerLogHandin_Struct)+(sizeof(QSHandinItems_Struct)* event_entry._detail_count)); - QSPlayerLogHandin_Struct* qs_buf = (QSPlayerLogHandin_Struct*)qs_pack->pBuffer; - - memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogHandin_Struct)); - - int offset = 0; - - for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { - QSHandinItems_Struct* detail = reinterpret_cast(*iter); - qs_buf->items[offset] = *detail; - safe_delete(detail); - } - - event_details.clear(); - - qs_pack->Deflate(); - - if(worldserver.Connected()) - worldserver.SendPacket(qs_pack); - - safe_delete(qs_pack); - } - else { - FinishTrade(with->CastToNPC()); - } - } -#ifdef BOTS - // TODO: Log Bot trades - else if(with->IsBot()) - with->CastToBot()->FinishTrade(this, Bot::BotTradeClientNormal); -#endif - trade->Reset(); - } - - - return; -} - -void Client::Handle_OP_TradeBusy(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeBusy_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeBusy, size=%i, expected %i", app->size, sizeof(TradeBusy_Struct)); - return; - } - // Trade request recipient is cancelling the trade due to being busy - // Trade requester gets message "I'm busy right now" - // Send busy message on to trade initiator if client - TradeBusy_Struct* msg = (TradeBusy_Struct*) app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - tradee->CastToClient()->QueuePacket(app); - } - return; -} - -void Client::Handle_OP_BoardBoat(const EQApplicationPacket *app) -{ - - if(app->size <= 5) - return; - - char *boatname; - boatname = new char[app->size-3]; - memset(boatname, 0, app->size-3); - memcpy(boatname, app->pBuffer, app->size-4); - - Mob* boat = entity_list.GetMob(boatname); - if (boat) - this->BoatID = boat->GetID(); // set the client's BoatID to show that it's on this boat - safe_delete_array(boatname); - return; -} - -void Client::Handle_OP_LeaveBoat(const EQApplicationPacket *app) -{ - Mob* boat = entity_list.GetMob(this->BoatID); // find the mob corresponding to the boat id - if (boat) { - if ((boat->GetTarget() == this) && boat->GetHateAmount(this) == 0) // if the client somehow left while still controlling the boat (and the boat isn't attacking them) - boat->SetTarget(0); // fix it to stop later problems - } - this->BoatID = 0; - return; -} - -void Client::Handle_OP_RandomReq(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RandomReq_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_RandomReq, size=%i, expected %i", app->size, sizeof(RandomReq_Struct)); - return; - } - const RandomReq_Struct* rndq = (const RandomReq_Struct*) app->pBuffer; - uint32 randLow=rndq->low > rndq->high?rndq->high:rndq->low; - uint32 randHigh=rndq->low > rndq->high?rndq->low:rndq->high; - uint32 randResult; - - if(randLow==0 && randHigh==0) - { // defaults - randLow=0; - randHigh=100; - } - randResult=MakeRandomInt(randLow, randHigh); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RandomReply, sizeof(RandomReply_Struct)); - RandomReply_Struct* rr = (RandomReply_Struct*)outapp->pBuffer; - rr->low=randLow; - rr->high=randHigh; - rr->result=randResult; - strcpy(rr->name, GetName()); - entity_list.QueueCloseClients(this, outapp, false, 400); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_Buff(const EQApplicationPacket *app) -{ - if (app->size != sizeof(SpellBuffFade_Struct)) - { - LogFile->write(EQEMuLog::Error, "Size mismatch in OP_Buff. expected %i got %i", sizeof(SpellBuffFade_Struct), app->size); - DumpPacket(app); - return; - } - - SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*) app->pBuffer; - uint32 spid = sbf->spellid; - mlog(SPELLS__BUFFS, "Client requested that buff with spell id %d be canceled.", spid); - - //something about IsDetrimentalSpell() crashes this portion of code.. - //tbh we shouldn't use it anyway since this is a simple red vs blue buff check and - //isdetrimentalspell() is much more complex - if(spid == 0xFFFF || (IsValidSpell(spid) && (spells[spid].goodEffect == 0))) - QueuePacket(app); - else - BuffFadeBySpellID(spid); - - return; -} - -void Client::Handle_OP_GMHideMe(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/hideme"); - return; - } - if (app->size != sizeof(SpawnAppearance_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMHideMe, size=%i, expected %i", app->size, sizeof(SpawnAppearance_Struct)); - return; - } - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; - Message(13, "#: %i, %i", sa->type, sa->parameter); - SetHideMe(!sa->parameter); - return; - -} - -void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMName_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMNameChange, size=%i, expected %i", app->size, sizeof(GMName_Struct)); - return; - } - const GMName_Struct* gmn = (const GMName_Struct *)app->pBuffer; - if(this->Admin() < minStatusToUseGMCommands){ - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/name"); - return; - } - Client* client = entity_list.GetClientByName(gmn->oldname); - LogFile->write(EQEMuLog::Status, "GM(%s) changeing players name. Old:%s New:%s", GetName(), gmn->oldname, gmn->newname); - bool usedname = database.CheckUsedName((const char*) gmn->newname); - if(client==0) { - Message(13, "%s not found for name change. Operation failed!", gmn->oldname); - return; - } - if((strlen(gmn->newname) > 63) || (strlen(gmn->newname) == 0)) { - Message(13, "Invalid number of characters in new name (%s).", gmn->newname); - return; - } - if (!usedname) { - Message(13, "%s is already in use. Operation failed!", gmn->newname); - return; - - } - database.UpdateName(gmn->oldname, gmn->newname); - strcpy(client->name, gmn->newname); - client->Save(); - - if(gmn->badname==1) { - database.AddToNameFilter(gmn->oldname); - } - EQApplicationPacket* outapp = app->Copy(); - GMName_Struct* gmn2 = (GMName_Struct*) outapp->pBuffer; - gmn2->unknown[0] = 1; - gmn2->unknown[1] = 1; - gmn2->unknown[2] = 1; - entity_list.QueueClients(this, outapp, false); - safe_delete(outapp); - UpdateWho(); - return; -} - -void Client::Handle_OP_GMKill(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/kill"); - return; - } - if (app->size != sizeof(GMKill_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMKill, size=%i, expected %i", app->size, sizeof(GMKill_Struct)); - return; - } - GMKill_Struct* gmk = (GMKill_Struct *)app->pBuffer; - Mob* obj = entity_list.GetMob(gmk->name); - Client* client = entity_list.GetClientByName(gmk->name); - if(obj!=0) { - if(client!=0) { - entity_list.QueueClients(this,app); - } - else { - obj->Kill(); - } - } - else { - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_KillPlayer, sizeof(ServerKillPlayer_Struct)); - ServerKillPlayer_Struct* skp = (ServerKillPlayer_Struct*) pack->pBuffer; - strcpy(skp->gmname, gmk->gmname); - strcpy(skp->target, gmk->name); - skp->admin = this->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } - return; -} - -void Client::Handle_OP_GMLastName(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMLastName_Struct)) { - std::cout << "Wrong size on OP_GMLastName. Got: " << app->size << ", Expected: " << sizeof(GMLastName_Struct) << std::endl; - return; - } - GMLastName_Struct* gmln = (GMLastName_Struct*) app->pBuffer; - if (strlen(gmln->lastname) >= 64) { - Message(13, "/LastName: New last name too long. (max=63)"); - } - else { - Client* client = entity_list.GetClientByName(gmln->name); - if (client == 0) { - Message(13, "/LastName: %s not found", gmln->name); - } - else { - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(client->account_name, client->name, "/lastname"); - return; - } - else - - client->ChangeLastName(gmln->lastname); - } - gmln->unknown[0] = 1; - gmln->unknown[1] = 1; - gmln->unknown[2] = 1; - gmln->unknown[3] = 1; - entity_list.QueueClients(this, app, false); - } - return; -} - -void Client::Handle_OP_GMToggle(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMToggle_Struct)) { - std::cout << "Wrong size on OP_GMToggle. Got: " << app->size << ", Expected: " << sizeof(GMToggle_Struct) << std::endl; - return; - } - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/toggle"); - return; - } - GMToggle_Struct *ts = (GMToggle_Struct *) app->pBuffer; - if (ts->toggle == 0) { - this->Message_StringID(0,TOGGLE_OFF); - //Message(0, "Turning tells OFF"); - tellsoff = true; - } - else if (ts->toggle == 1) { - //Message(0, "Turning tells ON"); - this->Message_StringID(0,TOGGLE_ON); - tellsoff = false; - } - else { - Message(0, "Unkown value in /toggle packet"); - } - UpdateWho(); - return; -} - -void Client::Handle_OP_LFGCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(LFG_Struct)) { - std::cout << "Wrong size on OP_LFGCommand. Got: " << app->size << ", Expected: " << sizeof(LFG_Struct) << std::endl; - DumpPacket(app); - return; - } - - // Process incoming packet - LFG_Struct* lfg = (LFG_Struct*) app->pBuffer; - - switch(lfg->value & 0xFF) { - case 0: - if(LFG) { - database.SetLFG(CharacterID(), false); - LFG = false; - LFGComments[0] = '\0'; - } - break; - case 1: - if(!LFG) { - LFG = true; - database.SetLFG(CharacterID(), true); - } - LFGFromLevel = lfg->FromLevel; - LFGToLevel = lfg->ToLevel; - LFGMatchFilter = lfg->MatchFilter; - strcpy(LFGComments, lfg->Comments); - break; - default: - Message(0, "Error: unknown LFG value %i", lfg->value); - } - - UpdateWho(); - - // Issue outgoing packet to notify other clients - EQApplicationPacket* outapp = new EQApplicationPacket(OP_LFGAppearance, sizeof(LFG_Appearance_Struct)); - LFG_Appearance_Struct* lfga = (LFG_Appearance_Struct*)outapp->pBuffer; - lfga->spawn_id = this->GetID(); - lfga->lfg = (uint8)LFG; - - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_GMGoto(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSummon_Struct)) { - std::cout << "Wrong size on OP_GMGoto. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; - return; - } - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/goto"); - return; - } - GMSummon_Struct* gmg = (GMSummon_Struct*) app->pBuffer; - Mob* gt = entity_list.GetMob(gmg->charname); - if (gt != nullptr) { - this->MovePC(zone->GetZoneID(), zone->GetInstanceID(), gt->GetX(), gt->GetY(), gt->GetZ(), gt->GetHeading()); - } - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected."); - else { - ServerPacket* pack = new ServerPacket(ServerOP_GMGoto, sizeof(ServerGMGoto_Struct)); - memset(pack->pBuffer, 0, pack->size); - ServerGMGoto_Struct* wsgmg = (ServerGMGoto_Struct*) pack->pBuffer; - strcpy(wsgmg->myname, this->GetName()); - strcpy(wsgmg->gotoname, gmg->charname); - wsgmg->admin = admin; - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - -void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // This is when a potential purchaser right clicks on this client who is in Trader mode to - // browse their goods. - // - _pkt(TRADING__PACKETS, app); - - TraderClick_Struct* tcs= (TraderClick_Struct*)app->pBuffer; - - if(app->size!=sizeof(TraderClick_Struct)) { - - _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: Returning due to struct size mismatch"); - - return; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct)); - - TraderClick_Struct* outtcs=(TraderClick_Struct*)outapp->pBuffer; - - Client* Customer = entity_list.GetClientByID(tcs->TraderID); - - if (Customer) - outtcs->Approval = Customer->WithCustomer(GetID()); - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" - " returned a nullptr pointer"); - return; - } - - outtcs->TraderID = tcs->TraderID; - - outtcs->Unknown008 = 0x3f800000; - - QueuePacket(outapp); - - _pkt(TRADING__PACKETS, outapp); - - if(outtcs->Approval) { - this->BulkSendTraderInventory(Customer->CharacterID()); - Customer->Trader_CustomerBrowsing(this); - } - else - Message_StringID(clientMessageYellow, TRADER_BUSY); - - safe_delete(outapp); - - return; -} - -void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Click_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ShopRequest, size=%i, expected %i", app->size, sizeof(Merchant_Click_Struct)); - return; - } - - Merchant_Click_Struct* mc=(Merchant_Click_Struct*)app->pBuffer; - - // Send back opcode OP_ShopRequest - tells client to open merchant window. - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - //Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; - int merchantid=0; - Mob* tmp = entity_list.GetMob(mc->npcid); - - if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid=tmp->CastToNPC()->MerchantType; - - int action = 1; - if(merchantid == 0) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; - mco->npcid = mc->npcid; - mco->playerid = 0; - mco->command = 1; //open... - mco->rate = 1.0; - QueuePacket(outapp); - safe_delete(outapp); - return; - } - if(tmp->IsEngaged()){ - this->Message_StringID(0,MERCHANT_BUSY); - action = 0; - } - if (GetFeigned() || IsInvisible()) - { - Message(0,"You cannot use a merchant right now."); - action = 0; - } - int primaryfaction = tmp->CastToNPC()->GetPrimaryFaction(); - int factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), primaryfaction, tmp); - if (factionlvl >= 7) { - MerchantRejectMessage(tmp, primaryfaction); - action = 0; - } - - if (tmp->Charmed()) - action = 0; - - // 1199 I don't have time for that now. etc - if (!tmp->CastToNPC()->IsMerchantOpen()) { - tmp->Say_StringID(MakeRandomInt(1199, 1202)); - action = 0; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; - - mco->npcid = mc->npcid; - mco->playerid = 0; - mco->command = action; // Merchant command 0x01 = open - if (RuleB(Merchant, UsePriceMod)){ - mco->rate = 1/((RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(tmp,true)); // works - } - else - mco->rate = 1/(RuleR(Merchant, BuyCostMod)); - - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - if (action == 1) - BulkSendMerchantInventory(merchantid,tmp->GetNPCTypeID()); - - return; -} - -void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app) -{ - _pkt(TRADING__PACKETS, app); - - if (app->size==sizeof(BazaarSearch_Struct)) { - - BazaarSearch_Struct* bss= (BazaarSearch_Struct*)app->pBuffer; - - this->SendBazaarResults(bss->TraderID, bss->Class_, bss->Race, bss->ItemStat, bss->Slot, bss->Type, - bss->Name, bss->MinPrice*1000, bss->MaxPrice*1000); - } - else if (app->size==sizeof(BazaarWelcome_Struct)) { - - BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)app->pBuffer; - - if (bws->Beginning.Action==BazaarWelcome) - SendBazaarWelcome(); - } - else if (app->size==sizeof(NewBazaarInspect_Struct)) { - - NewBazaarInspect_Struct *nbis = (NewBazaarInspect_Struct*)app->pBuffer; - - Client *c = entity_list.GetClientByName(nbis->Name); - if(c) { - ItemInst* inst = c->FindTraderItemBySerialNumber(nbis->SerialNumber); - if(inst) - SendItemPacket(0, inst, ItemPacketViewLink); - } - return; - } - else { - _log(TRADING__CLIENT, "Malformed BazaarSearch_Struct packe, Action %it received, ignoring..."); - LogFile->write(EQEMuLog::Error, "Malformed BazaarSearch_Struct packet received, ignoring...\n"); - } - - return; -} - -void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Sell_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerBuy: Expected %i, Got %i", - sizeof(Merchant_Sell_Struct), app->size); - return; - } - RDTSC_Timer t1; - t1.start(); - Merchant_Sell_Struct* mp=(Merchant_Sell_Struct*)app->pBuffer; -#if EQDEBUG >= 5 - LogFile->write(EQEMuLog::Debug, "%s, purchase item..", GetName()); - DumpPacket(app); -#endif - - int merchantid; - bool tmpmer_used = false; - Mob* tmp = entity_list.GetMob(mp->npcid); - - if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) - return; - - if (mp->quantity < 1) return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid=tmp->CastToNPC()->MerchantType; - - uint32 item_id = 0; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - for(itr = merlist.begin();itr != merlist.end();++itr){ - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - if(mp->itemslot == ml.slot){ - item_id = ml.item; - break; - } - } - const Item_Struct* item = nullptr; - uint32 prevcharges = 0; - if (item_id == 0) { //check to see if its on the temporary table - std::list tmp_merlist = zone->tmpmerchanttable[tmp->GetNPCTypeID()]; - std::list::const_iterator tmp_itr; - TempMerchantList ml; - for(tmp_itr = tmp_merlist.begin();tmp_itr != tmp_merlist.end();++tmp_itr){ - ml = *tmp_itr; - if(mp->itemslot == ml.slot){ - item_id = ml.item; - tmpmer_used = true; - prevcharges = ml.charges; - break; - } - } - } - item = database.GetItem(item_id); - if (!item){ - //error finding item, client didnt get the update packet for whatever reason, roleplay a tad - Message(15,"%s tells you 'Sorry, that item is for display purposes only.' as they take the item off the shelf.",tmp->GetCleanName()); - EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); - Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; - delitem->itemslot = mp->itemslot; - delitem->npcid = mp->npcid; - delitem->playerid = mp->playerid; - delitempacket->priority = 6; - entity_list.QueueCloseClients(tmp,delitempacket); //que for anyone that could be using the merchant so they see the update - safe_delete(delitempacket); - return; - } - if (CheckLoreConflict(item)) - { - Message(15,"You can only have one of a lore item."); - return; - } - if(tmpmer_used && (mp->quantity > prevcharges || item->MaxCharges > 1)) - { - if(prevcharges > item->MaxCharges && item->MaxCharges > 1) - mp->quantity = item->MaxCharges; - else - mp->quantity = prevcharges; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct)); - Merchant_Sell_Struct* mpo=(Merchant_Sell_Struct*)outapp->pBuffer; - mpo->quantity = mp->quantity; - mpo->playerid = mp->playerid; - mpo->npcid = mp->npcid; - mpo->itemslot=mp->itemslot; - - int16 freeslotid=0; - int16 charges = 0; - if (item->Stackable || item->MaxCharges > 1) - charges = mp->quantity; - else - charges = item->MaxCharges; - - ItemInst* inst = database.CreateItem(item, charges); - - int SinglePrice = 0; - if (RuleB(Merchant, UsePriceMod)) - SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false)); - else - SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate); - - if(item->MaxCharges > 1) - mpo->price = SinglePrice; - else - mpo->price = SinglePrice * mp->quantity; - if(mpo->price < 0 ) - { - safe_delete(outapp); - safe_delete(inst); - return; - } - - if(!TakeMoneyFromPP(mpo->price)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "Vendor Cheat: attempted to buy %i of %i: %s that cost %d cp but only has %d pp %d gp %d sp %d cp\n", - mpo->quantity, item->ID, item->Name, - mpo->price, m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - safe_delete(outapp); - safe_delete(inst); - return; - } - - bool stacked = TryStacking(inst); - if(!stacked) - freeslotid = m_inv.FindFreeSlot(false, true, item->Size); - - //make sure we are not completely full... - if (freeslotid == MainCursor) { - if (m_inv.GetItem(MainCursor) != nullptr) { - Message(13, "You do not have room for any more items."); - safe_delete(outapp); - safe_delete(inst); - return; - } - } - - if (freeslotid == INVALID_INDEX) - { - Message(13, "You do not have room for any more items."); - safe_delete(outapp); - safe_delete(inst); - return; - } - - std::string packet; - if (!stacked && inst) { - PutItemInInventory(freeslotid, *inst); - SendItemPacket(freeslotid, inst, ItemPacketTrade); - } - else if(!stacked){ - LogFile->write(EQEMuLog::Error, "OP_ShopPlayerBuy: item->ItemClass Unknown! Type: %i", item->ItemClass); - } - QueuePacket(outapp); - if(inst && tmpmer_used){ - int32 new_charges = prevcharges - mp->quantity; - zone->SaveTempItem(merchantid, tmp->GetNPCTypeID(),item_id,new_charges); - if(new_charges<=0){ - EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); - Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; - delitem->itemslot = mp->itemslot; - delitem->npcid = mp->npcid; - delitem->playerid = mp->playerid; - delitempacket->priority = 6; - entity_list.QueueClients(tmp,delitempacket); //que for anyone that could be using the merchant so they see the update - safe_delete(delitempacket); - } - else { - // Update the charges/quantity in the merchant window - inst->SetCharges(new_charges); - inst->SetPrice(SinglePrice); - inst->SetMerchantSlot(mp->itemslot); - inst->SetMerchantCount(new_charges); - - SendItemPacket(mp->itemslot, inst, ItemPacketMerchant); - } - } - safe_delete(inst); - safe_delete(outapp); - - // start QS code - if(RuleB(QueryServ, PlayerLogMerchantTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct) + sizeof(QSTransactionItems_Struct)); - QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; - - qsaudit->zone_id = zone->GetZoneID(); - qsaudit->merchant_id = tmp->CastToNPC()->MerchantType; - qsaudit->merchant_money.platinum = 0; - qsaudit->merchant_money.gold = 0; - qsaudit->merchant_money.silver = 0; - qsaudit->merchant_money.copper = 0; - qsaudit->merchant_count = 1; - qsaudit->char_id = character_id; - qsaudit->char_money.platinum = (mpo->price / 1000); - qsaudit->char_money.gold = (mpo->price / 100) % 10; - qsaudit->char_money.silver = (mpo->price / 10) % 10; - qsaudit->char_money.copper = mpo->price % 10; - qsaudit->char_count = 0; - - qsaudit->items[0].char_slot = freeslotid; - qsaudit->items[0].item_id = m_inv[freeslotid]->GetID(); - qsaudit->items[0].charges = mpo->quantity; - qsaudit->items[0].aug_1 = m_inv[freeslotid]->GetAugmentItemID(1); - qsaudit->items[0].aug_2 = m_inv[freeslotid]->GetAugmentItemID(2); - qsaudit->items[0].aug_3 = m_inv[freeslotid]->GetAugmentItemID(3); - qsaudit->items[0].aug_4 = m_inv[freeslotid]->GetAugmentItemID(4); - qsaudit->items[0].aug_5 = m_inv[freeslotid]->GetAugmentItemID(5); - - qspack->Deflate(); - if(worldserver.Connected()) { worldserver.SendPacket(qspack); } - safe_delete(qspack); - } - // end QS code - - if (RuleB(EventLog, RecordBuyFromMerchant)) - LogMerchant(this, tmp, mpo->quantity, mpo->price, item, true); - - if ((RuleB(Character, EnableDiscoveredItems))) - { - if(!GetGM() && !IsDiscovered(item_id)) - DiscoverItem(item_id); - } - - t1.stop(); - std::cout << "At 1: " << t1.getDuration() << std::endl; - return; -} - -void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Purchase_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerSell: Expected %i, Got %i", - sizeof(Merchant_Purchase_Struct), app->size); - return; - } - RDTSC_Timer t1(true); - Merchant_Purchase_Struct* mp=(Merchant_Purchase_Struct*)app->pBuffer; - - Mob* vendor = entity_list.GetMob(mp->npcid); - - if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != MERCHANT) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*vendor) > USE_NPC_RANGE2) - return; - - uint32 price=0; - uint32 itemid = GetItemIDAt(mp->itemslot); - if(itemid == 0) - return; - const Item_Struct* item = database.GetItem(itemid); - ItemInst* inst = GetInv().GetItem(mp->itemslot); - if(!item || !inst){ - Message(13,"You seemed to have misplaced that item.."); - return; - } - if(mp->quantity > 1) - { - if((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges())) - return; - } - - if (!item->NoDrop) { - //Message(13,"%s tells you, 'LOL NOPE'", vendor->GetName()); - return; - } - - int cost_quantity = mp->quantity; - if(inst->IsCharged()) - int cost_quantity = 1; - - if (RuleB(Merchant, UsePriceMod)) - price=(int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor,true)+0.5); // need to round up, because client does it automatically when displaying price - else - price=(int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))+0.5); - AddMoneyToPP(price,false); - - if (inst->IsStackable() || inst->IsCharged()) - { - unsigned int i_quan = inst->GetCharges(); - if (mp->quantity > i_quan || inst->IsCharged()) - mp->quantity = i_quan; - } - else - mp->quantity = 1; - - if (RuleB(EventLog, RecordSellToMerchant)) - LogMerchant(this, vendor, mp->quantity, price, item, false); - - int charges = mp->quantity; - //Hack workaround so usable items with 0 charges aren't simply deleted - if(charges == 0 && item->ItemType != 11 && item->ItemType != 17 && item->ItemType != 19 && item->ItemType != 21) - charges = 1; - - int freeslot = 0; - if(charges > 0 && (freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(),itemid,charges,true)) > 0){ - ItemInst* inst2 = inst->Clone(); - if (RuleB(Merchant, UsePriceMod)){ - inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor,false)); - } - else - inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate); - inst2->SetMerchantSlot(freeslot); - - uint32 MerchantQuantity = zone->GetTempMerchantQuantity(vendor->GetNPCTypeID(), freeslot); - - if(inst2->IsStackable()) { - inst2->SetCharges(MerchantQuantity); - } - inst2->SetMerchantCount(MerchantQuantity); - - SendItemPacket(freeslot-1, inst2, ItemPacketMerchant); - safe_delete(inst2); - } - - // start QS code - if(RuleB(QueryServ, PlayerLogMerchantTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct) + sizeof(QSTransactionItems_Struct)); - QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; - - qsaudit->zone_id = zone->GetZoneID(); - qsaudit->merchant_id = vendor->CastToNPC()->MerchantType; - qsaudit->merchant_money.platinum = (price / 1000); - qsaudit->merchant_money.gold = (price / 100) % 10; - qsaudit->merchant_money.silver = (price / 10) % 10; - qsaudit->merchant_money.copper = price % 10; - qsaudit->merchant_count = 0; - qsaudit->char_id = character_id; - qsaudit->char_money.platinum = 0; - qsaudit->char_money.gold = 0; - qsaudit->char_money.silver = 0; - qsaudit->char_money.copper = 0; - qsaudit->char_count = 1; - - qsaudit->items[0].char_slot = mp->itemslot; - qsaudit->items[0].item_id = itemid; - qsaudit->items[0].charges = charges; - qsaudit->items[0].aug_1 = m_inv[mp->itemslot]->GetAugmentItemID(1); - qsaudit->items[0].aug_2 = m_inv[mp->itemslot]->GetAugmentItemID(2); - qsaudit->items[0].aug_3 = m_inv[mp->itemslot]->GetAugmentItemID(3); - qsaudit->items[0].aug_4 = m_inv[mp->itemslot]->GetAugmentItemID(4); - qsaudit->items[0].aug_5 = m_inv[mp->itemslot]->GetAugmentItemID(5); - - qspack->Deflate(); - if(worldserver.Connected()) { worldserver.SendPacket(qspack); } - safe_delete(qspack); - } - // end QS code - - // Now remove the item from the player, this happens regardless of outcome - if (!inst->IsStackable()) - this->DeleteItemInInventory(mp->itemslot,0,false); - else - this->DeleteItemInInventory(mp->itemslot,mp->quantity,false); - - //This forces the price to show up correctly for charged items. - if(inst->IsCharged()) - mp->quantity = 1; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct)); - Merchant_Purchase_Struct* mco=(Merchant_Purchase_Struct*)outapp->pBuffer; - mco->npcid = vendor->GetID(); - mco->itemslot=mp->itemslot; - mco->quantity=mp->quantity; - mco->price=price; - QueuePacket(outapp); - safe_delete(outapp); - SendMoneyUpdate(); - t1.start(); - Save(1); - t1.stop(); - std::cout << "Save took: " << t1.getDuration() << std::endl; - return; -} - -void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app) -{ - EQApplicationPacket empty(OP_ShopEndConfirm); - QueuePacket(&empty); - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopEndConfirm, 2); - //outapp->pBuffer[0] = 0x0a; - //outapp->pBuffer[1] = 0x66; - //QueuePacket(outapp); - //safe_delete(outapp); - //Save(); - return; -} - -/* -void Client::Handle_OP_CloseContainer(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CloseContainer_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on CloseContainer_Struct: Expected %i, Got %i", - sizeof(CloseContainer_Struct), app->size); - return; - } - - SetTradeskillObject(nullptr); - - ClickObjectAck_Struct* oos = (ClickObjectAck_Struct*)app->pBuffer; - Entity* entity = entity_list.GetEntityObject(oos->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - object->Close(); - } - return; -} -*/ - -void Client::Handle_OP_ClickObjectAction(const EQApplicationPacket *app) -{ - if (app->size == 0) { - // RoF sends this packet 0 sized when switching from auto-combine to experiment windows. - // Not completely sure if 0 sized is for this or for closing objects as commented out below - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - - return; - - // RoF sends a 0 sized packet for closing objects - /* - Object* object = GetTradeskillObject(); - if (object) { - object->CastToObject()->Close(); - } - */ - } - else - { - if (app->size != sizeof(ClickObjectAction_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClickObjectAction: Expected %i, Got %i", - sizeof(ClickObjectAction_Struct), app->size); - return; - } - - ClickObjectAction_Struct* oos = (ClickObjectAction_Struct*)app->pBuffer; - Entity* entity = entity_list.GetEntityObject(oos->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - if(oos->open == 0) { - object->Close(); - } else { - LogFile->write(EQEMuLog::Error, "Unsupported action %d in OP_ClickObjectAction", oos->open); - } - } else { - LogFile->write(EQEMuLog::Error, "Invalid object %d in OP_ClickObjectAction", oos->drop_id); - } - } - - SetTradeskillObject(nullptr); - - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClickObject_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on ClickObject_Struct: Expected %i, Got %i", - sizeof(ClickObject_Struct), app->size); - return; - } - - ClickObject_Struct* click_object = (ClickObject_Struct*)app->pBuffer; - Entity* entity = entity_list.GetID(click_object->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - - object->HandleClick(this, click_object); - - std::vector args; - args.push_back(object); - - char buf[10]; - snprintf(buf, 9, "%u", click_object->drop_id); - buf[9] = '\0'; - parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, 0, &args); - } - - // Observed in RoF after OP_ClickObjectAction: - //EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - //QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeskillFavorites_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for TradeskillFavorites_Struct: Expected: %i, Got: %i", - sizeof(TradeskillFavorites_Struct), app->size); - return; - } - - TradeskillFavorites_Struct* tsf = (TradeskillFavorites_Struct*)app->pBuffer; - - LogFile->write(EQEMuLog::Debug, "Requested Favorites for: %d - %d\n", tsf->object_type, tsf->some_id); - - // results show that object_type is combiner type - // some_id = 0 if world combiner, item number otherwise - - // make where clause segment for container(s) - char containers[30]; - if (tsf->some_id == 0) { - // world combiner so no item number - snprintf(containers,29, "= %u", tsf->object_type); - } else { - // container in inventory - snprintf(containers,29, "in (%u,%u)", tsf->object_type, tsf->some_id); - } - - char *query = 0; - char buf[5500]; //gotta be big enough for 500 IDs - - bool first = true; - uint16 r; - char *pos = buf; - - //Assumes item IDs are <10 characters long - for(r = 0; r < 500; r++) { - if(tsf->favorite_recipes[r] == 0) - continue; - - if(first) { - pos += snprintf(pos, 10, "%u", tsf->favorite_recipes[r]); - first = false; - } else { - pos += snprintf(pos, 10, ",%u", tsf->favorite_recipes[r]); - } - } - - if(first) //no favorites.... - return; - - //To be a good kid, I should move this SQL somewhere else... - //but im lazy right now, so it stays here - uint32 qlen = 0; - qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " - " FROM tradeskill_recipe AS tr " - " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " - " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " - " WHERE tr.enabled <> 0 AND tr.id IN (%s) " - " AND tr.must_learn & 0x20 <> 0x20 AND ((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " - " GROUP BY tr.id " - " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " - " LIMIT 100 ", CharacterID(), buf, containers); - - TradeskillSearchResults(query, qlen, tsf->object_type, tsf->some_id); - - safe_delete_array(query); - return; -} - -void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RecipesSearch_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipesSearch_Struct: Expected: %i, Got: %i", - sizeof(RecipesSearch_Struct), app->size); - return; - } - - RecipesSearch_Struct* rss = (RecipesSearch_Struct*)app->pBuffer; - rss->query[55] = '\0'; //just to be sure. - - - LogFile->write(EQEMuLog::Debug, "Requested search recipes for: %d - %d\n", rss->object_type, rss->some_id); - - // make where clause segment for container(s) - char containers[30]; - if (rss->some_id == 0) { - // world combiner so no item number - snprintf(containers,29, "= %u", rss->object_type); - } else { - // container in inventory - snprintf(containers,29, "in (%u,%u)", rss->object_type, rss->some_id); - } - - char *query = 0; - char searchclause[140]; //2X rss->query + SQL crap - - //omit the rlike clause if query is empty - if(rss->query[0] != 0) { - char buf[120]; //larger than 2X rss->query - database.DoEscapeString(buf, rss->query, strlen(rss->query)); - - snprintf(searchclause, 139, "name rlike '%s' AND", buf); - } else { - searchclause[0] = '\0'; - } - uint32 qlen = 0; - - //arbitrary limit of 200 recipes, makes sense to me. - qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " - " FROM tradeskill_recipe AS tr " - " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " - " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " - " WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 " - " AND tr.must_learn & 0x20 <> 0x20 AND((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " - " GROUP BY tr.id " - " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " - " LIMIT 200 " - , CharacterID(), searchclause, rss->mintrivial, rss->maxtrivial, containers); - - TradeskillSearchResults(query, qlen, rss->object_type, rss->some_id); - - safe_delete_array(query); - return; -} - -void Client::Handle_OP_RecipeDetails(const EQApplicationPacket *app) -{ - if(app->size < sizeof(uint32)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipeDetails Request: Expected: %i, Got: %i", - sizeof(uint32), app->size); - return; - } - uint32 *recipe_id = (uint32*) app->pBuffer; - - SendTradeskillDetails(*recipe_id); - - return; -} - -void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RecipeAutoCombine_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipeAutoCombine_Struct: Expected: %i, Got: %i", - sizeof(RecipeAutoCombine_Struct), app->size); - return; - } - - RecipeAutoCombine_Struct* rac = (RecipeAutoCombine_Struct*)app->pBuffer; - - Object::HandleAutoCombine(this, rac); - return; -} - -void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app) -{ - if (app->size != sizeof(NewCombine_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for NewCombine_Struct: Expected: %i, Got: %i", - sizeof(NewCombine_Struct), app->size); - return; - } - /*if (m_tradeskill_object == nullptr) { - Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use"); - return; - }*/ - - //fixed this to work for non-world objects - - // Delegate to tradeskill object to perform combine - NewCombine_Struct* in_combine = (NewCombine_Struct*)app->pBuffer; - Object::HandleCombine(this, in_combine, m_tradeskill_object); - return; -} - -void Client::Handle_OP_ItemName(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ItemNamePacket_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for ItemNamePacket_Struct: Expected: %i, Got: %i", - sizeof(ItemNamePacket_Struct), app->size); - return; - } - ItemNamePacket_Struct *p = (ItemNamePacket_Struct*)app->pBuffer; - const Item_Struct *item = 0; - if ((item = database.GetItem(p->item_id))!=nullptr) { - EQApplicationPacket* outapp=new EQApplicationPacket(OP_ItemName,sizeof(ItemNamePacket_Struct)); - p=(ItemNamePacket_Struct*)outapp->pBuffer; - memset(p, 0, sizeof(ItemNamePacket_Struct)); - strcpy(p->name,item->Name); - FastQueuePacket(&outapp); - } - return; -} - -void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(AugmentItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for AugmentItem_Struct: Expected: %i, Got: %i", - sizeof(AugmentItem_Struct), app->size); - return; - } - - // Delegate to tradeskill object to perform combine - AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; - bool deleteItems = false; - if(GetClientVersion() >= EQClientRoF) - { - ItemInst *itemOneToPush = nullptr, *itemTwoToPush = nullptr; - - //Message(15, "%i %i %i %i %i %i", in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); - - // Adding augment - if (in_augment->augment_action == 0) - { - ItemInst *tobe_auged, *auged_with = nullptr; - int8 slot=-1; - Inventory& user_inv = GetInv(); - - uint16 slot_id = in_augment->container_slot; - uint16 aug_slot_id = in_augment->augment_slot; - //Message(13, "%i AugSlot", aug_slot_id); - if(slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) - { - Message(13, "Error: Invalid Aug Index."); - return; - } - - tobe_auged = user_inv.GetItem(slot_id); - auged_with = user_inv.GetItem(MainCursor); - - if (tobe_auged && auged_with) - { - if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) && - (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) - { - tobe_auged->PutAugment(in_augment->augment_index, *auged_with); - - ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); - if(aug) { - std::vector args; - args.push_back(aug); - parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); - - args.assign(1, tobe_auged); - parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index); - return; - } - - itemOneToPush = tobe_auged->Clone(); - // Must push items after the items in inventory are deleted - necessary due to lore items... - if (itemOneToPush) - { - DeleteItemInInventory(slot_id, 0, true); - DeleteItemInInventory(MainCursor, 0, true); - if(PutItemInInventory(slot_id, *itemOneToPush, true)) - { - //Message(13, "Sucessfully added an augment to your item!"); - return; - } - else - { - Message(13, "Error: No available slot for end result. Please free up some bag space."); - } - } - else - { - Message(13, "Error in cloning item for augment. Aborted."); - } - - } - else - { - Message(13, "Error: No available slot for augment in that item."); - } - } - } - else if(in_augment->augment_action == 1) - { - ItemInst *tobe_auged, *auged_with = nullptr; - int8 slot=-1; - Inventory& user_inv = GetInv(); - - uint16 slot_id = in_augment->container_slot; - uint16 aug_slot_id = in_augment->augment_slot; //it's actually solvent slot - if(slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) - { - Message(13, "Error: Invalid Aug Index."); - return; - } - - tobe_auged = user_inv.GetItem(slot_id); - auged_with = user_inv.GetItem(aug_slot_id); - - ItemInst *old_aug = nullptr; - if(!auged_with) - return; - const uint32 id = auged_with->GetID(); - ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); - if(aug) { - std::vector args; - args.push_back(aug); - parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); - - args.assign(1, tobe_auged); - - args.push_back(false); - - parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(13, "Error: Could not find augmentation at index %i. Aborting."); - return; - } - old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); - - itemOneToPush = tobe_auged->Clone(); - if (old_aug) - itemTwoToPush = old_aug->Clone(); - if(itemOneToPush && itemTwoToPush && auged_with) - { - DeleteItemInInventory(slot_id, 0, true); - DeleteItemInInventory(aug_slot_id, auged_with->IsStackable() ? 1 : 0, true); - if(!PutItemInInventory(slot_id, *itemOneToPush, true)) - { - Message(15, "Shouldn't happen, contact an admin!"); - } - - if(PutItemInInventory(MainCursor, *itemTwoToPush, true)) - { - //Message(15, "Successfully removed an augmentation!"); - } - } - } - } - else - { - Object::HandleAugmentation(this, in_augment, m_tradeskill_object); - } - return; -} - -void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClickDoor_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ClickDoor, size=%i, expected %i", app->size, sizeof(ClickDoor_Struct)); - return; - } - ClickDoor_Struct* cd = (ClickDoor_Struct*)app->pBuffer; - Doors* currentdoor = entity_list.FindDoor(cd->doorid); - if(!currentdoor) - { - Message(0,"Unable to find door, please notify a GM (DoorID: %i).",cd->doorid); - return; - } - - char buf[20]; - snprintf(buf, 19, "%u", cd->doorid); - buf[19] = '\0'; - std::vector args; - args.push_back(currentdoor); - parse->EventPlayer(EVENT_CLICK_DOOR, this, buf, 0, &args); - - currentdoor->HandleClick(this,0); - return; -} - -void Client::Handle_OP_CreateObject(const EQApplicationPacket *app) -{ - DropItem(MainCursor); - return; -} - -void Client::Handle_OP_FaceChange(const EQApplicationPacket *app) -{ - if (app->size != sizeof(FaceChange_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_FaceChange: Expected: %i, Got: %i", - sizeof(FaceChange_Struct), app->size); - return; - } - - // Notify other clients in zone - entity_list.QueueClients(this, app, false); - - FaceChange_Struct* fc = (FaceChange_Struct*)app->pBuffer; - m_pp.haircolor = fc->haircolor; - m_pp.beardcolor = fc->beardcolor; - m_pp.eyecolor1 = fc->eyecolor1; - m_pp.eyecolor2 = fc->eyecolor2; - m_pp.hairstyle = fc->hairstyle; - m_pp.face = fc->face; - m_pp.beard = fc->beard; - m_pp.drakkin_heritage = fc->drakkin_heritage; - m_pp.drakkin_tattoo = fc->drakkin_tattoo; - m_pp.drakkin_details = fc->drakkin_details; - Save(); - Message_StringID(13,FACE_ACCEPTED); - //Message(13, "Facial features updated."); - return; -} - -void Client::Handle_OP_GroupInvite(const EQApplicationPacket *app) -{ - //this seems to be the initial invite to form a group - Handle_OP_GroupInvite2(app); -} - -void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupInvite_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupInvite: Expected: %i, Got: %i", - sizeof(GroupInvite_Struct), app->size); - return; - } - - GroupInvite_Struct* gis = (GroupInvite_Struct*) app->pBuffer; - - Mob *Invitee = entity_list.GetMob(gis->invitee_name); - - if(Invitee == this) - { - Message_StringID(clientMessageWhite, GROUP_INVITEE_SELF); - return; - } - - if(Invitee) { - if(Invitee->IsClient()) { - if((!Invitee->IsGrouped() && !Invitee->IsRaidGrouped()) || - (Invitee->GetGroup() && Invitee->CastToClient()->GetMerc() && Invitee->GetGroup()->GroupCount() == 2)) - { - if(app->GetOpcode() == OP_GroupInvite2) - { - //Make a new packet using all the same information but make sure it's a fixed GroupInvite opcode so we - //Don't have to deal with GroupFollow2 crap. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct)); - memcpy(outapp->pBuffer, app->pBuffer, outapp->size); - Invitee->CastToClient()->QueuePacket(outapp); - safe_delete(outapp); - return; - } - else - { - //The correct opcode, no reason to bother wasting time reconstructing the packet - Invitee->CastToClient()->QueuePacket(app); - } - } - } -#ifdef BOTS - else if(Invitee->IsBot()) { - Bot::ProcessBotGroupInvite(this, std::string(Invitee->GetName())); - } -#endif - } - else - { - ServerPacket* pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct)); - memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct)); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - void Client::Handle_OP_GroupAcknowledge(const EQApplicationPacket *app) { return; @@ -7057,12 +6343,12 @@ void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) return; } - GroupCancel_Struct* gf = (GroupCancel_Struct*) app->pBuffer; + GroupCancel_Struct* gf = (GroupCancel_Struct*)app->pBuffer; Mob* inviter = entity_list.GetClientByName(gf->name1); - if(inviter != nullptr) + if (inviter != nullptr) { - if(inviter->IsClient()) + if (inviter->IsClient()) inviter->CastToClient()->QueuePacket(app); } else @@ -7077,6 +6363,193 @@ void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) return; } +void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) +{ + //should check for leader, only they should be able to do this.. + Group* group = GetGroup(); + if (group) + group->DisbandGroup(); + + if (LFP) + UpdateLFP(); + + return; +} + +void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupGeneric_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for GroupGeneric_Struct: Expected: %i, Got: %i", + sizeof(GroupGeneric_Struct), app->size); + return; + } + + LogFile->write(EQEMuLog::Debug, "Member Disband Request from %s\n", GetName()); + + GroupGeneric_Struct* gd = (GroupGeneric_Struct*)app->pBuffer; + + Raid *raid = entity_list.GetRaidByClient(this); + if (raid){ + Mob* memberToDisband = nullptr; + + if (!raid->IsGroupLeader(GetName())) + memberToDisband = this; + else + memberToDisband = GetTarget(); + + if (!memberToDisband) + memberToDisband = entity_list.GetMob(gd->name2); + + if (!memberToDisband) + memberToDisband = this; + + if (!memberToDisband->IsClient()) + return; + + //we have a raid.. see if we're in a raid group + uint32 grp = raid->GetGroup(memberToDisband->GetName()); + bool wasGrpLdr = raid->members[raid->GetPlayerIndex(memberToDisband->GetName())].IsGroupLeader; + if (grp < 12){ + if (wasGrpLdr){ + raid->SetGroupLeader(memberToDisband->GetName(), false); + for (int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if (raid->members[x].GroupNumber == grp) + { + if (strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, memberToDisband->GetName()) != 0) + { + raid->SetGroupLeader(raid->members[x].membername); + break; + } + } + } + } + raid->MoveMember(memberToDisband->GetName(), 0xFFFFFFFF); + raid->GroupUpdate(grp); //break + //raid->SendRaidGroupRemove(memberToDisband->GetName(), grp); + //raid->SendGroupUpdate(memberToDisband->CastToClient()); + raid->SendGroupDisband(memberToDisband->CastToClient()); + } + //we're done + return; + } + + Group* group = GetGroup(); + + if (!group) + return; + +#ifdef BOTS + // this block is necessary to allow more control over controlling how bots are zoned or camped. + if (Bot::GroupHasBot(group)) { + if (group->IsLeader(this)) { + if ((GetTarget() == 0 || GetTarget() == this) || (group->GroupCount() < 3)) { + Bot::ProcessBotGroupDisband(this, std::string()); + } + else { + Mob* tempMember = entity_list.GetMob(gd->name2); + if (tempMember) { + if (tempMember->IsBot()) + Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName())); + } + } + } + } +#endif + if ((group->IsLeader(this) && (GetTarget() == 0 || GetTarget() == this)) || (group->GroupCount()<3)) { + group->DisbandGroup(); + if (GetMerc() != nullptr) + GetMerc()->Suspend(); + } + else { + Mob* memberToDisband = nullptr; + memberToDisband = GetTarget(); + + if (!memberToDisband) + memberToDisband = entity_list.GetMob(gd->name2); + + if (memberToDisband) { + if (group->IsLeader(this)) { + // the group leader can kick other members out of the group... + //group->DelMember(memberToDisband,false); + if (memberToDisband->IsClient()) + { + group->DelMember(memberToDisband, false); + Client* memberClient = memberToDisband->CastToClient(); + Merc* memberMerc = memberToDisband->CastToClient()->GetMerc(); + if (memberClient && memberMerc && group) + { + if (!memberMerc->IsGrouped() && !memberClient->IsGrouped()) { + Group *g = new Group(memberClient); + + entity_list.AddGroup(g); + + if (g->GetID() == 0) { + safe_delete(g); + return; + } + if (Merc::AddMercToGroup(memberMerc, g)) { + database.SetGroupLeaderName(g->GetID(), memberClient->GetName()); + g->SaveGroupLeaderAA(); + database.SetGroupID(memberClient->GetName(), g->GetID(), memberClient->CharacterID()); + database.SetGroupID(memberMerc->GetName(), g->GetID(), memberClient->CharacterID(), true); + database.RefreshGroupFromDB(memberClient); + } + } + } + } + else if (memberToDisband->IsMerc()) { + memberToDisband->CastToMerc()->Suspend(); + } + } + else { + // ...but other members can only remove themselves + group->DelMember(this, false); + + if (!IsGrouped() && GetMerc() != nullptr) { + if (!IsGrouped()) { + Group *g = new Group(this); + + if (!g) { + delete g; + g = nullptr; + return; + } + + entity_list.AddGroup(g); + + if (g->GetID() == 0) { + safe_delete(g); + return; + } + + if (Merc::AddMercToGroup(GetMerc(), g)) { + database.SetGroupLeaderName(g->GetID(), this->GetName()); + g->SaveGroupLeaderAA(); + database.SetGroupID(this->GetName(), g->GetID(), this->CharacterID()); + database.SetGroupID(GetMerc()->GetName(), g->GetID(), this->CharacterID(), true); + database.RefreshGroupFromDB(this); + } + else + { + if (GetMerc()) + GetMerc()->Depop(); + } + } + } + } + } + else + LogFile->write(EQEMuLog::Error, "Failed to remove player from group. Unable to find player named %s in player group", gd->name2); + } + if (LFP) { + // If we are looking for players, update to show we are on our own now. + UpdateLFP(); + } + + return; +} + void Client::Handle_OP_GroupFollow(const EQApplicationPacket *app) { Handle_OP_GroupFollow2(app); @@ -7090,42 +6563,42 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) return; } - if(LFP) { + if (LFP) { // If we were looking for players to start our own group, but we accept an invitation to another // group, turn LFP off. database.SetLFP(CharacterID(), false); worldserver.StopLFP(CharacterID()); } - GroupGeneric_Struct* gf = (GroupGeneric_Struct*) app->pBuffer; + GroupGeneric_Struct* gf = (GroupGeneric_Struct*)app->pBuffer; Mob* inviter = entity_list.GetClientByName(gf->name1); - if(inviter != nullptr && inviter->IsClient()) { + if (inviter != nullptr && inviter->IsClient()) { isgrouped = true; - strn0cpy(gf->name1,inviter->GetName(), 64); - strn0cpy(gf->name2,this->GetName(), 64); + strn0cpy(gf->name1, inviter->GetName(), 64); + strn0cpy(gf->name2, this->GetName(), 64); Raid* raid = entity_list.GetRaidByClient(inviter->CastToClient()); Raid* iraid = entity_list.GetRaidByClient(this); //inviter has a raid don't do group stuff instead do raid stuff! - if(raid){ + if (raid){ // Suspend the merc while in a raid (maybe a rule could be added for this) if (GetMerc()) GetMerc()->Suspend(); uint32 groupToUse = 0xFFFFFFFF; - for(int x = 0; x < MAX_RAID_MEMBERS; x++){ - if(raid->members[x].member){ //this assumes the inviter is in the zone - if(raid->members[x].member == inviter->CastToClient()){ + for (int x = 0; x < MAX_RAID_MEMBERS; x++){ + if (raid->members[x].member){ //this assumes the inviter is in the zone + if (raid->members[x].member == inviter->CastToClient()){ groupToUse = raid->members[x].GroupNumber; break; } } } - if(iraid == raid){ //both in same raid + if (iraid == raid){ //both in same raid uint32 ngid = raid->GetGroup(inviter->GetName()); - if(raid->GroupCount(ngid) < 6){ + if (raid->GroupCount(ngid) < 6){ raid->MoveMember(GetName(), ngid); raid->SendGroupDisband(this); //raid->SendRaidGroupAdd(GetName(), ngid); @@ -7134,8 +6607,8 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) } return; } - if(raid->RaidCount() < MAX_RAID_MEMBERS){ - if(raid->GroupCount(groupToUse) < 6){ + if (raid->RaidCount() < MAX_RAID_MEMBERS){ + if (raid->GroupCount(groupToUse) < 6){ raid->SendRaidCreate(this); raid->SendMakeLeaderPacketTo(raid->leadername, this); raid->AddMember(this, groupToUse); @@ -7143,7 +6616,7 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) //raid->SendRaidGroupAdd(GetName(), groupToUse); //raid->SendGroupUpdate(this); raid->GroupUpdate(groupToUse); //break - if(raid->IsLocked()) { + if (raid->IsLocked()) { raid->SendRaidLockTo(this); } return; @@ -7153,7 +6626,7 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) raid->SendMakeLeaderPacketTo(raid->leadername, this); raid->AddMember(this); raid->SendBulkRaid(this); - if(raid->IsLocked()) { + if (raid->IsLocked()) { raid->SendRaidLockTo(this); } return; @@ -7163,14 +6636,14 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) Group* group = entity_list.GetGroupByClient(inviter->CastToClient()); - if(!group){ + if (!group){ //Make new group group = new Group(inviter); - if(!group) + if (!group) return; entity_list.AddGroup(group); - if(group->GetID() == 0) { + if (group->GetID() == 0) { Message(13, "Unable to get new group id. Cannot create group."); inviter->Message(13, "Unable to get new group id. Cannot create group."); return; @@ -7183,10 +6656,10 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) group->UpdateGroupAAs(); //Invite the inviter into the group first.....dont ask - if(inviter->CastToClient()->GetClientVersion() < EQClientSoD) + if (inviter->CastToClient()->GetClientVersion() < EQClientSoD) { - EQApplicationPacket* outapp=new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); - GroupJoin_Struct* outgj=(GroupJoin_Struct*)outapp->pBuffer; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); + GroupJoin_Struct* outgj = (GroupJoin_Struct*)outapp->pBuffer; strcpy(outgj->membername, inviter->GetName()); strcpy(outgj->yourname, inviter->GetName()); outgj->action = groupActInviteInitial; // 'You have formed the group'. @@ -7206,28 +6679,28 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) } } - if(!group) + if (!group) return; inviter->CastToClient()->QueuePacket(app);//notify inviter the client accepted - if(!group->AddMember(this)) + if (!group->AddMember(this)) return; - if(inviter->CastToClient()->IsLFP()) { + if (inviter->CastToClient()->IsLFP()) { // If the player who invited us to a group is LFP, have them update world now that we have joined // their group. inviter->CastToClient()->UpdateLFP(); } - if(GetClientVersion() >= EQClientSoD) + if (GetClientVersion() >= EQClientSoD) SendGroupJoinAcknowledge(); database.RefreshGroupFromDB(this); group->SendHPPacketsTo(this); // Temporary hack for SoD, as things seem to work quite differently - if(inviter->CastToClient()->GetClientVersion() >= EQClientSoD) + if (inviter->CastToClient()->GetClientVersion() >= EQClientSoD) database.RefreshGroupFromDB(inviter->CastToClient()); // Add the merc back into the new group @@ -7248,7 +6721,7 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) worldserver.SendPacket(pack); safe_delete(pack); } - else if(inviter == nullptr) + else if (inviter == nullptr) { ServerPacket* pack = new ServerPacket(ServerOP_GroupFollow, sizeof(ServerGroupFollow_Struct)); ServerGroupFollow_Struct *sgfs = (ServerGroupFollow_Struct *)pack->pBuffer; @@ -7260,4233 +6733,137 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) } } -void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) +void Client::Handle_OP_GroupInvite(const EQApplicationPacket *app) { - if (app->size != sizeof(GroupGeneric_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for GroupGeneric_Struct: Expected: %i, Got: %i", - sizeof(GroupGeneric_Struct), app->size); + //this seems to be the initial invite to form a group + Handle_OP_GroupInvite2(app); +} + +void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupInvite_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupInvite: Expected: %i, Got: %i", + sizeof(GroupInvite_Struct), app->size); return; } - LogFile->write(EQEMuLog::Debug, "Member Disband Request from %s\n", GetName()); + GroupInvite_Struct* gis = (GroupInvite_Struct*)app->pBuffer; - GroupGeneric_Struct* gd = (GroupGeneric_Struct*) app->pBuffer; + Mob *Invitee = entity_list.GetMob(gis->invitee_name); - Raid *raid = entity_list.GetRaidByClient(this); - if(raid){ - Mob* memberToDisband = nullptr; + if (Invitee == this) + { + Message_StringID(clientMessageWhite, GROUP_INVITEE_SELF); + return; + } - if(!raid->IsGroupLeader(GetName())) - memberToDisband = this; - else - memberToDisband = GetTarget(); - - if(!memberToDisband) - memberToDisband = entity_list.GetMob(gd->name2); - - if(!memberToDisband) - memberToDisband = this; - - if(!memberToDisband->IsClient()) - return; - - //we have a raid.. see if we're in a raid group - uint32 grp = raid->GetGroup(memberToDisband->GetName()); - bool wasGrpLdr = raid->members[raid->GetPlayerIndex(memberToDisband->GetName())].IsGroupLeader; - if(grp < 12){ - if(wasGrpLdr){ - raid->SetGroupLeader(memberToDisband->GetName(), false); - for(int x = 0; x < MAX_RAID_MEMBERS; x++) + if (Invitee) { + if (Invitee->IsClient()) { + if ((!Invitee->IsGrouped() && !Invitee->IsRaidGrouped()) || + (Invitee->GetGroup() && Invitee->CastToClient()->GetMerc() && Invitee->GetGroup()->GroupCount() == 2)) + { + if (app->GetOpcode() == OP_GroupInvite2) { - if(raid->members[x].GroupNumber == grp) - { - if(strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, memberToDisband->GetName()) != 0) - { - raid->SetGroupLeader(raid->members[x].membername); - break; - } - } + //Make a new packet using all the same information but make sure it's a fixed GroupInvite opcode so we + //Don't have to deal with GroupFollow2 crap. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct)); + memcpy(outapp->pBuffer, app->pBuffer, outapp->size); + Invitee->CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + return; } - } - raid->MoveMember(memberToDisband->GetName(), 0xFFFFFFFF); - raid->GroupUpdate(grp); //break - //raid->SendRaidGroupRemove(memberToDisband->GetName(), grp); - //raid->SendGroupUpdate(memberToDisband->CastToClient()); - raid->SendGroupDisband(memberToDisband->CastToClient()); - } - //we're done - return; - } - - Group* group = GetGroup(); - - if(!group) - return; - -#ifdef BOTS - // this block is necessary to allow more control over controlling how bots are zoned or camped. - if(Bot::GroupHasBot(group)) { - if(group->IsLeader(this)) { - if((GetTarget() == 0 || GetTarget() == this) || (group->GroupCount() < 3)) { - Bot::ProcessBotGroupDisband(this, std::string()); - } else { - Mob* tempMember = entity_list.GetMob(gd->name2); - if(tempMember) { - if(tempMember->IsBot()) - Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName())); - } - } - } - } -#endif - if((group->IsLeader(this) && (GetTarget() == 0 || GetTarget() == this)) || (group->GroupCount()<3)) { - group->DisbandGroup(); - if(GetMerc() != nullptr) - GetMerc()->Suspend(); - } else { - Mob* memberToDisband = nullptr; - memberToDisband = GetTarget(); - - if(!memberToDisband) - memberToDisband = entity_list.GetMob(gd->name2); - - if(memberToDisband ) { - if(group->IsLeader(this)) { - // the group leader can kick other members out of the group... - //group->DelMember(memberToDisband,false); - if(memberToDisband->IsClient()) - { - group->DelMember(memberToDisband,false); - Client* memberClient = memberToDisband->CastToClient(); - Merc* memberMerc = memberToDisband->CastToClient()->GetMerc(); - if(memberClient && memberMerc && group) - { - if(!memberMerc->IsGrouped() && !memberClient->IsGrouped()) { - Group *g = new Group(memberClient); - - entity_list.AddGroup(g); - - if(g->GetID() == 0) { - safe_delete(g); - return; - } - if(Merc::AddMercToGroup(memberMerc, g)) { - database.SetGroupLeaderName(g->GetID(), memberClient->GetName()); - g->SaveGroupLeaderAA(); - database.SetGroupID(memberClient->GetName(), g->GetID(), memberClient->CharacterID()); - database.SetGroupID(memberMerc->GetName(), g->GetID(), memberClient->CharacterID(), true); - database.RefreshGroupFromDB(memberClient); - } - } - } - } - else if(memberToDisband->IsMerc()) { - memberToDisband->CastToMerc()->Suspend(); - } - } - else { - // ...but other members can only remove themselves - group->DelMember(this,false); - - if(!IsGrouped() && GetMerc() != nullptr) { - if(!IsGrouped()) { - Group *g = new Group(this); - - if(!g) { - delete g; - g = nullptr; - return; - } - - entity_list.AddGroup(g); - - if(g->GetID() == 0) { - safe_delete(g); - return; - } - - if(Merc::AddMercToGroup(GetMerc(), g)) { - database.SetGroupLeaderName(g->GetID(), this->GetName()); - g->SaveGroupLeaderAA(); - database.SetGroupID(this->GetName(), g->GetID(), this->CharacterID()); - database.SetGroupID(GetMerc()->GetName(), g->GetID(), this->CharacterID(), true); - database.RefreshGroupFromDB(this); - } - else - { - if(GetMerc()) - GetMerc()->Depop(); - } - } - } - } - } - else - LogFile->write(EQEMuLog::Error, "Failed to remove player from group. Unable to find player named %s in player group", gd->name2); - } - if(LFP) { - // If we are looking for players, update to show we are on our own now. - UpdateLFP(); - } - - return; -} - -void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) -{ -//should check for leader, only they should be able to do this.. - Group* group = GetGroup(); - if (group) - group->DisbandGroup(); - - if(LFP) - UpdateLFP(); - - return; -} - -void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/emote"); - return; - } - if (app->size != sizeof(GMEmoteZone_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMEmoteZone, size=%i, expected %i", app->size, sizeof(GMEmoteZone_Struct)); - return; - } - GMEmoteZone_Struct* gmez = (GMEmoteZone_Struct*)app->pBuffer; - char* newmessage=0; - if(strstr(gmez->text,"^")==0) - entity_list.Message(0, 15, gmez->text); - else{ - for(newmessage = strtok((char*)gmez->text,"^");newmessage!=nullptr;newmessage=strtok(nullptr, "^")) - entity_list.Message(0, 15, newmessage); - } - return; -} - -void Client::Handle_OP_InspectRequest(const EQApplicationPacket *app) { - - if(app->size != sizeof(Inspect_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectRequest, size=%i, expected %i", app->size, sizeof(Inspect_Struct)); - return; - } - - Inspect_Struct* ins = (Inspect_Struct*) app->pBuffer; - Mob* tmp = entity_list.GetMob(ins->TargetID); - - if(tmp != 0 && tmp->IsClient()) { - if(tmp->CastToClient()->GetClientVersion() < EQClientSoF) { tmp->CastToClient()->QueuePacket(app); } // Send request to target - // Inspecting an SoF or later client will make the server handle the request - else { ProcessInspectRequest(tmp->CastToClient(), this); } - } - -#ifdef BOTS - if(tmp != 0 && tmp->IsBot()) { Bot::ProcessBotInspectionRequest(tmp->CastToBot(), this); } -#endif - - return; -} - -void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) { - - if (app->size != sizeof(InspectResponse_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectAnswer, size=%i, expected %i", app->size, sizeof(InspectResponse_Struct)); - return; - } - - //Fills the app sent from client. - EQApplicationPacket* outapp = app->Copy(); - InspectResponse_Struct* insr = (InspectResponse_Struct*) outapp->pBuffer; - Mob* tmp = entity_list.GetMob(insr->TargetID); - const Item_Struct* item = nullptr; - - for (int16 L = EmuConstants::EQUIPMENT_BEGIN; L <= MainWaist; L++) { - const ItemInst* inst = GetInv().GetItem(L); - item = inst ? inst->GetItem() : nullptr; - - if(item) { - strcpy(insr->itemnames[L], item->Name); - insr->itemicons[L] = item->Icon; - } - else { insr->itemicons[L] = 0xFFFFFFFF; } - } - - const ItemInst* inst = GetInv().GetItem(MainAmmo); - item = inst ? inst->GetItem() : nullptr; - - if(item) { - // another one..I did these, didn't I!!? - strcpy(insr->itemnames[SoF::slots::MainAmmo], item->Name); - insr->itemicons[SoF::slots::MainAmmo] = item->Icon; - } - else { insr->itemicons[SoF::slots::MainAmmo] = 0xFFFFFFFF; } - - InspectMessage_Struct* newmessage = (InspectMessage_Struct*) insr->text; - InspectMessage_Struct& playermessage = this->GetInspectMessage(); - memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); - database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); - - if(tmp != 0 && tmp->IsClient()) { tmp->CastToClient()->QueuePacket(outapp); } // Send answer to requester - - return; -} - -void Client::Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app) { - - if (app->size != sizeof(InspectMessage_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectMessageUpdate, size=%i, expected %i", app->size, sizeof(InspectMessage_Struct)); - return; - } - - InspectMessage_Struct* newmessage = (InspectMessage_Struct*) app->pBuffer; - InspectMessage_Struct& playermessage = this->GetInspectMessage(); - memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); - database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); -} - -#if 0 // I dont think there's an op for this now, and we check this - // when the client is sitting -void Client::Handle_OP_Medding(const EQApplicationPacket *app) -{ - if (app->pBuffer[0]) - medding = true; - else - medding = false; - return; -} -#endif - -void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app) -{ - if(app->size != sizeof(DeleteSpell_Struct)) - return; - - EQApplicationPacket* outapp = app->Copy(); - DeleteSpell_Struct* dss = (DeleteSpell_Struct*) outapp->pBuffer; - - if(dss->spell_slot < 0 || dss->spell_slot > int(MAX_PP_SPELLBOOK)) - return; - - if(m_pp.spell_book[dss->spell_slot] != SPELLBOOK_UNKNOWN) { - m_pp.spell_book[dss->spell_slot] = SPELLBOOK_UNKNOWN; - dss->success = 1; - } - else - dss->success = 0; - - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_LoadSpellSet(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(LoadSpellSet_Struct)) { - printf("Wrong size of LoadSpellSet_Struct! Expected: %zu, Got: %i\n",sizeof(LoadSpellSet_Struct),app->size); - return; - } - int i; - LoadSpellSet_Struct* ss=(LoadSpellSet_Struct*)app->pBuffer; - for(i=0;ispell[i] != 0xFFFFFFFF) - UnmemSpell(i,true); - } -} - - -void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(PetitionBug_Struct)) - printf("Wrong size of BugStruct! Expected: %zu, Got: %i\n",sizeof(PetitionBug_Struct),app->size); - else{ - Message(0, "Petition Bugs are not supported, please use /bug."); - } - return; -} - -void Client::Handle_OP_Bug(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(BugStruct)) - printf("Wrong size of BugStruct got %d expected %zu!\n", app->size, sizeof(BugStruct)); - else{ - BugStruct* bug=(BugStruct*)app->pBuffer; - database.UpdateBug(bug); - } - return; -} - -void Client::Handle_OP_Petition(const EQApplicationPacket *app) -{ - if (app->size <= 1) - return; - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - /*else if(petition_list.FindPetitionByAccountName(this->AccountName())) - { - Message(0,"You already have a petition in queue, you cannot petition again until this one has been responded to or you have deleted the petition."); - return; - }*/ - else - { - if(petition_list.FindPetitionByAccountName(AccountName())) - { - Message(0,"You already have a petition in the queue, you must wait for it to be answered or use /deletepetition to delete it."); - return; - } - Petition* pet = new Petition(CharacterID()); - pet->SetAName(this->AccountName()); - pet->SetClass(this->GetClass()); - pet->SetLevel(this->GetLevel()); - pet->SetCName(this->GetName()); - pet->SetRace(this->GetRace()); - pet->SetLastGM(""); - pet->SetCName(this->GetName()); - pet->SetPetitionText((char*) app->pBuffer); - pet->SetZone(zone->GetZoneID()); - pet->SetUrgency(0); - petition_list.AddPetition(pet); - database.InsertPetitionToDB(pet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", GetName(), pet->GetID()); - } - return; -} - -void Client::Handle_OP_PetitionCheckIn(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Petition_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionCheckIn, size=%i, expected %i", app->size, sizeof(Petition_Struct)); - return; - } - Petition_Struct* inpet = (Petition_Struct*) app->pBuffer; - - Petition* pet = petition_list.GetPetitionByID(inpet->petnumber); - //if (inpet->urgency != pet->GetUrgency()) - pet->SetUrgency(inpet->urgency); - pet->SetLastGM(this->GetName()); - pet->SetGMText(inpet->gmtext); - - pet->SetCheckedOut(false); - petition_list.UpdatePetition(pet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - return; -} - -void Client::Handle_OP_PetitionResolve(const EQApplicationPacket *app) -{ - Handle_OP_PetitionDelete(app); -} - -void Client::Handle_OP_PetitionDelete(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PetitionUpdate_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionDelete, size=%i, expected %i", app->size, sizeof(PetitionUpdate_Struct)); - return; - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate,sizeof(PetitionUpdate_Struct)); - PetitionUpdate_Struct* pet = (PetitionUpdate_Struct*) outapp->pBuffer; - pet->petnumber = *((int*) app->pBuffer); - pet->color = 0x00; - pet->status = 0xFFFFFFFF; - pet->senttime = 0; - strcpy(pet->accountid, ""); - strcpy(pet->gmsenttoo, ""); - pet->quetotal = petition_list.GetTotalPetitions(); - strcpy(pet->charname, ""); - FastQueuePacket(&outapp); - - if (petition_list.DeletePetition(pet->petnumber) == -1) - std::cout << "Something is borked with: " << pet->petnumber << std::endl; - petition_list.ClearPetitions(); - petition_list.UpdateGMQueue(); - petition_list.ReadDatabase(); - petition_list.UpdateZoneListQueue(); - return; -} - -void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PetCommand_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetCommands, size=%i, expected %i", app->size, sizeof(PetCommand_Struct)); - return; - } - char val1[20]={0}; - PetCommand_Struct* pet = (PetCommand_Struct*) app->pBuffer; - Mob* mypet = this->GetPet(); - - if(!mypet || pet->command == PET_LEADER) - { - if(pet->command == PET_LEADER) - { - if(mypet && (!GetTarget() || GetTarget() == mypet)) - { - mypet->Say_StringID(PET_LEADERIS, GetName()); - } - else if((mypet = GetTarget())) - { - Mob *Owner = mypet->GetOwner(); - if(Owner) - mypet->Say_StringID(PET_LEADERIS, Owner->GetCleanName()); else - mypet->Say_StringID(I_FOLLOW_NOONE); - } - } - - return; - } - - if(mypet->GetPetType() == petAnimation && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST) && !GetAA(aaAnimationEmpathy)) - return; - - // just let the command "/pet get lost" work for familiars - if(mypet->GetPetType() == petFamiliar && pet->command != PET_GETLOST) - return; - - uint32 PetCommand = pet->command; - - // Handle Sit/Stand toggle in UF and later. - if(GetClientVersion() >= EQClientUnderfoot) - { - if(PetCommand == PET_SITDOWN) - if(mypet->GetPetOrder() == SPO_Sit) - PetCommand = PET_STANDUP; - } - - switch(PetCommand) - { - case PET_ATTACK: { - if (!GetTarget()) - break; - if (GetTarget()->IsMezzed()) { - Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); - break; - } - if (mypet->IsFeared()) - break; //prevent pet from attacking stuff while feared - - if (!mypet->IsAttackAllowed(GetTarget())) { - mypet->Say_StringID(NOT_LEGAL_TARGET); - break; - } - - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { - if (GetTarget() != this && mypet->DistNoRootNoZ(*GetTarget()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { - if (mypet->IsHeld()) { - if (!mypet->IsFocused()) { - mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack. - if(mypet->GetPetOrder() != SPO_Guard) - mypet->SetPetOrder(SPO_Follow); - } else { - mypet->SetTarget(GetTarget()); - } + { + //The correct opcode, no reason to bother wasting time reconstructing the packet + Invitee->CastToClient()->QueuePacket(app); } - zone->AddAggroMob(); - mypet->AddToHateList(GetTarget(), 1); - Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); } } - break; +#ifdef BOTS + else if (Invitee->IsBot()) { + Bot::ProcessBotGroupInvite(this, std::string(Invitee->GetName())); + } +#endif } - case PET_BACKOFF: { - if (mypet->IsFeared()) break; //keeps pet running while feared + else + { + ServerPacket* pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct)); + memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct)); + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; +} - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_CALMING); - mypet->WipeHateList(); - mypet->SetTarget(nullptr); - } - break; - } - case PET_HEALTHREPORT: { - Message_StringID(MT_PetResponse, PET_REPORT_HP, ConvertArrayF(mypet->GetHPRatio(), val1)); - mypet->ShowBuffList(this); - //Message(10,"%s tells you, 'I have %d percent of my hit points left.'",mypet->GetName(),(uint8)mypet->GetHPRatio()); - break; - } - case PET_GETLOST: { - if (mypet->Charmed()) - break; - if (mypet->GetPetType() == petCharmed || !mypet->IsNPC()) { - // eqlive ignores this command - // we could just remove the charm - // and continue - mypet->BuffFadeByEffect(SE_Charm); - break; - } else { - SetPet(nullptr); - } +void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_GroupMakeLeader, app, GroupMakeLeader_Struct); - mypet->Say_StringID(MT_PetResponse, PET_GETLOST_STRING); - mypet->CastToNPC()->Depop(); + GroupMakeLeader_Struct *gmls = (GroupMakeLeader_Struct *)app->pBuffer; - //Oddly, the client (Titanium) will still allow "/pet get lost" command despite me adding the code below. If someone can figure that out, you can uncomment this code and use it. - /* - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(PET_GETLOST_STRING); - mypet->CastToNPC()->Depop(); - } - */ + Mob* NewLeader = entity_list.GetClientByName(gmls->NewLeader); - break; - } - case PET_GUARDHERE: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + Group* g = GetGroup(); - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - if(mypet->IsNPC()) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_GUARDINGLIFE); - mypet->SetPetOrder(SPO_Guard); - mypet->CastToNPC()->SaveGuardSpot(); - } + if (NewLeader && g) + { + if (g->IsLeader(this)) + g->ChangeLeader(NewLeader); + else { + LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); + DumpPacket(app); } - break; } - case PET_FOLLOWME: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF +} - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_FOLLOWING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } - break; +void Client::Handle_OP_GroupRoles(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupRole_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupRoles, size=%i, expected %i", app->size, sizeof(GroupRole_Struct)); + DumpPacket(app); + return; } - case PET_TAUNT: { - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - Message_StringID(MT_PetResponse, PET_DO_TAUNT); - mypet->CastToNPC()->SetTaunting(true); - } - break; - } - case PET_NOTAUNT: { - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - Message_StringID(MT_PetResponse, PET_NO_TAUNT); - mypet->CastToNPC()->SetTaunting(false); - } - break; - } - case PET_GUARDME: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + GroupRole_Struct *grs = (GroupRole_Struct*)app->pBuffer; - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_GUARDME_STRING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } - break; - } - case PET_SITDOWN: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + Group *g = GetGroup(); - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Sit); - mypet->SetRunAnimSpeed(0); - if(!mypet->UseBardSpellLogic()) //maybe we can have a bard pet - mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting - mypet->SendAppearancePacket(AT_Anim, ANIM_SIT); - } - break; - } - case PET_STANDUP: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + if (!g) + return; - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } + switch (grs->RoleNumber) + { + case 1: //Main Tank + { + if (grs->Toggle) + g->DelegateMainTank(grs->Name1, grs->Toggle); + else + g->UnDelegateMainTank(grs->Name1, grs->Toggle); break; } - case PET_SLUMBER: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - - if(mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Sit); - mypet->SetRunAnimSpeed(0); - if(!mypet->UseBardSpellLogic()) //maybe we can have a bard pet - mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting - mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH); - } + case 2: //Main Assist + { + if (grs->Toggle) + g->DelegateMainAssist(grs->Name1, grs->Toggle); + else + g->UnDelegateMainAssist(grs->Name1, grs->Toggle); break; } - case PET_HOLD: { - if(GetAA(aaPetDiscipline) && mypet->IsNPC()){ - if (mypet->IsFeared()) - break; //could be exploited like PET_BACKOFF - - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); - mypet->SetHeld(true); - } - break; - } - case PET_HOLD_ON: { - if (GetAA(aaPetDiscipline) && mypet->IsNPC() && !mypet->IsHeld()) { - if (mypet->IsFeared()) - break; //could be exploited like PET_BACKOFF - - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); - mypet->SetHeld(true); - } - break; - } - case PET_HOLD_OFF: { - if (GetAA(aaPetDiscipline) && mypet->IsNPC() && mypet->IsHeld()) - mypet->SetHeld(false); - break; - } - case PET_NOCAST: { - if(GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsNoCast()) { - Message_StringID(MT_PetResponse, PET_CASTING); - mypet->CastToNPC()->SetNoCast(false); - } else { - Message_StringID(MT_PetResponse, PET_NOT_CASTING); - mypet->CastToNPC()->SetNoCast(true); - } - } - break; - } - case PET_FOCUS: { - if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); - mypet->CastToNPC()->SetFocused(false); - } else { - Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); - mypet->CastToNPC()->SetFocused(true); - } - } - break; - } - case PET_FOCUS_ON: { - if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (!mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); - mypet->CastToNPC()->SetFocused(true); - } - } - break; - } - case PET_FOCUS_OFF: { - if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); - mypet->CastToNPC()->SetFocused(false); - } - } + case 3: //Puller + { + if (grs->Toggle) + g->DelegatePuller(grs->Name1, grs->Toggle); + else + g->UnDelegatePuller(grs->Name1, grs->Toggle); break; } default: - printf("Client attempted to use a unknown pet command:\n"); break; } } -void Client::Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_PetitionUnCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - uint32 getpetnum = *((uint32*) app->pBuffer); - Petition* getpet = petition_list.GetPetitionByID(getpetnum); - if (getpet != 0) { - getpet->SetCheckedOut(false); - petition_list.UpdatePetition(getpet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - } - } - return; -} - -void Client::Handle_OP_PetitionQue(const EQApplicationPacket *app) -{ -#ifdef _EQDEBUG - printf("%s looking at petitions..\n",this->GetName()); -#endif - return; -} - -void Client::Handle_OP_PDeletePetition(const EQApplicationPacket *app) -{ - if (app->size < 2) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PDeletePetition, size=%i, expected %i", app->size, 2); - return; - } - if(petition_list.DeletePetitionByCharName((char*)app->pBuffer)) - Message_StringID(0,PETITION_DELETED); - else - Message_StringID(0,PETITION_NO_DELETE); - return; -} - -void Client::Handle_OP_PetitionCheckout(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_PetitionCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - uint32 getpetnum = *((uint32*) app->pBuffer); - Petition* getpet = petition_list.GetPetitionByID(getpetnum); - if (getpet != 0) { - getpet->AddCheckout(); - getpet->SetCheckedOut(true); - getpet->SendPetitionToPlayer(this->CastToClient()); - petition_list.UpdatePetition(getpet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - } - } - return; -} - -void Client::Handle_OP_PetitionRefresh(const EQApplicationPacket *app) -{ - // This is When Client Asks for Petition Again and Again... - // break is here because it floods the zones and causes lag if it - // Were to actually do something:P We update on our own schedule now. - return; -} - -void Client::Handle_OP_ReadBook(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BookRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ReadBook, size=%i, expected %i", app->size, sizeof(BookRequest_Struct)); - return; - } - BookRequest_Struct* book = (BookRequest_Struct*) app->pBuffer; - ReadBook(book); - if(GetClientVersion() >= EQClientSoF) - { - EQApplicationPacket EndOfBook(OP_FinishWindow, 0); - QueuePacket(&EndOfBook); - } - return; -} - -void Client::Handle_OP_Emote(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Emote_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_Emote: got %d, expected %d", app->size, - sizeof(Emote_Struct)); - DumpPacket(app); - return; - } - - // Calculate new packet dimensions - Emote_Struct* in = (Emote_Struct*)app->pBuffer; - in->message[1023] = '\0'; - - const char* name = GetName(); - uint32 len_name = strlen(name); - uint32 len_msg = strlen(in->message); - // crash protection -- cheater - if (len_msg > 512) { - in->message[512] = '\0'; - len_msg = 512; - } - uint32 len_packet = sizeof(in->unknown01) + len_name - + len_msg + 1; - - // Construct outgoing packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Emote, len_packet); - Emote_Struct* out = (Emote_Struct*)outapp->pBuffer; - out->unknown01 = in->unknown01; - memcpy(out->message, name, len_name); - memcpy(&out->message[len_name], in->message, len_msg); - - /* - if (target && target->IsClient()) { - entity_list.QueueCloseClients(this, outapp, false, 100, target); - - cptr = outapp->pBuffer + 2; - - // not sure if live does this or not. thought it was a nice feature, but would take a lot to - // clean up grammatical and other errors. Maybe with a regex parser... - replacestr((char *)cptr, target->GetName(), "you"); - replacestr((char *)cptr, " he", " you"); - replacestr((char *)cptr, " she", " you"); - replacestr((char *)cptr, " him", " you"); - replacestr((char *)cptr, " her", " you"); - target->CastToClient()->QueuePacket(outapp); - - } - else - */ - entity_list.QueueCloseClients(this, outapp, true, 100, 0, true, FilterSocials); - - safe_delete(outapp); - return; -} - -void Client::Handle_OP_Animation(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Animation_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_Animation: got %d, expected %d", app->size, - sizeof(Animation_Struct)); - DumpPacket(app); - return; - } - - Animation_Struct *s = (Animation_Struct *) app->pBuffer; - - //might verify spawn ID, but it wouldent affect anything - - DoAnim(s->action, s->value); - - return; -} - -void Client::Handle_OP_SetServerFilter(const EQApplicationPacket *app) -{ - if(app->size != sizeof(SetServerFilter_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_SetServerFilter: got %d, expected %d", app->size, - sizeof(SetServerFilter_Struct)); - DumpPacket(app); - return; - } - SetServerFilter_Struct* filter=(SetServerFilter_Struct*)app->pBuffer; - ServerFilter(filter); - return; -} - -void Client::Handle_OP_GMDelCorpse(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GMDelCorpse_Struct)) - return; - if(this->Admin() < commandEditPlayerCorpses) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/delcorpse"); - return; - } - GMDelCorpse_Struct* dc = (GMDelCorpse_Struct *)app->pBuffer; - Mob* corpse = entity_list.GetMob(dc->corpsename); - if(corpse==0) { - return; - } - if(corpse->IsCorpse() != true) { - return; - } - corpse->CastToCorpse()->Delete(); - std::cout << name << " deleted corpse " << dc->corpsename << std::endl; - Message(13, "Corpse %s deleted.", dc->corpsename); - return; -} - -void Client::Handle_OP_GMKick(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GMKick_Struct)) - return; - if(this->Admin() < minStatusToKick) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/kick"); - return; - } - GMKick_Struct* gmk = (GMKick_Struct *)app->pBuffer; - - Client* client = entity_list.GetClientByName(gmk->name); - if(client==0) { - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; - strcpy(skp->adminname, gmk->gmname); - strcpy(skp->name, gmk->name); - skp->adminrank = this->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } - else { - entity_list.QueueClients(this,app); - //client->Kick(); - } - return; -} - -void Client::Handle_OP_GMServers(const EQApplicationPacket *app) -{ - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_ZoneStatus, strlen(this->GetName())+2); - memset(pack->pBuffer, (uint8) admin, 1); - strcpy((char *) &pack->pBuffer[1], this->GetName()); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - -void Client::Handle_OP_Illusion(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Illusion_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Illusion: got %d, expected %d", app->size, - sizeof(Illusion_Struct)); - DumpPacket(app); - return; - } - - if(!GetGM()) - { - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_Illusion sent by non Game Master.", zone->GetShortName()); - return; - } - - Illusion_Struct* bnpc = (Illusion_Struct*)app->pBuffer; - //these need to be implemented - /* - texture = bnpc->texture; - helmtexture = bnpc->helmtexture; - luclinface = bnpc->luclinface; - */ - race = bnpc->race; - size = 0; - - entity_list.QueueClients(this,app); - return; -} - -void Client::Handle_OP_GMBecomeNPC(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/becomenpc"); - return; - } - if (app->size != sizeof(BecomeNPC_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMBecomeNPC, size=%i, expected %i", app->size, sizeof(BecomeNPC_Struct)); - return; - } - //entity_list.QueueClients(this, app, false); - BecomeNPC_Struct* bnpc = (BecomeNPC_Struct*)app->pBuffer; - - Mob* cli = (Mob*) entity_list.GetMob(bnpc->id); - if(cli==0) - return; - - if(cli->IsClient()) - cli->CastToClient()->QueuePacket(app); - cli->SendAppearancePacket(AT_NPCName, 1, true); - cli->CastToClient()->SetBecomeNPC(true); - cli->CastToClient()->SetBecomeNPCLevel(bnpc->maxlevel); - cli->Message_StringID(0,TOGGLE_OFF); - cli->CastToClient()->tellsoff = true; - //TODO: Make this toggle a BecomeNPC flag so that it gets updated when people zone in as well; Make combat work with this. - return; -} - -void Client::Handle_OP_Fishing(const EQApplicationPacket *app) -{ - if(!p_timers.Expired(&database, pTimerFishing, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - - if (CanFish()) { - parse->EventPlayer(EVENT_FISH_START, this, "", 0); - - //these will trigger GoFish() after a delay if we're able to actually fish, and if not, we won't stop the client from trying again immediately (although we may need to tell it to repop the button) - p_timers.Start(pTimerFishing, FishingReuseTime-1); - fishing_timer.Start(); - } - return; -// Changes made based on Bobs work on foraging. Now can set items in the forage database table to -// forage for. -} - -void Client::Handle_OP_Forage(const EQApplicationPacket *app) -{ - - if(!p_timers.Expired(&database, pTimerForaging, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerForaging, ForagingReuseTime-1); - - ForageItem(); - - return; -} - -void Client::Handle_OP_Mend(const EQApplicationPacket *app) -{ - if(!HasSkill(SkillMend)) - return; - - if(!p_timers.Expired(&database, pTimerMend, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerMend, MendReuseTime-1); - - int mendhp = GetMaxHP() / 4; - int currenthp = GetHP(); - if (MakeRandomInt(0, 199) < (int)GetSkill(SkillMend)) { - - int criticalchance = spellbonuses.CriticalMend + itembonuses.CriticalMend + aabonuses.CriticalMend; - - if(MakeRandomInt(0,99) < criticalchance){ - mendhp *= 2; - Message_StringID(4,MEND_CRITICAL); - } - SetHP(GetHP() + mendhp); - SendHPUpdate(); - Message_StringID(4,MEND_SUCCESS); - } else { - /* the purpose of the following is to make the chance to worsen wounds much less common, - which is more consistent with the way eq live works. - according to my math, this should result in the following probability: - 0 skill - 25% chance to worsen - 20 skill - 23% chance to worsen - 50 skill - 16% chance to worsen */ - if ((GetSkill(SkillMend) <= 75) && (MakeRandomInt(GetSkill(SkillMend),100) < 75) && (MakeRandomInt(1, 3) == 1)) - { - SetHP(currenthp > mendhp ? (GetHP() - mendhp) : 1); - SendHPUpdate(); - Message_StringID(4,MEND_WORSEN); - } - else - Message_StringID(4,MEND_FAIL); - } - - CheckIncreaseSkill(SkillMend, nullptr, 10); - return; -} - -void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) -{ - if(!ClientFinishedLoading()) - { - SetHP(GetHP()-1); - return; - } - - if(app->size != sizeof(EnvDamage2_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_EnvDamage: got %d, expected %d", app->size, - sizeof(EnvDamage2_Struct)); - DumpPacket(app); - return; - } - EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer; - if(admin >= minStatusToAvoidFalling && GetGM()){ - Message(13, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); - SetHP(GetHP()-1);//needed or else the client wont acknowledge - return; - } else if(GetInvul()) { - Message(13, "Your invuln status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); - SetHP(GetHP()-1);//needed or else the client wont acknowledge - return; - } - - int damage = ed->damage; - - if (ed->dmgtype == 252) { - - switch(GetAA(aaAcrobatics)) { //Don't know what acrobatics effect is yet but it should be done client side via aa effect.. till then - case 1: - damage = damage * 95 / 100; - break; - case 2: - damage = damage * 90 / 100; - break; - case 3: - damage = damage * 80 / 100; - break; - } - } - - if(damage < 0) - damage = 31337; - - else if(zone->GetZoneID() == 183 || zone->GetZoneID() == 184) - return; - else - SetHP(GetHP() - damage); - - if(GetHP() <= 0) - { - mod_client_death_env(); - - Death(0, 32000, SPELL_UNKNOWN, SkillHandtoHand); - } - SendHPUpdate(); - return; -} - -void Client::Handle_OP_Damage(const EQApplicationPacket *app) -{ - if(app->size != sizeof(CombatDamage_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Damage: got %d, expected %d", app->size, - sizeof(CombatDamage_Struct)); - DumpPacket(app); - return; - } - - // Broadcast to other clients - CombatDamage_Struct* damage = (CombatDamage_Struct*)app->pBuffer; - //dont send to originator of falling damage packets - entity_list.QueueClients(this, app, (damage->type==DamageTypeFalling)); - return; -} - -void Client::Handle_OP_AAAction(const EQApplicationPacket *app) -{ - mlog(AA__IN, "Received OP_AAAction"); - mpkt(AA__IN, app); - - if(app->size!=sizeof(AA_Action)){ - printf("Error! OP_AAAction size didnt match!\n"); - return; - } - AA_Action* action=(AA_Action*)app->pBuffer; - - if(action->action == aaActionActivate) {//AA Hotkey - mlog(AA__MESSAGE, "Activating AA %d", action->ability); - ActivateAA((aaID) action->ability); - } else if(action->action == aaActionBuy) { - BuyAA(action); - } - else if(action->action == aaActionDisableEXP){ //Turn Off AA Exp - if(m_epp.perAA > 0) - Message_StringID(0, AA_OFF); - m_epp.perAA = 0; - SendAAStats(); - } else if(action->action == aaActionSetEXP) { - if(m_epp.perAA == 0) - Message_StringID(0, AA_ON); - m_epp.perAA = action->exp_value; - if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA=0; // stop exploit with sanity check - // send an update - SendAAStats(); - SendAATable(); - } else { - printf("Unknown AA action: %u %u 0x%x %d\n", action->action, action->ability, action->unknown08, action->exp_value); - } - - return; -} - -void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // Client has elected to buy an item from a Trader - // - _pkt(TRADING__PACKETS, app); - - if(app->size==sizeof(TraderBuy_Struct)){ - - TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer; - - if(Client* Trader=entity_list.GetClientByID(tbs->TraderID)){ - - BuyTraderItem(tbs,Trader,app); - } - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); - } - } - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Struct size mismatch"); - - } - return; -} - -void Client::Handle_OP_Trader(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // SoF sends 1 or more unhandled OP_Trader packets of size 96 when a trade has completed. - // I don't know what they are for (yet), but it doesn't seem to matter that we ignore them. - - _pkt(TRADING__PACKETS, app); - - uint32 max_items = 80; - - /* - if (GetClientVersion() >= EQClientRoF) - max_items = 200; - */ - - //Show Items - if(app->size==sizeof(Trader_ShowItems_Struct)) - { - Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)app->pBuffer; - - switch(sis->Code) - { - case BazaarTrader_EndTraderMode: { - Trader_EndTrader(); - break; - } - case BazaarTrader_EndTransaction: { - - Client* c=entity_list.GetClientByID(sis->TraderID); - if(c) - c->WithCustomer(0); - else - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); - - break; - } - case BazaarTrader_ShowItems: { - Trader_ShowItems(); - break; - } - default: { - _log(TRADING__CLIENT, "Unhandled action code in OP_Trader ShowItems_Struct"); - break; - } - } - } - else if(app->size==sizeof(ClickTrader_Struct)) - { - if(Buyer) { - Trader_EndTrader(); - Message(13, "You cannot be a Trader and Buyer at the same time."); - return; - } - - ClickTrader_Struct* ints = (ClickTrader_Struct*)app->pBuffer; - - if(ints->Code==BazaarTrader_StartTraderMode) - { - GetItems_Struct* gis=GetTraderItems(); - - // Verify there are no NODROP or items with a zero price - bool TradeItemsValid = true; - - for(uint32 i = 0; i < max_items; i++) { - - if(gis->Items[i] == 0) break; - - if(ints->ItemCost[i] == 0) { - Message(13, "Item in Trader Satchel with no price. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - const Item_Struct *Item = database.GetItem(gis->Items[i]); - - if(!Item) { - Message(13, "Unexpected error. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - - if(Item->NoDrop == 0) { - Message(13, "NODROP Item in Trader Satchel. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - } - - if(!TradeItemsValid) { - Trader_EndTrader(); - return; - } - - for (uint32 i = 0; i < max_items; i++) { - if(database.GetItem(gis->Items[i])) { - database.SaveTraderItem(this->CharacterID(),gis->Items[i],gis->SerialNumber[i], - gis->Charges[i],ints->ItemCost[i],i); - } else { - //return; //sony doesnt memset so assume done on first bad item - break; - } - - } - safe_delete(gis); - - this->Trader_StartTrader(); - - if (GetClientVersion() >= EQClientRoF) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct)); - TraderStatus_Struct* tss = (TraderStatus_Struct*)outapp->pBuffer; - tss->Code = BazaarTrader_StartTraderMode2; - QueuePacket(outapp); - _pkt(TRADING__PACKETS, outapp); - safe_delete(outapp); - } - } - else if (app->size==sizeof(TraderStatus_Struct)) - { - TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer; - - if(tss->Code==BazaarTrader_ShowItems) - { - Trader_ShowItems(); - } - } - else { - _log(TRADING__CLIENT,"Client::Handle_OP_Trader: Unknown TraderStruct code of: %i\n", - ints->Code); - - LogFile->write(EQEMuLog::Error, "Unknown TraderStruct code of: %i\n", ints->Code); - } - } - - else if(app->size==sizeof(TraderPriceUpdate_Struct)) - { - HandleTraderPriceUpdate(app); - } - else { - _log(TRADING__CLIENT,"Unknown size for OP_Trader: %i\n", app->size); - LogFile->write(EQEMuLog::Error, "Unknown size for OP_Trader: %i\n", app->size); - DumpPacket(app); - return; - } - - return; -} - -void Client::Handle_OP_GMFind(const EQApplicationPacket *app) -{ - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/find"); - return; - } - if (app->size != sizeof(GMSummon_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMFind, size=%i, expected %i", app->size, sizeof(GMSummon_Struct)); - return; - } - //Break down incoming - GMSummon_Struct* request=(GMSummon_Struct*)app->pBuffer; - //Create a new outgoing - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GMFind, sizeof(GMSummon_Struct)); - GMSummon_Struct* foundplayer=(GMSummon_Struct*)outapp->pBuffer; - //Copy the constants - strcpy(foundplayer->charname,request->charname); - strcpy(foundplayer->gmname, request->gmname); - //Check if the NPC exits intrazone... - Mob* gt = entity_list.GetMob(request->charname); - if (gt != 0) { - foundplayer->success=1; - foundplayer->x=(int32)gt->GetX(); - foundplayer->y=(int32)gt->GetY(); - - foundplayer->z=(int32)gt->GetZ(); - foundplayer->zoneID=zone->GetZoneID(); - } - //Send the packet... - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PickPocket_Struct)) - { - LogFile->write(EQEMuLog::Error, "Size mismatch for Pick Pocket packet"); - DumpPacket(app); - } - - if(!HasSkill(SkillPickPockets)) - { - return; - } - - if(!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) - { - Message(13,"Ability recovery time not yet met."); - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_PickPocket was sent again too quickly.", zone->GetShortName()); - return; - } - PickPocket_Struct* pick_in = (PickPocket_Struct*) app->pBuffer; - - Mob* victim = entity_list.GetMob(pick_in->to); - if (!victim) - return; - - p_timers.Start(pTimerBeggingPickPocket, 8); - if (victim == this){ - Message(0,"You catch yourself red-handed."); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } - else if (victim->GetOwnerID()){ - Message(0,"You cannot steal from pets!"); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } - else if (victim->IsNPC()){ - victim->CastToNPC()->PickPocket(this); - } - else{ - Message(0,"Stealing from clients not yet supported."); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } -} - -void Client::Handle_OP_Bind_Wound(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BindWound_Struct)){ - LogFile->write(EQEMuLog::Error, "Size mismatch for Bind wound packet"); - DumpPacket(app); - } - BindWound_Struct* bind_in = (BindWound_Struct*) app->pBuffer; - Mob* bindmob = entity_list.GetMob(bind_in->to); - if (!bindmob){ - LogFile->write(EQEMuLog::Error, "Bindwound on non-exsistant mob from %s", this->GetName()); - } - else { - LogFile->write(EQEMuLog::Debug, "BindWound in: to:\'%s\' from=\'%s\'", bindmob->GetName(), GetName()); - BindWound(bindmob, true); - } - return; -} - -void Client::Handle_OP_TrackTarget(const EQApplicationPacket *app) -{ - int PlayerClass = GetClass(); - - if((PlayerClass != RANGER) && (PlayerClass != DRUID) && (PlayerClass != BARD)) - return; - - if (app->size != sizeof(TrackTarget_Struct)) - { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_TrackTarget: Expected: %i, Got: %i", - sizeof(TrackTarget_Struct), app->size); - return; - } - - TrackTarget_Struct *tts = (TrackTarget_Struct*)app->pBuffer; - - TrackingID = tts->EntityID; -} - -void Client::Handle_OP_Track(const EQApplicationPacket *app) -{ - if(GetClass() != RANGER && GetClass() != DRUID && GetClass() != BARD) - return; - - if( GetSkill(SkillTracking)==0 ) - SetSkill(SkillTracking,1); - else - CheckIncreaseSkill(SkillTracking, nullptr, 15); - - if(!entity_list.MakeTrackPacket(this)) - LogFile->write(EQEMuLog::Error, "Unable to generate OP_Track packet requested by client."); - - return; -} - -void Client::Handle_OP_TrackUnknown(const EQApplicationPacket *app) -{ - // size 0 send right after OP_Track - return; -} - -void Client::Handle_0x0193(const EQApplicationPacket *app) -{ - // Not sure what this opcode does. It started being sent when OP_ClientUpdate was - // changed to pump OP_ClientUpdate back out instead of OP_MobUpdate - // 2 bytes: 00 00 -} - -void Client::Handle_OP_ClientError(const EQApplicationPacket *app) -{ - ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; - LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); - LogFile->write(EQEMuLog::Error, "Error message:%s", error->message); - return; -} - -void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app) -{ - if(IsInAGuild()) - { - SendGuildRanks(); - SendGuildMembers(); - } - return; -} - -void Client::Handle_OP_TGB(const EQApplicationPacket *app) -{ - OPTGB(app); - return; -} - -void Client::Handle_OP_Split(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Split_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_Split, size=%i, expected %i", app->size, sizeof(Split_Struct)); - return; - } - // The client removes the money on its own, but we have to - // update our state anyway, and make sure they had enough to begin - // with. - Split_Struct *split = (Split_Struct *)app->pBuffer; - //Per the note above, Im not exactly sure what to do on error - //to notify the client of the error... - if(!isgrouped) { - Message(13, "You can not split money if your not in a group."); - return; - } - Group *cgroup = GetGroup(); - if(cgroup == nullptr) { - //invalid group, not sure if we should say more... - Message(13, "You can not split money if your not in a group."); - return; - } - - if(!TakeMoneyFromPP(static_cast(split->copper) + - 10 * static_cast(split->silver) + - 100 * static_cast(split->gold) + - 1000 * static_cast(split->platinum))) { - Message(13, "You do not have enough money to do that split."); - return; - } - cgroup->SplitMoney(split->copper, split->silver, split->gold, split->platinum); - - return; - -} - -void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillSenseTraps)) - return; - - if(!p_timers.Expired(&database, pTimerSenseTraps, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - int reuse = SenseTrapsReuseTime; - switch(GetAA(aaAdvTrapNegotiation)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 3; - break; - case 3: - reuse -= 5; - break; - } - p_timers.Start(pTimerSenseTraps, reuse-1); - - Trap* trap = entity_list.FindNearbyTrap(this,800); - - CheckIncreaseSkill(SkillSenseTraps, nullptr); - - if (trap && trap->skill > 0) { - int uskill = GetSkill(SkillSenseTraps); - if ((MakeRandomInt(0,99) + uskill) >= (MakeRandomInt(0,99) + trap->skill*0.75)) - { - float xdif = trap->x - GetX(); - float ydif = trap->y - GetY(); - if (xdif == 0 && ydif == 0) - Message(MT_Skills,"You sense a trap right under your feet!"); - else if (xdif > 10 && ydif > 10) - Message(MT_Skills,"You sense a trap to the NorthWest."); - else if (xdif < -10 && ydif > 10) - Message(MT_Skills,"You sense a trap to the NorthEast."); - else if (ydif > 10) - Message(MT_Skills,"You sense a trap to the North."); - else if (xdif > 10 && ydif < -10) - Message(MT_Skills,"You sense a trap to the SouthWest."); - else if (xdif < -10 && ydif < -10) - Message(MT_Skills,"You sense a trap to the SouthEast."); - else if (ydif < -10) - Message(MT_Skills,"You sense a trap to the South."); - else if (xdif > 10) - Message(MT_Skills,"You sense a trap to the West."); - else - Message(MT_Skills,"You sense a trap to the East."); - trap->detected = true; - - float angle = CalculateHeadingToTarget(trap->x, trap->y); - - if(angle < 0) - angle = (256+angle); - - angle *= 2; - MovePC(zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ(), angle); - return; - } - } - Message(MT_Skills,"You did not find any traps nearby."); - return; -} - -void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillDisarmTraps)) - return; - - if(!p_timers.Expired(&database, pTimerDisarmTraps, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - int reuse = DisarmTrapsReuseTime; - switch(GetAA(aaAdvTrapNegotiation)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 3; - break; - case 3: - reuse -= 5; - break; - } - p_timers.Start(pTimerDisarmTraps, reuse-1); - - Trap* trap = entity_list.FindNearbyTrap(this,60); - if (trap && trap->detected) - { - int uskill = GetSkill(SkillDisarmTraps); - if ((MakeRandomInt(0, 49) + uskill) >= (MakeRandomInt(0, 49) + trap->skill)) - { - Message(MT_Skills,"You disarm a trap."); - trap->disarmed = true; - trap->chkarea_timer.Disable(); - trap->respawn_timer.Start((trap->respawn_time + MakeRandomInt(0, trap->respawn_var))*1000); - } - else - { - if(MakeRandomInt(0, 99) < 25){ - Message(MT_Skills,"You set off the trap while trying to disarm it!"); - trap->Trigger(this); - } - else{ - Message(MT_Skills,"You failed to disarm a trap."); - } - } - CheckIncreaseSkill(SkillDisarmTraps, nullptr); - return; - } - Message(MT_Skills,"You did not find any traps close enough to disarm."); - return; -} - -void Client::Handle_OP_OpenTributeMaster(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_OpenTributeMaster of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if(app->size != sizeof(StartTribute_Struct)) - printf("Error in OP_OpenTributeMaster. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - //Opens the tribute master window - StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; - Mob* tribmast = entity_list.GetMob(st->tribute_master_id); - if(tribmast && tribmast->IsNPC() && tribmast->GetClass()==TRIBUTE_MASTER - && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { - st->response = 1; - QueuePacket(app); - tribute_master_id = st->tribute_master_id; - DoTributeUpdate(); - } else { - st->response=0; - QueuePacket(app); - } - } - return; -} - -void Client::Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_OpenGuildTributeMaster of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if(app->size != sizeof(StartTribute_Struct)) - printf("Error in OP_OpenGuildTributeMaster. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - //Opens the guild tribute master window - StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; - Mob* tribmast = entity_list.GetMob(st->tribute_master_id); - if(tribmast && tribmast->IsNPC() && tribmast->GetClass()==GUILD_TRIBUTE_MASTER - && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { - st->response = 1; - QueuePacket(app); - tribute_master_id = st->tribute_master_id; - DoTributeUpdate(); - } else { - st->response=0; - QueuePacket(app); - } - } - return; -} - -void Client::Handle_OP_TributeItem(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeItem of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //player donates an item... - if(app->size != sizeof(TributeItem_Struct)) - printf("Error in OP_TributeItem. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - TributeItem_Struct* t = (TributeItem_Struct*)app->pBuffer; - - tribute_master_id = t->tribute_master_id; - //make sure they are dealing with a valid tribute master - Mob* tribmast = entity_list.GetMob(t->tribute_master_id); - if(!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) - return; - if(DistNoRoot(*tribmast) > USE_NPC_RANGE2) - return; - - t->tribute_points = TributeItem(t->slot, t->quantity); - - _log(TRIBUTE__OUT, "Sending tribute item reply with %d points", t->tribute_points); - _pkt(TRIBUTE__OUT, app); - - QueuePacket(app); - } - return; -} - -void Client::Handle_OP_TributeMoney(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeMoney of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //player donates money - if(app->size != sizeof(TributeMoney_Struct)) - printf("Error in OP_TributeMoney. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - TributeMoney_Struct* t = (TributeMoney_Struct*)app->pBuffer; - - tribute_master_id = t->tribute_master_id; - //make sure they are dealing with a valid tribute master - Mob* tribmast = entity_list.GetMob(t->tribute_master_id); - if(!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) - return; - if(DistNoRoot(*tribmast) > USE_NPC_RANGE2) - return; - - t->tribute_points = TributeMoney(t->platinum); - - _log(TRIBUTE__OUT, "Sending tribute money reply with %d points", t->tribute_points); - _pkt(TRIBUTE__OUT, app); - - QueuePacket(app); - } - return; -} - -void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_SelectTribute of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //we should enforce being near a real tribute master to change this - //but im not sure how I wanna do that right now. - if(app->size != sizeof(SelectTributeReq_Struct)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_SelectTribute packet"); - else { - SelectTributeReq_Struct *t = (SelectTributeReq_Struct *) app->pBuffer; - SendTributeDetails(t->client_id, t->tribute_id); - } - return; -} - -void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeUpdate of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //sent when the client changes their tribute settings... - if(app->size != sizeof(TributeInfo_Struct)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeUpdate packet"); - else { - TributeInfo_Struct *t = (TributeInfo_Struct *) app->pBuffer; - ChangeTributeSettings(t); - } - return; -} - -void Client::Handle_OP_TributeToggle(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeToggle of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if(app->size != sizeof(uint32)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeToggle packet"); - else { - uint32 *val = (uint32 *) app->pBuffer; - ToggleTribute(*val? true : false); - } - return; -} - -void Client::Handle_OP_TributeNPC(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeNPC of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - return; -} - -void Client::Handle_OP_CrashDump(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_ControlBoat(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ControlBoat_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ControlBoat, size=%i, expected %i", app->size, sizeof(ControlBoat_Struct)); - return; - } - ControlBoat_Struct* cbs = (ControlBoat_Struct*)app->pBuffer; - Mob* boat = entity_list.GetMob(cbs->boatId); - if (boat == 0) - return; // do nothing if the boat isn't valid - - if(!boat->IsNPC() || (boat->GetRace() != CONTROLLED_BOAT && boat->GetRace() != 502)) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "OP_Control Boat was sent against %s which is of race %u", boat->GetName(), boat->GetRace()); - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - - if (cbs->TakeControl) { - // this uses the boat's target to indicate who has control of it. It has to check hate to make sure the boat isn't actually attacking anyone. - if ((boat->GetTarget() == 0) || (boat->GetTarget() == this && boat->GetHateAmount(this) == 0)) { - boat->SetTarget(this); - } - else { - this->Message_StringID(13,IN_USE); - return; - } - } - else - boat->SetTarget(0); - - EQApplicationPacket* outapp=new EQApplicationPacket(OP_ControlBoat,0); - FastQueuePacket(&outapp); - safe_delete(outapp); - // have the boat signal itself, so quests can be triggered by boat use - boat->CastToNPC()->SignalNPC(0); -} - -void Client::Handle_OP_DumpName(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_SetRunMode(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail) -{ - if(HasSkill(SkillSafeFall)) //this should only get called if the client has safe fall, but just in case... - CheckIncreaseSkill(SkillSafeFall, nullptr); //check for skill up -} - -void Client::Handle_OP_Heartbeat(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_SafePoint(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_Ignore(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) -{ - if(app->size != sizeof(FindPersonRequest_Struct)) - printf("Error in FindPersonRequest_Struct. Expected size of: %zu, but got: %i\n",sizeof(FindPersonRequest_Struct),app->size); - else { - FindPersonRequest_Struct* t = (FindPersonRequest_Struct*)app->pBuffer; - - std::vector points; - Mob* target = entity_list.GetMob(t->npc_id); - - if(target == nullptr) { - //empty length packet == not found. - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - if(!RuleB(Pathing, Find) && RuleB(Bazaar, EnableWarpToTrader) && target->IsClient() && (target->CastToClient()->Trader || - target->CastToClient()->Buyer)) { - Message(15, "Moving you to Trader %s", target->GetName()); - MovePC(zone->GetZoneID(), zone->GetInstanceID(), target->GetX(), target->GetY(), target->GetZ() , 0.0f); - } - - if(!RuleB(Pathing, Find) || !zone->pathing) - { - //fill in the path array... - // - points.resize(2); - points[0].x = GetX(); - points[0].y = GetY(); - points[0].z = GetZ(); - points[1].x = target->GetX(); - points[1].y = target->GetY(); - points[1].z = target->GetZ(); - } - else - { - Map::Vertex Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); - Map::Vertex End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION); - - if(!zone->zonemap->LineIntersectsZone(Start, End, 1.0f, nullptr) && zone->pathing->NoHazards(Start, End)) - { - points.resize(2); - points[0].x = Start.x; - points[0].y = Start.y; - points[0].z = Start.z; - - points[1].x = End.x; - points[1].y = End.y; - points[1].z = End.z; - - } - else - { - std::list pathlist = zone->pathing->FindRoute(Start, End); - - if(pathlist.size() == 0) - { - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - //the client seems to have issues with packets larger than this - if(pathlist.size() > 36) - { - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - // Live appears to send the points in this order: - // Final destination. - // Current Position. - // rest of the points. - FindPerson_Point p; - - int PointNumber = 0; - - bool LeadsToTeleporter = false; - - Map::Vertex v = zone->pathing->GetPathNodeCoordinates(pathlist.back()); - - p.x = v.x; - p.y = v.y; - p.z = v.z; - points.push_back(p); - - p.x = GetX(); - p.y = GetY(); - p.z = GetZ(); - points.push_back(p); - - for(std::list::iterator Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) - { - if((*Iterator) == -1) // Teleporter - { - LeadsToTeleporter = true; - break; - } - - Map::Vertex v = zone->pathing->GetPathNodeCoordinates((*Iterator), false); - p.x = v.x; - p.y = v.y; - p.z = v.z; - points.push_back(p); - ++PointNumber; - } - - if(!LeadsToTeleporter) - { - p.x = target->GetX(); - p.y = target->GetY(); - p.z = target->GetZ(); - - points.push_back(p); - } - - } - } - - SendPathPacket(points); - } - return; -} - -/* Finish client connecting state */ -void Client::CompleteConnect() { - UpdateWho(); - client_state = CLIENT_CONNECTED; - - hpupdate_timer.Start(); - position_timer.Start(); - autosave_timer.Start(); - SetDuelTarget(0); - SetDueling(false); - - EnteringMessages(this); - LoadZoneFlags(); - - /* Sets GM Flag if needed & Sends Petition Queue */ - UpdateAdmin(false); - - if (IsInAGuild()){ - uint8 rank = GuildRank(); - if(GetClientVersion() >= EQClientRoF) - { - switch (rank) { - case 0: { rank = 5; break; } // GUILD_MEMBER 0 - case 1: { rank = 3; break; } // GUILD_OFFICER 1 - case 2: { rank = 1; break; } // GUILD_LEADER 2 - default: { break; } // GUILD_NONE - } - } - SendAppearancePacket(AT_GuildID, GuildID(), false); - SendAppearancePacket(AT_GuildRank, rank, false); - } - for (uint32 spellInt = 0; spellInt < MAX_PP_REF_SPELLBOOK; spellInt++) { - if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000) - m_pp.spell_book[spellInt] = 0xFFFFFFFF; - } - //SendAATable(); - - if (GetHideMe()) Message(13, "[GM] You are currently hidden to all clients"); - - uint32 raidid = database.GetRaidID(GetName()); - Raid *raid = nullptr; - if (raidid > 0){ - raid = entity_list.GetRaidByID(raidid); - if (!raid){ - raid = new Raid(raidid); - if (raid->GetID() != 0){ - entity_list.AddRaid(raid, raidid); - } - else - raid = nullptr; - } - if (raid){ - SetRaidGrouped(true); - raid->LearnMembers(); - raid->VerifyRaid(); - raid->GetRaidDetails(); - /* - Only leader should get this; send to all for now till - I figure out correct creation; can probably also send a no longer leader packet for non leaders - but not important for now. - */ - raid->SendRaidCreate(this); - raid->SendMakeLeaderPacketTo(raid->leadername, this); - raid->SendRaidAdd(GetName(), this); - raid->SendBulkRaid(this); - raid->SendGroupUpdate(this); - uint32 grpID = raid->GetGroup(GetName()); - if (grpID < 12){ - raid->SendRaidGroupRemove(GetName(), grpID); - raid->SendRaidGroupAdd(GetName(), grpID); - } - if (raid->IsLocked()) - raid->SendRaidLockTo(this); - } - } - - //bulk raid send in here eventually - - //reapply some buffs - uint32 buff_count = GetMaxTotalSlots(); - for (uint32 j1 = 0; j1 < buff_count; j1++) { - if (!IsValidSpell(buffs[j1].spellid)) - continue; - - const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; - - int NimbusEffect = GetNimbusEffect(buffs[j1].spellid); - if(NimbusEffect) { - if(!IsNimbusEffectActive(NimbusEffect)) - SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true); - } - - for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { - switch (spell.effectid[x1]) { - case SE_IllusionCopy: - case SE_Illusion: { - if (spell.base[x1] == -1) { - if (gender == 1) - gender = 0; - else if (gender == 0) - gender = 1; - SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); - } - else if (spell.base[x1] == -2) - { - if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) - SendIllusionPacket(GetRace(), GetGender(), spell.max[x1], spell.max[x1]); - } - else if (spell.max[x1] > 0) - { - SendIllusionPacket(spell.base[x1], 0xFF, spell.max[x1], spell.max[x1]); - } - else - { - SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); - } - switch (spell.base[x1]){ - case OGRE: - SendAppearancePacket(AT_Size, 9); - break; - case TROLL: - SendAppearancePacket(AT_Size, 8); - break; - case VAHSHIR: - case BARBARIAN: - SendAppearancePacket(AT_Size, 7); - break; - case HALF_ELF: - case WOOD_ELF: - case DARK_ELF: - case FROGLOK: - SendAppearancePacket(AT_Size, 5); - break; - case DWARF: - SendAppearancePacket(AT_Size, 4); - break; - case HALFLING: - case GNOME: - SendAppearancePacket(AT_Size, 3); - break; - default: - SendAppearancePacket(AT_Size, 6); - break; - } - break; - } - case SE_SummonHorse: { - SummonHorse(buffs[j1].spellid); - //hasmount = true; //this was false, is that the correct thing? - break; - } - case SE_Silence: - { - Silence(true); - break; - } - case SE_Amnesia: - { - Amnesia(true); - break; - } - case SE_DivineAura: - { - invulnerable = true; - break; - } - case SE_Invisibility2: - case SE_Invisibility: - { - invisible = true; - SendAppearancePacket(AT_Invis, 1); - break; - } - case SE_Levitate: - { - if (!zone->CanLevitate()) - { - if (!GetGM()) - { - SendAppearancePacket(AT_Levitate, 0); - BuffFadeByEffect(SE_Levitate); - Message(13, "You can't levitate in this zone."); - } - } - else{ - SendAppearancePacket(AT_Levitate, 2); - } - break; - } - case SE_InvisVsUndead2: - case SE_InvisVsUndead: - { - invisible_undead = true; - break; - } - case SE_InvisVsAnimals: - { - invisible_animals = true; - break; - } - case SE_AddMeleeProc: - case SE_WeaponProc: - { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - case SE_DefensiveProc: - { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - case SE_RangedProc: - { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - } - } - } - - /* Sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead */ - entity_list.SendZoneAppearance(this); - /* Sends the Nimbus particle effects (up to 3) for any mob using them */ - entity_list.SendNimbusEffects(this); - - entity_list.SendUntargetable(this); - - int x; - for (x = 0; x < 8; x++) - SendWearChange(x); - Mob *pet = GetPet(); - if (pet != nullptr) { - for (x = 0; x < 8; x++) - pet->SendWearChange(x); - } - - entity_list.SendTraders(this); - - zoneinpacket_timer.Start(); - - if (GetPet()){ - GetPet()->SendPetBuffsToClient(); - } - - if (GetGroup()) - database.RefreshGroupFromDB(this); - - if (RuleB(TaskSystem, EnableTaskSystem)) - TaskPeriodic_Timer.Start(); - else - TaskPeriodic_Timer.Disable(); - - conn_state = ClientConnectFinished; - - //enforce some rules.. - if (!CanBeInZone()) { - _log(CLIENT__ERROR, "Kicking char from zone, not allowed here"); - GoToSafeCoords(database.GetZoneID("arena"), 0); - return; - } - - if (zone) - zone->weatherSend(); - - TotalKarma = database.GetKarma(AccountID()); - SendDisciplineTimers(); - - parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); - - /* This sub event is for if a player logs in for the first time since entering world. */ - if (firstlogon == 1){ - parse->EventPlayer(EVENT_CONNECT, this, "", 0); - /* QS: PlayerLogConnectDisconnect */ - if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ - std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); - } - } - - if(zone) { - if(zone->GetInstanceTimer()) { - uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime(); - uint32 day = (ttime/86400000); - uint32 hour = (ttime/3600000)%24; - uint32 minute = (ttime/60000)%60; - uint32 second = (ttime/1000)%60; - if(day) { - Message(15, "%s(%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second); - } - else if(hour) { - Message(15, "%s(%u) will expire in %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), hour, minute, second); - } - else if(minute) { - Message(15, "%s(%u) will expire in %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), minute, second); - } - else { - Message(15, "%s(%u) will expire in in %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), second); - } - } - } - - SendRewards(); - SendAltCurrencies(); - database.LoadAltCurrencyValues(CharacterID(), alternate_currency); - SendAlternateCurrencyValues(); - alternate_currency_loaded = true; - ProcessAlternateCurrencyQueue(); - - /* This needs to be set, this determines whether or not data was loaded properly before a save */ - client_data_loaded = true; - - CalcItemScale(); - DoItemEnterZone(); - - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) - GuildBanks->SendGuildBank(this); - - if(GetClientVersion() >= EQClientSoD) - entity_list.SendFindableNPCList(this); - - if(IsInAGuild()) { - SendGuildRanks(); - guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); - guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID()); - } - - /** Request adventure info **/ - ServerPacket *pack = new ServerPacket(ServerOP_AdventureDataRequest, 64); - strcpy((char*)pack->pBuffer, GetName()); - worldserver.SendPacket(pack); - delete pack; - - if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { - EQApplicationPacket *outapp = MakeBuffsPacket(false); - CastToClient()->FastQueuePacket(&outapp); - } - - entity_list.RefreshClientXTargets(this); - - worldserver.RequestTellQueue(GetName()); -} - -void Client::Handle_OP_KeyRing(const EQApplicationPacket *app) -{ - KeyRingList(); -} - -void Client::Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app) { - if(app->size != 1) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); - DumpPacket(app); - return; - } - uint8 *mode = (uint8 *) app->pBuffer; - if(*mode) { - m_pp.leadAAActive = 1; - Save(); - Message_StringID(clientMessageYellow, LEADERSHIP_EXP_ON); - } else { - m_pp.leadAAActive = 0; - Save(); - Message_StringID(clientMessageYellow, LEADERSHIP_EXP_OFF); - } -} - - -void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) { - if(app->size != sizeof(uint32)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); - DumpPacket(app); - return; - } - uint32 aaid = *((uint32 *) app->pBuffer); - - if(aaid >= _maxLeaderAA) - return; - - uint32 current_rank = m_pp.leader_abilities.ranks[aaid]; - if(current_rank >= MAX_LEADERSHIP_TIERS) { - Message(13, "This ability can be trained no further."); - return; - } - - uint8 cost = LeadershipAACosts[aaid][current_rank]; - if(cost == 0) { - Message(13, "This ability can be trained no further."); - return; - } - - //TODO: we need to enforce prerequisits - - if(aaid >= raidAAMarkNPC) { - //it is a raid ability. - if(cost > m_pp.raid_leadership_points) { - Message(13, "You do not have enough points to purchase this ability."); - return; - } - - //sell them the ability. - m_pp.raid_leadership_points -= cost; - m_pp.leader_abilities.ranks[aaid]++; - } else { - //it is a group ability. - if(cost > m_pp.group_leadership_points) { - Message(13, "You do not have enough points to purchase this ability."); - return; - } - - //sell them the ability. - m_pp.group_leadership_points -= cost; - m_pp.leader_abilities.ranks[aaid]++; - - database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); - } - - //success, send them an update - EQApplicationPacket *outapp = new EQApplicationPacket(OP_UpdateLeadershipAA, sizeof(UpdateLeadershipAA_Struct)); - UpdateLeadershipAA_Struct *u = (UpdateLeadershipAA_Struct *) outapp->pBuffer; - u->ability_id = aaid; - u->new_rank = m_pp.leader_abilities.ranks[aaid]; - u->pointsleft = m_pp.group_leadership_points; // FIXME: Take into account raid abilities - FastQueuePacket(&outapp); - - Group *g = GetGroup(); - - // Update all group members with the new AA the leader has purchased. - if(g) { - g->UpdateGroupAAs(); - g->SendLeadershipAAUpdate(); - } - -} - -void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) -{ - if(app->size != sizeof(SetTitle_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_SetTitle expected %i got %i", sizeof(SetTitle_Struct), app->size); - DumpPacket(app); - return; - } - - SetTitle_Struct *sts = (SetTitle_Struct *)app->pBuffer; - - std::string Title; - - if(!sts->is_suffix) - { - Title = title_manager.GetPrefix(sts->title_id); - SetAATitle(Title.c_str()); - } - else - { - Title = title_manager.GetSuffix(sts->title_id); - SetTitleSuffix(Title.c_str()); - } -} - -void Client::Handle_OP_RequestTitles(const EQApplicationPacket *app) -{ - - EQApplicationPacket *outapp = title_manager.MakeTitlesPacket(this); - - if(outapp != nullptr) - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_BankerChange(const EQApplicationPacket *app) -{ - if(app->size != sizeof(BankerChange_Struct) && app->size!=4) //Titanium only sends 4 Bytes for this - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BankerChange expected %i got %i", sizeof(BankerChange_Struct), app->size); - DumpPacket(app); - return; - } - - uint32 distance = 0; - NPC *banker = entity_list.GetClosestBanker(this, distance); - - if(!banker || distance > USE_NPC_RANGE2) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(money) but %s is non-existant or too far away (%u units).", - banker ? banker->GetName() : "UNKNOWN NPC", distance); - database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - - EQApplicationPacket *outapp=new EQApplicationPacket(OP_BankerChange,nullptr,sizeof(BankerChange_Struct)); - BankerChange_Struct *bc=(BankerChange_Struct *)outapp->pBuffer; - - if(m_pp.platinum < 0) - m_pp.platinum = 0; - if(m_pp.gold < 0) - m_pp.gold = 0; - if(m_pp.silver < 0) - m_pp.silver = 0; - if(m_pp.copper < 0) - m_pp.copper = 0; - - if(m_pp.platinum_bank < 0) - m_pp.platinum_bank = 0; - if(m_pp.gold_bank < 0) - m_pp.gold_bank = 0; - if(m_pp.silver_bank < 0) - m_pp.silver_bank = 0; - if(m_pp.copper_bank < 0) - m_pp.copper_bank = 0; - - uint64 cp = static_cast(m_pp.copper) + - (static_cast(m_pp.silver) * 10) + - (static_cast(m_pp.gold) * 100) + - (static_cast(m_pp.platinum) * 1000); - - m_pp.copper=cp%10; - cp/=10; - m_pp.silver=cp%10; - cp/=10; - m_pp.gold=cp%10; - cp/=10; - m_pp.platinum=cp; - - cp = static_cast(m_pp.copper_bank) + - (static_cast(m_pp.silver_bank) * 10) + - (static_cast(m_pp.gold_bank) * 100) + - (static_cast(m_pp.platinum_bank) * 1000); - - m_pp.copper_bank=cp%10; - cp/=10; - m_pp.silver_bank=cp%10; - cp/=10; - m_pp.gold_bank=cp%10; - cp/=10; - m_pp.platinum_bank=cp; - - bc->copper=m_pp.copper; - bc->silver=m_pp.silver; - bc->gold=m_pp.gold; - bc->platinum=m_pp.platinum; - - bc->copper_bank=m_pp.copper_bank; - bc->silver_bank=m_pp.silver_bank; - bc->gold_bank=m_pp.gold_bank; - bc->platinum_bank=m_pp.platinum_bank; - - FastQueuePacket(&outapp); - - return; -} - -void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) -{ - if(app->size != sizeof(bool)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AutoFire expected %i got %i", sizeof(bool), app->size); - DumpPacket(app); - return; - } - bool *af = (bool*)app->pBuffer; - auto_fire = *af; - auto_attack = false; - SetAttackTimer(); -} -void Client::Handle_OP_Rewind(const EQApplicationPacket *app) -{ - if ((rewind_timer.GetRemainingTime() > 1 && rewind_timer.Enabled())) { - Message_StringID(MT_System, REWIND_WAIT); - } else { - CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), rewind_x, rewind_y, rewind_z, 0, 2, Rewind); - rewind_timer.Start(30000, true); - } -} - -void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RaidGeneral_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_RaidCommand, size=%i, expected %i", app->size, sizeof(RaidGeneral_Struct)); - DumpPacket(app); - return; - } - - RaidGeneral_Struct *ri = (RaidGeneral_Struct*)app->pBuffer; - switch(ri->action) - { - case RaidCommandInviteIntoExisting: - case RaidCommandInvite: { - Client *i = entity_list.GetClientByName(ri->player_name); - if(i){ - Group *g = i->GetGroup(); - if(g){ - if(g->IsLeader(i) == false) - Message(13, "You can only invite an ungrouped player or group leader to join your raid."); - else{ - //This sends an "invite" to the client in question. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(rg->leader_name, ri->leader_name, 64); - strn0cpy(rg->player_name, ri->player_name, 64); - - rg->parameter = 0; - rg->action = 20; - i->QueuePacket(outapp); - safe_delete(outapp); - } - } - else{ - //This sends an "invite" to the client in question. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(rg->leader_name, ri->leader_name, 64); - strn0cpy(rg->player_name, ri->player_name, 64); - - rg->parameter = 0; - rg->action = 20; - i->QueuePacket(outapp); - safe_delete(outapp); - } - } - break; - } - case RaidCommandAcceptInvite: { - Client *i = entity_list.GetClientByName(ri->player_name); - if(i){ - if(IsRaidGrouped()){ - i->Message_StringID(0, 5060); //group failed, must invite members not in raid... - return; - } - Raid *r = entity_list.GetRaidByClient(i); - if(r){ - r->VerifyRaid(); - Group *g = GetGroup(); - if(g){ - if(g->GroupCount()+r->RaidCount() > MAX_RAID_MEMBERS) - { - i->Message(13, "Invite failed, group invite would create a raid larger than the maximum number of members allowed."); - return; - } - } - else{ - if(1+r->RaidCount() > MAX_RAID_MEMBERS) - { - i->Message(13, "Invite failed, member invite would create a raid larger than the maximum number of members allowed."); - return; - } - } - if(g){//add us all - uint32 freeGroup = r->GetFreeGroup(); - Client *addClient = nullptr; - for(int x = 0; x < 6; x++) - { - if(g->members[x]){ - Client *c = nullptr; - if(g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - - if(!addClient) - { - addClient = c; - r->SetGroupLeader(addClient->GetName()); - } - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - if(g->IsLeader(g->members[x])) - r->AddMember(c, freeGroup, false, true); - else - r->AddMember(c, freeGroup); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - g->DisbandGroup(); - r->GroupUpdate(freeGroup); - } - else{ - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->AddMember(this); - r->SendBulkRaid(this); - if(r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - } - else - { - Group *ig = i->GetGroup(); - Group *g = GetGroup(); - if(g) //if our target has a group - { - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - - uint32 groupFree = r->GetFreeGroup(); //get a free group - if(ig){ //if we already have a group then cycle through adding us... - Client *addClientig = nullptr; - for(int x = 0; x < 6; x++) - { - if(ig->members[x]){ - if(!addClientig){ - if(ig->members[x]->IsClient()){ - addClientig = ig->members[x]->CastToClient(); - r->SetGroupLeader(addClientig->GetName()); - } - } - if(ig->IsLeader(ig->members[x])){ - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree, true, true, true); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else{ - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - ig->DisbandGroup(); - r->GroupUpdate(groupFree); - groupFree = r->GetFreeGroup(); - } - else{ //else just add the inviter - r->SendRaidCreate(i); - r->AddMember(i,0xFFFFFFFF, true, false, true); - } - - Client *addClient = nullptr; - //now add the existing group - for(int x = 0; x < 6; x++) - { - if(g->members[x]){ - if(!addClient) - { - if(g->members[x]->IsClient()){ - addClient = g->members[x]->CastToClient(); - r->SetGroupLeader(addClient->GetName()); - } - } - if(g->IsLeader(g->members[x])) - { - Client *c = nullptr; - if(g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree, false, true); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else - { - Client *c = nullptr; - if(g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - g->DisbandGroup(); - r->GroupUpdate(groupFree); - } - else - { - if(ig){ - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - Client *addClientig = nullptr; - for(int x = 0; x < 6; x++) - { - if(ig->members[x]) - { - if(!addClientig){ - if(ig->members[x]->IsClient()){ - addClientig = ig->members[x]->CastToClient(); - r->SetGroupLeader(addClientig->GetName()); - } - } - if(ig->IsLeader(ig->members[x])) - { - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, 0, true, true, true); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else - { - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, 0); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->SendBulkRaid(this); - r->AddMember(this); - ig->DisbandGroup(); - r->GroupUpdate(0); - if(r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - else{ - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - r->SendRaidCreate(i); - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->AddMember(i,0xFFFFFFFF, true, false, true); - r->SendBulkRaid(this); - r->AddMember(this); - if(r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - } - } - } - break; - } - case RaidCommandDisband: { - Raid *r = entity_list.GetRaidByClient(this); - if(r){ - //if(this == r->GetLeader()){ - uint32 grp = r->GetGroup(ri->leader_name); - - if(grp < 12){ - uint32 i = r->GetPlayerIndex(ri->leader_name); - if(r->members[i].IsGroupLeader){ //assign group leader to someone else - for(int x = 0; x < MAX_RAID_MEMBERS; x++){ - if(strlen(r->members[x].membername) > 0 && i != x){ - if(r->members[x].GroupNumber == grp){ - r->SetGroupLeader(ri->leader_name, false); - r->SetGroupLeader(r->members[x].membername); - break; - } - } - } - - } - if(r->members[i].IsRaidLeader){ - for(int x = 0; x < MAX_RAID_MEMBERS; x++){ - if(strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, r->members[i].membername) != 0) - { - r->SetRaidLeader(r->members[i].membername, r->members[x].membername); - break; - } - } - } - } - - r->RemoveMember(ri->leader_name); - Client *c = entity_list.GetClientByName(ri->leader_name); - if(c) - r->SendGroupDisband(c); - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupRemove(ri->leader_name, grp); - r->GroupUpdate(grp);// break - //} - } - break; - } - case RaidCommandMoveGroup: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(ri->parameter < 12) //moving to a group - { - uint8 grpcount = r->GroupCount(ri->parameter); - - if(grpcount < 6) - { - Client *c = entity_list.GetClientByName(ri->leader_name); - uint32 oldgrp = r->GetGroup(ri->leader_name); - if(ri->parameter == oldgrp) //don't rejoin grp if we order to join same group. - break; - - if(r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader) - { - r->SetGroupLeader(ri->leader_name, false); - if(oldgrp < 12){ //we were the leader of our old grp - for(int x = 0; x < MAX_RAID_MEMBERS; x++) //assign a new grp leader if we can - { - if(r->members[x].GroupNumber == oldgrp) - { - if(strcmp(ri->leader_name, r->members[x].membername) != 0 && strlen(ri->leader_name) > 0) - { - r->SetGroupLeader(r->members[x].membername); - Client *cgl = entity_list.GetClientByName(r->members[x].membername); - if(cgl){ - r->SendRaidRemove(r->members[x].membername, cgl); - r->SendRaidCreate(cgl); - r->SendMakeLeaderPacketTo(r->leadername, cgl); - r->SendRaidAdd(r->members[x].membername, cgl); - r->SendBulkRaid(cgl); - if(r->IsLocked()) { - r->SendRaidLockTo(cgl); - } - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - strn0cpy(rga->playername, r->members[x].membername, 64); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - break; - } - } - } - } - } - if(grpcount == 0) - r->SetGroupLeader(ri->leader_name); - - r->MoveMember(ri->leader_name, ri->parameter); - if(c){ - r->SendGroupDisband(c); - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupAdd(ri->leader_name, ri->parameter); - //r->SendRaidGroupRemove(ri->leader_name, oldgrp); - //r->SendGroupUpdate(c); - //break - r->GroupUpdate(ri->parameter); //send group update to our new group - if(oldgrp < 12) //if our old was a group send update there too - r->GroupUpdate(oldgrp); - - //r->SendMakeGroupLeaderPacketAll(); - } - } - else //moving to ungrouped - { - Client *c = entity_list.GetClientByName(ri->leader_name); - uint32 oldgrp = r->GetGroup(ri->leader_name); - if(r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader){ - r->SetGroupLeader(ri->leader_name, false); - for(int x = 0; x < MAX_RAID_MEMBERS; x++) - { - if(strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, ri->leader_name) != 0) - { - r->SetGroupLeader(r->members[x].membername); - Client *cgl = entity_list.GetClientByName(r->members[x].membername); - if(cgl){ - r->SendRaidRemove(r->members[x].membername, cgl); - r->SendRaidCreate(cgl); - r->SendMakeLeaderPacketTo(r->leadername, cgl); - r->SendRaidAdd(r->members[x].membername, cgl); - r->SendBulkRaid(cgl); - if(r->IsLocked()) { - r->SendRaidLockTo(cgl); - } - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - strn0cpy(rga->playername,r->members[x].membername, 64); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - break; - } - } - } - r->MoveMember(ri->leader_name, 0xFFFFFFFF); - if(c){ - r->SendGroupDisband(c); - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupRemove(ri->leader_name, oldgrp); - r->GroupUpdate(oldgrp); - //r->SendMakeGroupLeaderPacketAll(); - } - } - break; - } - case RaidCommandRaidLock: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(!r->IsLocked()) - r->LockRaid(true); - else - r->SendRaidLockTo(this); - } - break; - } - case RaidCommandRaidUnlock: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(r->IsLocked()) - r->LockRaid(false); - else - r->SendRaidUnlockTo(this); - } - break; - } - case RaidCommandLootType2: - case RaidCommandLootType: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - Message(15, "Loot type changed to: %d.", ri->parameter); - r->ChangeLootType(ri->parameter); - } - break; - } - - case RaidCommandAddLooter2: - case RaidCommandAddLooter: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - Message(15, "Adding %s as a raid looter.", ri->leader_name); - r->AddRaidLooter(ri->leader_name); - } - break; - } - - case RaidCommandRemoveLooter2: - case RaidCommandRemoveLooter: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - Message(15, "Removing %s as a raid looter.", ri->leader_name); - r->RemoveRaidLooter(ri->leader_name); - } - break; - } - - case RaidCommandMakeLeader: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(strcmp(r->leadername, GetName()) == 0){ - r->SetRaidLeader(GetName(), ri->leader_name); - } - } - break; - } - - default: { - Message(13, "Raid command (%d) NYI", ri->action); - break; - } - } -} - -void Client::Handle_OP_Translocate(const EQApplicationPacket *app) { - - if(app->size != sizeof(Translocate_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Translocate expected %i got %i", sizeof(Translocate_Struct), app->size); - DumpPacket(app); - return; - } - Translocate_Struct *its = (Translocate_Struct*)app->pBuffer; - - if(!PendingTranslocate) return; - - if((RuleI(Spells, TranslocateTimeLimit) > 0) && (time(nullptr) > (TranslocateTime + RuleI(Spells, TranslocateTimeLimit)))) { - Message(13, "You did not accept the Translocate within the required time limit."); - PendingTranslocate = false; - return; - } - - if(its->Complete == 1) { - - int SpellID = PendingTranslocateData.SpellID; - int i = parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, SpellID, 0); - - if(i == 0) - { - // If the spell has a translocate to bind effect, AND we are already in the zone the client - // is bound in, use the GoToBind method. If we send OP_Translocate in this case, the client moves itself - // to the bind coords it has from the PlayerProfile, but with the X and Y reversed. I suspect they are - // reversed in the pp, and since spells like Gate are handled serverside, this has not mattered before. - if (((SpellID == 1422) || (SpellID == 1334) || (SpellID == 3243)) && - zone->GetZoneID() == PendingTranslocateData.ZoneID) - { - PendingTranslocate = false; - GoToBind(); - return; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Translocate, sizeof(Translocate_Struct)); - Translocate_Struct *ots = (Translocate_Struct*)outapp->pBuffer; - memcpy(ots, &PendingTranslocateData, sizeof(Translocate_Struct)); - - //Was sending the packet back to initiate client zone... - //but that could be abusable, so lets go through proper channels - MovePC(ots->ZoneID, 0, ots->x, ots->y, ots->z, GetHeading(), 0, ZoneSolicited); - } - } - - PendingTranslocate = false; -} - -void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app) { - - if(app->size != sizeof(Sacrifice_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Sacrifice expected %i got %i", sizeof(Sacrifice_Struct), app->size); - DumpPacket(app); - return; - } - Sacrifice_Struct *ss = (Sacrifice_Struct*)app->pBuffer; - - if(!PendingSacrifice) { - LogFile->write(EQEMuLog::Error, "Unexpected OP_Sacrifice reply"); - DumpPacket(app); - return; - } - - if(ss->Confirm) { - Client *Caster = entity_list.GetClientByName(SacrificeCaster.c_str()); - if(Caster) Sacrifice(Caster); - } - PendingSacrifice = false; - SacrificeCaster.clear(); -} - -void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app) { - - if(app->size != sizeof(AcceptNewTask_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AcceptNewTask expected %i got %i", - sizeof(AcceptNewTask_Struct), app->size); - DumpPacket(app); - return; - } - AcceptNewTask_Struct *ant = (AcceptNewTask_Struct*)app->pBuffer; - - if(ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->AcceptNewTask(this, ant->task_id, ant->task_master_id); -} - -void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) { - - if(app->size != sizeof(CancelTask_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_CancelTask expected %i got %i", - sizeof(CancelTask_Struct), app->size); - DumpPacket(app); - return; - } - CancelTask_Struct *cts = (CancelTask_Struct*)app->pBuffer; - - if(RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->CancelTask(this, cts->SequenceNumber); -} - -void Client::Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app) { - - if(app->size != sizeof(TaskHistoryRequest_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_TaskHistoryRequest expected %i got %i", - sizeof(TaskHistoryRequest_Struct), app->size); - DumpPacket(app); - return; - } - TaskHistoryRequest_Struct *ths = (TaskHistoryRequest_Struct*)app->pBuffer; - - if(RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->SendTaskHistory(this, ths->TaskIndex); -} - -void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) { - - // Although there are three different structs for OP_Bandolier, they are all the same size. - // - if(app->size != sizeof(BandolierCreate_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Bandolier expected %i got %i", - sizeof(BandolierCreate_Struct), app->size); - DumpPacket(app); - return; - } - - BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; - - switch(bs->action) { - case BandolierCreate: - CreateBandolier(app); - break; - case BandolierRemove: - RemoveBandolier(app); - break; - case BandolierSet: - SetBandolier(app); - break; - default: - LogFile->write(EQEMuLog::Debug, "Uknown Bandolier action %i", bs->action); - - } -} - -void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) { - - if(app->size != sizeof(PopupResponse_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PopupResponse expected %i got %i", - sizeof(PopupResponse_Struct), app->size); - DumpPacket(app); - return; - } - PopupResponse_Struct *prs = (PopupResponse_Struct*)app->pBuffer; - - // Handle any EQEmu defined popup Ids first - switch(prs->popupid) - { - case POPUPID_UPDATE_SHOWSTATSWINDOW: - if(GetTarget() && GetTarget()->IsClient()) - GetTarget()->CastToClient()->SendStatsWindow(this, true); - else - SendStatsWindow(this, true); - return; - - default: - break; - } - - char buf[16]; - sprintf(buf, "%d\0", prs->popupid); - - parse->EventPlayer(EVENT_POPUP_RESPONSE, this, buf, 0); - - Mob* Target = GetTarget(); - if(Target && Target->IsNPC()) { - parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, buf, 0); - } -} - -void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) { - if(app->size != sizeof(MovePotionToBelt_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PotionBelt expected %i got %i", - sizeof(MovePotionToBelt_Struct), app->size); - DumpPacket(app); - return; - } - MovePotionToBelt_Struct *mptbs = (MovePotionToBelt_Struct*)app->pBuffer; - if(mptbs->Action == 0) { - const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID); - if(BaseItem) { - m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID; - m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon; - strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, BaseItem->Name, sizeof(BaseItem->Name)); - database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.items[mptbs->SlotNumber].item_id, m_pp.potionbelt.items[mptbs->SlotNumber].icon); - } - } - else { - m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 0; - m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0; - strncpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, "\0", 1); - } -} - -void Client::Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app) { - - if (app->size != sizeof(LFGGetMatchesRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFGGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFGGetMatchesRequest_Struct)); - DumpPacket(app); - return; - } - LFGGetMatchesRequest_Struct* gmrs = (LFGGetMatchesRequest_Struct*)app->pBuffer; - - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_LFGMatches, sizeof(ServerLFGMatchesRequest_Struct)); - ServerLFGMatchesRequest_Struct* smrs = (ServerLFGMatchesRequest_Struct*) pack->pBuffer; - smrs->FromID = GetID(); - smrs->QuerierLevel = GetLevel(); - strcpy(smrs->FromName, GetName()); - smrs->FromLevel = gmrs->FromLevel; - smrs->ToLevel = gmrs->ToLevel; - smrs->Classes = gmrs->Classes; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - - -void Client::Handle_OP_LFPCommand(const EQApplicationPacket *app) { - - if (app->size != sizeof(LFP_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPCommand, size=%i, expected %i", app->size, sizeof(LFP_Struct)); - DumpPacket(app); - return; - } - LFP_Struct *lfp = (LFP_Struct*)app->pBuffer; - - LFP = lfp->Action != LFPOff; - database.SetLFP(CharacterID(), LFP); - - if(!LFP) { - worldserver.StopLFP(CharacterID()); - return; - } - - GroupLFPMemberEntry LFPMembers[MAX_GROUP_MEMBERS]; - - for(unsigned int i=0; iGetZoneID(); - LFPMembers[0].GuildID = GuildID(); - - if(g) { - // This should not happen. The client checks if you are in a group and will not let you put LFP on if - // you are not the leader. - if(!g->IsLeader(this)) { - LogFile->write(EQEMuLog::Error,"Client sent LFP on for character %s who is grouped but not leader.", GetName()); - return; - } - // Fill the LFPMembers array with the rest of the group members, excluding ourself - // We don't fill in the class, level or zone, because we may not be able to determine - // them if the other group members are not in this zone. World will fill in this information - // for us, if it can. - int NextFreeSlot = 1; - for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(strcasecmp(g->membername[i], LFPMembers[0].Name)) - strcpy(LFPMembers[NextFreeSlot++].Name, g->membername[i]); - } - } - - - worldserver.UpdateLFP(CharacterID(), lfp->Action, lfp->MatchFilter, lfp->FromLevel, lfp->ToLevel, lfp->Classes, - lfp->Comments, LFPMembers); - - -} - -void Client::Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app) { - - if (app->size != sizeof(LFPGetMatchesRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFPGetMatchesRequest_Struct)); - DumpPacket(app); - return; - } - LFPGetMatchesRequest_Struct* gmrs = (LFPGetMatchesRequest_Struct*)app->pBuffer; - - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_LFPMatches, sizeof(ServerLFPMatchesRequest_Struct)); - ServerLFPMatchesRequest_Struct* smrs = (ServerLFPMatchesRequest_Struct*) pack->pBuffer; - smrs->FromID = GetID(); - smrs->FromLevel = gmrs->FromLevel; - smrs->ToLevel = gmrs->ToLevel; - smrs->QuerierLevel = GetLevel(); - smrs->QuerierClass = GetClass(); - strcpy(smrs->FromName, GetName()); - worldserver.SendPacket(pack); - safe_delete(pack); - } - - return; -} - -void Client::Handle_OP_Barter(const EQApplicationPacket *app) -{ - - if(app->size < 4) - { - LogFile->write(EQEMuLog::Debug, "OP_Barter packet below minimum expected size. The packet was %i bytes.", app->size); - DumpPacket(app); - return; - } - - char* Buf = (char *)app->pBuffer; - - // The first 4 bytes of the packet determine the action. A lot of Barter packets require the - // packet the client sent, sent back to it as an acknowledgement. - // - uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf); - - _pkt(TRADING__BARTER, app); - - switch(Action) - { - - case Barter_BuyerSearch: - { - BuyerItemSearch(app); - break; - } - - case Barter_SellerSearch: - { - BarterSearchRequest_Struct *bsr = (BarterSearchRequest_Struct*)app->pBuffer; - SendBuyerResults(bsr->SearchString, bsr->SearchID); - break; - } - - case Barter_BuyerModeOn: - { - if(!Trader) { - ToggleBuyerMode(true); - } - else { - Buf = (char *)app->pBuffer; - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerModeOff); - Message(13, "You cannot be a Trader and Buyer at the same time."); - } - QueuePacket(app); - break; - } - - case Barter_BuyerModeOff: - { - QueuePacket(app); - ToggleBuyerMode(false); - break; - } - - case Barter_BuyerItemUpdate: - { - UpdateBuyLine(app); - break; - } - - case Barter_BuyerItemRemove: - { - BuyerRemoveItem_Struct* bris = (BuyerRemoveItem_Struct*)app->pBuffer; - database.RemoveBuyLine(CharacterID(), bris->BuySlot); - QueuePacket(app); - break; - } - - case Barter_SellItem: - { - SellToBuyer(app); - break; - } - - case Barter_BuyerInspectBegin: - { - ShowBuyLines(app); - break; - } - - case Barter_BuyerInspectEnd: - { - BuyerInspectRequest_Struct* bir = ( BuyerInspectRequest_Struct*)app->pBuffer; - Client *Buyer = entity_list.GetClientByID(bir->BuyerID); - if(Buyer) - Buyer->WithCustomer(0); - - break; - } - - case Barter_BarterItemInspect: - { - BarterItemSearchLinkRequest_Struct* bislr = (BarterItemSearchLinkRequest_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bislr->ItemID); - - if (!item) - Message(13, "Error: This item does not exist!"); - else - { - ItemInst* inst = database.CreateItem(item); - if (inst) - { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - } - break; - } - - case Barter_Welcome: - { - SendBazaarWelcome(); - break; - } - - case Barter_WelcomeMessageUpdate: - { - BuyerWelcomeMessageUpdate_Struct* bwmu = (BuyerWelcomeMessageUpdate_Struct*)app->pBuffer; - SetBuyerWelcomeMessage(bwmu->WelcomeMessage); - break; - } - - case Barter_BuyerItemInspect: - { - BuyerItemSearchLinkRequest_Struct* bislr = (BuyerItemSearchLinkRequest_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bislr->ItemID); - - if (!item) - Message(13, "Error: This item does not exist!"); - else - { - ItemInst* inst = database.CreateItem(item); - if (inst) - { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - } - break; - } - - case Barter_Unknown23: - { - // Sent by SoD client for no discernible reason. - break; - } - - default: - Message(13, "Unrecognised Barter action."); - _log(TRADING__BARTER, "Unrecognised Barter Action %i", Action); - - } -} - -void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) { - - if(app->size != sizeof(VoiceMacroIn_Struct)) { - - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_VoiceMacroIn expected %i got %i", - sizeof(VoiceMacroIn_Struct), app->size); - - DumpPacket(app); - - return; - } - - if(!RuleB(Chat, EnableVoiceMacros)) return; - - VoiceMacroIn_Struct* vmi = (VoiceMacroIn_Struct*)app->pBuffer; - - VoiceMacroReceived(vmi->Type, vmi->Target, vmi->MacroNumber); - -} - -void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) { - - if(app->size != sizeof(DoGroupLeadershipAbility_Struct)) { - - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DoGroupLeadershipAbility expected %i got %i", - sizeof(DoGroupLeadershipAbility_Struct), app->size); - - DumpPacket(app); - - return; - } - - DoGroupLeadershipAbility_Struct* dglas = (DoGroupLeadershipAbility_Struct*)app->pBuffer; - - switch(dglas->Ability) - { - case GroupLeadershipAbility_MarkNPC: - { - if(GetTarget()) - { - Group* g = GetGroup(); - if(g) - g->MarkNPC(GetTarget(), dglas->Parameter); - } - break; - } - - case groupAAInspectBuffs: - { - Mob *Target = GetTarget(); - - if(!Target || !Target->IsClient()) - return; - - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return; - - Target->CastToClient()->InspectBuffs(this, g->GetLeadershipAA(groupAAInspectBuffs)); - - break; - } - - default: - break; - } -} - -void Client::Handle_OP_ClearNPCMarks(const EQApplicationPacket *app) { - - if(app->size != 0) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearNPCMarks expected 0 got %i", - app->size); - - DumpPacket(app); - - return; - } - - Group *g = GetGroup(); - - if(g) - g->ClearAllNPCMarks(); -} - -void Client::Handle_OP_DelegateAbility(const EQApplicationPacket *app) { - - if(app->size != sizeof(DelegateAbility_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DelegateAbility expected %i got %i", - sizeof(DelegateAbility_Struct), app->size); - - DumpPacket(app); - - return; - } - - DelegateAbility_Struct* das = (DelegateAbility_Struct*)app->pBuffer; - - Group *g = GetGroup(); - - if(!g) return; - - switch(das->DelegateAbility) - { - case 0: - { - g->DelegateMainAssist(das->Name); - break; - } - case 1: - { - g->DelegateMarkNPC(das->Name); - break; - } - case 2: - { - g->DelegateMainTank(das->Name); - break; - } - case 3: - { - g->DelegatePuller(das->Name); - break; - } - default: - break; - } -} - -void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) { - if (app->size != sizeof(ApplyPoison_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ApplyPoison, size=%i, expected %i", app->size, sizeof(ApplyPoison_Struct)); - DumpPacket(app); - return; - } - uint32 ApplyPoisonSuccessResult = 0; - ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer; - const ItemInst* PrimaryWeapon = GetInv().GetItem(MainPrimary); - const ItemInst* SecondaryWeapon = GetInv().GetItem(MainSecondary); - const ItemInst* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot]; - - bool IsPoison = PoisonItemInstance && (PoisonItemInstance->GetItem()->ItemType == ItemTypePoison); - - if(!IsPoison) - { - mlog(SPELLS__CASTING_ERR, "Item used to cast spell effect from a poison item was missing from inventory slot %d " - "after casting, or is not a poison!", ApplyPoisonData->inventorySlot); - - Message(0, "Error: item not found for inventory slot #%i or is not a poison", ApplyPoisonData->inventorySlot); - } - else if(GetClass() == ROGUE) - { - if ((PrimaryWeapon && PrimaryWeapon->GetItem()->ItemType == ItemType1HPiercing) || - (SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == ItemType1HPiercing)) { - float SuccessChance = (GetSkill(SkillApplyPoison) + GetLevel()) / 400.0f; - double ChanceRoll = MakeRandomFloat(0, 1); - - CheckIncreaseSkill(SkillApplyPoison, nullptr, 10); - - if(ChanceRoll < SuccessChance) { - ApplyPoisonSuccessResult = 1; - // NOTE: Someone may want to tweak the chance to proc the poison effect that is added to the weapon here. - // My thinking was that DEX should be apart of the calculation. - AddProcToWeapon(PoisonItemInstance->GetItem()->Proc.Effect, false, (GetDEX()/100) + 103); - } - - DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); - - LogFile->write(EQEMuLog::Debug, "Chance to Apply Poison was %f. Roll was %f. Result is %u.", SuccessChance, ChanceRoll, ApplyPoisonSuccessResult); - } - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApplyPoison, nullptr, sizeof(ApplyPoison_Struct)); - ApplyPoison_Struct* ApplyPoisonResult = (ApplyPoison_Struct*)outapp->pBuffer; - ApplyPoisonResult->success = ApplyPoisonSuccessResult; - ApplyPoisonResult->inventorySlot = ApplyPoisonData->inventorySlot; - - FastQueuePacket(&outapp); -} - - -void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) { - - // This packet is sent by the client when an Augment item information window is opened. - // We respond with an OP_ReadBook containing the type of distiller required to remove the augment. - // The OP_Augment packet includes a window parameter to determine which Item window in the UI the - // text is to be displayed in. out->type = 2 indicates the BookText_Struct contains item information. - // - - if(app->size != sizeof(AugmentInfo_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AugmentInfo expected %i got %i", - sizeof(AugmentInfo_Struct), app->size); - - DumpPacket(app); - - return; - } - AugmentInfo_Struct* AugInfo = (AugmentInfo_Struct*) app->pBuffer; - - char *outstring = nullptr; - - const Item_Struct * item = database.GetItem(AugInfo->itemid); - - if (item) - { - MakeAnyLenString(&outstring, "You must use the solvent %s to remove this augment safely.", item->Name); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, strlen(outstring) + sizeof(BookText_Struct)); - - BookText_Struct *out = (BookText_Struct *) outapp->pBuffer; - - out->window = AugInfo->window; - - out->type = 2; - - out->invslot = 0; - - strcpy(out->booktext, outstring); - - safe_delete_array(outstring); - - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) -{ - // This Opcode is sent by the client when the Leaderboard button on the PVP Stats window is pressed. - // - // It has a single uint32 payload which is the sort method: - // - // PVPSortByKills = 0, PVPSortByPoints = 1, PVPSortByInfamy = 2 - // - if(app->size != sizeof(PVPLeaderBoardRequest_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardRequest expected %i got %i", - sizeof(PVPLeaderBoardRequest_Struct), app->size); - - DumpPacket(app); - - return; - } - /*PVPLeaderBoardRequest_Struct *pvplbrs = (PVPLeaderBoardRequest_Struct *)app->pBuffer;*/ //unused - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardReply, sizeof(PVPLeaderBoard_Struct)); - /*PVPLeaderBoard_Struct *pvplb = (PVPLeaderBoard_Struct *)outapp->pBuffer;*/ //unused - - // TODO: Record and send this data. - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app) -{ - // This opcode is sent by the client when the player right clicks a name on the PVP leaderboard and sends - // further details about the selected player, e.g. Race/Class/AAs/Guild etc. - // - if(app->size != sizeof(PVPLeaderBoardDetailsRequest_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardDetailsRequest expected %i got %i", - sizeof(PVPLeaderBoardDetailsRequest_Struct), app->size); - - DumpPacket(app); - - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardDetailsReply, sizeof(PVPLeaderBoardDetailsReply_Struct)); - PVPLeaderBoardDetailsReply_Struct *pvplbdrs = (PVPLeaderBoardDetailsReply_Struct *)outapp->pBuffer; - - // TODO: Record and send this data. - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Adventure_Sell_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_AdventureMerchantSell: got %u expected %u", - app->size, sizeof(Adventure_Sell_Struct)); - DumpPacket(app); - return; - } - - Adventure_Sell_Struct *ams_in = (Adventure_Sell_Struct*)app->pBuffer; - - Mob* vendor = entity_list.GetMob(ams_in->npcid); - if (vendor == 0 || !vendor->IsNPC() || ((vendor->GetClass() != ADVENTUREMERCHANT) && - (vendor->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (vendor->GetClass() != DARK_REIGN_MERCHANT))) - { - Message(13, "Vendor was not found."); - return; - } - - if(DistNoRoot(*vendor) > USE_NPC_RANGE2) - { - Message(13, "Vendor is out of range."); - return; - } - - uint32 itemid = GetItemIDAt(ams_in->slot); - - if(itemid == 0) - { - Message(13, "Found no item at that slot."); - return; - } - - const Item_Struct* item = database.GetItem(itemid); - ItemInst* inst = GetInv().GetItem(ams_in->slot); - if(!item || !inst){ - Message(13, "You seemed to have misplaced that item..."); - return; - } - - // Note that Lucy has ldonsold values of 4 and 5 for items sold by Norrath's Keepers and Dark Reign, whereas 13th Floor - // has ldonsold = 0 for these items, so some manual editing of the items DB will be required to support sell back of the - // items. - // - // The Merchant seems to have some other way of knowing whether he will accept the item, other than the ldonsold field, - // e.g. if you summon items 76036 and 76053 (good and evil versions of Spell: Ward Of Vengeance), if you are interacting - // with a Norrath's Keeper merchant and click on 76036 in your inventory, he says he will give you radiant crystals for - // it, but he will refuse for item 76053. - // - // Similarly, just giving a cloth cap an ldonsold value of 4 will not make the Merchant buy it. - // - // Note that the the Client will not allow you to sell anything back to a Discord merchant, so there is no need to handle - // that case here. - if(item->LDoNSold == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - if(item->LDoNPrice == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - int32 price = item->LDoNPrice * 70 / 100; - - if(price == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - if (RuleB(EventLog, RecordSellToMerchant)) - LogMerchant(this, vendor, ams_in->charges, price, item, false); - - if(!inst->IsStackable()) - { - DeleteItemInInventory(ams_in->slot, 0, false); - } - else - { - if(inst->GetCharges() < ams_in->charges) - { - ams_in->charges = inst->GetCharges(); - } - - if(ams_in->charges == 0) - { - Message(13, "Charge mismatch error."); - return; - } - - DeleteItemInInventory(ams_in->slot, ams_in->charges, false); - price *= ams_in->charges; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantSell, sizeof(Adventure_Sell_Struct)); - Adventure_Sell_Struct *ams = (Adventure_Sell_Struct*)outapp->pBuffer; - ams->slot = ams_in->slot; - ams->unknown000 = 1; - ams->npcid = ams->npcid; - ams->charges = ams_in->charges; - ams->sell_price = price; - FastQueuePacket(&outapp); - - switch(vendor->GetClass()) - { - case ADVENTUREMERCHANT: - { - UpdateLDoNPoints(price, 6); - break; - } - case NORRATHS_KEEPERS_MERCHANT: - { - SetRadiantCrystals(GetRadiantCrystals() + price); - break; - } - case DARK_REIGN_MERCHANT: - { - SetEbonCrystals(GetEbonCrystals() + price); - break; - } - - default: - break; - } - - Save(1); -} - -void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app) -{ - if(adventure_stats_timer) - { - return; - } - - adventure_stats_timer = new Timer(8000); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureStatsReply, sizeof(AdventureStats_Struct)); - AdventureStats_Struct *as = (AdventureStats_Struct*)outapp->pBuffer; - - if(database.GetAdventureStats(CharacterID(), as->success.guk, as->success.mir, as->success.mmc, as->success.ruj, - as->success.tak, as->failure.guk, as->failure.mir, as->failure.mmc, as->failure.ruj, as->failure.tak)) - { - as->failure.total = as->failure.guk + as->failure.mir + as->failure.mmc + as->failure.ruj + as->failure.tak; - as->success.total = as->success.guk + as->success.mir + as->success.mmc + as->success.ruj + as->success.tak; - m_pp.ldon_wins_guk = as->success.guk; - m_pp.ldon_wins_mir = as->success.mir; - m_pp.ldon_wins_mmc = as->success.mmc; - m_pp.ldon_wins_ruj = as->success.ruj; - m_pp.ldon_wins_tak = as->success.tak; - m_pp.ldon_losses_guk = as->failure.guk; - m_pp.ldon_losses_mir = as->failure.mir; - m_pp.ldon_losses_mmc = as->failure.mmc; - m_pp.ldon_losses_ruj = as->failure.ruj; - m_pp.ldon_losses_tak = as->failure.tak; - } - - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(AdventureLeaderboardRequest_Struct)) - { - return; - } - - if(adventure_leaderboard_timer) - { - return; - } - - adventure_leaderboard_timer = new Timer(4000); - ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, sizeof(ServerLeaderboardRequest_Struct)); - ServerLeaderboardRequest_Struct *lr = (ServerLeaderboardRequest_Struct*)pack->pBuffer; - strcpy(lr->player, GetName()); - - AdventureLeaderboardRequest_Struct *lrs = (AdventureLeaderboardRequest_Struct*)app->pBuffer; - lr->type = 1 + (lrs->theme * 2) + lrs->type; - worldserver.SendPacket(pack); - delete pack; -} - -void Client::Handle_OP_RespawnWindow(const EQApplicationPacket *app) -{ -// This opcode is sent by the client when the player choses which bind to return to. -// The client sends just a 4 byte packet with the selection number in it -// - if(app->size != 4) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RespawnWindow expected %i got %i", - 4, app->size); - DumpPacket(app); - return; - } - char *Buffer = (char *)app->pBuffer; - - uint32 Option = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - HandleRespawnFromHover(Option); -} - void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app) { - if(app->size != sizeof(GroupUpdate_Struct)) + if (app->size != sizeof(GroupUpdate_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_GroupUpdate: got %u expected %u", app->size, sizeof(GroupUpdate_Struct)); @@ -11496,285 +6873,39 @@ void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app) GroupUpdate_Struct* gu = (GroupUpdate_Struct*)app->pBuffer; - switch(gu->action) { - case groupActMakeLeader: - { - Mob* newleader = entity_list.GetClientByName(gu->membername[0]); - Group* group = this->GetGroup(); + switch (gu->action) { + case groupActMakeLeader: + { + Mob* newleader = entity_list.GetClientByName(gu->membername[0]); + Group* group = this->GetGroup(); - if (newleader && group) { - // the client only sends this if it's the group leader, but check anyway - if(group->IsLeader(this)) - group->ChangeLeader(newleader); - else { - LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s",GetName()); - DumpPacket(app); - } + if (newleader && group) { + // the client only sends this if it's the group leader, but check anyway + if (group->IsLeader(this)) + group->ChangeLeader(newleader); + else { + LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); + DumpPacket(app); } - break; } - - default: - { - LogFile->write(EQEMuLog::Debug, "Received unhandled OP_GroupUpdate requesting action %u", gu->action); - DumpPacket(app); - return; - } - } -} - -void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) -{ - // if the character has a start city, don't let them use the command - if(m_pp.binds[4].zoneId != 0) { - Message(15,"Your home city has already been set.", m_pp.binds[4].zoneId, database.GetZoneName(m_pp.binds[4].zoneId)); - return; + break; } - if (app->size < 1) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_SetStartCity, size=%i, expected %i", app->size, 1); + default: + { + LogFile->write(EQEMuLog::Debug, "Received unhandled OP_GroupUpdate requesting action %u", gu->action); DumpPacket(app); return; } - - float x(0),y(0),z(0); - uint32 zoneid = 0; - uint32 startCity = (uint32)strtol((const char*)app->pBuffer, nullptr, 10); - - std::string query = StringFormat("SELECT zone_id, bind_id, x, y, z FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - m_pp.class_, m_pp.deity, m_pp.race); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - LogFile->write(EQEMuLog::Error, "No valid start zones found for /setstartcity"); - return; } - - bool validCity = false; - for (auto row = results.begin(); row != results.end(); ++row) { - if(atoi(row[1]) != 0) - zoneid = atoi(row[1]); - else - zoneid = atoi(row[0]); - - if(zoneid != startCity) - continue; - - validCity = true; - x = atof(row[2]); - y = atof(row[3]); - z = atof(row[4]); - } - - if(validCity) { - Message(15,"Your home city has been set"); - SetStartZone(startCity, x, y, z); - return; - } - - query = StringFormat("SELECT zone_id, bind_id FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - m_pp.class_, m_pp.deity, m_pp.race); - results = database.QueryDatabase(query); - if (!results.Success()) - return; - - Message(15,"Use \"/startcity #\" to choose a home city from the following list:"); - - for (auto row = results.begin(); row != results.end(); ++row) { - if(atoi(row[1]) != 0) - zoneid = atoi(row[1]); - else - zoneid = atoi(row[0]); - - char* name; - database.GetZoneLongName(database.GetZoneName(zoneid), &name); - Message(15,"%d - %s", zoneid, name); - } - -} - -void Client::Handle_OP_Report(const EQApplicationPacket *app) -{ - if(!CanUseReport) - { - Message_StringID(MT_System, REPORT_ONCE); - return; - } - - uint32 size = app->size; - uint32 current_point = 0; - std::string reported, reporter; - std::string current_string; - int mode = 0; - - while(current_point < size) - { - if(mode < 2) - { - if(app->pBuffer[current_point] == '|') - { - mode++; - } - else - { - if(mode == 0) - { - reported += app->pBuffer[current_point]; - } - else - { - reporter += app->pBuffer[current_point]; - } - } - current_point++; - } - else - { - if(app->pBuffer[current_point] == 0x0a) - { - current_string += '\n'; - } - else if(app->pBuffer[current_point] == 0x00) - { - CanUseReport = false; - database.AddReport(reporter, reported, current_string); - return; - } - else - { - current_string += app->pBuffer[current_point]; - } - current_point++; - } - } - - CanUseReport = false; - database.AddReport(reporter, reported, current_string); -} - -void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(VeteranClaimRequest)) - { - LogFile->write(EQEMuLog::Debug, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", - app->size, sizeof(VeteranClaimRequest)); - DumpPacket(app); - return; - } - - VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer; - - if(vcr->claim_id == 0xFFFFFFFF) //request update packet - { - SendRewards(); - } - else //try to claim something! - { - if(!TryReward(vcr->claim_id)) - { - Message(13, "Your claim has been rejected."); - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = -1; - FastQueuePacket(&vetapp); - } - else - { - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = 0; - FastQueuePacket(&vetapp); - } - } -} - -void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app) -{ - // Could make this into a rule, although there is a hard limit since we are using a popup, of 4096 bytes that can - // be displayed in the window, including all the HTML formatting tags. - // - const int maxResults = 10; - - if(app->size < sizeof(GMSearchCorpse_Struct)) - { - LogFile->write(EQEMuLog::Debug, "OP_GMSearchCorpse size lower than expected: got %u expected at least %u", - app->size, sizeof(GMSearchCorpse_Struct)); - DumpPacket(app); - return; - } - - GMSearchCorpse_Struct *gmscs = (GMSearchCorpse_Struct *)app->pBuffer; - gmscs->Name[63] = '\0'; - - char *escSearchString = new char[129]; - database.DoEscapeString(escSearchString, gmscs->Name, strlen(gmscs->Name)); - - std::string query = StringFormat("SELECT charname, zoneid, x, y, z, timeofdeath, rezzed, IsBurried " - "FROM player_corpses WheRE charname LIKE '%%%s%%' ORDER BY charname LIMIT %i", - escSearchString, maxResults); - safe_delete_array(escSearchString); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Message(0, "Query failed: %s.", results.ErrorMessage().c_str()); - return; - } - - if (results.RowCount() == 0) - return; - - if(results.RowCount() == maxResults) - Message(clientMessageError, "Your search found too many results; some are not displayed."); - else - Message(clientMessageYellow, "There are %i corpse(s) that match the search string '%s'.", results.RowCount(), gmscs->Name); - - char charName[64], timeOfDeath[20]; - - std::string popupText = ""; - - for (auto row = results.begin(); row != results.end(); ++row) { - - strn0cpy(charName, row[0], sizeof(charName)); - - uint32 ZoneID = atoi(row[1]); - float CorpseX = atof(row[2]); - float CorpseY = atof(row[3]); - float CorpseZ = atof(row[4]); - - strn0cpy(timeOfDeath, row[5], sizeof(timeOfDeath)); - - bool corpseRezzed = atoi(row[6]); - bool corpseBuried = atoi(row[7]); - - popupText += StringFormat("", - charName, StaticGetZoneName(ZoneID), CorpseX, CorpseY, CorpseZ, timeOfDeath, - corpseRezzed ? "Yes" : "No", corpseBuried ? "Yes" : "No"); - - if(popupText.size() > 4000) { - Message(clientMessageError, "Unable to display all the results."); - break; - } - - } - - popupText += "
NameZoneXYZDate" - "RezzedBuried
 " - "
%s%s%8.0f%8.0f%8.0f%s%s%s
"; - - SendPopupToClient("Corpses", popupText.c_str()); - } void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) { - if(!GuildBanks) + if (!GuildBanks) return; - if((int)zone->GetZoneID() != RuleI(World, GuildBankZoneID)) + if ((int)zone->GetZoneID() != RuleI(World, GuildBankZoneID)) { Message(13, "The Guild Bank is not available in this zone."); @@ -11791,11 +6922,11 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - if(!IsInAGuild()) + if (!IsInAGuild()) { Message(13, "You must be in a Guild to use the Guild Bank."); - if(Action == GuildBankDeposit) + if (Action == GuildBankDeposit) GuildBankDepositAck(true); else GuildBankAck(); @@ -11803,9 +6934,9 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) return; } - if(!IsGuildBanker()) + if (!IsGuildBanker()) { - if((Action != GuildBankDeposit) && (Action != GuildBankViewItem) && (Action != GuildBankWithdraw)) + if ((Action != GuildBankDeposit) && (Action != GuildBankViewItem) && (Action != GuildBankWithdraw)) { _log(GUILDS__BANK_ERROR, "Suspected hacking attempt on guild bank from %s", GetName()); @@ -11815,700 +6946,253 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) } } - switch(Action) + switch (Action) { - case GuildBankPromote: + case GuildBankPromote: + { + if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) { - if(GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) - { - Message_StringID(13, GUILD_BANK_FULL); + Message_StringID(13, GUILD_BANK_FULL); - GuildBankDepositAck(true); + GuildBankDepositAck(true); - return; - } - - GuildBankPromote_Struct *gbps = (GuildBankPromote_Struct*)app->pBuffer; - - int Slot = GuildBanks->Promote(GuildID(), gbps->Slot); - - if(Slot >= 0) - { - ItemInst* inst = GuildBanks->GetItem(GuildID(), GuildBankMainArea, Slot, 1); - - if(inst) - { - Message_StringID(clientMessageWhite, GUILD_BANK_TRANSFERRED, inst->GetItem()->Name); - safe_delete(inst); - } - } - else - Message(13, "Unexpected error while moving item into Guild Bank."); - - GuildBankAck(); - - break; - } - - case GuildBankViewItem: - { - GuildBankViewItem_Struct *gbvis = (GuildBankViewItem_Struct*)app->pBuffer; - - ItemInst* inst = GuildBanks->GetItem(GuildID(), gbvis->Area, gbvis->SlotID, 1); - - if(!inst) - break; - - SendItemPacket(0, inst, ItemPacketViewLink); - - safe_delete(inst); - - break; - } - - case GuildBankDeposit: // Deposit Item - { - if(GuildBanks->IsAreaFull(GuildID(), GuildBankDepositArea)) - { - Message_StringID(13, GUILD_BANK_FULL); - - GuildBankDepositAck(true); - - return; - } - - ItemInst *CursorItemInst = GetInv().GetItem(MainCursor); - - bool Allowed = true; - - if(!CursorItemInst) - { - Message(13, "No Item on the cursor."); - - GuildBankDepositAck(true); - - return; - } - - const Item_Struct* CursorItem = CursorItemInst->GetItem(); - - if(!CursorItem->NoDrop || CursorItemInst->IsInstNoDrop()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItemInst->IsNoneEmptyContainer()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItemInst->IsAugmented()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItem->NoRent == 0) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItem->LoreFlag && GuildBanks->HasItem(GuildID(), CursorItem->ID)) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - - if(!Allowed) - { - GuildBankDepositAck(true); - - return; - } - - if(GuildBanks->AddItem(GuildID(), GuildBankDepositArea, CursorItem->ID, CursorItemInst->GetCharges(), GetName(), GuildBankBankerOnly, "")) - { - GuildBankDepositAck(false); - - DeleteItemInInventory(MainCursor, 0, false); - } - - break; - } - - case GuildBankPermissions: - { - GuildBankPermissions_Struct *gbps = (GuildBankPermissions_Struct*)app->pBuffer; - - if(gbps->Permissions == 1) - GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, gbps->MemberName); - else - GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, ""); - - GuildBankAck(); - break; - } - - case GuildBankWithdraw: - { - if (GetInv()[MainCursor]) - { - Message_StringID(13, GUILD_BANK_EMPTY_HANDS); - - GuildBankAck(); - - break; - } - - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - ItemInst* inst = GuildBanks->GetItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); - - if(!inst) - { - GuildBankAck(); - - break; - } - - if(!IsGuildBanker() && !GuildBanks->AllowedToWithdraw(GuildID(), gbwis->Area, gbwis->SlotID, GetName())) - { - _log(GUILDS__BANK_ERROR, "Suspected attempted hack on the guild bank from %s", GetName()); - - GuildBankAck(); - - safe_delete(inst); - - break; - } - - if(CheckLoreConflict(inst->GetItem())) - { - Message_StringID(13, DUP_LORE); - - GuildBankAck(); - - safe_delete(inst); - - break; - } - - if (gbwis->Quantity > 0) - { - PushItemOnCursor(*inst); - - SendItemPacket(MainCursor, inst, ItemPacketSummonItem); - - GuildBanks->DeleteItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); - } - else - { - Message(0, "Unable to withdraw 0 quantity of %s", inst->GetItem()->Name); - } - - safe_delete(inst); - - GuildBankAck(); - - break; - } - - case GuildBankSplitStacks: - { - if(GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) - Message_StringID(13, GUILD_BANK_FULL); - else - { - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - GuildBanks->SplitStack(GuildID(), gbwis->SlotID, gbwis->Quantity); - } - - GuildBankAck(); - - break; - } - - case GuildBankMergeStacks: - { - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - GuildBanks->MergeStacks(GuildID(), gbwis->SlotID); - - GuildBankAck(); - - break; - } - - default: - { - Message(13, "Unexpected GuildBank action."); - - _log(GUILDS__BANK_ERROR, "Received unexpected guild bank action code %i from %s", Action, GetName()); - } - } -} - -void Client::Handle_OP_GroupRoles(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupRole_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupRoles, size=%i, expected %i", app->size, sizeof(GroupRole_Struct)); - DumpPacket(app); - return; - } - GroupRole_Struct *grs = (GroupRole_Struct*)app->pBuffer; - - Group *g = GetGroup(); - - if(!g) - return; - - switch(grs->RoleNumber) - { - case 1: //Main Tank - { - if(grs->Toggle) - g->DelegateMainTank(grs->Name1, grs->Toggle); - else - g->UnDelegateMainTank(grs->Name1, grs->Toggle); - break; - } - case 2: //Main Assist - { - if(grs->Toggle) - g->DelegateMainAssist(grs->Name1, grs->Toggle); - else - g->UnDelegateMainAssist(grs->Name1, grs->Toggle); - break; - } - case 3: //Puller - { - if(grs->Toggle) - g->DelegatePuller(grs->Name1, grs->Toggle); - else - g->UnDelegatePuller(grs->Name1, grs->Toggle); - break; - } - default: - break; - } -} - -void Client::Handle_OP_HideCorpse(const EQApplicationPacket *app) -{ - // New OPCode for SOD+ as /hidecorpse is handled serverside now. - // - if(app->size != sizeof(HideCorpse_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_HideCorpse expected %i got %i", - sizeof(HideCorpse_Struct), app->size); - - DumpPacket(app); - - return; - } - - HideCorpse_Struct *hcs = (HideCorpse_Struct*)app->pBuffer; - - if(hcs->Action == HideCorpseLooted) - return; - - if((HideCorpseMode == HideCorpseNone) && (hcs->Action == HideCorpseNone)) - return; - - entity_list.HideCorpses(this, HideCorpseMode, hcs->Action); - - HideCorpseMode = hcs->Action; -} - -void Client::Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GuildUpdateURLAndChannel_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildUpdateURLAndChannel expected %i got %i", - sizeof(GuildUpdateURLAndChannel_Struct), app->size); - - DumpPacket(app); - - return; - } - - GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*)app->pBuffer; - - if(!IsInAGuild()) - return; - - if(!guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - { - Message(13, "Only the guild leader can change the Channel or URL.!"); - return; - } - - if(guuacs->Action == 0) - guild_mgr.SetGuildURL(GuildID(), guuacs->Text); - else - guild_mgr.SetGuildChannel(GuildID(), guuacs->Text); - -} - -void Client::Handle_OP_GuildStatus(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GuildStatus_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildStatus expected %i got %i", - sizeof(GuildStatus_Struct), app->size); - - DumpPacket(app); - - return; - } - GuildStatus_Struct *gss = (GuildStatus_Struct*)app->pBuffer; - - Client *c = entity_list.GetClientByName(gss->Name); - - if(!c) - { - Message_StringID(clientMessageWhite, TARGET_PLAYER_FOR_GUILD_STATUS); - return; - } - - uint32 TargetGuildID = c->GuildID(); - - if(TargetGuildID == GUILD_NONE) - { - Message_StringID(clientMessageWhite, NOT_IN_A_GUILD, c->GetName()); - return; - } - - const char *GuildName = guild_mgr.GetGuildName(TargetGuildID); - - if(!GuildName) - return; - - bool IsLeader = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_PROMOTE); - bool IsOfficer = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_INVITE); - - if((TargetGuildID == GuildID()) && (c != this)) - { - if(IsLeader) - Message_StringID(clientMessageWhite, LEADER_OF_YOUR_GUILD, c->GetName()); - else if(IsOfficer) - Message_StringID(clientMessageWhite, OFFICER_OF_YOUR_GUILD, c->GetName()); - else - Message_StringID(clientMessageWhite, MEMBER_OF_YOUR_GUILD, c->GetName()); - - return; - } - - if(IsLeader) - Message_StringID(clientMessageWhite, LEADER_OF_X_GUILD, c->GetName(), GuildName); - else if(IsOfficer) - Message_StringID(clientMessageWhite, OFFICER_OF_X_GUILD, c->GetName(), GuildName); - else - Message_StringID(clientMessageWhite, MEMBER_OF_X_GUILD, c->GetName(), GuildName); -} - -void Client::Handle_OP_BlockedBuffs(const EQApplicationPacket *app) -{ - if(!RuleB(Spells, EnableBlockedBuffs)) - return; - - if(app->size != sizeof(BlockedBuffs_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BlockedBuffs expected %i got %i", - sizeof(BlockedBuffs_Struct), app->size); - - DumpPacket(app); - - return; - } - - std::set::iterator Iterator; - - BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; - - std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; - - if(bbs->Initialise == 1) - { - BlockedBuffs->clear(); - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - { - if((bbs->SpellID[i] > 0) && IsBeneficialSpell(bbs->SpellID[i])) - { - if(BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end()) - BlockedBuffs->insert(bbs->SpellID[i]); - } - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = -1; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 1; - obbs->Flags = 0x54; - obbs->Count = BlockedBuffs->size(); - - unsigned int Element = 0; - - Iterator = BlockedBuffs->begin(); - - while(Iterator != BlockedBuffs->end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - return; - } - - if((bbs->Initialise == 0) && (bbs->Count > 0)) - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = -1; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 0; - obbs->Flags = 0x54; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - { - if(!IsBeneficialSpell(bbs->SpellID[i])) - continue; - - if((BlockedBuffs->size() < BLOCKED_BUFF_COUNT) && (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end())) - BlockedBuffs->insert(bbs->SpellID[i]); - } - obbs->Count = BlockedBuffs->size(); - - Iterator = BlockedBuffs->begin(); - - unsigned int Element = 0; - - while(Iterator != BlockedBuffs->end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app) -{ - if(!RuleB(Spells, EnableBlockedBuffs)) - return; - - if(app->size != sizeof(BlockedBuffs_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RemoveBlockedBuffs expected %i got %i", - sizeof(BlockedBuffs_Struct), app->size); - - DumpPacket(app); - - return; - } - BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; - - std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; - - std::set RemovedBuffs; - - if(bbs->Count > 0) - { - std::set::iterator Iterator; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RemoveBlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = 0; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 0; - obbs->Flags = 0x5a; - - for(unsigned int i = 0; i < bbs->Count; ++i) - { - Iterator = BlockedBuffs->find(bbs->SpellID[i]); - - if(Iterator != BlockedBuffs->end()) - { - RemovedBuffs.insert(bbs->SpellID[i]); - - BlockedBuffs->erase(Iterator); - } - } - obbs->Count = RemovedBuffs.size(); - - Iterator = RemovedBuffs.begin(); - - unsigned int Element = 0; - - while(Iterator != RemovedBuffs.end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - } -} -void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app) -{ - if(!RuleB(Spells, EnableBlockedBuffs)) - return; - - if(app->size != 1) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearBlockedBuffs expected 1 got %i", app->size); - - DumpPacket(app); - - return; - } - - bool Pet = app->pBuffer[0]; - - if(Pet) - PetBlockedBuffs.clear(); - else - PlayerBlockedBuffs.clear(); - - QueuePacket(app); -} - -void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) -{ - // In SoD, this is used for clicking off Pet Buffs only. In Underfoot, it is used both for Client and Pets - // The payload contains buffslot and EntityID only, so we must check if the EntityID is ours or our pets. - // - VERIFY_PACKET_LENGTH(OP_BuffRemoveRequest, app, BuffRemoveRequest_Struct); - - BuffRemoveRequest_Struct *brrs = (BuffRemoveRequest_Struct*)app->pBuffer; - - Mob *m = nullptr; - - if(brrs->EntityID == GetID()) - m = this; - else if(brrs->EntityID == GetPetID()) - m = GetPet(); - - if(!m) - return; - - if(brrs->SlotID > (uint32)m->GetMaxTotalSlots()) - return; - - uint16 SpellID = m->GetSpellIDFromSlot(brrs->SlotID); - - if(SpellID && IsBeneficialSpell(SpellID)) - m->BuffFadeBySlot(brrs->SlotID, true); -} - -void Client::Handle_OP_CorpseDrag(const EQApplicationPacket *app) -{ - if(DraggedCorpses.size() >= (unsigned int)RuleI(Character, MaxDraggedCorpses)) - { - Message_StringID(13, CORPSEDRAG_LIMIT); - return; - } - - VERIFY_PACKET_LENGTH(OP_CorpseDrag, app, CorpseDrag_Struct); - - CorpseDrag_Struct *cds = (CorpseDrag_Struct*)app->pBuffer; - - Mob* corpse = entity_list.GetMob(cds->CorpseName); - - if(!corpse || !corpse->IsPlayerCorpse() || corpse->CastToCorpse()->IsBeingLooted()) - return; - - Client *c = entity_list.FindCorpseDragger(corpse->GetID()); - - if(c) - { - if(c == this) - Message_StringID(MT_DefaultText, CORPSEDRAG_ALREADY, corpse->GetCleanName()); - else - Message_StringID(MT_DefaultText, CORPSEDRAG_SOMEONE_ELSE, corpse->GetCleanName()); - - return; - } - - if(!corpse->CastToCorpse()->Summon(this, false, true)) - return; - - DraggedCorpses.push_back(std::pair(cds->CorpseName, corpse->GetID())); - - Message_StringID(MT_DefaultText, CORPSEDRAG_BEGIN, cds->CorpseName); -} - -void Client::Handle_OP_CorpseDrop(const EQApplicationPacket *app) -{ - if(app->size == 1) - { - Message_StringID(MT_DefaultText, CORPSEDRAG_STOPALL); - ClearDraggedCorpses(); - return; - } - - for (auto Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator) - { - if(!strcasecmp(Iterator->first.c_str(), (const char *)app->pBuffer)) - { - Message_StringID(MT_DefaultText, CORPSEDRAG_STOP); - Iterator = DraggedCorpses.erase(Iterator); return; } - } -} -void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_GroupMakeLeader, app, GroupMakeLeader_Struct); + GuildBankPromote_Struct *gbps = (GuildBankPromote_Struct*)app->pBuffer; - GroupMakeLeader_Struct *gmls = (GroupMakeLeader_Struct *)app->pBuffer; + int Slot = GuildBanks->Promote(GuildID(), gbps->Slot); - Mob* NewLeader = entity_list.GetClientByName(gmls->NewLeader); + if (Slot >= 0) + { + ItemInst* inst = GuildBanks->GetItem(GuildID(), GuildBankMainArea, Slot, 1); - Group* g = GetGroup(); - - if (NewLeader && g) - { - if(g->IsLeader(this)) - g->ChangeLeader(NewLeader); - else { - LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); - DumpPacket(app); + if (inst) + { + Message_StringID(clientMessageWhite, GUILD_BANK_TRANSFERRED, inst->GetItem()->Name); + safe_delete(inst); + } } + else + Message(13, "Unexpected error while moving item into Guild Bank."); + + GuildBankAck(); + + break; + } + + case GuildBankViewItem: + { + GuildBankViewItem_Struct *gbvis = (GuildBankViewItem_Struct*)app->pBuffer; + + ItemInst* inst = GuildBanks->GetItem(GuildID(), gbvis->Area, gbvis->SlotID, 1); + + if (!inst) + break; + + SendItemPacket(0, inst, ItemPacketViewLink); + + safe_delete(inst); + + break; + } + + case GuildBankDeposit: // Deposit Item + { + if (GuildBanks->IsAreaFull(GuildID(), GuildBankDepositArea)) + { + Message_StringID(13, GUILD_BANK_FULL); + + GuildBankDepositAck(true); + + return; + } + + ItemInst *CursorItemInst = GetInv().GetItem(MainCursor); + + bool Allowed = true; + + if (!CursorItemInst) + { + Message(13, "No Item on the cursor."); + + GuildBankDepositAck(true); + + return; + } + + const Item_Struct* CursorItem = CursorItemInst->GetItem(); + + if (!CursorItem->NoDrop || CursorItemInst->IsInstNoDrop()) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItemInst->IsNoneEmptyContainer()) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItemInst->IsAugmented()) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItem->NoRent == 0) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItem->LoreFlag && GuildBanks->HasItem(GuildID(), CursorItem->ID)) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + + if (!Allowed) + { + GuildBankDepositAck(true); + + return; + } + + if (GuildBanks->AddItem(GuildID(), GuildBankDepositArea, CursorItem->ID, CursorItemInst->GetCharges(), GetName(), GuildBankBankerOnly, "")) + { + GuildBankDepositAck(false); + + DeleteItemInInventory(MainCursor, 0, false); + } + + break; + } + + case GuildBankPermissions: + { + GuildBankPermissions_Struct *gbps = (GuildBankPermissions_Struct*)app->pBuffer; + + if (gbps->Permissions == 1) + GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, gbps->MemberName); + else + GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, ""); + + GuildBankAck(); + break; + } + + case GuildBankWithdraw: + { + if (GetInv()[MainCursor]) + { + Message_StringID(13, GUILD_BANK_EMPTY_HANDS); + + GuildBankAck(); + + break; + } + + GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; + + ItemInst* inst = GuildBanks->GetItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); + + if (!inst) + { + GuildBankAck(); + + break; + } + + if (!IsGuildBanker() && !GuildBanks->AllowedToWithdraw(GuildID(), gbwis->Area, gbwis->SlotID, GetName())) + { + _log(GUILDS__BANK_ERROR, "Suspected attempted hack on the guild bank from %s", GetName()); + + GuildBankAck(); + + safe_delete(inst); + + break; + } + + if (CheckLoreConflict(inst->GetItem())) + { + Message_StringID(13, DUP_LORE); + + GuildBankAck(); + + safe_delete(inst); + + break; + } + + if (gbwis->Quantity > 0) + { + PushItemOnCursor(*inst); + + SendItemPacket(MainCursor, inst, ItemPacketSummonItem); + + GuildBanks->DeleteItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); + } + else + { + Message(0, "Unable to withdraw 0 quantity of %s", inst->GetItem()->Name); + } + + safe_delete(inst); + + GuildBankAck(); + + break; + } + + case GuildBankSplitStacks: + { + if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) + Message_StringID(13, GUILD_BANK_FULL); + else + { + GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; + + GuildBanks->SplitStack(GuildID(), gbwis->SlotID, gbwis->Quantity); + } + + GuildBankAck(); + + break; + } + + case GuildBankMergeStacks: + { + GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; + + GuildBanks->MergeStacks(GuildID(), gbwis->SlotID); + + GuildBankAck(); + + break; + } + + default: + { + Message(13, "Unexpected GuildBank action."); + + _log(GUILDS__BANK_ERROR, "Received unexpected guild bank action code %i from %s", Action, GetName()); + } } } void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) { - if(IsInAGuild()) + if (IsInAGuild()) { Message(clientMessageError, "You are already in a guild!"); return; } - if(!RuleB(Guild, PlayerCreationAllowed)) + if (!RuleB(Guild, PlayerCreationAllowed)) { Message(clientMessageError, "This feature is disabled on this server. Contact a GM or post on your server message boards to create a guild."); return; @@ -12531,19 +7215,19 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) #if __DARWIN_C_LEVEL < 200809L if (strlen(GuildName) > 60) #else - if(strnlen(GuildName, 64) > 60) + if (strnlen(GuildName, 64) > 60) #endif // __DARWIN_C_LEVEL #else - if(strnlen(GuildName, 64) > 60) + if (strnlen(GuildName, 64) > 60) #endif // DARWIN { Message(clientMessageError, "Guild name too long."); return; } - for(unsigned int i = 0; i < strlen(GuildName); ++i) + for (unsigned int i = 0; i < strlen(GuildName); ++i) { - if(!isalpha(GuildName[i]) && (GuildName[i] != ' ')) + if (!isalpha(GuildName[i]) && (GuildName[i] != ' ')) { Message(clientMessageError, "Invalid character in Guild name."); return; @@ -12552,13 +7236,13 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) int32 GuildCount = guild_mgr.DoesAccountContainAGuildLeader(AccountID()); - if(GuildCount >= RuleI(Guild, PlayerCreationLimit)) + if (GuildCount >= RuleI(Guild, PlayerCreationLimit)) { Message(clientMessageError, "You cannot create this guild because this account may only be leader of %i guilds.", RuleI(Guild, PlayerCreationLimit)); return; } - if(guild_mgr.GetGuildIDByName(GuildName) != GUILD_NONE) + if (guild_mgr.GetGuildIDByName(GuildName) != GUILD_NONE) { Message_StringID(clientMessageError, GUILD_NAME_IN_USE); return; @@ -12573,820 +7257,1047 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) Message(clientMessageError, "Guild creation failed."); else { - if(!guild_mgr.SetGuild(CharacterID(), NewGuildID, GUILD_LEADER)) + if (!guild_mgr.SetGuild(CharacterID(), NewGuildID, GUILD_LEADER)) Message(clientMessageError, "Unable to set guild leader's guild in the database. Contact a GM."); else { Message(clientMessageYellow, "You are now the leader of %s", GuildName); - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) GuildBanks->SendGuildBank(this); SendGuildRanks(); } } } -void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32); +void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildDelete"); + mpkt(GUILDS__IN_PACKET_TRACE, app); - NPC* tar = entity_list.GetNPCByID(*((uint32*)app->pBuffer)); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } - - std::list::iterator altc_iter = zone->AlternateCurrencies.begin(); - bool found = false; - while(altc_iter != zone->AlternateCurrencies.end()) { - if((*altc_iter).id == alt_cur_id) { - found = true; - break; - } - ++altc_iter; - } - - if(!found) { - return; - } - - std::stringstream ss(std::stringstream::in | std::stringstream::out); - std::stringstream item_ss(std::stringstream::in | std::stringstream::out); - ss << alt_cur_id << "|1|" << alt_cur_id; - uint32 count = 0; - uint32 merchant_id = tar->MerchantType; - const Item_Struct *item = nullptr; - - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for(itr = merlist.begin(); itr != merlist.end() && count < 255; ++itr){ - const MerchantList &ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(item) - { - item_ss << "^" << item->Name << "|"; - item_ss << item->ID << "|"; - item_ss << ml.alt_currency_cost << "|"; - item_ss << "0|"; - item_ss << "1|"; - item_ss << item->Races << "|"; - item_ss << item->Classes; - count++; - } - } - - if(count > 0) { - ss << "|" << count << item_ss.str(); - } else { - ss << "|0"; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencyMerchantReply, ss.str().length() + 1); - memcpy(outapp->pBuffer, ss.str().c_str(), ss.str().length()); - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencySellSelection, app, AltCurrencySelectItem_Struct); - - AltCurrencySelectItem_Struct *select = (AltCurrencySelectItem_Struct*)app->pBuffer; - NPC* tar = entity_list.GetNPCByID(select->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } - - ItemInst *inst = m_inv.GetItem(select->slot_id); - if(!inst) { - return; - } - - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - - if (RuleB(Merchant, EnableAltCurrencySell)) { - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for (itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if (GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if (!item) - continue; - - if (item->ID == inst->GetItem()->ID) { - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if (!found) { - cost = 0; - } - } + if (!IsInAGuild() || !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) + Message(0, "You are not a guild leader or not in a guild."); + else { + mlog(GUILDS__ACTIONS, "Deleting guild %s (%d)", guild_mgr.GetGuildName(GuildID()), GuildID()); + if (!guild_mgr.DeleteGuild(GuildID())) + Message(0, "Guild delete failed."); else { - cost = 0; + Message(0, "Guild successfully deleted."); } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencySellSelection, sizeof(AltCurrencySelectItemReply_Struct)); - AltCurrencySelectItemReply_Struct *reply = (AltCurrencySelectItemReply_Struct*)outapp->pBuffer; - reply->unknown004 = 0xFF; - reply->unknown005 = 0xFF; - reply->unknown006 = 0xFF; - reply->unknown007 = 0xFF; - strcpy(reply->item_name, inst->GetItem()->Name); - reply->cost = cost; - FastQueuePacket(&outapp); } } -void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencyPurchase, app, AltCurrencyPurchaseItem_Struct); - AltCurrencyPurchaseItem_Struct *purchase = (AltCurrencyPurchaseItem_Struct*)app->pBuffer; - NPC* tar = entity_list.GetNPCByID(purchase->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; +void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildDemote"); + mpkt(GUILDS__IN_PACKET_TRACE, app); - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } - - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for(itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(!item) - continue; - - if(item->ID == purchase->item_id) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if (!item || !found) { - Message(13, "Error: The item you purchased does not exist!"); - return; - } - - if(cost > current_currency) { - Message(13, "You cannot afford that item right now."); - return; - } - - if(CheckLoreConflict(item)) - { - Message(15,"You can only have one of a lore item."); - return; - } - - /* QS: PlayerLogAlternateCurrencyTransactions :: Merchant Purchase */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Merchant Purchase :: Spent alt_currency_id:%i cost:%i for itemid:%i in zoneid:%i instid:%i", alt_cur_id, cost, item->ID, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); - } - - AddAlternateCurrencyValue(alt_cur_id, -((int32)cost)); - int16 charges = 1; - if(item->MaxCharges != 0) - charges = item->MaxCharges; - - ItemInst *inst = database.CreateItem(item, charges); - if(!AutoPutLootInInventory(*inst, true, true)) - { - PutLootInInventory(MainCursor, *inst); - } - - Save(1); - } -} - -void Client::Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencyReclaim, app, AltCurrencyReclaim_Struct); - AltCurrencyReclaim_Struct *reclaim = (AltCurrencyReclaim_Struct*)app->pBuffer; - uint32 item_id = 0; - std::list::iterator iter = zone->AlternateCurrencies.begin(); - while(iter != zone->AlternateCurrencies.end()) { - if((*iter).id == reclaim->currency_id) { - item_id = (*iter).item_id; - } - ++iter; - } - - if(item_id == 0) { + if (app->size != sizeof(GuildDemoteStruct)) { + mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n", app->size, sizeof(GuildDemoteStruct)); return; } - /* Item to Currency Storage */ - if(reclaim->reclaim_flag == 1) { - uint32 removed = NukeItem(item_id, invWhereWorn | invWherePersonal | invWhereCursor); - if(removed > 0) { - AddAlternateCurrencyValue(reclaim->currency_id, removed); + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) + Message(0, "You dont have permission to invite."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + GuildDemoteStruct* demote = (GuildDemoteStruct*)app->pBuffer; - /* QS: PlayerLogAlternateCurrencyTransactions :: Item to Currency */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Reclaim :: Item to Currency :: alt_currency_id:%i amount:%i to currency tab in zoneid:%i instid:%i", reclaim->currency_id, removed, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(demote->target, gci)) { + Message(0, "Unable to find '%s'", demote->target); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + if (gci.rank < 1) { + Message(0, "%s cannot be demoted any further!", demote->target); + return; + } + uint8 rank = gci.rank - 1; + + + mlog(GUILDS__ACTIONS, "Demoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", + demote->target, gci.char_id, + guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, + guild_mgr.GetRankName(GuildID(), rank), rank, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(gci.char_id, rank)) { + Message(13, "Error while setting rank %d on '%s'.", rank, demote->target); + return; + } + Message(0, "Successfully demoted %s to rank %d", demote->target, rank); + } + // SendGuildMembers(GuildID(), true); + return; +} + +void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildInvite"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildCommand_Struct)) { + std::cout << "Wrong size: OP_GuildInvite, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; + return; + } + + GuildCommand_Struct* gc = (GuildCommand_Struct*)app->pBuffer; + + if (!IsInAGuild()) + Message(0, "Error: You are not in a guild!"); + else if (gc->officer > GUILD_MAX_RANK) + Message(13, "Invalid rank."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + + //ok, the invite is also used for changing rank as well. + Mob* invitee = entity_list.GetMob(gc->othername); + + if (!invitee) { + Message(13, "Prospective guild member %s must be in zone to preform guild operations on them.", gc->othername); + return; + } + + if (invitee->IsClient()) { + Client* client = invitee->CastToClient(); + + //ok, figure out what they are trying to do. + if (client->GuildID() == GuildID()) { + //they are already in this guild, must be a promotion or demotion + if (gc->officer < client->GuildRank()) { + //demotion + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) { + Message(13, "You dont have permission to demote."); + return; + } + + //we could send this to the member and prompt them to see if they want to + //be demoted (I guess), but I dont see a point in that. + + mlog(GUILDS__ACTIONS, "%s (%d) is demoting %s (%d) to rank %d in guild %s (%d)", + GetName(), CharacterID(), + client->GetName(), client->CharacterID(), + gc->officer, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(client->CharacterID(), gc->officer)) { + Message(13, "There was an error during the demotion, DB may now be inconsistent."); + return; + } + + } + else if (gc->officer > client->GuildRank()) { + //promotion + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) { + Message(13, "You dont have permission to demote."); + return; + } + + mlog(GUILDS__ACTIONS, "%s (%d) is asking to promote %s (%d) to rank %d in guild %s (%d)", + GetName(), CharacterID(), + client->GetName(), client->CharacterID(), + gc->officer, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + //record the promotion with guild manager so we know its valid when we get the reply + guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); + + if (gc->guildeqid == 0) + gc->guildeqid = GuildID(); + + mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for promotion to %s, length %d", client->GetName(), app->size); + mpkt(GUILDS__OUT_PACKET_TRACE, app); + client->QueuePacket(app); + + } + else { + Message(13, "That member is already that rank."); + return; + } + } + else if (!client->IsInAGuild()) { + //they are not in this or any other guild, this is an invite + // + if (client->GetPendingGuildInvitation()) + { + Message(13, "That person is already considering a guild invitation."); + return; + } + + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_INVITE)) { + Message(13, "You dont have permission to invite."); + return; + } + + mlog(GUILDS__ACTIONS, "Inviting %s (%d) into guild %s (%d)", + client->GetName(), client->CharacterID(), + guild_mgr.GetGuildName(GuildID()), GuildID()); + + //record the invite with guild manager so we know its valid when we get the reply + guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); + + if (gc->guildeqid == 0) + gc->guildeqid = GuildID(); + + mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size); + mpkt(GUILDS__OUT_PACKET_TRACE, app); + client->SetPendingGuildInvitation(true); + client->QueuePacket(app); + + } + else { + //they are in some other guild + Message(13, "Player is in a guild."); + return; } } +#ifdef BOTS + else if (invitee->IsBot()) { + // The guild system is too tightly coupled with the character_data table so we have to avoid using much of the system + Bot::ProcessGuildInvite(this, invitee->CastToBot()); + return; + } +#endif } - /* Cursor to Item storage */ - else { - uint32 max_currency = GetAlternateCurrencyValue(reclaim->currency_id); +} - /* If you input more than you have currency wise, just give the max of the currency you currently have */ - if(reclaim->count > max_currency) { - SummonItem(item_id, max_currency); - SetAlternateCurrencyValue(reclaim->currency_id, 0); +void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildInviteAccept"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + SetPendingGuildInvitation(false); + + if (app->size != sizeof(GuildInviteAccept_Struct)) { + std::cout << "Wrong size: OP_GuildInviteAccept, size=" << app->size << ", expected " << sizeof(GuildJoin_Struct) << std::endl; + return; + } + + GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*)app->pBuffer; + + if (GetClientVersion() >= EQClientRoF) + { + if (gj->response > 9) + { + //dont care if the check fails (since we dont know the rank), just want to clear the entry. + guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); + worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); + return; + } + } + if (gj->response == 5 || gj->response == 4) { + //dont care if the check fails (since we dont know the rank), just want to clear the entry. + guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); + + worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); + + return; + } + + //uint32 tmpeq = gj->guildeqid; + if (IsInAGuild() && gj->response == GuildRank()) + Message(0, "Error: You're already in a guild!"); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + mlog(GUILDS__ACTIONS, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s", + gj->guildeqid, gj->response, gj->inviter, gj->newmember); + + //we dont really care a lot about what this packet means, as long as + //it has been authorized with the guild manager + if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) { + worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName()); + Message(13, "Invalid invite response packet!"); + return; + } + + if (gj->guildeqid == GuildID()) { + //only need to change rank. + + mlog(GUILDS__ACTIONS, "Changing guild rank of %s (%d) to rank %d in guild %s (%d)", + GetName(), CharacterID(), + gj->response, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(CharacterID(), gj->response)) { + Message(13, "There was an error during the rank change, DB may now be inconsistent."); + return; + } } else { - SummonItem(item_id, reclaim->count, 0, 0, 0, 0, 0, false, MainCursor); - AddAlternateCurrencyValue(reclaim->currency_id, -((int32)reclaim->count)); - } - /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Reclaim :: Cursor to Item :: alt_currency_id:%i amount:-%i in zoneid:%i instid:%i", reclaim->currency_id, reclaim->count, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + + mlog(GUILDS__ACTIONS, "Adding %s (%d) to guild %s (%d) at rank %d", + GetName(), CharacterID(), + guild_mgr.GetGuildName(gj->guildeqid), gj->guildeqid, + gj->response); + + //change guild and rank + + uint32 guildrank = gj->response; + + if (GetClientVersion() == EQClientRoF) + { + if (gj->response == 8) + { + guildrank = 0; + } + } + + if (!guild_mgr.SetGuild(CharacterID(), gj->guildeqid, guildrank)) { + Message(13, "There was an error during the invite, DB may now be inconsistent."); + return; + } + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) + GuildBanks->SendGuildBank(this); } } } -void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencySell, app, AltCurrencySellItem_Struct); - EQApplicationPacket *outapp = app->Copy(); - AltCurrencySellItem_Struct *sell = (AltCurrencySellItem_Struct*)outapp->pBuffer; +void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildLeader"); + mpkt(GUILDS__IN_PACKET_TRACE, app); - NPC* tar = entity_list.GetNPCByID(sell->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; + if (app->size < 2) { + mlog(GUILDS__ERROR, "Invalid length %d on OP_GuildLeader", app->size); + return; + } - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } + app->pBuffer[app->size - 1] = 0; + GuildMakeLeader* gml = (GuildMakeLeader*)app->pBuffer; + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + else if (GuildRank() != GUILD_LEADER) + Message(0, "Error: You arent the guild leader!"); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } + //NOTE: we could do cross-zone lookups here... - ItemInst* inst = GetInv().GetItem(sell->slot_id); - if(!inst) { - return; - } + Client* newleader = entity_list.GetClientByName(gml->target); + if (newleader) { - if (!RuleB(Merchant, EnableAltCurrencySell)) { - return; - } + mlog(GUILDS__ACTIONS, "Transfering leadership of %s (%d) to %s (%d)", + guild_mgr.GetGuildName(GuildID()), GuildID(), + newleader->GetName(), newleader->CharacterID()); - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - uint32 npc_id = tar->GetNPCTypeID(); - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for(itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; + if (guild_mgr.SetGuildLeader(GuildID(), newleader->CharacterID())){ + Message(0, "Successfully Transfered Leadership to %s.", gml->target); + newleader->Message(15, "%s has transfered the guild leadership into your hands.", GetName()); } - - int32 fac = tar->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(!item) - continue; - - if(item->ID == inst->GetItem()->ID) { - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if(!found) { - return; - } - - if(!inst->IsStackable()) - { - DeleteItemInInventory(sell->slot_id, 0, false); + else + Message(0, "Could not change leadership at this time."); } else - { - if(inst->GetCharges() < sell->charges) - { - sell->charges = inst->GetCharges(); - } + Message(0, "Failed to change leader, could not find target."); + } + // SendGuildMembers(GuildID(), true); + return; +} - if(sell->charges == 0) - { - Message(13, "Charge mismatch error."); +void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) +{ + + mlog(GUILDS__IN_PACKETS, "Got OP_GuildManageBanker of len %d", app->size); + mpkt(GUILDS__IN_PACKET_TRACE, app); + if (app->size != sizeof(GuildManageBanker_Struct)) { + mlog(GUILDS__ERROR, "Error: app size of %i != size of OP_GuildManageBanker of %i\n", app->size, sizeof(GuildManageBanker_Struct)); + return; + } + GuildManageBanker_Struct* gmb = (GuildManageBanker_Struct*)app->pBuffer; + + if (!IsInAGuild()) { + Message(13, "Your not in a guild!"); + return; + } + + CharGuildInfo gci; + + if (!guild_mgr.GetCharInfo(gmb->member, gci)) + { + Message(0, "Unable to find '%s'", gmb->member); + return; + } + bool IsCurrentlyABanker = guild_mgr.GetBankerFlag(gci.char_id); + + bool IsCurrentlyAnAlt = guild_mgr.GetAltFlag(gci.char_id); + + bool NewBankerStatus = gmb->enabled & 0x01; + + bool NewAltStatus = gmb->enabled & 0x02; + + if ((IsCurrentlyABanker != NewBankerStatus) && !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) + { + Message(13, "Only the guild leader can assign guild bankers!"); + return; + } + + if (IsCurrentlyAnAlt != NewAltStatus) + { + bool IsAllowed = !strncasecmp(GetName(), gmb->member, strlen(GetName())) || (GuildRank() >= GUILD_OFFICER); + + if (!IsAllowed) + { + Message(13, "You are not allowed to change the alt status of %s", gmb->member); + return; + } + } + + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + if (IsCurrentlyABanker != NewBankerStatus) + { + if (!guild_mgr.SetBankerFlag(gci.char_id, NewBankerStatus)) { + Message(13, "Error setting guild banker flag."); + return; + } + + if (NewBankerStatus) + Message(0, "%s has been made a guild banker.", gmb->member); + else + Message(0, "%s is no longer a guild banker.", gmb->member); + } + if (IsCurrentlyAnAlt != NewAltStatus) + { + if (!guild_mgr.SetAltFlag(gci.char_id, NewAltStatus)) { + Message(13, "Error setting guild alt flag."); + return; + } + + if (NewAltStatus) + Message(0, "%s has been marked as an alt.", gmb->member); + else + Message(0, "%s is no longer marked as an alt.", gmb->member); + } +} + +void Client::Handle_OP_GuildPeace(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Got OP_GuildPeace of len %d", app->size); + mpkt(GUILDS__IN_PACKET_TRACE, app); + return; +} + +void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildPromote"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildPromoteStruct)) { + mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n", app->size, sizeof(GuildPromoteStruct)); + return; + } + + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) + Message(0, "You dont have permission to invite."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + GuildPromoteStruct* promote = (GuildPromoteStruct*)app->pBuffer; + + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(promote->target, gci)) { + Message(0, "Unable to find '%s'", promote->target); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + uint8 rank = gci.rank + 1; + + if (rank > GUILD_OFFICER) + return; + + + mlog(GUILDS__ACTIONS, "Promoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", + promote->target, gci.char_id, + guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, + guild_mgr.GetRankName(GuildID(), rank), rank, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(gci.char_id, rank)) { + Message(13, "Error while setting rank %d on '%s'.", rank, promote->target); + return; + } + Message(0, "Successfully promoted %s to rank %d", promote->target, rank); + } + return; +} + +void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildPublicNote"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size < sizeof(GuildUpdate_PublicNote)) { + // client calls for a motd on login even if they arent in a guild + printf("Error: app size of %i < size of OP_GuildPublicNote of %zu\n", app->size, sizeof(GuildUpdate_PublicNote)); + return; + } + GuildUpdate_PublicNote* gpn = (GuildUpdate_PublicNote*)app->pBuffer; + + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(gpn->target, gci)) { + Message(0, "Unable to find '%s'", gpn->target); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + mlog(GUILDS__ACTIONS, "Setting public note on %s (%d) in guild %s (%d) to: %s", + gpn->target, gci.char_id, + guild_mgr.GetGuildName(GuildID()), GuildID(), + gpn->note); + + if (!guild_mgr.SetPublicNote(gci.char_id, gpn->note)) { + Message(13, "Failed to set public note on %s", gpn->target); + } + else { + Message(0, "Successfully changed public note on %s", gpn->target); + } + // SendGuildMembers(GuildID(), true); + return; +} + +void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildRemove"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildCommand_Struct)) { + std::cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; + return; + } + GuildCommand_Struct* gc = (GuildCommand_Struct*)app->pBuffer; + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + // we can always remove ourself, otherwise, our rank needs remove permissions + else if (strcasecmp(gc->othername, GetName()) != 0 && + !guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_REMOVE)) + Message(0, "You dont have permission to remove guild members."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { +#ifdef BOTS + if (Bot::ProcessGuildRemoval(this, gc->othername)) + return; +#endif + uint32 char_id; + Client* client = entity_list.GetClientByName(gc->othername); + + if (client) { + if (!client->IsInGuild(GuildID())) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); return; } + char_id = client->CharacterID(); - DeleteItemInInventory(sell->slot_id, sell->charges, false); - cost *= sell->charges; + mlog(GUILDS__ACTIONS, "Removing %s (%d) from guild %s (%d)", + client->GetName(), client->CharacterID(), + guild_mgr.GetGuildName(GuildID()), GuildID()); + } + else { + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(gc->othername, gci)) { + Message(0, "Unable to find '%s'", gc->othername); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + char_id = gci.char_id; + + mlog(GUILDS__ACTIONS, "Removing remote/offline %s (%d) into guild %s (%d)", + gci.char_name.c_str(), gci.char_id, + guild_mgr.GetGuildName(GuildID()), GuildID()); } - sell->cost = cost; - - /* QS: PlayerLogAlternateCurrencyTransactions :: Sold to Merchant*/ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Sold to Merchant :: itemid:%u npcid:%u alt_currency_id:%u cost:%u in zoneid:%u instid:%i", item->ID, npc_id, alt_cur_id, cost, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + if (!guild_mgr.SetGuild(char_id, GUILD_NONE, 0)) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildManageRemove, sizeof(GuildManageRemove_Struct)); + GuildManageRemove_Struct* gm = (GuildManageRemove_Struct*)outapp->pBuffer; + gm->guildeqid = GuildID(); + strcpy(gm->member, gc->othername); + Message(0, "%s successfully removed from your guild.", gc->othername); + entity_list.QueueClientsGuild(this, outapp, false, GuildID()); + safe_delete(outapp); } + else + Message(0, "Unable to remove %s from your guild.", gc->othername); + } + // SendGuildMembers(GuildID(), true); + return; +} +void Client::Handle_OP_GuildStatus(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildStatus_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildStatus expected %i got %i", + sizeof(GuildStatus_Struct), app->size); + + DumpPacket(app); + + return; + } + GuildStatus_Struct *gss = (GuildStatus_Struct*)app->pBuffer; + + Client *c = entity_list.GetClientByName(gss->Name); + + if (!c) + { + Message_StringID(clientMessageWhite, TARGET_PLAYER_FOR_GUILD_STATUS); + return; + } + + uint32 TargetGuildID = c->GuildID(); + + if (TargetGuildID == GUILD_NONE) + { + Message_StringID(clientMessageWhite, NOT_IN_A_GUILD, c->GetName()); + return; + } + + const char *GuildName = guild_mgr.GetGuildName(TargetGuildID); + + if (!GuildName) + return; + + bool IsLeader = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_PROMOTE); + bool IsOfficer = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_INVITE); + + if ((TargetGuildID == GuildID()) && (c != this)) + { + if (IsLeader) + Message_StringID(clientMessageWhite, LEADER_OF_YOUR_GUILD, c->GetName()); + else if (IsOfficer) + Message_StringID(clientMessageWhite, OFFICER_OF_YOUR_GUILD, c->GetName()); + else + Message_StringID(clientMessageWhite, MEMBER_OF_YOUR_GUILD, c->GetName()); + + return; + } + + if (IsLeader) + Message_StringID(clientMessageWhite, LEADER_OF_X_GUILD, c->GetName(), GuildName); + else if (IsOfficer) + Message_StringID(clientMessageWhite, OFFICER_OF_X_GUILD, c->GetName(), GuildName); + else + Message_StringID(clientMessageWhite, MEMBER_OF_X_GUILD, c->GetName(), GuildName); +} + +void Client::Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildUpdateURLAndChannel_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildUpdateURLAndChannel expected %i got %i", + sizeof(GuildUpdateURLAndChannel_Struct), app->size); + + DumpPacket(app); + + return; + } + + GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*)app->pBuffer; + + if (!IsInAGuild()) + return; + + if (!guild_mgr.IsGuildLeader(GuildID(), CharacterID())) + { + Message(13, "Only the guild leader can change the Channel or URL.!"); + return; + } + + if (guuacs->Action == 0) + guild_mgr.SetGuildURL(GuildID(), guuacs->Text); + else + guild_mgr.SetGuildChannel(GuildID(), guuacs->Text); + +} + +void Client::Handle_OP_GuildWar(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Got OP_GuildWar of len %d", app->size); + mpkt(GUILDS__IN_PACKET_TRACE, app); + return; +} + +void Client::Handle_OP_Heartbeat(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_Hide(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) + { + //Can not be able to train hide but still have it from racial though + return; //You cannot hide if you do not have hide + } + + if (!p_timers.Expired(&database, pTimerHide, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + int reuse = HideReuseTime - GetAA(209); + p_timers.Start(pTimerHide, reuse - 1); + + float hidechance = ((GetSkill(SkillHide) / 250.0f) + .25) * 100; + float random = MakeRandomFloat(0, 100); + CheckIncreaseSkill(SkillHide, nullptr, 5); + if (random < hidechance) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x03; + sa_out->parameter = 1; + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + if (GetAA(aaShroudofStealth)){ + improved_hidden = true; + hidden = true; + } + else + hidden = true; + } + if (GetClass() == ROGUE){ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage, sizeof(SimpleMessage_Struct)); + SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; + msg->color = 0x010E; + Mob *evadetar = GetTarget(); + if (!auto_attack && (evadetar && evadetar->CheckAggro(this) + && evadetar->IsNPC())) { + if (MakeRandomInt(0, 260) < (int)GetSkill(SkillHide)) { + msg->string_id = EVADE_SUCCESS; + RogueEvade(evadetar); + } + else { + msg->string_id = EVADE_FAIL; + } + } + else { + if (hidden){ + msg->string_id = HIDE_SUCCESS; + } + else { + msg->string_id = HIDE_FAIL; + } + } FastQueuePacket(&outapp); - AddAlternateCurrencyValue(alt_cur_id, cost); - Save(1); } + return; } -void Client::Handle_OP_CrystalReclaim(const EQApplicationPacket *app) { - uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); - uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); - if((ebon + radiant) > 0) { - AddCrystals(radiant, ebon); - } -} - -void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_CrystalCreate, app, CrystalReclaim_Struct); - CrystalReclaim_Struct *cr = (CrystalReclaim_Struct*)app->pBuffer; - - if(cr->type == 5) { - if(cr->amount > GetEbonCrystals()) { - SummonItem(RuleI(Zone, EbonCrystalItemID), GetEbonCrystals()); - m_pp.currentEbonCrystals = 0; - m_pp.careerEbonCrystals = 0; - SaveCurrency(); - SendCrystalCounts(); - } else { - SummonItem(RuleI(Zone, EbonCrystalItemID), cr->amount); - m_pp.currentEbonCrystals -= cr->amount; - m_pp.careerEbonCrystals -= cr->amount; - SaveCurrency(); - SendCrystalCounts(); - } - } else if(cr->type == 4) { - if(cr->amount > GetRadiantCrystals()) { - SummonItem(RuleI(Zone, RadiantCrystalItemID), GetRadiantCrystals()); - m_pp.currentRadCrystals = 0; - m_pp.careerRadCrystals = 0; - SaveCurrency(); - SendCrystalCounts(); - } else { - SummonItem(RuleI(Zone, RadiantCrystalItemID), cr->amount); - m_pp.currentRadCrystals -= cr->amount; - m_pp.careerRadCrystals -= cr->amount; - SaveCurrency(); - SendCrystalCounts(); - } - } -} - -void Client::Handle_OP_LFGuild(const EQApplicationPacket *app) +void Client::Handle_OP_HideCorpse(const EQApplicationPacket *app) { - if(app->size < 4) + // New OPCode for SOD+ as /hidecorpse is handled serverside now. + // + if (app->size != sizeof(HideCorpse_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_HideCorpse expected %i got %i", + sizeof(HideCorpse_Struct), app->size); + + DumpPacket(app); + + return; + } + + HideCorpse_Struct *hcs = (HideCorpse_Struct*)app->pBuffer; + + if (hcs->Action == HideCorpseLooted) return; - uint32 Command = *((uint32 *) app->pBuffer); + if ((HideCorpseMode == HideCorpseNone) && (hcs->Action == HideCorpseNone)) + return; - switch(Command) - { - case 0: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct); - LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer; + entity_list.HideCorpses(this, HideCorpseMode, hcs->Action); -#ifdef DARWIN -#if __DARWIN_C_LEVEL < 200809L - if (strlen(pts->Comment) > 256) -#else - if(strnlen(pts->Comment, 256) > 256) -#endif // __DARWIN_C_LEVEL -#else - if(strnlen(pts->Comment, 256) > 256) -#endif // DARWIN - return; - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_UpdatePlayerInfo); - pack->WriteUInt32(GetBaseClass()); - pack->WriteUInt32(GetLevel()); - pack->WriteUInt32(GetAAPointsSpent()); - pack->WriteString(pts->Comment); - pack->WriteUInt32(pts->Toggle); - pack->WriteUInt32(pts->TimeZone); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 1: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct); - LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer; - -#ifdef DARWIN -#if __DARWIN_C_LEVEL < 200809L - if (strlen(gts->Comment) > 256) -#else - if(strnlen(gts->Comment, 256) > 256) -#endif // __DARWIN_C_LEVEL -#else - if(strnlen(gts->Comment, 256) > 256) -#endif // __DARWIN - return; - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_UpdateGuildInfo); - pack->WriteString(guild_mgr.GetGuildName(GuildID())); - pack->WriteString(gts->Comment); - pack->WriteUInt32(gts->FromLevel); - pack->WriteUInt32(gts->ToLevel); - pack->WriteUInt32(gts->Classes); - pack->WriteUInt32(gts->AACount); - pack->WriteUInt32(gts->Toggle); - pack->WriteUInt32(gts->TimeZone); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 3: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchPlayer_Struct); - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 37); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_PlayerMatches); - - LFGuild_SearchPlayer_Struct *sps = (LFGuild_SearchPlayer_Struct *)app->pBuffer; - pack->WriteUInt32(sps->FromLevel); - pack->WriteUInt32(sps->ToLevel); - pack->WriteUInt32(sps->MinAA); - pack->WriteUInt32(sps->TimeZone); - pack->WriteUInt32(sps->Classes); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 4: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchGuild_Struct); - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 33); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_GuildMatches); - - LFGuild_SearchGuild_Struct *sgs = (LFGuild_SearchGuild_Struct *)app->pBuffer; - - pack->WriteUInt32(sgs->Level); - pack->WriteUInt32(sgs->AAPoints); - pack->WriteUInt32(sgs->TimeZone); - pack->WriteUInt32(sgs->Class); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - default: - break; - } + HideCorpseMode = hcs->Action; } -void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app) +void Client::Handle_OP_Ignore(const EQApplicationPacket *app) { - if(app->size < 12) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetRequest, expected at least 12, got %i", app->size); + return; +} + +void Client::Handle_OP_Illusion(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Illusion_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Illusion: got %d, expected %d", app->size, + sizeof(Illusion_Struct)); DumpPacket(app); return; } - uint32 Unknown000 = app->ReadUInt32(0); - - if(Unknown000 != 1) - return; - - uint32 Slot = app->ReadUInt32(4); - - if(Slot >= XTARGET_HARDCAP) - return; - - XTargetType Type = (XTargetType)app->ReadUInt32(8); - - XTargets[Slot].Type = Type; - XTargets[Slot].ID = 0; - XTargets[Slot].Name[0] = 0; - - switch(Type) + if (!GetGM()) { - case Empty: - case Auto: - { - break; - } - - case CurrentTargetPC: - { - char Name[65]; - - app->ReadString(Name, 12, 64); - Client *c = entity_list.GetClientByName(Name); - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, Name, 64); - } - SendXTargetPacket(Slot, c); - - break; - } - - case CurrentTargetNPC: - { - char Name[65]; - app->ReadString(Name, 12, 64); - Mob *m = entity_list.GetMob(Name); - if(m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - break; - } - } - - case TargetsTarget: - { - if(GetTarget()) - UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); - else - UpdateXTargetType(TargetsTarget, nullptr); - - break; - } - - case GroupTank: - { - Group *g = GetGroup(); - - if(g) - { - Client *c = entity_list.GetClientByName(g->GetMainTankName()); - - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetMainTankName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - case GroupTankTarget: - { - Group *g = GetGroup(); - - if(g) - g->NotifyTankTarget(this); - - break; - } - - case GroupAssist: - { - Group *g = GetGroup(); - - if(g) - { - Client *c = entity_list.GetClientByName(g->GetMainAssistName()); - - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetMainAssistName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - - case GroupAssistTarget: - { - - Group *g = GetGroup(); - - if(g) - g->NotifyAssistTarget(this); - - break; - } - - case Puller: - { - Group *g = GetGroup(); - - if(g) - { - Client *c = entity_list.GetClientByName(g->GetPullerName()); - - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetPullerName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - - case PullerTarget: - { - - Group *g = GetGroup(); - - if(g) - g->NotifyPullerTarget(this); - - break; - } - - case GroupMarkTarget1: - case GroupMarkTarget2: - case GroupMarkTarget3: - { - Group *g = GetGroup(); - - if(g) - g->SendMarkedNPCsToMember(this); - - break; - } - - case RaidAssist1: - case RaidAssist2: - case RaidAssist3: - case RaidAssist1Target: - case RaidAssist2Target: - case RaidAssist3Target: - case RaidMarkTarget1: - case RaidMarkTarget2: - case RaidMarkTarget3: - { - // Not implemented yet. - break; - } - - case MyPet: - { - Mob *m = GetPet(); - if(m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - - } - break; - } - case MyPetTarget: - { - Mob *m = GetPet(); - - if(m) - m = m->GetTarget(); - - if(m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - - } - break; - } - - default: - LogFile->write(EQEMuLog::Debug, "Unhandled XTarget Type %i", Type); - break; + database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_Illusion sent by non Game Master.", zone->GetShortName()); + return; } + Illusion_Struct* bnpc = (Illusion_Struct*)app->pBuffer; + //these need to be implemented + /* + texture = bnpc->texture; + helmtexture = bnpc->helmtexture; + luclinface = bnpc->luclinface; + */ + race = bnpc->race; + size = 0; + + entity_list.QueueClients(this, app); + return; } -void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) +void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) { - if(app->size != 1) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetAutoAddHaters, expected 1, got %i", app->size); + + if (app->size != sizeof(InspectResponse_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectAnswer, size=%i, expected %i", app->size, sizeof(InspectResponse_Struct)); + return; + } + + //Fills the app sent from client. + EQApplicationPacket* outapp = app->Copy(); + InspectResponse_Struct* insr = (InspectResponse_Struct*)outapp->pBuffer; + Mob* tmp = entity_list.GetMob(insr->TargetID); + const Item_Struct* item = nullptr; + + for (int16 L = EmuConstants::EQUIPMENT_BEGIN; L <= MainWaist; L++) { + const ItemInst* inst = GetInv().GetItem(L); + item = inst ? inst->GetItem() : nullptr; + + if (item) { + strcpy(insr->itemnames[L], item->Name); + insr->itemicons[L] = item->Icon; + } + else { insr->itemicons[L] = 0xFFFFFFFF; } + } + + const ItemInst* inst = GetInv().GetItem(MainAmmo); + item = inst ? inst->GetItem() : nullptr; + + if (item) { + // another one..I did these, didn't I!!? + strcpy(insr->itemnames[SoF::slots::MainAmmo], item->Name); + insr->itemicons[SoF::slots::MainAmmo] = item->Icon; + } + else { insr->itemicons[SoF::slots::MainAmmo] = 0xFFFFFFFF; } + + InspectMessage_Struct* newmessage = (InspectMessage_Struct*)insr->text; + InspectMessage_Struct& playermessage = this->GetInspectMessage(); + memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); + database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); + + if (tmp != 0 && tmp->IsClient()) { tmp->CastToClient()->QueuePacket(outapp); } // Send answer to requester + + return; +} + +void Client::Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(InspectMessage_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectMessageUpdate, size=%i, expected %i", app->size, sizeof(InspectMessage_Struct)); + return; + } + + InspectMessage_Struct* newmessage = (InspectMessage_Struct*)app->pBuffer; + InspectMessage_Struct& playermessage = this->GetInspectMessage(); + memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); + database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); +} + +void Client::Handle_OP_InspectRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(Inspect_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectRequest, size=%i, expected %i", app->size, sizeof(Inspect_Struct)); + return; + } + + Inspect_Struct* ins = (Inspect_Struct*)app->pBuffer; + Mob* tmp = entity_list.GetMob(ins->TargetID); + + if (tmp != 0 && tmp->IsClient()) { + if (tmp->CastToClient()->GetClientVersion() < EQClientSoF) { tmp->CastToClient()->QueuePacket(app); } // Send request to target + // Inspecting an SoF or later client will make the server handle the request + else { ProcessInspectRequest(tmp->CastToClient(), this); } + } + +#ifdef BOTS + if (tmp != 0 && tmp->IsBot()) { Bot::ProcessBotInspectionRequest(tmp->CastToBot(), this); } +#endif + + return; +} + +void Client::Handle_OP_InstillDoubt(const EQApplicationPacket *app) +{ + //packet is empty as of 12/14/04 + + if (!p_timers.Expired(&database, pTimerInstillDoubt, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerInstillDoubt, InstillDoubtReuseTime - 1); + + InstillDoubt(GetTarget()); + return; +} + +void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ItemViewRequest_Struct)){ + LogFile->write(EQEMuLog::Error, "Wrong size on OP_ItemLinkClick. Got: %i, Expected: %i", app->size, sizeof(ItemViewRequest_Struct)); DumpPacket(app); return; } - XTargetAutoAddHaters = app->ReadUInt8(0); + DumpPacket(app); + ItemViewRequest_Struct* ivrs = (ItemViewRequest_Struct*)app->pBuffer; + + //todo: verify ivrs->link_hash based on a rule, in case we don't care about people being able to sniff data from the item DB + + const Item_Struct* item = database.GetItem(ivrs->item_id); + if (!item) { + if (ivrs->item_id > 500000) + { + std::string response = ""; + int sayid = ivrs->item_id - 500000; + bool silentsaylink = false; + + if (sayid > 250000) //Silent Saylink + { + sayid = sayid - 250000; + silentsaylink = true; + } + + if (sayid > 0) + { + + std::string query = StringFormat("SELECT `phrase` FROM saylink WHERE `id` = '%i'", sayid); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); + return; + } + + if (results.RowCount() != 1) { + Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); + return; + } + + auto row = results.begin(); + response = row[0]; + + } + + if ((response).size() > 0) + { + if (!mod_saylink(response, silentsaylink)) { return; } + + if (GetTarget() && GetTarget()->IsNPC()) + { + if (silentsaylink) + { + parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); + parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + } + else + { + Message(7, "You say, '%s'", response.c_str()); + ChannelMessageReceived(8, 0, 100, response.c_str()); + } + return; + } + else + { + if (silentsaylink) + { + parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + } + else + { + Message(7, "You say, '%s'", response.c_str()); + ChannelMessageReceived(8, 0, 100, response.c_str()); + } + return; + } + } + else + { + Message(13, "Error: Say Link not found or is too long."); + return; + } + } + else { + Message(13, "Error: The item for the link you have clicked on does not exist!"); + return; + } + + } + + ItemInst* inst = database.CreateItem(item, item->MaxCharges, ivrs->augments[0], ivrs->augments[1], ivrs->augments[2], ivrs->augments[3], ivrs->augments[4]); + if (inst) { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + return; +} + +void Client::Handle_OP_ItemLinkResponse(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LDONItemViewRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemLinkResponse expected:%i got:%i", sizeof(LDONItemViewRequest_Struct), app->size); + return; + } + LDONItemViewRequest_Struct* item = (LDONItemViewRequest_Struct*)app->pBuffer; + ItemInst* inst = database.CreateItem(item->item_id); + if (inst) { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + return; +} + +void Client::Handle_OP_ItemName(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ItemNamePacket_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for ItemNamePacket_Struct: Expected: %i, Got: %i", + sizeof(ItemNamePacket_Struct), app->size); + return; + } + ItemNamePacket_Struct *p = (ItemNamePacket_Struct*)app->pBuffer; + const Item_Struct *item = 0; + if ((item = database.GetItem(p->item_id)) != nullptr) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ItemName, sizeof(ItemNamePacket_Struct)); + p = (ItemNamePacket_Struct*)outapp->pBuffer; + memset(p, 0, sizeof(ItemNamePacket_Struct)); + strcpy(p->name, item->Name); + FastQueuePacket(&outapp); + } + return; } void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app) @@ -13562,14 +8473,983 @@ void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app) QueuePacket(outapp); safe_delete(outapp); - } else + } + else return; } +void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ItemVerifyRequest_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size); + return; + } + + ItemVerifyRequest_Struct* request = (ItemVerifyRequest_Struct*)app->pBuffer; + int32 slot_id; + int32 target_id; + int32 spell_id = 0; + slot_id = request->slot; + target_id = request->target; + + + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct)); + ItemVerifyReply_Struct* reply = (ItemVerifyReply_Struct*)outapp->pBuffer; + reply->slot = slot_id; + reply->target = target_id; + + QueuePacket(outapp); + safe_delete(outapp); + + + if (IsAIControlled()) { + this->Message_StringID(13, NOT_IN_CONTROL); + return; + } + + if (slot_id < 0) { + LogFile->write(EQEMuLog::Debug, "Unknown slot being used by %s, slot being used is: %i", GetName(), request->slot); + return; + } + + const ItemInst* inst = m_inv[slot_id]; + if (!inst) { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + DeleteItemInInventory(slot_id, 0, true); + return; + } + + const Item_Struct* item = inst->GetItem(); + if (!item) { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + DeleteItemInInventory(slot_id, 0, true); + return; + } + + spell_id = item->Click.Effect; + + if + ( + spell_id > 0 && + ( + !IsValidSpell(spell_id) || + casting_spell_id || + delaytimer || + spellend_timer.Enabled() || + IsStunned() || + IsFeared() || + IsMezzed() || + DivineAura() || + (IsSilenced() && !IsDiscipline(spell_id)) || + (IsAmnesiad() && IsDiscipline(spell_id)) || + (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) + ) + ) + { + SendSpellBarEnable(spell_id); + return; + } + + LogFile->write(EQEMuLog::Debug, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id); + + if ((slot_id < MainCursor) || (slot_id == MainPowerSource) || (slot_id > 250 && slot_id < 331 && ((item->ItemType == ItemTypePotion) || item->PotionBelt))) // sanity check + { + ItemInst* p_inst = (ItemInst*)inst; + + parse->EventItem(EVENT_ITEM_CLICK, this, p_inst, nullptr, "", slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + return; + } + + int r; + bool tryaug = false; + ItemInst* clickaug = 0; + Item_Struct* augitem = 0; + + for (r = 0; r < EmuConstants::ITEM_COMMON_SIZE; r++) { + const ItemInst* aug_i = inst->GetAugment(r); + if (!aug_i) + continue; + const Item_Struct* aug = aug_i->GetItem(); + if (!aug) + continue; + + if ((aug->Click.Type == ET_ClickEffect) || (aug->Click.Type == ET_Expendable) || (aug->Click.Type == ET_EquipClick) || (aug->Click.Type == ET_ClickEffect2)) + { + tryaug = true; + clickaug = (ItemInst*)aug_i; + augitem = (Item_Struct*)aug; + spell_id = aug->Click.Effect; + break; + } + } + + if ((spell_id <= 0) && (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol && item->ItemType != ItemTypeSpell)) + { + LogFile->write(EQEMuLog::Debug, "Item with no effect right clicked by %s", GetName()); + } + else if (inst->IsType(ItemClassCommon)) + { + if (item->ItemType == ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) + { + DeleteItemInInventory(slot_id, 1, true); + TrainDiscipline(item->ID); + } + else if (item->ItemType == ItemTypeSpell) + { + return; + } + else if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) + { + if (inst->GetCharges() == 0) + { + //Message(0, "This item is out of charges."); + Message_StringID(13, ITEM_OUT_OF_CHARGES); + return; + } + if (GetLevel() >= item->Click.Level2) + { + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + return; + } + + if (i == 0) { + CastSpell(item->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, item->CastTime, 0, 0, slot_id); + } + } + else + { + Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); + return; + } + } + else if (tryaug) + { + if (clickaug->GetCharges() == 0) + { + //Message(0, "This item is out of charges."); + Message_StringID(13, ITEM_OUT_OF_CHARGES); + return; + } + if (GetLevel() >= augitem->Click.Level2) + { + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, clickaug, nullptr, "", slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + return; + } + + if (i == 0) { + CastSpell(augitem->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, augitem->CastTime, 0, 0, slot_id); + } + } + else + { + Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); + return; + } + } + else + { + if (GetClientVersion() >= EQClientSoD && !inst->IsEquipable(GetBaseRace(), GetClass())) + { + if (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol) + { + LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); + } + else + { + //This is food/drink - consume it + if (item->ItemType == ItemTypeFood && m_pp.hunger_level < 5000) + { + Consume(item, item->ItemType, slot_id, false); + } + else if (item->ItemType == ItemTypeDrink && m_pp.thirst_level < 5000) + { + Consume(item, item->ItemType, slot_id, false); + } + else if (item->ItemType == ItemTypeAlcohol) + { +#if EQDEBUG >= 1 + LogFile->write(EQEMuLog::Debug, "Drinking Alcohol from slot:%i", slot_id); +#endif + // This Seems to be handled in OP_DeleteItem handling + //DeleteItemInInventory(slot_id, 1, false); + //entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); + //Should add intoxication level to the PP at some point + //CheckIncreaseSkill(ALCOHOL_TOLERANCE, nullptr, 25); + } + + if (m_pp.hunger_level > 6000) + m_pp.hunger_level = 6000; + if (m_pp.thirst_level > 6000) + m_pp.thirst_level = 6000; + + EQApplicationPacket *outapp2; + outapp2 = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); + Stamina_Struct* sta = (Stamina_Struct*)outapp2->pBuffer; + sta->food = m_pp.hunger_level; + sta->water = m_pp.thirst_level; + + QueuePacket(outapp2); + safe_delete(outapp2); + } + + } + else + { + LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); + } + } + } + else + { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + } + } + else + { + Message(0, "Error: Invalid inventory slot for using effects (inventory slot #%i)", slot_id); + } + + return; +} + +void Client::Handle_OP_Jump(const EQApplicationPacket *app) +{ + SetEndurance(GetEndurance() - (GetLevel()<20 ? (225 * GetLevel() / 100) : 50)); + return; +} + +void Client::Handle_OP_KeyRing(const EQApplicationPacket *app) +{ + KeyRingList(); +} + +void Client::Handle_OP_LDoNButton(const EQApplicationPacket *app) +{ + if (app->size < sizeof(bool)) + { + return; + } + + if (GetPendingAdventureCreate()) + { + return; + } + + if (IsOnAdventure()) + { + return; + } + + bool* p = (bool*)app->pBuffer; + if (*p == true) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestCreate, sizeof(ServerAdventureRequestCreate_Struct)+(64 * adv_requested_member_count)); + ServerAdventureRequestCreate_Struct *sac = (ServerAdventureRequestCreate_Struct*)pack->pBuffer; + strcpy(sac->leader, GetName()); + sac->id = adv_requested_id; + sac->theme = adv_requested_theme; + sac->member_count = adv_requested_member_count; + memcpy((pack->pBuffer + sizeof(ServerAdventureRequestCreate_Struct)), adv_requested_data, (64 * adv_requested_member_count)); + pack->Deflate(); + worldserver.SendPacket(pack); + delete pack; + PendingAdventureCreate(); + ClearPendingAdventureData(); + } + else + { + ClearPendingAdventureData(); + } +} + +void Client::Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target->IsNPC()) + { + if (HasSkill(SkillDisarmTraps)) + { + if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNDisarm(target->CastToNPC(), GetSkill(SkillDisarmTraps), LDoNTypeMechanical); + } + else + Message(13, "You do not have the disarm trap skill."); + } +} + +void Client::Handle_OP_LDoNInspect(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target && target->GetClass() == LDON_TREASURE) + Message(15, "%s", target->GetCleanName()); +} + +void Client::Handle_OP_LDoNOpen(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target && target->IsNPC()) + HandleLDoNOpen(target->CastToNPC()); +} + +void Client::Handle_OP_LDoNPickLock(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target->IsNPC()) + { + if (HasSkill(SkillPickLock)) + { + if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNPickLock(target->CastToNPC(), GetSkill(SkillPickLock), LDoNTypeMechanical); + } + else + Message(13, "You do not have the pick locks skill."); + } +} + +void Client::Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target->IsNPC()) + { + if (HasSkill(SkillSenseTraps)) + { + if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNSenseTraps(target->CastToNPC(), GetSkill(SkillSenseTraps), LDoNTypeMechanical); + } + else + Message(13, "You do not have the sense traps skill."); + } +} + +void Client::Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app) +{ + if (app->size != 1) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); + DumpPacket(app); + return; + } + uint8 *mode = (uint8 *)app->pBuffer; + if (*mode) { + m_pp.leadAAActive = 1; + Save(); + Message_StringID(clientMessageYellow, LEADERSHIP_EXP_ON); + } + else { + m_pp.leadAAActive = 0; + Save(); + Message_StringID(clientMessageYellow, LEADERSHIP_EXP_OFF); + } +} + +void Client::Handle_OP_LeaveAdventure(const EQApplicationPacket *app) +{ + if (!IsOnAdventure()) + { + return; + } + LeaveAdventure(); +} + +void Client::Handle_OP_LeaveBoat(const EQApplicationPacket *app) +{ + Mob* boat = entity_list.GetMob(this->BoatID); // find the mob corresponding to the boat id + if (boat) { + if ((boat->GetTarget() == this) && boat->GetHateAmount(this) == 0) // if the client somehow left while still controlling the boat (and the boat isn't attacking them) + boat->SetTarget(0); // fix it to stop later problems + } + this->BoatID = 0; + return; +} + +void Client::Handle_OP_LFGCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LFG_Struct)) { + std::cout << "Wrong size on OP_LFGCommand. Got: " << app->size << ", Expected: " << sizeof(LFG_Struct) << std::endl; + DumpPacket(app); + return; + } + + // Process incoming packet + LFG_Struct* lfg = (LFG_Struct*)app->pBuffer; + + switch (lfg->value & 0xFF) { + case 0: + if (LFG) { + database.SetLFG(CharacterID(), false); + LFG = false; + LFGComments[0] = '\0'; + } + break; + case 1: + if (!LFG) { + LFG = true; + database.SetLFG(CharacterID(), true); + } + LFGFromLevel = lfg->FromLevel; + LFGToLevel = lfg->ToLevel; + LFGMatchFilter = lfg->MatchFilter; + strcpy(LFGComments, lfg->Comments); + break; + default: + Message(0, "Error: unknown LFG value %i", lfg->value); + } + + UpdateWho(); + + // Issue outgoing packet to notify other clients + EQApplicationPacket* outapp = new EQApplicationPacket(OP_LFGAppearance, sizeof(LFG_Appearance_Struct)); + LFG_Appearance_Struct* lfga = (LFG_Appearance_Struct*)outapp->pBuffer; + lfga->spawn_id = this->GetID(); + lfga->lfg = (uint8)LFG; + + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + return; +} + +void Client::Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(LFGGetMatchesRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFGGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFGGetMatchesRequest_Struct)); + DumpPacket(app); + return; + } + LFGGetMatchesRequest_Struct* gmrs = (LFGGetMatchesRequest_Struct*)app->pBuffer; + + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_LFGMatches, sizeof(ServerLFGMatchesRequest_Struct)); + ServerLFGMatchesRequest_Struct* smrs = (ServerLFGMatchesRequest_Struct*)pack->pBuffer; + smrs->FromID = GetID(); + smrs->QuerierLevel = GetLevel(); + strcpy(smrs->FromName, GetName()); + smrs->FromLevel = gmrs->FromLevel; + smrs->ToLevel = gmrs->ToLevel; + smrs->Classes = gmrs->Classes; + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void Client::Handle_OP_LFGuild(const EQApplicationPacket *app) +{ + if (app->size < 4) + return; + + uint32 Command = *((uint32 *)app->pBuffer); + + switch (Command) + { + case 0: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct); + LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer; + +#ifdef DARWIN +#if __DARWIN_C_LEVEL < 200809L + if (strlen(pts->Comment) > 256) +#else + if (strnlen(pts->Comment, 256) > 256) +#endif // __DARWIN_C_LEVEL +#else + if (strnlen(pts->Comment, 256) > 256) +#endif // DARWIN + return; + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_UpdatePlayerInfo); + pack->WriteUInt32(GetBaseClass()); + pack->WriteUInt32(GetLevel()); + pack->WriteUInt32(GetAAPointsSpent()); + pack->WriteString(pts->Comment); + pack->WriteUInt32(pts->Toggle); + pack->WriteUInt32(pts->TimeZone); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + case 1: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct); + LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer; + +#ifdef DARWIN +#if __DARWIN_C_LEVEL < 200809L + if (strlen(gts->Comment) > 256) +#else + if (strnlen(gts->Comment, 256) > 256) +#endif // __DARWIN_C_LEVEL +#else + if (strnlen(gts->Comment, 256) > 256) +#endif // __DARWIN + return; + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_UpdateGuildInfo); + pack->WriteString(guild_mgr.GetGuildName(GuildID())); + pack->WriteString(gts->Comment); + pack->WriteUInt32(gts->FromLevel); + pack->WriteUInt32(gts->ToLevel); + pack->WriteUInt32(gts->Classes); + pack->WriteUInt32(gts->AACount); + pack->WriteUInt32(gts->Toggle); + pack->WriteUInt32(gts->TimeZone); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + case 3: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchPlayer_Struct); + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 37); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_PlayerMatches); + + LFGuild_SearchPlayer_Struct *sps = (LFGuild_SearchPlayer_Struct *)app->pBuffer; + pack->WriteUInt32(sps->FromLevel); + pack->WriteUInt32(sps->ToLevel); + pack->WriteUInt32(sps->MinAA); + pack->WriteUInt32(sps->TimeZone); + pack->WriteUInt32(sps->Classes); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + case 4: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchGuild_Struct); + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 33); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_GuildMatches); + + LFGuild_SearchGuild_Struct *sgs = (LFGuild_SearchGuild_Struct *)app->pBuffer; + + pack->WriteUInt32(sgs->Level); + pack->WriteUInt32(sgs->AAPoints); + pack->WriteUInt32(sgs->TimeZone); + pack->WriteUInt32(sgs->Class); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + default: + break; + } +} + +void Client::Handle_OP_LFPCommand(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(LFP_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPCommand, size=%i, expected %i", app->size, sizeof(LFP_Struct)); + DumpPacket(app); + return; + } + LFP_Struct *lfp = (LFP_Struct*)app->pBuffer; + + LFP = lfp->Action != LFPOff; + database.SetLFP(CharacterID(), LFP); + + if (!LFP) { + worldserver.StopLFP(CharacterID()); + return; + } + + GroupLFPMemberEntry LFPMembers[MAX_GROUP_MEMBERS]; + + for (unsigned int i = 0; iGetZoneID(); + LFPMembers[0].GuildID = GuildID(); + + if (g) { + // This should not happen. The client checks if you are in a group and will not let you put LFP on if + // you are not the leader. + if (!g->IsLeader(this)) { + LogFile->write(EQEMuLog::Error, "Client sent LFP on for character %s who is grouped but not leader.", GetName()); + return; + } + // Fill the LFPMembers array with the rest of the group members, excluding ourself + // We don't fill in the class, level or zone, because we may not be able to determine + // them if the other group members are not in this zone. World will fill in this information + // for us, if it can. + int NextFreeSlot = 1; + for (unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (strcasecmp(g->membername[i], LFPMembers[0].Name)) + strcpy(LFPMembers[NextFreeSlot++].Name, g->membername[i]); + } + } + + + worldserver.UpdateLFP(CharacterID(), lfp->Action, lfp->MatchFilter, lfp->FromLevel, lfp->ToLevel, lfp->Classes, + lfp->Comments, LFPMembers); + + +} + +void Client::Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(LFPGetMatchesRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFPGetMatchesRequest_Struct)); + DumpPacket(app); + return; + } + LFPGetMatchesRequest_Struct* gmrs = (LFPGetMatchesRequest_Struct*)app->pBuffer; + + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_LFPMatches, sizeof(ServerLFPMatchesRequest_Struct)); + ServerLFPMatchesRequest_Struct* smrs = (ServerLFPMatchesRequest_Struct*)pack->pBuffer; + smrs->FromID = GetID(); + smrs->FromLevel = gmrs->FromLevel; + smrs->ToLevel = gmrs->ToLevel; + smrs->QuerierLevel = GetLevel(); + smrs->QuerierClass = GetClass(); + strcpy(smrs->FromName, GetName()); + worldserver.SendPacket(pack); + safe_delete(pack); + } + + return; +} + +void Client::Handle_OP_LoadSpellSet(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LoadSpellSet_Struct)) { + printf("Wrong size of LoadSpellSet_Struct! Expected: %zu, Got: %i\n", sizeof(LoadSpellSet_Struct), app->size); + return; + } + int i; + LoadSpellSet_Struct* ss = (LoadSpellSet_Struct*)app->pBuffer; + for (i = 0; ispell[i] != 0xFFFFFFFF) + UnmemSpell(i, true); + } +} + +void Client::Handle_OP_Logout(const EQApplicationPacket *app) +{ + //LogFile->write(EQEMuLog::Debug, "%s sent a logout packet.", GetName()); + //we will save when we get destroyed soon anyhow + //Save(); + + SendLogoutPackets(); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); + FastQueuePacket(&outapp); + + Disconnect(); + return; +} + +void Client::Handle_OP_LootItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LootingItem_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LootItem, size=%i, expected %i", app->size, sizeof(LootingItem_Struct)); + return; + } + /* + ** fixed the looting code so that it sends the correct opcodes + ** and now correctly removes the looted item the player selected + ** as well as gives the player the proper item. + ** Also fixed a few UI lock ups that would occur. + */ + + EQApplicationPacket* outapp = 0; + Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); + if (entity == 0) { + Message(13, "Error: OP_LootItem: Corpse not found (ent = 0)"); + outapp = new EQApplicationPacket(OP_LootComplete, 0); + QueuePacket(outapp); + safe_delete(outapp); + return; + } + + if (entity->IsCorpse()) { + entity->CastToCorpse()->LootItem(this, app); + return; + } + else { + Message(13, "Error: Corpse not found! (!ent->IsCorpse())"); + Corpse::SendEndLootErrorPacket(this); + } + + return; +} + +void Client::Handle_OP_LootRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_LootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; + return; + } + + SetLooting(true); + + Entity* ent = entity_list.GetID(*((uint32*)app->pBuffer)); + if (ent == 0) { + Message(13, "Error: OP_LootRequest: Corpse not found (ent = 0)"); + Corpse::SendLootReqErrorPacket(this); + return; + } + if (ent->IsCorpse()) + { + Corpse *ent_corpse = ent->CastToCorpse(); + if (DistNoRootNoZ(ent_corpse->GetX(), ent_corpse->GetY()) > 625) + { + Message(13, "Corpse too far away."); + Corpse::SendLootReqErrorPacket(this); + return; + } + + if (invisible) { + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if (invisible_undead) { + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if (invisible_animals){ + BuffFadeByEffect(SE_InvisVsAnimals); + invisible_animals = false; + } + if (hidden || improved_hidden){ + hidden = false; + improved_hidden = false; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x03; + sa_out->parameter = 0; + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + } + ent->CastToCorpse()->MakeLootRequestPackets(this, app); + return; + } + else { + std::cout << "npc == 0 LOOTING FOOKED3" << std::endl; + Message(13, "Error: OP_LootRequest: Corpse not a corpse?"); + Corpse::SendLootReqErrorPacket(this); + } + return; +} + +void Client::Handle_OP_ManaChange(const EQApplicationPacket *app) +{ + if (app->size == 0) { + // i think thats the sign to stop the songs + if (IsBardSong(casting_spell_id) || bardsong != 0) + InterruptSpell(SONG_ENDS, 0x121); + else + InterruptSpell(INTERRUPT_SPELL, 0x121); + + return; + } + else // I don't think the client sends proper manachanges + { // with a length, just the 0 len ones for stopping songs + //ManaChange_Struct* p = (ManaChange_Struct*)app->pBuffer; + printf("OP_ManaChange from client:\n"); + DumpPacket(app); + } + return; +} + +/* +#if 0 // I dont think there's an op for this now, and we check this +// when the client is sitting +void Client::Handle_OP_Medding(const EQApplicationPacket *app) +{ + if (app->pBuffer[0]) + medding = true; + else + medding = false; + return; +} +#endif +*/ + +void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app) +{ + OPMemorizeSpell(app); + return; +} + +void Client::Handle_OP_Mend(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillMend)) + return; + + if (!p_timers.Expired(&database, pTimerMend, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerMend, MendReuseTime - 1); + + int mendhp = GetMaxHP() / 4; + int currenthp = GetHP(); + if (MakeRandomInt(0, 199) < (int)GetSkill(SkillMend)) { + + int criticalchance = spellbonuses.CriticalMend + itembonuses.CriticalMend + aabonuses.CriticalMend; + + if (MakeRandomInt(0, 99) < criticalchance){ + mendhp *= 2; + Message_StringID(4, MEND_CRITICAL); + } + SetHP(GetHP() + mendhp); + SendHPUpdate(); + Message_StringID(4, MEND_SUCCESS); + } + else { + /* the purpose of the following is to make the chance to worsen wounds much less common, + which is more consistent with the way eq live works. + according to my math, this should result in the following probability: + 0 skill - 25% chance to worsen + 20 skill - 23% chance to worsen + 50 skill - 16% chance to worsen */ + if ((GetSkill(SkillMend) <= 75) && (MakeRandomInt(GetSkill(SkillMend), 100) < 75) && (MakeRandomInt(1, 3) == 1)) + { + SetHP(currenthp > mendhp ? (GetHP() - mendhp) : 1); + SendHPUpdate(); + Message_StringID(4, MEND_WORSEN); + } + else + Message_StringID(4, MEND_FAIL); + } + + CheckIncreaseSkill(SkillMend, nullptr, 10); + return; +} + +void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(MercenaryCommand_Struct)) + { + Message(13, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); + DumpPacket(app); + return; + } + + MercenaryCommand_Struct* mc = (MercenaryCommand_Struct*)app->pBuffer; + uint32 merc_command = mc->MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 20 (unknown), 36 (zone in with merc) + int32 option = mc->Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Command %i, Option %i received.", merc_command, option); + + if (!RuleB(Mercs, AllowMercs)) + return; + + // Handle the Command here... + // Will need a list of what every type of command is supposed to do + // Unsure if there is a server response to this packet + if (option >= 0) + { + Merc* merc = GetMerc(); + GetMercInfo().State = option; + + if (merc) { + uint8 numStances = 0; + + //get number of available stances for the current merc + std::list mercStanceList = zone->merc_stance_list[merc->GetMercTemplateID()]; + std::list::iterator iter = mercStanceList.begin(); + while (iter != mercStanceList.end()) { + numStances++; + ++iter; + } + + MercTemplate* mercTemplate = zone->GetMercTemplate(GetMerc()->GetMercTemplateID()); + if (mercTemplate) { + + //check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance) + if (option >= 0 && option < numStances) { + merc->SetStance(mercTemplate->Stances[option]); + GetMercInfo().Stance = mercTemplate->Stances[option]; + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Set Stance: %u", merc->GetStance()); + } + } + } + } +} + void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) { // The payload is 4 bytes. The EntityID of the Mercenary Liason which are of class 71. - if(app->size != sizeof(MercenaryMerchantShopRequest_Struct)) + if (app->size != sizeof(MercenaryMerchantShopRequest_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataRequest expected 4 got %i", app->size); @@ -13578,41 +9458,41 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) return; } - MercenaryMerchantShopRequest_Struct* mmsr = (MercenaryMerchantShopRequest_Struct*) app->pBuffer; + MercenaryMerchantShopRequest_Struct* mmsr = (MercenaryMerchantShopRequest_Struct*)app->pBuffer; uint32 merchant_id = mmsr->MercMerchantID; uint32 altCurrentType = 19; - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Data Request for Merchant ID (%i)", merchant_id); //client is requesting data about currently owned mercenary - if(merchant_id == 0) { + if (merchant_id == 0) { //send info about your current merc(s) } - if(!RuleB(Mercs, AllowMercs)) { + if (!RuleB(Mercs, AllowMercs)) { return; } NPC* tar = entity_list.GetNPCByID(merchant_id); - if(tar) { + if (tar) { int mercTypeCount = 0; int mercCount = 0; - if(DistNoRoot(*tar) > USE_NPC_RANGE2) + if (DistNoRoot(*tar) > USE_NPC_RANGE2) return; - if(tar->GetClass() != MERCERNARY_MASTER) { + if (tar->GetClass() != MERCERNARY_MASTER) { return; } mercTypeCount = tar->GetNumMercTypes(GetClientVersion()); mercCount = tar->GetNumMercs(GetClientVersion()); - if(mercCount > MAX_MERC) - return; + if (mercCount > MAX_MERC) + return; std::list mercTypeList = tar->GetMercTypesList(GetClientVersion()); std::list mercDataList = tar->GetMercsList(GetClientVersion()); @@ -13620,10 +9500,10 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) int i = 0; int StanceCount = 0; - for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) + for (std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) { std::list::iterator siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); - for(siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); siter != zone->merc_stance_list[mercListItr->MercTemplateID].end(); ++siter) + for (siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); siter != zone->merc_stance_list[mercListItr->MercTemplateID].end(); ++siter) { StanceCount++; } @@ -13633,28 +9513,28 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) MercenaryMerchantList_Struct* mml = (MercenaryMerchantList_Struct*)outapp->pBuffer; mml->MercTypeCount = mercTypeCount; - if(mercTypeCount > 0) + if (mercTypeCount > 0) { - for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) { - mml->MercGrades[i] = mercTypeListItr->Type; // DBStringID for Type - i++; + for (std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) { + mml->MercGrades[i] = mercTypeListItr->Type; // DBStringID for Type + i++; } } mml->MercCount = mercCount; - if(mercCount > 0) + if (mercCount > 0) { i = 0; - for(std::list::iterator mercListIter = mercDataList.begin(); mercListIter != mercDataList.end(); ++mercListIter) + for (std::list::iterator mercListIter = mercDataList.begin(); mercListIter != mercDataList.end(); ++mercListIter) { mml->Mercs[i].MercID = mercListIter->MercTemplateID; mml->Mercs[i].MercType = mercListIter->MercType; mml->Mercs[i].MercSubType = mercListIter->MercSubType; - mml->Mercs[i].PurchaseCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), 0): 0; - mml->Mercs[i].UpkeepCost = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), 0): 0; + mml->Mercs[i].PurchaseCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), 0) : 0; + mml->Mercs[i].UpkeepCost = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), 0) : 0; mml->Mercs[i].Status = 0; - mml->Mercs[i].AltCurrencyCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType): 0; - mml->Mercs[i].AltCurrencyUpkeep = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType): 0; + mml->Mercs[i].AltCurrencyCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType) : 0; + mml->Mercs[i].AltCurrencyUpkeep = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType) : 0; mml->Mercs[i].AltCurrencyType = altCurrentType; mml->Mercs[i].MercUnk01 = 0; mml->Mercs[i].TimeLeft = -1; @@ -13662,19 +9542,19 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) mml->Mercs[i].MercUnk02 = 1; int mercStanceCount = 0; std::list::iterator iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); - for(iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); iter != zone->merc_stance_list[mercListIter->MercTemplateID].end(); ++iter) + for (iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); iter != zone->merc_stance_list[mercListIter->MercTemplateID].end(); ++iter) { - mercStanceCount++; + mercStanceCount++; } mml->Mercs[i].StanceCount = mercStanceCount; mml->Mercs[i].MercUnk03 = 519044964; mml->Mercs[i].MercUnk04 = 1; //mml->Mercs[i].MercName; int stanceindex = 0; - if(mercStanceCount > 0) + if (mercStanceCount > 0) { std::list::iterator iter2 = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); - while(iter2 != zone->merc_stance_list[mercListIter->MercTemplateID].end()) + while (iter2 != zone->merc_stance_list[mercListIter->MercTemplateID].end()) { mml->Mercs[i].Stances[stanceindex].StanceIndex = stanceindex; mml->Mercs[i].Stances[stanceindex].Stance = (iter2->StanceID); @@ -13689,10 +9569,65 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) } } +void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app) +{ + // The payload is 0 bytes. + if (app->size != 0) + { + Message(13, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); + DumpPacket(app); + return; + } + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Data Update Request Received."); + + if (GetMercID()) + { + SendMercPersonalInfo(); + } +} + +void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) +{ + // The payload is 0 or 1 bytes. + if (app->size > 1) + { + Message(13, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); + DumpPacket(app); + return; + } + + uint8 Command = 0; + if (app->size > 0) + { + char *InBuffer = (char *)app->pBuffer; + Command = VARSTRUCT_DECODE_TYPE(uint8, InBuffer); + } + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Dismiss Request ( %i ) Received.", Command); + + // Handle the dismiss here... + if (GetMercID()) { + Merc* merc = GetMerc(); + + if (merc) { + if (CheckCanDismissMerc()) { + merc->Dismiss(); + } + } + } + + //SendMercMerchantResponsePacket(10); +} + void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) { // The payload is 16 bytes. First four bytes are the Merc ID (Template ID) - if(app->size != sizeof(MercenaryMerchantRequest_Struct)) + if (app->size != sizeof(MercenaryMerchantRequest_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryHire expected %i got %i", sizeof(MercenaryMerchantRequest_Struct), app->size); @@ -13701,32 +9636,32 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) return; } - MercenaryMerchantRequest_Struct* mmrq = (MercenaryMerchantRequest_Struct*) app->pBuffer; + MercenaryMerchantRequest_Struct* mmrq = (MercenaryMerchantRequest_Struct*)app->pBuffer; uint32 merc_template_id = mmrq->MercID; uint32 merchant_id = mmrq->MercMerchantID; uint32 merc_unk1 = mmrq->MercUnk01; uint32 merc_unk2 = mmrq->MercUnk02; - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Template ID (%i), Merchant ID (%i), Unknown1 (%i), Unknown2 (%i)", merc_template_id, merchant_id, merc_unk1, merc_unk2); //HirePending = true; SetHoTT(0); SendTargetCommand(0); - if(!RuleB(Mercs, AllowMercs)) + if (!RuleB(Mercs, AllowMercs)) return; MercTemplate* merc_template = zone->GetMercTemplate(merc_template_id); - if(merc_template) { + if (merc_template) { Mob* merchant = entity_list.GetNPCByID(merchant_id); - if(!CheckCanHireMerc(merchant, merc_template_id)) { + if (!CheckCanHireMerc(merchant, merc_template_id)) { return; } - if(RuleB(Mercs, ChargeMercPurchaseCost)) { + if (RuleB(Mercs, ChargeMercPurchaseCost)) { uint32 cost = Merc::CalcPurchaseCost(merc_template->MercTemplateID, GetLevel()) * 100; // Cost is in gold TakeMoneyFromPP(cost, true); } @@ -13737,7 +9672,7 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) // Get merc, assign it to client & spawn Merc* merc = Merc::LoadMerc(this, merc_template, merchant_id, false); - if(merc) { + if (merc) { SpawnMerc(merc, true); merc->Save(); @@ -13757,7 +9692,7 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) void Client::Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app) { - if(app->size != sizeof(SuspendMercenary_Struct)) + if (app->size != sizeof(SuspendMercenary_Struct)) { Message(13, "Size mismatch in OP_MercenarySuspendRequest expected %i got %i", sizeof(SuspendMercenary_Struct), app->size); LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenarySuspendRequest expected %i got %i", sizeof(SuspendMercenary_Struct), app->size); @@ -13765,133 +9700,23 @@ void Client::Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app) return; } - SuspendMercenary_Struct* sm = (SuspendMercenary_Struct*) app->pBuffer; + SuspendMercenary_Struct* sm = (SuspendMercenary_Struct*)app->pBuffer; uint32 merc_suspend = sm->SuspendMerc; // Seen 30 for suspending or unsuspending - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Suspend ( %i ) received.", merc_suspend); - if(!RuleB(Mercs, AllowMercs)) + if (!RuleB(Mercs, AllowMercs)) return; // Check if the merc is suspended and if so, unsuspend, otherwise suspend it SuspendMercCommand(); } -void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app) -{ - if(app->size != sizeof(MercenaryCommand_Struct)) - { - Message(13, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); - DumpPacket(app); - return; - } - - MercenaryCommand_Struct* mc = (MercenaryCommand_Struct*) app->pBuffer; - uint32 merc_command = mc->MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 20 (unknown), 36 (zone in with merc) - int32 option = mc->Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Command %i, Option %i received.", merc_command, option); - - if(!RuleB(Mercs, AllowMercs)) - return; - - // Handle the Command here... - // Will need a list of what every type of command is supposed to do - // Unsure if there is a server response to this packet - if(option >= 0) - { - Merc* merc = GetMerc(); - GetMercInfo().State = option; - - if(merc) { - uint8 numStances = 0; - - //get number of available stances for the current merc - std::list mercStanceList = zone->merc_stance_list[merc->GetMercTemplateID()]; - std::list::iterator iter = mercStanceList.begin(); - while(iter != mercStanceList.end()) { - numStances++; - ++iter; - } - - MercTemplate* mercTemplate = zone->GetMercTemplate(GetMerc()->GetMercTemplateID()); - if(mercTemplate) { - - //check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance) - if(option >= 0 && option < numStances) { - merc->SetStance(mercTemplate->Stances[option]); - GetMercInfo().Stance = mercTemplate->Stances[option]; - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Set Stance: %u", merc->GetStance()); - } - } - } - } -} - -void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app) -{ - // The payload is 0 bytes. - if(app->size != 0) - { - Message(13, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); - DumpPacket(app); - return; - } - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Data Update Request Received."); - - if(GetMercID()) - { - SendMercPersonalInfo(); - } -} - -void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) -{ - // The payload is 0 or 1 bytes. - if(app->size > 1) - { - Message(13, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); - DumpPacket(app); - return; - } - - uint8 Command = 0; - if(app->size > 0) - { - char *InBuffer = (char *)app->pBuffer; - Command = VARSTRUCT_DECODE_TYPE(uint8, InBuffer); - } - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Dismiss Request ( %i ) Received.", Command); - - // Handle the dismiss here... - if(GetMercID()) { - Merc* merc = GetMerc(); - - if(merc) { - if(CheckCanDismissMerc()) { - merc->Dismiss(); - } - } - } - - //SendMercMerchantResponsePacket(10); -} - void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) { // The payload is 0 bytes. - if(app->size > 1) + if (app->size > 1) { Message(13, "Size mismatch in OP_MercenaryTimerRequest expected 0 got %i", app->size); LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryTimerRequest expected 0 got %i", app->size); @@ -13899,10 +9724,10 @@ void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) return; } - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Timer Request received."); - if(!RuleB(Mercs, AllowMercs)) { + if (!RuleB(Mercs, AllowMercs)) { return; } @@ -13911,30 +9736,100 @@ void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) uint32 entityID = 0; uint32 mercState = 5; uint32 suspendedTime = 0; - if(GetMercID()) { + if (GetMercID()) { Merc* merc = GetMerc(); - if(merc) { + if (merc) { entityID = merc->GetID(); - if(GetMercInfo().IsSuspended) { + if (GetMercInfo().IsSuspended) { mercState = 1; suspendedTime = GetMercInfo().SuspendedTime; } } } - if(entityID > 0) { + if (entityID > 0) { SendMercTimerPacket(entityID, mercState, suspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); } } -void Client::Handle_OP_OpenInventory(const EQApplicationPacket *app) { - // Does not exist in Ti, UF or RoF clients - // SoF and SoD both send a 4-byte packet with a uint32 value of '8' +void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) +{ + if (app->size != sizeof(MoveCoin_Struct)){ + LogFile->write(EQEMuLog::Error, "Wrong size on OP_MoveCoin. Got: %i, Expected: %i", app->size, sizeof(MoveCoin_Struct)); + DumpPacket(app); + return; + } + OPMoveCoin(app); + return; } -void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) { +void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) +{ + if (!CharacterID()) + { + return; + } + + if (app->size != sizeof(MoveItem_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_MoveItem, size=%i, expected %i", app->size, sizeof(MoveItem_Struct)); + return; + } + + MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer; + if (spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) + { + if (mi->from_slot != mi->to_slot && (mi->from_slot <= EmuConstants::GENERAL_END || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) + { + char *detect = nullptr; + const ItemInst *itm_from = GetInv().GetItem(mi->from_slot); + const ItemInst *itm_to = GetInv().GetItem(mi->to_slot); + MakeAnyLenString(&detect, "Player issued a move item from %u(item id %u) to %u(item id %u) while casting %u.", + mi->from_slot, + itm_from ? itm_from->GetID() : 0, + mi->to_slot, + itm_to ? itm_to->GetID() : 0, + casting_spell_id); + database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName()); + safe_delete_array(detect); + Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots + return; + } + } + + // Illegal bagslot useage checks. Currently, user only receives a message if this check is triggered. + bool mi_hack = false; + + if (mi->from_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->from_slot <= EmuConstants::CURSOR_BAG_END) { + if (mi->from_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } + else { + int16 from_parent = m_inv.CalcSlotId(mi->from_slot); + if (!m_inv[from_parent]) { mi_hack = true; } + else if (!m_inv[from_parent]->IsType(ItemClassContainer)) { mi_hack = true; } + else if (m_inv.CalcBagIdx(mi->from_slot) >= m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; } + } + } + + if (mi->to_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->to_slot <= EmuConstants::CURSOR_BAG_END) { + if (mi->to_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } + else { + int16 to_parent = m_inv.CalcSlotId(mi->to_slot); + if (!m_inv[to_parent]) { mi_hack = true; } + else if (!m_inv[to_parent]->IsType(ItemClassContainer)) { mi_hack = true; } + else if (m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } + } + } + + if (mi_hack) { Message(15, "Caution: Illegal use of inaccessable bag slots!"); } + + if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { SwapItemResync(mi); } + + return; +} + +void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) +{ // Does not exist in Ti client // SoF, SoD and UF clients send a 4-byte packet indicating the 'parent' slot // SoF, SoD and UF slots are defined by a uint32 value and currently untranslated @@ -13952,6 +9847,4208 @@ void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) { // Manually looting a corpse results in a from '34' to '68' value for equipment items, '0' to '0' for inventory. } -void Client::Handle_OP_ClientTimeStamp(const EQApplicationPacket *app) { - // handle as needed or ignore like we have been doing... +void Client::Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_OpenGuildTributeMaster of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + if (app->size != sizeof(StartTribute_Struct)) + printf("Error in OP_OpenGuildTributeMaster. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + //Opens the guild tribute master window + StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; + Mob* tribmast = entity_list.GetMob(st->tribute_master_id); + if (tribmast && tribmast->IsNPC() && tribmast->GetClass() == GUILD_TRIBUTE_MASTER + && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { + st->response = 1; + QueuePacket(app); + tribute_master_id = st->tribute_master_id; + DoTributeUpdate(); + } + else { + st->response = 0; + QueuePacket(app); + } + } + return; } + +void Client::Handle_OP_OpenInventory(const EQApplicationPacket *app) +{ + // Does not exist in Ti, UF or RoF clients + // SoF and SoD both send a 4-byte packet with a uint32 value of '8' +} + +void Client::Handle_OP_OpenTributeMaster(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_OpenTributeMaster of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + if (app->size != sizeof(StartTribute_Struct)) + printf("Error in OP_OpenTributeMaster. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + //Opens the tribute master window + StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; + Mob* tribmast = entity_list.GetMob(st->tribute_master_id); + if (tribmast && tribmast->IsNPC() && tribmast->GetClass() == TRIBUTE_MASTER + && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { + st->response = 1; + QueuePacket(app); + tribute_master_id = st->tribute_master_id; + DoTributeUpdate(); + } + else { + st->response = 0; + QueuePacket(app); + } + } + return; +} + +void Client::Handle_OP_PDeletePetition(const EQApplicationPacket *app) +{ + if (app->size < 2) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PDeletePetition, size=%i, expected %i", app->size, 2); + return; + } + if (petition_list.DeletePetitionByCharName((char*)app->pBuffer)) + Message_StringID(0, PETITION_DELETED); + else + Message_StringID(0, PETITION_NO_DELETE); + return; +} + +void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetCommand_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetCommands, size=%i, expected %i", app->size, sizeof(PetCommand_Struct)); + return; + } + char val1[20] = { 0 }; + PetCommand_Struct* pet = (PetCommand_Struct*)app->pBuffer; + Mob* mypet = this->GetPet(); + + if (!mypet || pet->command == PET_LEADER) + { + if (pet->command == PET_LEADER) + { + if (mypet && (!GetTarget() || GetTarget() == mypet)) + { + mypet->Say_StringID(PET_LEADERIS, GetName()); + } + else if ((mypet = GetTarget())) + { + Mob *Owner = mypet->GetOwner(); + if (Owner) + mypet->Say_StringID(PET_LEADERIS, Owner->GetCleanName()); + else + mypet->Say_StringID(I_FOLLOW_NOONE); + } + } + + return; + } + + if (mypet->GetPetType() == petAnimation && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST) && !GetAA(aaAnimationEmpathy)) + return; + + // just let the command "/pet get lost" work for familiars + if (mypet->GetPetType() == petFamiliar && pet->command != PET_GETLOST) + return; + + uint32 PetCommand = pet->command; + + // Handle Sit/Stand toggle in UF and later. + if (GetClientVersion() >= EQClientUnderfoot) + { + if (PetCommand == PET_SITDOWN) + if (mypet->GetPetOrder() == SPO_Sit) + PetCommand = PET_STANDUP; + } + + switch (PetCommand) + { + case PET_ATTACK: { + if (!GetTarget()) + break; + if (GetTarget()->IsMezzed()) { + Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); + break; + } + if (mypet->IsFeared()) + break; //prevent pet from attacking stuff while feared + + if (!mypet->IsAttackAllowed(GetTarget())) { + mypet->Say_StringID(NOT_LEGAL_TARGET); + break; + } + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { + if (GetTarget() != this && mypet->DistNoRootNoZ(*GetTarget()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { + if (mypet->IsHeld()) { + if (!mypet->IsFocused()) { + mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack. + if (mypet->GetPetOrder() != SPO_Guard) + mypet->SetPetOrder(SPO_Follow); + } + else { + mypet->SetTarget(GetTarget()); + } + } + zone->AddAggroMob(); + mypet->AddToHateList(GetTarget(), 1); + Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); + } + } + break; + } + case PET_BACKOFF: { + if (mypet->IsFeared()) break; //keeps pet running while feared + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_CALMING); + mypet->WipeHateList(); + mypet->SetTarget(nullptr); + } + break; + } + case PET_HEALTHREPORT: { + Message_StringID(MT_PetResponse, PET_REPORT_HP, ConvertArrayF(mypet->GetHPRatio(), val1)); + mypet->ShowBuffList(this); + //Message(10,"%s tells you, 'I have %d percent of my hit points left.'",mypet->GetName(),(uint8)mypet->GetHPRatio()); + break; + } + case PET_GETLOST: { + if (mypet->Charmed()) + break; + if (mypet->GetPetType() == petCharmed || !mypet->IsNPC()) { + // eqlive ignores this command + // we could just remove the charm + // and continue + mypet->BuffFadeByEffect(SE_Charm); + break; + } + else { + SetPet(nullptr); + } + + mypet->Say_StringID(MT_PetResponse, PET_GETLOST_STRING); + mypet->CastToNPC()->Depop(); + + //Oddly, the client (Titanium) will still allow "/pet get lost" command despite me adding the code below. If someone can figure that out, you can uncomment this code and use it. + /* + if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(PET_GETLOST_STRING); + mypet->CastToNPC()->Depop(); + } + */ + + break; + } + case PET_GUARDHERE: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { + if (mypet->IsNPC()) { + mypet->SetHeld(false); + mypet->Say_StringID(MT_PetResponse, PET_GUARDINGLIFE); + mypet->SetPetOrder(SPO_Guard); + mypet->CastToNPC()->SaveGuardSpot(); + } + } + break; + } + case PET_FOLLOWME: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { + mypet->SetHeld(false); + mypet->Say_StringID(MT_PetResponse, PET_FOLLOWING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } + case PET_TAUNT: { + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + Message_StringID(MT_PetResponse, PET_DO_TAUNT); + mypet->CastToNPC()->SetTaunting(true); + } + break; + } + case PET_NOTAUNT: { + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + Message_StringID(MT_PetResponse, PET_NO_TAUNT); + mypet->CastToNPC()->SetTaunting(false); + } + break; + } + case PET_GUARDME: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { + mypet->SetHeld(false); + mypet->Say_StringID(MT_PetResponse, PET_GUARDME_STRING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } + case PET_SITDOWN: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Sit); + mypet->SetRunAnimSpeed(0); + if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet + mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting + mypet->SendAppearancePacket(AT_Anim, ANIM_SIT); + } + break; + } + case PET_STANDUP: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } + case PET_SLUMBER: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if (mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Sit); + mypet->SetRunAnimSpeed(0); + if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet + mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting + mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH); + } + break; + } + case PET_HOLD: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC()){ + if (mypet->IsFeared()) + break; //could be exploited like PET_BACKOFF + + mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); + mypet->WipeHateList(); + mypet->SetHeld(true); + } + break; + } + case PET_HOLD_ON: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC() && !mypet->IsHeld()) { + if (mypet->IsFeared()) + break; //could be exploited like PET_BACKOFF + + mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); + mypet->WipeHateList(); + mypet->SetHeld(true); + } + break; + } + case PET_HOLD_OFF: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC() && mypet->IsHeld()) + mypet->SetHeld(false); + break; + } + case PET_NOCAST: { + if (GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsNoCast()) { + Message_StringID(MT_PetResponse, PET_CASTING); + mypet->CastToNPC()->SetNoCast(false); + } + else { + Message_StringID(MT_PetResponse, PET_NOT_CASTING); + mypet->CastToNPC()->SetNoCast(true); + } + } + break; + } + case PET_FOCUS: { + if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsFocused()) { + Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); + mypet->CastToNPC()->SetFocused(false); + } + else { + Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); + mypet->CastToNPC()->SetFocused(true); + } + } + break; + } + case PET_FOCUS_ON: { + if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (!mypet->IsFocused()) { + Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); + mypet->CastToNPC()->SetFocused(true); + } + } + break; + } + case PET_FOCUS_OFF: { + if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsFocused()) { + Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); + mypet->CastToNPC()->SetFocused(false); + } + } + break; + } + default: + printf("Client attempted to use a unknown pet command:\n"); + break; + } +} + +void Client::Handle_OP_Petition(const EQApplicationPacket *app) +{ + if (app->size <= 1) + return; + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + /*else if(petition_list.FindPetitionByAccountName(this->AccountName())) + { + Message(0,"You already have a petition in queue, you cannot petition again until this one has been responded to or you have deleted the petition."); + return; + }*/ + else + { + if (petition_list.FindPetitionByAccountName(AccountName())) + { + Message(0, "You already have a petition in the queue, you must wait for it to be answered or use /deletepetition to delete it."); + return; + } + Petition* pet = new Petition(CharacterID()); + pet->SetAName(this->AccountName()); + pet->SetClass(this->GetClass()); + pet->SetLevel(this->GetLevel()); + pet->SetCName(this->GetName()); + pet->SetRace(this->GetRace()); + pet->SetLastGM(""); + pet->SetCName(this->GetName()); + pet->SetPetitionText((char*)app->pBuffer); + pet->SetZone(zone->GetZoneID()); + pet->SetUrgency(0); + petition_list.AddPetition(pet); + database.InsertPetitionToDB(pet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", GetName(), pet->GetID()); + } + return; +} + +void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetitionBug_Struct)) + printf("Wrong size of BugStruct! Expected: %zu, Got: %i\n", sizeof(PetitionBug_Struct), app->size); + else{ + Message(0, "Petition Bugs are not supported, please use /bug."); + } + return; +} + +void Client::Handle_OP_PetitionCheckIn(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Petition_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionCheckIn, size=%i, expected %i", app->size, sizeof(Petition_Struct)); + return; + } + Petition_Struct* inpet = (Petition_Struct*)app->pBuffer; + + Petition* pet = petition_list.GetPetitionByID(inpet->petnumber); + //if (inpet->urgency != pet->GetUrgency()) + pet->SetUrgency(inpet->urgency); + pet->SetLastGM(this->GetName()); + pet->SetGMText(inpet->gmtext); + + pet->SetCheckedOut(false); + petition_list.UpdatePetition(pet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + return; +} + +void Client::Handle_OP_PetitionCheckout(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_PetitionCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; + return; + } + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + uint32 getpetnum = *((uint32*)app->pBuffer); + Petition* getpet = petition_list.GetPetitionByID(getpetnum); + if (getpet != 0) { + getpet->AddCheckout(); + getpet->SetCheckedOut(true); + getpet->SendPetitionToPlayer(this->CastToClient()); + petition_list.UpdatePetition(getpet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + } + } + return; +} + +void Client::Handle_OP_PetitionDelete(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetitionUpdate_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionDelete, size=%i, expected %i", app->size, sizeof(PetitionUpdate_Struct)); + return; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate, sizeof(PetitionUpdate_Struct)); + PetitionUpdate_Struct* pet = (PetitionUpdate_Struct*)outapp->pBuffer; + pet->petnumber = *((int*)app->pBuffer); + pet->color = 0x00; + pet->status = 0xFFFFFFFF; + pet->senttime = 0; + strcpy(pet->accountid, ""); + strcpy(pet->gmsenttoo, ""); + pet->quetotal = petition_list.GetTotalPetitions(); + strcpy(pet->charname, ""); + FastQueuePacket(&outapp); + + if (petition_list.DeletePetition(pet->petnumber) == -1) + std::cout << "Something is borked with: " << pet->petnumber << std::endl; + petition_list.ClearPetitions(); + petition_list.UpdateGMQueue(); + petition_list.ReadDatabase(); + petition_list.UpdateZoneListQueue(); + return; +} + +void Client::Handle_OP_PetitionQue(const EQApplicationPacket *app) +{ +#ifdef _EQDEBUG + printf("%s looking at petitions..\n", this->GetName()); +#endif + return; +} + +void Client::Handle_OP_PetitionRefresh(const EQApplicationPacket *app) +{ + // This is When Client Asks for Petition Again and Again... + // break is here because it floods the zones and causes lag if it + // Were to actually do something:P We update on our own schedule now. + return; +} + +void Client::Handle_OP_PetitionResolve(const EQApplicationPacket *app) +{ + Handle_OP_PetitionDelete(app); +} + +void Client::Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_PetitionUnCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; + return; + } + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + uint32 getpetnum = *((uint32*)app->pBuffer); + Petition* getpet = petition_list.GetPetitionByID(getpetnum); + if (getpet != 0) { + getpet->SetCheckedOut(false); + petition_list.UpdatePetition(getpet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + } + } + return; +} + +void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PickPocket_Struct)) + { + LogFile->write(EQEMuLog::Error, "Size mismatch for Pick Pocket packet"); + DumpPacket(app); + } + + if (!HasSkill(SkillPickPockets)) + { + return; + } + + if (!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) + { + Message(13, "Ability recovery time not yet met."); + database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_PickPocket was sent again too quickly.", zone->GetShortName()); + return; + } + PickPocket_Struct* pick_in = (PickPocket_Struct*)app->pBuffer; + + Mob* victim = entity_list.GetMob(pick_in->to); + if (!victim) + return; + + p_timers.Start(pTimerBeggingPickPocket, 8); + if (victim == this){ + Message(0, "You catch yourself red-handed."); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); + sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; + pick_out->coin = 0; + pick_out->from = victim->GetID(); + pick_out->to = GetID(); + pick_out->myskill = GetSkill(SkillPickPockets); + pick_out->type = 0; + //if we do not send this packet the client will lock up and require the player to relog. + QueuePacket(outapp); + safe_delete(outapp); + } + else if (victim->GetOwnerID()){ + Message(0, "You cannot steal from pets!"); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); + sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; + pick_out->coin = 0; + pick_out->from = victim->GetID(); + pick_out->to = GetID(); + pick_out->myskill = GetSkill(SkillPickPockets); + pick_out->type = 0; + //if we do not send this packet the client will lock up and require the player to relog. + QueuePacket(outapp); + safe_delete(outapp); + } + else if (victim->IsNPC()){ + victim->CastToNPC()->PickPocket(this); + } + else{ + Message(0, "Stealing from clients not yet supported."); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); + sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; + pick_out->coin = 0; + pick_out->from = victim->GetID(); + pick_out->to = GetID(); + pick_out->myskill = GetSkill(SkillPickPockets); + pick_out->type = 0; + //if we do not send this packet the client will lock up and require the player to relog. + QueuePacket(outapp); + safe_delete(outapp); + } +} + +void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(PopupResponse_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PopupResponse expected %i got %i", + sizeof(PopupResponse_Struct), app->size); + DumpPacket(app); + return; + } + PopupResponse_Struct *prs = (PopupResponse_Struct*)app->pBuffer; + + // Handle any EQEmu defined popup Ids first + switch (prs->popupid) + { + case POPUPID_UPDATE_SHOWSTATSWINDOW: + if (GetTarget() && GetTarget()->IsClient()) + GetTarget()->CastToClient()->SendStatsWindow(this, true); + else + SendStatsWindow(this, true); + return; + + default: + break; + } + + char buf[16]; + sprintf(buf, "%d\0", prs->popupid); + + parse->EventPlayer(EVENT_POPUP_RESPONSE, this, buf, 0); + + Mob* Target = GetTarget(); + if (Target && Target->IsNPC()) { + parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, buf, 0); + } +} + +void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) +{ + if (app->size != sizeof(MovePotionToBelt_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PotionBelt expected %i got %i", + sizeof(MovePotionToBelt_Struct), app->size); + DumpPacket(app); + return; + } + MovePotionToBelt_Struct *mptbs = (MovePotionToBelt_Struct*)app->pBuffer; + if (mptbs->Action == 0) { + const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID); + if (BaseItem) { + m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID; + m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon; + strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, BaseItem->Name, sizeof(BaseItem->Name)); + database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.items[mptbs->SlotNumber].item_id, m_pp.potionbelt.items[mptbs->SlotNumber].icon); + } + } + else { + m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 0; + m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0; + strncpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, "\0", 1); + } +} + +void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); + DumpPacket(app); + return; + } + uint32 aaid = *((uint32 *)app->pBuffer); + + if (aaid >= _maxLeaderAA) + return; + + uint32 current_rank = m_pp.leader_abilities.ranks[aaid]; + if (current_rank >= MAX_LEADERSHIP_TIERS) { + Message(13, "This ability can be trained no further."); + return; + } + + uint8 cost = LeadershipAACosts[aaid][current_rank]; + if (cost == 0) { + Message(13, "This ability can be trained no further."); + return; + } + + //TODO: we need to enforce prerequisits + + if (aaid >= raidAAMarkNPC) { + //it is a raid ability. + if (cost > m_pp.raid_leadership_points) { + Message(13, "You do not have enough points to purchase this ability."); + return; + } + + //sell them the ability. + m_pp.raid_leadership_points -= cost; + m_pp.leader_abilities.ranks[aaid]++; + } + else { + //it is a group ability. + if (cost > m_pp.group_leadership_points) { + Message(13, "You do not have enough points to purchase this ability."); + return; + } + + //sell them the ability. + m_pp.group_leadership_points -= cost; + m_pp.leader_abilities.ranks[aaid]++; + + database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); + } + + //success, send them an update + EQApplicationPacket *outapp = new EQApplicationPacket(OP_UpdateLeadershipAA, sizeof(UpdateLeadershipAA_Struct)); + UpdateLeadershipAA_Struct *u = (UpdateLeadershipAA_Struct *)outapp->pBuffer; + u->ability_id = aaid; + u->new_rank = m_pp.leader_abilities.ranks[aaid]; + u->pointsleft = m_pp.group_leadership_points; // FIXME: Take into account raid abilities + FastQueuePacket(&outapp); + + Group *g = GetGroup(); + + // Update all group members with the new AA the leader has purchased. + if (g) { + g->UpdateGroupAAs(); + g->SendLeadershipAAUpdate(); + } + +} + +void Client::Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app) +{ + // This opcode is sent by the client when the player right clicks a name on the PVP leaderboard and sends + // further details about the selected player, e.g. Race/Class/AAs/Guild etc. + // + if (app->size != sizeof(PVPLeaderBoardDetailsRequest_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardDetailsRequest expected %i got %i", + sizeof(PVPLeaderBoardDetailsRequest_Struct), app->size); + + DumpPacket(app); + + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardDetailsReply, sizeof(PVPLeaderBoardDetailsReply_Struct)); + PVPLeaderBoardDetailsReply_Struct *pvplbdrs = (PVPLeaderBoardDetailsReply_Struct *)outapp->pBuffer; + + // TODO: Record and send this data. + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) +{ + // This Opcode is sent by the client when the Leaderboard button on the PVP Stats window is pressed. + // + // It has a single uint32 payload which is the sort method: + // + // PVPSortByKills = 0, PVPSortByPoints = 1, PVPSortByInfamy = 2 + // + if (app->size != sizeof(PVPLeaderBoardRequest_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardRequest expected %i got %i", + sizeof(PVPLeaderBoardRequest_Struct), app->size); + + DumpPacket(app); + + return; + } + /*PVPLeaderBoardRequest_Struct *pvplbrs = (PVPLeaderBoardRequest_Struct *)app->pBuffer;*/ //unused + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardReply, sizeof(PVPLeaderBoard_Struct)); + /*PVPLeaderBoard_Struct *pvplb = (PVPLeaderBoard_Struct *)outapp->pBuffer;*/ //unused + + // TODO: Record and send this data. + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RaidGeneral_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_RaidCommand, size=%i, expected %i", app->size, sizeof(RaidGeneral_Struct)); + DumpPacket(app); + return; + } + + RaidGeneral_Struct *ri = (RaidGeneral_Struct*)app->pBuffer; + switch (ri->action) + { + case RaidCommandInviteIntoExisting: + case RaidCommandInvite: { + Client *i = entity_list.GetClientByName(ri->player_name); + if (i){ + Group *g = i->GetGroup(); + if (g){ + if (g->IsLeader(i) == false) + Message(13, "You can only invite an ungrouped player or group leader to join your raid."); + else{ + //This sends an "invite" to the client in question. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(rg->leader_name, ri->leader_name, 64); + strn0cpy(rg->player_name, ri->player_name, 64); + + rg->parameter = 0; + rg->action = 20; + i->QueuePacket(outapp); + safe_delete(outapp); + } + } + else{ + //This sends an "invite" to the client in question. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(rg->leader_name, ri->leader_name, 64); + strn0cpy(rg->player_name, ri->player_name, 64); + + rg->parameter = 0; + rg->action = 20; + i->QueuePacket(outapp); + safe_delete(outapp); + } + } + break; + } + case RaidCommandAcceptInvite: { + Client *i = entity_list.GetClientByName(ri->player_name); + if (i){ + if (IsRaidGrouped()){ + i->Message_StringID(0, 5060); //group failed, must invite members not in raid... + return; + } + Raid *r = entity_list.GetRaidByClient(i); + if (r){ + r->VerifyRaid(); + Group *g = GetGroup(); + if (g){ + if (g->GroupCount() + r->RaidCount() > MAX_RAID_MEMBERS) + { + i->Message(13, "Invite failed, group invite would create a raid larger than the maximum number of members allowed."); + return; + } + } + else{ + if (1 + r->RaidCount() > MAX_RAID_MEMBERS) + { + i->Message(13, "Invite failed, member invite would create a raid larger than the maximum number of members allowed."); + return; + } + } + if (g){//add us all + uint32 freeGroup = r->GetFreeGroup(); + Client *addClient = nullptr; + for (int x = 0; x < 6; x++) + { + if (g->members[x]){ + Client *c = nullptr; + if (g->members[x]->IsClient()) + c = g->members[x]->CastToClient(); + else + continue; + + if (!addClient) + { + addClient = c; + r->SetGroupLeader(addClient->GetName()); + } + + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + if (g->IsLeader(g->members[x])) + r->AddMember(c, freeGroup, false, true); + else + r->AddMember(c, freeGroup); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + g->DisbandGroup(); + r->GroupUpdate(freeGroup); + } + else{ + r->SendRaidCreate(this); + r->SendMakeLeaderPacketTo(r->leadername, this); + r->AddMember(this); + r->SendBulkRaid(this); + if (r->IsLocked()) { + r->SendRaidLockTo(this); + } + } + } + else + { + Group *ig = i->GetGroup(); + Group *g = GetGroup(); + if (g) //if our target has a group + { + r = new Raid(i); + entity_list.AddRaid(r); + r->SetRaidDetails(); + + uint32 groupFree = r->GetFreeGroup(); //get a free group + if (ig){ //if we already have a group then cycle through adding us... + Client *addClientig = nullptr; + for (int x = 0; x < 6; x++) + { + if (ig->members[x]){ + if (!addClientig){ + if (ig->members[x]->IsClient()){ + addClientig = ig->members[x]->CastToClient(); + r->SetGroupLeader(addClientig->GetName()); + } + } + if (ig->IsLeader(ig->members[x])){ + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree, true, true, true); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + else{ + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + } + ig->DisbandGroup(); + r->GroupUpdate(groupFree); + groupFree = r->GetFreeGroup(); + } + else{ //else just add the inviter + r->SendRaidCreate(i); + r->AddMember(i, 0xFFFFFFFF, true, false, true); + } + + Client *addClient = nullptr; + //now add the existing group + for (int x = 0; x < 6; x++) + { + if (g->members[x]){ + if (!addClient) + { + if (g->members[x]->IsClient()){ + addClient = g->members[x]->CastToClient(); + r->SetGroupLeader(addClient->GetName()); + } + } + if (g->IsLeader(g->members[x])) + { + Client *c = nullptr; + if (g->members[x]->IsClient()) + c = g->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree, false, true); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + else + { + Client *c = nullptr; + if (g->members[x]->IsClient()) + c = g->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + } + g->DisbandGroup(); + r->GroupUpdate(groupFree); + } + else + { + if (ig){ + r = new Raid(i); + entity_list.AddRaid(r); + r->SetRaidDetails(); + Client *addClientig = nullptr; + for (int x = 0; x < 6; x++) + { + if (ig->members[x]) + { + if (!addClientig){ + if (ig->members[x]->IsClient()){ + addClientig = ig->members[x]->CastToClient(); + r->SetGroupLeader(addClientig->GetName()); + } + } + if (ig->IsLeader(ig->members[x])) + { + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, 0, true, true, true); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + else + { + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, 0); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + } + r->SendRaidCreate(this); + r->SendMakeLeaderPacketTo(r->leadername, this); + r->SendBulkRaid(this); + r->AddMember(this); + ig->DisbandGroup(); + r->GroupUpdate(0); + if (r->IsLocked()) { + r->SendRaidLockTo(this); + } + } + else{ + r = new Raid(i); + entity_list.AddRaid(r); + r->SetRaidDetails(); + r->SendRaidCreate(i); + r->SendRaidCreate(this); + r->SendMakeLeaderPacketTo(r->leadername, this); + r->AddMember(i, 0xFFFFFFFF, true, false, true); + r->SendBulkRaid(this); + r->AddMember(this); + if (r->IsLocked()) { + r->SendRaidLockTo(this); + } + } + } + } + } + break; + } + case RaidCommandDisband: { + Raid *r = entity_list.GetRaidByClient(this); + if (r){ + //if(this == r->GetLeader()){ + uint32 grp = r->GetGroup(ri->leader_name); + + if (grp < 12){ + uint32 i = r->GetPlayerIndex(ri->leader_name); + if (r->members[i].IsGroupLeader){ //assign group leader to someone else + for (int x = 0; x < MAX_RAID_MEMBERS; x++){ + if (strlen(r->members[x].membername) > 0 && i != x){ + if (r->members[x].GroupNumber == grp){ + r->SetGroupLeader(ri->leader_name, false); + r->SetGroupLeader(r->members[x].membername); + break; + } + } + } + + } + if (r->members[i].IsRaidLeader){ + for (int x = 0; x < MAX_RAID_MEMBERS; x++){ + if (strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, r->members[i].membername) != 0) + { + r->SetRaidLeader(r->members[i].membername, r->members[x].membername); + break; + } + } + } + } + + r->RemoveMember(ri->leader_name); + Client *c = entity_list.GetClientByName(ri->leader_name); + if (c) + r->SendGroupDisband(c); + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + strn0cpy(rga->playername, ri->leader_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); + } + //r->SendRaidGroupRemove(ri->leader_name, grp); + r->GroupUpdate(grp);// break + //} + } + break; + } + case RaidCommandMoveGroup: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (ri->parameter < 12) //moving to a group + { + uint8 grpcount = r->GroupCount(ri->parameter); + + if (grpcount < 6) + { + Client *c = entity_list.GetClientByName(ri->leader_name); + uint32 oldgrp = r->GetGroup(ri->leader_name); + if (ri->parameter == oldgrp) //don't rejoin grp if we order to join same group. + break; + + if (r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader) + { + r->SetGroupLeader(ri->leader_name, false); + if (oldgrp < 12){ //we were the leader of our old grp + for (int x = 0; x < MAX_RAID_MEMBERS; x++) //assign a new grp leader if we can + { + if (r->members[x].GroupNumber == oldgrp) + { + if (strcmp(ri->leader_name, r->members[x].membername) != 0 && strlen(ri->leader_name) > 0) + { + r->SetGroupLeader(r->members[x].membername); + Client *cgl = entity_list.GetClientByName(r->members[x].membername); + if (cgl){ + r->SendRaidRemove(r->members[x].membername, cgl); + r->SendRaidCreate(cgl); + r->SendMakeLeaderPacketTo(r->leadername, cgl); + r->SendRaidAdd(r->members[x].membername, cgl); + r->SendBulkRaid(cgl); + if (r->IsLocked()) { + r->SendRaidLockTo(cgl); + } + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + strn0cpy(rga->playername, r->members[x].membername, 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + break; + } + } + } + } + } + if (grpcount == 0) + r->SetGroupLeader(ri->leader_name); + + r->MoveMember(ri->leader_name, ri->parameter); + if (c){ + r->SendGroupDisband(c); + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + strn0cpy(rga->playername, ri->leader_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); + } + //r->SendRaidGroupAdd(ri->leader_name, ri->parameter); + //r->SendRaidGroupRemove(ri->leader_name, oldgrp); + //r->SendGroupUpdate(c); + //break + r->GroupUpdate(ri->parameter); //send group update to our new group + if (oldgrp < 12) //if our old was a group send update there too + r->GroupUpdate(oldgrp); + + //r->SendMakeGroupLeaderPacketAll(); + } + } + else //moving to ungrouped + { + Client *c = entity_list.GetClientByName(ri->leader_name); + uint32 oldgrp = r->GetGroup(ri->leader_name); + if (r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader){ + r->SetGroupLeader(ri->leader_name, false); + for (int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if (strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, ri->leader_name) != 0) + { + r->SetGroupLeader(r->members[x].membername); + Client *cgl = entity_list.GetClientByName(r->members[x].membername); + if (cgl){ + r->SendRaidRemove(r->members[x].membername, cgl); + r->SendRaidCreate(cgl); + r->SendMakeLeaderPacketTo(r->leadername, cgl); + r->SendRaidAdd(r->members[x].membername, cgl); + r->SendBulkRaid(cgl); + if (r->IsLocked()) { + r->SendRaidLockTo(cgl); + } + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + strn0cpy(rga->playername, r->members[x].membername, 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + break; + } + } + } + r->MoveMember(ri->leader_name, 0xFFFFFFFF); + if (c){ + r->SendGroupDisband(c); + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + strn0cpy(rga->playername, ri->leader_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); + } + //r->SendRaidGroupRemove(ri->leader_name, oldgrp); + r->GroupUpdate(oldgrp); + //r->SendMakeGroupLeaderPacketAll(); + } + } + break; + } + case RaidCommandRaidLock: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (!r->IsLocked()) + r->LockRaid(true); + else + r->SendRaidLockTo(this); + } + break; + } + case RaidCommandRaidUnlock: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (r->IsLocked()) + r->LockRaid(false); + else + r->SendRaidUnlockTo(this); + } + break; + } + case RaidCommandLootType2: + case RaidCommandLootType: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + Message(15, "Loot type changed to: %d.", ri->parameter); + r->ChangeLootType(ri->parameter); + } + break; + } + + case RaidCommandAddLooter2: + case RaidCommandAddLooter: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + Message(15, "Adding %s as a raid looter.", ri->leader_name); + r->AddRaidLooter(ri->leader_name); + } + break; + } + + case RaidCommandRemoveLooter2: + case RaidCommandRemoveLooter: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + Message(15, "Removing %s as a raid looter.", ri->leader_name); + r->RemoveRaidLooter(ri->leader_name); + } + break; + } + + case RaidCommandMakeLeader: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (strcmp(r->leadername, GetName()) == 0){ + r->SetRaidLeader(GetName(), ri->leader_name); + } + } + break; + } + + default: { + Message(13, "Raid command (%d) NYI", ri->action); + break; + } + } +} + +void Client::Handle_OP_RandomReq(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RandomReq_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_RandomReq, size=%i, expected %i", app->size, sizeof(RandomReq_Struct)); + return; + } + const RandomReq_Struct* rndq = (const RandomReq_Struct*)app->pBuffer; + uint32 randLow = rndq->low > rndq->high ? rndq->high : rndq->low; + uint32 randHigh = rndq->low > rndq->high ? rndq->low : rndq->high; + uint32 randResult; + + if (randLow == 0 && randHigh == 0) + { // defaults + randLow = 0; + randHigh = 100; + } + randResult = MakeRandomInt(randLow, randHigh); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RandomReply, sizeof(RandomReply_Struct)); + RandomReply_Struct* rr = (RandomReply_Struct*)outapp->pBuffer; + rr->low = randLow; + rr->high = randHigh; + rr->result = randResult; + strcpy(rr->name, GetName()); + entity_list.QueueCloseClients(this, outapp, false, 400); + safe_delete(outapp); + return; +} + +void Client::Handle_OP_ReadBook(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BookRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ReadBook, size=%i, expected %i", app->size, sizeof(BookRequest_Struct)); + return; + } + BookRequest_Struct* book = (BookRequest_Struct*)app->pBuffer; + ReadBook(book); + if (GetClientVersion() >= EQClientSoF) + { + EQApplicationPacket EndOfBook(OP_FinishWindow, 0); + QueuePacket(&EndOfBook); + } + return; +} + +void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RecipeAutoCombine_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for RecipeAutoCombine_Struct: Expected: %i, Got: %i", + sizeof(RecipeAutoCombine_Struct), app->size); + return; + } + + RecipeAutoCombine_Struct* rac = (RecipeAutoCombine_Struct*)app->pBuffer; + + Object::HandleAutoCombine(this, rac); + return; +} + +void Client::Handle_OP_RecipeDetails(const EQApplicationPacket *app) +{ + if (app->size < sizeof(uint32)) { + LogFile->write(EQEMuLog::Error, "Invalid size for RecipeDetails Request: Expected: %i, Got: %i", + sizeof(uint32), app->size); + return; + } + uint32 *recipe_id = (uint32*)app->pBuffer; + + SendTradeskillDetails(*recipe_id); + + return; +} + +void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeskillFavorites_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for TradeskillFavorites_Struct: Expected: %i, Got: %i", + sizeof(TradeskillFavorites_Struct), app->size); + return; + } + + TradeskillFavorites_Struct* tsf = (TradeskillFavorites_Struct*)app->pBuffer; + + LogFile->write(EQEMuLog::Debug, "Requested Favorites for: %d - %d\n", tsf->object_type, tsf->some_id); + + // results show that object_type is combiner type + // some_id = 0 if world combiner, item number otherwise + + // make where clause segment for container(s) + char containers[30]; + if (tsf->some_id == 0) { + // world combiner so no item number + snprintf(containers, 29, "= %u", tsf->object_type); + } + else { + // container in inventory + snprintf(containers, 29, "in (%u,%u)", tsf->object_type, tsf->some_id); + } + + char *query = 0; + char buf[5500]; //gotta be big enough for 500 IDs + + bool first = true; + uint16 r; + char *pos = buf; + + //Assumes item IDs are <10 characters long + for (r = 0; r < 500; r++) { + if (tsf->favorite_recipes[r] == 0) + continue; + + if (first) { + pos += snprintf(pos, 10, "%u", tsf->favorite_recipes[r]); + first = false; + } + else { + pos += snprintf(pos, 10, ",%u", tsf->favorite_recipes[r]); + } + } + + if (first) //no favorites.... + return; + + //To be a good kid, I should move this SQL somewhere else... + //but im lazy right now, so it stays here + uint32 qlen = 0; + qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " + " FROM tradeskill_recipe AS tr " + " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " + " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " + " WHERE tr.enabled <> 0 AND tr.id IN (%s) " + " AND tr.must_learn & 0x20 <> 0x20 AND ((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " + " GROUP BY tr.id " + " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " + " LIMIT 100 ", CharacterID(), buf, containers); + + TradeskillSearchResults(query, qlen, tsf->object_type, tsf->some_id); + + safe_delete_array(query); + return; +} + +void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RecipesSearch_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for RecipesSearch_Struct: Expected: %i, Got: %i", + sizeof(RecipesSearch_Struct), app->size); + return; + } + + RecipesSearch_Struct* rss = (RecipesSearch_Struct*)app->pBuffer; + rss->query[55] = '\0'; //just to be sure. + + + LogFile->write(EQEMuLog::Debug, "Requested search recipes for: %d - %d\n", rss->object_type, rss->some_id); + + // make where clause segment for container(s) + char containers[30]; + if (rss->some_id == 0) { + // world combiner so no item number + snprintf(containers, 29, "= %u", rss->object_type); + } + else { + // container in inventory + snprintf(containers, 29, "in (%u,%u)", rss->object_type, rss->some_id); + } + + char *query = 0; + char searchclause[140]; //2X rss->query + SQL crap + + //omit the rlike clause if query is empty + if (rss->query[0] != 0) { + char buf[120]; //larger than 2X rss->query + database.DoEscapeString(buf, rss->query, strlen(rss->query)); + + snprintf(searchclause, 139, "name rlike '%s' AND", buf); + } + else { + searchclause[0] = '\0'; + } + uint32 qlen = 0; + + //arbitrary limit of 200 recipes, makes sense to me. + qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " + " FROM tradeskill_recipe AS tr " + " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " + " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " + " WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 " + " AND tr.must_learn & 0x20 <> 0x20 AND((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " + " GROUP BY tr.id " + " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " + " LIMIT 200 " + , CharacterID(), searchclause, rss->mintrivial, rss->maxtrivial, containers); + + TradeskillSearchResults(query, qlen, rss->object_type, rss->some_id); + + safe_delete_array(query); + return; +} + +void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app) +{ + if (IsInAGuild()) + { + SendGuildRanks(); + SendGuildMembers(); + } + return; +} + +void Client::Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app) +{ + if (!RuleB(Spells, EnableBlockedBuffs)) + return; + + if (app->size != sizeof(BlockedBuffs_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RemoveBlockedBuffs expected %i got %i", + sizeof(BlockedBuffs_Struct), app->size); + + DumpPacket(app); + + return; + } + BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; + + std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; + + std::set RemovedBuffs; + + if (bbs->Count > 0) + { + std::set::iterator Iterator; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RemoveBlockedBuffs, sizeof(BlockedBuffs_Struct)); + + BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + obbs->SpellID[i] = 0; + + obbs->Pet = bbs->Pet; + obbs->Initialise = 0; + obbs->Flags = 0x5a; + + for (unsigned int i = 0; i < bbs->Count; ++i) + { + Iterator = BlockedBuffs->find(bbs->SpellID[i]); + + if (Iterator != BlockedBuffs->end()) + { + RemovedBuffs.insert(bbs->SpellID[i]); + + BlockedBuffs->erase(Iterator); + } + } + obbs->Count = RemovedBuffs.size(); + + Iterator = RemovedBuffs.begin(); + + unsigned int Element = 0; + + while (Iterator != RemovedBuffs.end()) + { + obbs->SpellID[Element++] = (*Iterator); + ++Iterator; + } + + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_Report(const EQApplicationPacket *app) +{ + if (!CanUseReport) + { + Message_StringID(MT_System, REPORT_ONCE); + return; + } + + uint32 size = app->size; + uint32 current_point = 0; + std::string reported, reporter; + std::string current_string; + int mode = 0; + + while (current_point < size) + { + if (mode < 2) + { + if (app->pBuffer[current_point] == '|') + { + mode++; + } + else + { + if (mode == 0) + { + reported += app->pBuffer[current_point]; + } + else + { + reporter += app->pBuffer[current_point]; + } + } + current_point++; + } + else + { + if (app->pBuffer[current_point] == 0x0a) + { + current_string += '\n'; + } + else if (app->pBuffer[current_point] == 0x00) + { + CanUseReport = false; + database.AddReport(reporter, reported, current_string); + return; + } + else + { + current_string += app->pBuffer[current_point]; + } + current_point++; + } + } + + CanUseReport = false; + database.AddReport(reporter, reported, current_string); +} + +void Client::Handle_OP_RequestDuel(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Duel_Struct)) + return; + + EQApplicationPacket* outapp = app->Copy(); + Duel_Struct* ds = (Duel_Struct*)outapp->pBuffer; + uint32 duel = ds->duel_initiator; + ds->duel_initiator = ds->duel_target; + ds->duel_target = duel; + Entity* entity = entity_list.GetID(ds->duel_target); + if (GetID() != ds->duel_target && entity->IsClient() && (entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() != 0)) { + Message_StringID(10, DUEL_CONSIDERING, entity->GetName()); + return; + } + if (IsDueling()) { + Message_StringID(10, DUEL_INPROGRESS); + return; + } + + if (GetID() != ds->duel_target && entity->IsClient() && GetDuelTarget() == 0 && !IsDueling() && !entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() == 0) { + SetDuelTarget(ds->duel_target); + entity->CastToClient()->SetDuelTarget(GetID()); + ds->duel_target = ds->duel_initiator; + entity->CastToClient()->FastQueuePacket(&outapp); + entity->CastToClient()->SetDueling(false); + SetDueling(false); + } + else + safe_delete(outapp); + return; +} + +void Client::Handle_OP_RequestTitles(const EQApplicationPacket *app) +{ + + EQApplicationPacket *outapp = title_manager.MakeTitlesPacket(this); + + if (outapp != nullptr) + FastQueuePacket(&outapp); +} + +void Client::Handle_OP_RespawnWindow(const EQApplicationPacket *app) +{ + // This opcode is sent by the client when the player choses which bind to return to. + // The client sends just a 4 byte packet with the selection number in it + // + if (app->size != 4) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RespawnWindow expected %i got %i", + 4, app->size); + DumpPacket(app); + return; + } + char *Buffer = (char *)app->pBuffer; + + uint32 Option = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + HandleRespawnFromHover(Option); +} + +void Client::Handle_OP_Rewind(const EQApplicationPacket *app) +{ + if ((rewind_timer.GetRemainingTime() > 1 && rewind_timer.Enabled())) { + Message_StringID(MT_System, REWIND_WAIT); + } + else { + CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), rewind_x, rewind_y, rewind_z, 0, 2, Rewind); + rewind_timer.Start(30000, true); + } +} + +void Client::Handle_OP_RezzAnswer(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_RezzAnswer, app, Resurrect_Struct); + + const Resurrect_Struct* ra = (const Resurrect_Struct*)app->pBuffer; + + _log(SPELLS__REZ, "Received OP_RezzAnswer from client. Pendingrezzexp is %i, action is %s", + PendingRezzXP, ra->action ? "ACCEPT" : "DECLINE"); + + _pkt(SPELLS__REZ, app); + + OPRezzAnswer(ra->action, ra->spellid, ra->zone_id, ra->instance_id, ra->x, ra->y, ra->z); + + if (ra->action == 1) + { + EQApplicationPacket* outapp = app->Copy(); + // Send the OP_RezzComplete to the world server. This finds it's way to the zone that + // the rezzed corpse is in to mark the corpse as rezzed. + outapp->SetOpcode(OP_RezzComplete); + worldserver.RezzPlayer(outapp, 0, 0, OP_RezzComplete); + safe_delete(outapp); + } + return; +} + +void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(Sacrifice_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Sacrifice expected %i got %i", sizeof(Sacrifice_Struct), app->size); + DumpPacket(app); + return; + } + Sacrifice_Struct *ss = (Sacrifice_Struct*)app->pBuffer; + + if (!PendingSacrifice) { + LogFile->write(EQEMuLog::Error, "Unexpected OP_Sacrifice reply"); + DumpPacket(app); + return; + } + + if (ss->Confirm) { + Client *Caster = entity_list.GetClientByName(SacrificeCaster.c_str()); + if (Caster) Sacrifice(Caster); + } + PendingSacrifice = false; + SacrificeCaster.clear(); +} + +void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail) +{ + if (HasSkill(SkillSafeFall)) //this should only get called if the client has safe fall, but just in case... + CheckIncreaseSkill(SkillSafeFall, nullptr); //check for skill up +} + +void Client::Handle_OP_SafePoint(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_Save(const EQApplicationPacket *app) +{ + // The payload is 192 bytes - Not sure what is contained in payload + Save(); + return; +} + +void Client::Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app) +{ + Handle_OP_Save(app); +} + +void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_SelectTribute of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //we should enforce being near a real tribute master to change this + //but im not sure how I wanna do that right now. + if (app->size != sizeof(SelectTributeReq_Struct)) + LogFile->write(EQEMuLog::Error, "Invalid size on OP_SelectTribute packet"); + else { + SelectTributeReq_Struct *t = (SelectTributeReq_Struct *)app->pBuffer; + SendTributeDetails(t->client_id, t->tribute_id); + } + return; +} + +void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillSenseTraps)) + return; + + if (!p_timers.Expired(&database, pTimerSenseTraps, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + int reuse = SenseTrapsReuseTime; + switch (GetAA(aaAdvTrapNegotiation)) { + case 1: + reuse -= 1; + break; + case 2: + reuse -= 3; + break; + case 3: + reuse -= 5; + break; + } + p_timers.Start(pTimerSenseTraps, reuse - 1); + + Trap* trap = entity_list.FindNearbyTrap(this, 800); + + CheckIncreaseSkill(SkillSenseTraps, nullptr); + + if (trap && trap->skill > 0) { + int uskill = GetSkill(SkillSenseTraps); + if ((MakeRandomInt(0, 99) + uskill) >= (MakeRandomInt(0, 99) + trap->skill*0.75)) + { + float xdif = trap->x - GetX(); + float ydif = trap->y - GetY(); + if (xdif == 0 && ydif == 0) + Message(MT_Skills, "You sense a trap right under your feet!"); + else if (xdif > 10 && ydif > 10) + Message(MT_Skills, "You sense a trap to the NorthWest."); + else if (xdif < -10 && ydif > 10) + Message(MT_Skills, "You sense a trap to the NorthEast."); + else if (ydif > 10) + Message(MT_Skills, "You sense a trap to the North."); + else if (xdif > 10 && ydif < -10) + Message(MT_Skills, "You sense a trap to the SouthWest."); + else if (xdif < -10 && ydif < -10) + Message(MT_Skills, "You sense a trap to the SouthEast."); + else if (ydif < -10) + Message(MT_Skills, "You sense a trap to the South."); + else if (xdif > 10) + Message(MT_Skills, "You sense a trap to the West."); + else + Message(MT_Skills, "You sense a trap to the East."); + trap->detected = true; + + float angle = CalculateHeadingToTarget(trap->x, trap->y); + + if (angle < 0) + angle = (256 + angle); + + angle *= 2; + MovePC(zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ(), angle); + return; + } + } + Message(MT_Skills, "You did not find any traps nearby."); + return; +} + +void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_SetGuildMOTD"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildMOTD_Struct)) { + // client calls for a motd on login even if they arent in a guild + printf("Error: app size of %i != size of GuildMOTD_Struct of %zu\n", app->size, sizeof(GuildMOTD_Struct)); + return; + } + if (!IsInAGuild()) { + Message(13, "You are not in a guild!"); + return; + } + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_MOTD)) { + Message(13, "You do not have permissions to edit your guild's MOTD."); + return; + } + + GuildMOTD_Struct* gmotd = (GuildMOTD_Struct*)app->pBuffer; + + mlog(GUILDS__ACTIONS, "Setting MOTD for %s (%d) to: %s - %s", + guild_mgr.GetGuildName(GuildID()), GuildID(), GetName(), gmotd->motd); + + if (!guild_mgr.SetGuildMOTD(GuildID(), gmotd->motd, GetName())) { + Message(0, "Motd update failed."); + } + + return; +} + +void Client::Handle_OP_SetRunMode(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_SetServerFilter(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SetServerFilter_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized " + "OP_SetServerFilter: got %d, expected %d", app->size, + sizeof(SetServerFilter_Struct)); + DumpPacket(app); + return; + } + SetServerFilter_Struct* filter = (SetServerFilter_Struct*)app->pBuffer; + ServerFilter(filter); + return; +} + +void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) +{ + // if the character has a start city, don't let them use the command + if (m_pp.binds[4].zoneId != 0) { + Message(15, "Your home city has already been set.", m_pp.binds[4].zoneId, database.GetZoneName(m_pp.binds[4].zoneId)); + return; + } + + if (app->size < 1) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_SetStartCity, size=%i, expected %i", app->size, 1); + DumpPacket(app); + return; + } + + float x(0), y(0), z(0); + uint32 zoneid = 0; + uint32 startCity = (uint32)strtol((const char*)app->pBuffer, nullptr, 10); + + std::string query = StringFormat("SELECT zone_id, bind_id, x, y, z FROM start_zones " + "WHERE player_class=%i AND player_deity=%i AND player_race=%i", + m_pp.class_, m_pp.deity, m_pp.race); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "No valid start zones found for /setstartcity"); + return; + } + + bool validCity = false; + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[1]) != 0) + zoneid = atoi(row[1]); + else + zoneid = atoi(row[0]); + + if (zoneid != startCity) + continue; + + validCity = true; + x = atof(row[2]); + y = atof(row[3]); + z = atof(row[4]); + } + + if (validCity) { + Message(15, "Your home city has been set"); + SetStartZone(startCity, x, y, z); + return; + } + + query = StringFormat("SELECT zone_id, bind_id FROM start_zones " + "WHERE player_class=%i AND player_deity=%i AND player_race=%i", + m_pp.class_, m_pp.deity, m_pp.race); + results = database.QueryDatabase(query); + if (!results.Success()) + return; + + Message(15, "Use \"/startcity #\" to choose a home city from the following list:"); + + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[1]) != 0) + zoneid = atoi(row[1]); + else + zoneid = atoi(row[0]); + + char* name; + database.GetZoneLongName(database.GetZoneName(zoneid), &name); + Message(15, "%d - %s", zoneid, name); + } + +} + +void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SetTitle_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_SetTitle expected %i got %i", sizeof(SetTitle_Struct), app->size); + DumpPacket(app); + return; + } + + SetTitle_Struct *sts = (SetTitle_Struct *)app->pBuffer; + + std::string Title; + + if (!sts->is_suffix) + { + Title = title_manager.GetPrefix(sts->title_id); + SetAATitle(Title.c_str()); + } + else + { + Title = title_manager.GetSuffix(sts->title_id); + SetTitleSuffix(Title.c_str()); + } +} + +void Client::Handle_OP_Shielding(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Shielding_Struct)) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_Shielding expected:%i got:%i", sizeof(Shielding_Struct), app->size); + return; + } + if (GetClass() != WARRIOR) + { + return; + } + + if (shield_target) + { + entity_list.MessageClose_StringID(this, false, 100, 0, + END_SHIELDING, GetName(), shield_target->GetName()); + for (int y = 0; y < 2; y++) + { + if (shield_target->shielder[y].shielder_id == GetID()) + { + shield_target->shielder[y].shielder_id = 0; + shield_target->shielder[y].shielder_bonus = 0; + } + } + } + Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer; + shield_target = entity_list.GetMob(shield->target_id); + bool ack = false; + ItemInst* inst = GetInv().GetItem(MainSecondary); + if (!shield_target) + return; + if (inst) + { + const Item_Struct* shield = inst->GetItem(); + if (shield && shield->ItemType == ItemTypeShield) + { + for (int x = 0; x < 2; x++) + { + if (shield_target->shielder[x].shielder_id == 0) + { + entity_list.MessageClose_StringID(this, false, 100, 0, + START_SHIELDING, GetName(), shield_target->GetName()); + shield_target->shielder[x].shielder_id = GetID(); + int shieldbonus = shield->AC * 2; + switch (GetAA(197)) + { + case 1: + shieldbonus = shieldbonus * 115 / 100; + break; + case 2: + shieldbonus = shieldbonus * 125 / 100; + break; + case 3: + shieldbonus = shieldbonus * 150 / 100; + break; + } + shield_target->shielder[x].shielder_bonus = shieldbonus; + shield_timer.Start(); + ack = true; + break; + } + } + } + else + { + Message(0, "You must have a shield equipped to shield a target!"); + shield_target = 0; + return; + } + } + else + { + Message(0, "You must have a shield equipped to shield a target!"); + shield_target = 0; + return; + } + if (!ack) + { + Message_StringID(0, ALREADY_SHIELDED); + shield_target = 0; + return; + } + return; +} + +void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app) +{ + EQApplicationPacket empty(OP_ShopEndConfirm); + QueuePacket(&empty); + //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopEndConfirm, 2); + //outapp->pBuffer[0] = 0x0a; + //outapp->pBuffer[1] = 0x66; + //QueuePacket(outapp); + //safe_delete(outapp); + //Save(); + return; +} + +void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Merchant_Sell_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerBuy: Expected %i, Got %i", + sizeof(Merchant_Sell_Struct), app->size); + return; + } + RDTSC_Timer t1; + t1.start(); + Merchant_Sell_Struct* mp = (Merchant_Sell_Struct*)app->pBuffer; +#if EQDEBUG >= 5 + LogFile->write(EQEMuLog::Debug, "%s, purchase item..", GetName()); + DumpPacket(app); +#endif + + int merchantid; + bool tmpmer_used = false; + Mob* tmp = entity_list.GetMob(mp->npcid); + + if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) + return; + + if (mp->quantity < 1) return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + + uint32 item_id = 0; + std::list merlist = zone->merchanttable[merchantid]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr){ + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tmp->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + if (mp->itemslot == ml.slot){ + item_id = ml.item; + break; + } + } + const Item_Struct* item = nullptr; + uint32 prevcharges = 0; + if (item_id == 0) { //check to see if its on the temporary table + std::list tmp_merlist = zone->tmpmerchanttable[tmp->GetNPCTypeID()]; + std::list::const_iterator tmp_itr; + TempMerchantList ml; + for (tmp_itr = tmp_merlist.begin(); tmp_itr != tmp_merlist.end(); ++tmp_itr){ + ml = *tmp_itr; + if (mp->itemslot == ml.slot){ + item_id = ml.item; + tmpmer_used = true; + prevcharges = ml.charges; + break; + } + } + } + item = database.GetItem(item_id); + if (!item){ + //error finding item, client didnt get the update packet for whatever reason, roleplay a tad + Message(15, "%s tells you 'Sorry, that item is for display purposes only.' as they take the item off the shelf.", tmp->GetCleanName()); + EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); + Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; + delitem->itemslot = mp->itemslot; + delitem->npcid = mp->npcid; + delitem->playerid = mp->playerid; + delitempacket->priority = 6; + entity_list.QueueCloseClients(tmp, delitempacket); //que for anyone that could be using the merchant so they see the update + safe_delete(delitempacket); + return; + } + if (CheckLoreConflict(item)) + { + Message(15, "You can only have one of a lore item."); + return; + } + if (tmpmer_used && (mp->quantity > prevcharges || item->MaxCharges > 1)) + { + if (prevcharges > item->MaxCharges && item->MaxCharges > 1) + mp->quantity = item->MaxCharges; + else + mp->quantity = prevcharges; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct)); + Merchant_Sell_Struct* mpo = (Merchant_Sell_Struct*)outapp->pBuffer; + mpo->quantity = mp->quantity; + mpo->playerid = mp->playerid; + mpo->npcid = mp->npcid; + mpo->itemslot = mp->itemslot; + + int16 freeslotid = 0; + int16 charges = 0; + if (item->Stackable || item->MaxCharges > 1) + charges = mp->quantity; + else + charges = item->MaxCharges; + + ItemInst* inst = database.CreateItem(item, charges); + + int SinglePrice = 0; + if (RuleB(Merchant, UsePriceMod)) + SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false)); + else + SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate); + + if (item->MaxCharges > 1) + mpo->price = SinglePrice; + else + mpo->price = SinglePrice * mp->quantity; + if (mpo->price < 0) + { + safe_delete(outapp); + safe_delete(inst); + return; + } + + if (!TakeMoneyFromPP(mpo->price)) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "Vendor Cheat: attempted to buy %i of %i: %s that cost %d cp but only has %d pp %d gp %d sp %d cp\n", + mpo->quantity, item->ID, item->Name, + mpo->price, m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + safe_delete(outapp); + safe_delete(inst); + return; + } + + bool stacked = TryStacking(inst); + if (!stacked) + freeslotid = m_inv.FindFreeSlot(false, true, item->Size); + + //make sure we are not completely full... + if (freeslotid == MainCursor) { + if (m_inv.GetItem(MainCursor) != nullptr) { + Message(13, "You do not have room for any more items."); + safe_delete(outapp); + safe_delete(inst); + return; + } + } + + if (freeslotid == INVALID_INDEX) + { + Message(13, "You do not have room for any more items."); + safe_delete(outapp); + safe_delete(inst); + return; + } + + std::string packet; + if (!stacked && inst) { + PutItemInInventory(freeslotid, *inst); + SendItemPacket(freeslotid, inst, ItemPacketTrade); + } + else if (!stacked){ + LogFile->write(EQEMuLog::Error, "OP_ShopPlayerBuy: item->ItemClass Unknown! Type: %i", item->ItemClass); + } + QueuePacket(outapp); + if (inst && tmpmer_used){ + int32 new_charges = prevcharges - mp->quantity; + zone->SaveTempItem(merchantid, tmp->GetNPCTypeID(), item_id, new_charges); + if (new_charges <= 0){ + EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); + Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; + delitem->itemslot = mp->itemslot; + delitem->npcid = mp->npcid; + delitem->playerid = mp->playerid; + delitempacket->priority = 6; + entity_list.QueueClients(tmp, delitempacket); //que for anyone that could be using the merchant so they see the update + safe_delete(delitempacket); + } + else { + // Update the charges/quantity in the merchant window + inst->SetCharges(new_charges); + inst->SetPrice(SinglePrice); + inst->SetMerchantSlot(mp->itemslot); + inst->SetMerchantCount(new_charges); + + SendItemPacket(mp->itemslot, inst, ItemPacketMerchant); + } + } + safe_delete(inst); + safe_delete(outapp); + + // start QS code + if (RuleB(QueryServ, PlayerLogMerchantTransactions)) { + ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct)+sizeof(QSTransactionItems_Struct)); + QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; + + qsaudit->zone_id = zone->GetZoneID(); + qsaudit->merchant_id = tmp->CastToNPC()->MerchantType; + qsaudit->merchant_money.platinum = 0; + qsaudit->merchant_money.gold = 0; + qsaudit->merchant_money.silver = 0; + qsaudit->merchant_money.copper = 0; + qsaudit->merchant_count = 1; + qsaudit->char_id = character_id; + qsaudit->char_money.platinum = (mpo->price / 1000); + qsaudit->char_money.gold = (mpo->price / 100) % 10; + qsaudit->char_money.silver = (mpo->price / 10) % 10; + qsaudit->char_money.copper = mpo->price % 10; + qsaudit->char_count = 0; + + qsaudit->items[0].char_slot = freeslotid; + qsaudit->items[0].item_id = m_inv[freeslotid]->GetID(); + qsaudit->items[0].charges = mpo->quantity; + qsaudit->items[0].aug_1 = m_inv[freeslotid]->GetAugmentItemID(1); + qsaudit->items[0].aug_2 = m_inv[freeslotid]->GetAugmentItemID(2); + qsaudit->items[0].aug_3 = m_inv[freeslotid]->GetAugmentItemID(3); + qsaudit->items[0].aug_4 = m_inv[freeslotid]->GetAugmentItemID(4); + qsaudit->items[0].aug_5 = m_inv[freeslotid]->GetAugmentItemID(5); + + qspack->Deflate(); + if (worldserver.Connected()) { worldserver.SendPacket(qspack); } + safe_delete(qspack); + } + // end QS code + + if (RuleB(EventLog, RecordBuyFromMerchant)) + LogMerchant(this, tmp, mpo->quantity, mpo->price, item, true); + + if ((RuleB(Character, EnableDiscoveredItems))) + { + if (!GetGM() && !IsDiscovered(item_id)) + DiscoverItem(item_id); + } + + t1.stop(); + std::cout << "At 1: " << t1.getDuration() << std::endl; + return; +} + +void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Merchant_Purchase_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerSell: Expected %i, Got %i", + sizeof(Merchant_Purchase_Struct), app->size); + return; + } + RDTSC_Timer t1(true); + Merchant_Purchase_Struct* mp = (Merchant_Purchase_Struct*)app->pBuffer; + + Mob* vendor = entity_list.GetMob(mp->npcid); + + if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != MERCHANT) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*vendor) > USE_NPC_RANGE2) + return; + + uint32 price = 0; + uint32 itemid = GetItemIDAt(mp->itemslot); + if (itemid == 0) + return; + const Item_Struct* item = database.GetItem(itemid); + ItemInst* inst = GetInv().GetItem(mp->itemslot); + if (!item || !inst){ + Message(13, "You seemed to have misplaced that item.."); + return; + } + if (mp->quantity > 1) + { + if ((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges())) + return; + } + + if (!item->NoDrop) { + //Message(13,"%s tells you, 'LOL NOPE'", vendor->GetName()); + return; + } + + int cost_quantity = mp->quantity; + if (inst->IsCharged()) + int cost_quantity = 1; + + if (RuleB(Merchant, UsePriceMod)) + price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price + else + price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod)) + 0.5); + AddMoneyToPP(price, false); + + if (inst->IsStackable() || inst->IsCharged()) + { + unsigned int i_quan = inst->GetCharges(); + if (mp->quantity > i_quan || inst->IsCharged()) + mp->quantity = i_quan; + } + else + mp->quantity = 1; + + if (RuleB(EventLog, RecordSellToMerchant)) + LogMerchant(this, vendor, mp->quantity, price, item, false); + + int charges = mp->quantity; + //Hack workaround so usable items with 0 charges aren't simply deleted + if (charges == 0 && item->ItemType != 11 && item->ItemType != 17 && item->ItemType != 19 && item->ItemType != 21) + charges = 1; + + int freeslot = 0; + if (charges > 0 && (freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(), itemid, charges, true)) > 0){ + ItemInst* inst2 = inst->Clone(); + if (RuleB(Merchant, UsePriceMod)){ + inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor, false)); + } + else + inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate); + inst2->SetMerchantSlot(freeslot); + + uint32 MerchantQuantity = zone->GetTempMerchantQuantity(vendor->GetNPCTypeID(), freeslot); + + if (inst2->IsStackable()) { + inst2->SetCharges(MerchantQuantity); + } + inst2->SetMerchantCount(MerchantQuantity); + + SendItemPacket(freeslot - 1, inst2, ItemPacketMerchant); + safe_delete(inst2); + } + + // start QS code + if (RuleB(QueryServ, PlayerLogMerchantTransactions)) { + ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct)+sizeof(QSTransactionItems_Struct)); + QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; + + qsaudit->zone_id = zone->GetZoneID(); + qsaudit->merchant_id = vendor->CastToNPC()->MerchantType; + qsaudit->merchant_money.platinum = (price / 1000); + qsaudit->merchant_money.gold = (price / 100) % 10; + qsaudit->merchant_money.silver = (price / 10) % 10; + qsaudit->merchant_money.copper = price % 10; + qsaudit->merchant_count = 0; + qsaudit->char_id = character_id; + qsaudit->char_money.platinum = 0; + qsaudit->char_money.gold = 0; + qsaudit->char_money.silver = 0; + qsaudit->char_money.copper = 0; + qsaudit->char_count = 1; + + qsaudit->items[0].char_slot = mp->itemslot; + qsaudit->items[0].item_id = itemid; + qsaudit->items[0].charges = charges; + qsaudit->items[0].aug_1 = m_inv[mp->itemslot]->GetAugmentItemID(1); + qsaudit->items[0].aug_2 = m_inv[mp->itemslot]->GetAugmentItemID(2); + qsaudit->items[0].aug_3 = m_inv[mp->itemslot]->GetAugmentItemID(3); + qsaudit->items[0].aug_4 = m_inv[mp->itemslot]->GetAugmentItemID(4); + qsaudit->items[0].aug_5 = m_inv[mp->itemslot]->GetAugmentItemID(5); + + qspack->Deflate(); + if (worldserver.Connected()) { worldserver.SendPacket(qspack); } + safe_delete(qspack); + } + // end QS code + + // Now remove the item from the player, this happens regardless of outcome + if (!inst->IsStackable()) + this->DeleteItemInInventory(mp->itemslot, 0, false); + else + this->DeleteItemInInventory(mp->itemslot, mp->quantity, false); + + //This forces the price to show up correctly for charged items. + if (inst->IsCharged()) + mp->quantity = 1; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct)); + Merchant_Purchase_Struct* mco = (Merchant_Purchase_Struct*)outapp->pBuffer; + mco->npcid = vendor->GetID(); + mco->itemslot = mp->itemslot; + mco->quantity = mp->quantity; + mco->price = price; + QueuePacket(outapp); + safe_delete(outapp); + SendMoneyUpdate(); + t1.start(); + Save(1); + t1.stop(); + std::cout << "Save took: " << t1.getDuration() << std::endl; + return; +} + +void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Merchant_Click_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ShopRequest, size=%i, expected %i", app->size, sizeof(Merchant_Click_Struct)); + return; + } + + Merchant_Click_Struct* mc = (Merchant_Click_Struct*)app->pBuffer; + + // Send back opcode OP_ShopRequest - tells client to open merchant window. + //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); + //Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; + int merchantid = 0; + Mob* tmp = entity_list.GetMob(mc->npcid); + + if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + + int action = 1; + if (merchantid == 0) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); + Merchant_Click_Struct* mco = (Merchant_Click_Struct*)outapp->pBuffer; + mco->npcid = mc->npcid; + mco->playerid = 0; + mco->command = 1; //open... + mco->rate = 1.0; + QueuePacket(outapp); + safe_delete(outapp); + return; + } + if (tmp->IsEngaged()){ + this->Message_StringID(0, MERCHANT_BUSY); + action = 0; + } + if (GetFeigned() || IsInvisible()) + { + Message(0, "You cannot use a merchant right now."); + action = 0; + } + int primaryfaction = tmp->CastToNPC()->GetPrimaryFaction(); + int factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), primaryfaction, tmp); + if (factionlvl >= 7) { + MerchantRejectMessage(tmp, primaryfaction); + action = 0; + } + + if (tmp->Charmed()) + action = 0; + + // 1199 I don't have time for that now. etc + if (!tmp->CastToNPC()->IsMerchantOpen()) { + tmp->Say_StringID(MakeRandomInt(1199, 1202)); + action = 0; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); + Merchant_Click_Struct* mco = (Merchant_Click_Struct*)outapp->pBuffer; + + mco->npcid = mc->npcid; + mco->playerid = 0; + mco->command = action; // Merchant command 0x01 = open + if (RuleB(Merchant, UsePriceMod)){ + mco->rate = 1 / ((RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(tmp, true)); // works + } + else + mco->rate = 1 / (RuleR(Merchant, BuyCostMod)); + + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + if (action == 1) + BulkSendMerchantInventory(merchantid, tmp->GetNPCTypeID()); + + return; +} + +void Client::Handle_OP_Sneak(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillSneak) && GetSkill(SkillSneak) == 0) { + return; //You cannot sneak if you do not have sneak + } + + if (!p_timers.Expired(&database, pTimerSneak, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerSneak, SneakReuseTime - 1); + + bool was = sneaking; + if (sneaking){ + sneaking = false; + hidden = false; + improved_hidden = false; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x03; + sa_out->parameter = 0; + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + } + else { + CheckIncreaseSkill(SkillSneak, nullptr, 5); + } + float hidechance = ((GetSkill(SkillSneak) / 300.0f) + .25) * 100; + float random = MakeRandomFloat(0, 99); + if (!was && random < hidechance) { + sneaking = true; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x0F; + sa_out->parameter = sneaking; + QueuePacket(outapp); + safe_delete(outapp); + if (GetClass() == ROGUE){ + outapp = new EQApplicationPacket(OP_SimpleMessage, 12); + SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; + msg->color = 0x010E; + if (sneaking){ + msg->string_id = 347; + } + else { + msg->string_id = 348; + } + FastQueuePacket(&outapp); + } + return; +} + +void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SpawnAppearance_Struct)) { + std::cout << "Wrong size on OP_SpawnAppearance. Got: " << app->size << ", Expected: " << sizeof(SpawnAppearance_Struct) << std::endl; + return; + } + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; + + if (sa->spawn_id != GetID()) + return; + + if (sa->type == AT_Invis) { + if (sa->parameter != 0) + { + if (!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) + { + if (GetClientVersion() < EQClientSoF) + { + char *hack_str = nullptr; + MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Invis: %i", sa->parameter); + database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); + safe_delete_array(hack_str); + } + } + return; + } + invisible = false; + hidden = false; + improved_hidden = false; + entity_list.QueueClients(this, app, true); + return; + } + else if (sa->type == AT_Anim) { + if (IsAIControlled()) + return; + if (sa->parameter == ANIM_STAND) { + SetAppearance(eaStanding); + playeraction = 0; + SetFeigned(false); + BindWound(this, false, true); + camp_timer.Disable(); + } + else if (sa->parameter == ANIM_SIT) { + SetAppearance(eaSitting); + playeraction = 1; + if (!UseBardSpellLogic()) + InterruptSpell(); + SetFeigned(false); + BindWound(this, false, true); + } + else if (sa->parameter == ANIM_CROUCH) { + if (!UseBardSpellLogic()) + InterruptSpell(); + SetAppearance(eaCrouching); + playeraction = 2; + SetFeigned(false); + } + else if (sa->parameter == ANIM_DEATH) { // feign death too + SetAppearance(eaDead); + playeraction = 3; + InterruptSpell(); + } + else if (sa->parameter == ANIM_LOOT) { + SetAppearance(eaLooting); + playeraction = 4; + SetFeigned(false); + } + + // This is from old code + // I have no clue what it's for + /* + else if (sa->parameter == 0x05) { + // Illusion + std::cout << "Illusion packet recv'd:" << std::endl; + DumpPacket(app); + } + */ + else { + std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl; + return; + } + + entity_list.QueueClients(this, app, true); + } + else if (sa->type == AT_Anon) { + // For Anon/Roleplay + if (sa->parameter == 1) { // Anon + m_pp.anon = 1; + } + else if ((sa->parameter == 2) || (sa->parameter == 3)) { // This is Roleplay, or anon+rp + m_pp.anon = 2; + } + else if (sa->parameter == 0) { // This is Non-Anon + m_pp.anon = 0; + } + else { + std::cerr << "Client " << name << " unknown Anon/Roleplay Switch " << (int)sa->parameter << std::endl; + return; + } + entity_list.QueueClients(this, app, true); + UpdateWho(); + } + else if ((sa->type == AT_HP) && (dead == 0)) { + return; + } + else if (sa->type == AT_AFK) { + this->AFK = (sa->parameter == 1); + entity_list.QueueClients(this, app, true); + } + else if (sa->type == AT_Split) { + m_pp.autosplit = (sa->parameter == 1); + } + else if (sa->type == AT_Sneak) { + if (sa->parameter != 0) + { + if (!HasSkill(SkillSneak)) + { + char *hack_str = nullptr; + MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Sneak: %i", sa->parameter); + database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); + safe_delete_array(hack_str); + } + return; + } + this->sneaking = 0; + entity_list.QueueClients(this, app, true); + } + else if (sa->type == AT_Size) + { + char *hack_str = nullptr; + MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Size: %i", sa->parameter); + database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); + safe_delete_array(hack_str); + } + else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield) + { + entity_list.QueueClients(this, app, false); + } + else if (sa->type == AT_Levitate) + { + // don't do anything with this, we tell the client when it's + // levitating, not the other way around + } + else if (sa->type == AT_ShowHelm) + { + m_pp.showhelm = (sa->parameter == 1); + entity_list.QueueClients(this, app, true); + } + else { + std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec + << " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl; + } + return; +} + +void Client::Handle_OP_Split(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Split_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_Split, size=%i, expected %i", app->size, sizeof(Split_Struct)); + return; + } + // The client removes the money on its own, but we have to + // update our state anyway, and make sure they had enough to begin + // with. + Split_Struct *split = (Split_Struct *)app->pBuffer; + //Per the note above, Im not exactly sure what to do on error + //to notify the client of the error... + if (!isgrouped) { + Message(13, "You can not split money if your not in a group."); + return; + } + Group *cgroup = GetGroup(); + if (cgroup == nullptr) { + //invalid group, not sure if we should say more... + Message(13, "You can not split money if your not in a group."); + return; + } + + if (!TakeMoneyFromPP(static_cast(split->copper) + + 10 * static_cast(split->silver) + + 100 * static_cast(split->gold) + + 1000 * static_cast(split->platinum))) { + Message(13, "You do not have enough money to do that split."); + return; + } + cgroup->SplitMoney(split->copper, split->silver, split->gold, split->platinum); + + return; + +} + +void Client::Handle_OP_Surname(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Surname_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in Surname expected %i got %i", sizeof(Surname_Struct), app->size); + return; + } + + if (!p_timers.Expired(&database, pTimerSurnameChange, false) && !GetGM()) + { + Message(15, "You may only change surnames once every 7 days, your /surname is currently on cooldown."); + return; + } + + if (GetLevel() < 20) + { + Message_StringID(15, SURNAME_LEVEL); + return; + } + + Surname_Struct* surname = (Surname_Struct*)app->pBuffer; + + char *c = nullptr; + bool first = true; + for (c = surname->lastname; *c; c++) + { + if (first) + { + *c = toupper(*c); + first = false; + } + else + { + *c = tolower(*c); + } + } + + if (strlen(surname->lastname) >= 20) { + Message_StringID(15, SURNAME_TOO_LONG); + return; + } + + if (!database.CheckNameFilter(surname->lastname, true)) + { + Message_StringID(15, SURNAME_REJECTED); + return; + } + + ChangeLastName(surname->lastname); + p_timers.Start(pTimerSurnameChange, 604800); + + EQApplicationPacket* outapp = app->Copy(); + outapp = app->Copy(); + surname = (Surname_Struct*)outapp->pBuffer; + surname->unknown0064 = 1; + FastQueuePacket(&outapp); + return; +} + +void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(SwapSpell_Struct)) { + std::cout << "Wrong size on OP_SwapSpell. Got: " << app->size << ", Expected: " << sizeof(SwapSpell_Struct) << std::endl; + return; + } + const SwapSpell_Struct* swapspell = (const SwapSpell_Struct*)app->pBuffer; + int swapspelltemp; + + if (swapspell->from_slot < 0 || swapspell->from_slot > MAX_PP_SPELLBOOK || swapspell->to_slot < 0 || swapspell->to_slot > MAX_PP_SPELLBOOK) + return; + + swapspelltemp = m_pp.spell_book[swapspell->from_slot]; + if (swapspelltemp < 0){ + return; + } + m_pp.spell_book[swapspell->from_slot] = m_pp.spell_book[swapspell->to_slot]; + m_pp.spell_book[swapspell->to_slot] = swapspelltemp; + + database.SaveCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot); + database.SaveCharacterSpell(this->CharacterID(), swapspelltemp, swapspell->to_slot); + + QueuePacket(app); + return; +} + +void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClientTarget_Struct)) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_TargetMouse expected:%i got:%i", sizeof(ClientTarget_Struct), app->size); + return; + } + + if (GetTarget()) + { + GetTarget()->IsTargeted(-1); + } + + // Locate and cache new target + ClientTarget_Struct* ct = (ClientTarget_Struct*)app->pBuffer; + pClientSideTarget = ct->new_target; + if (!IsAIControlled()) + { + Mob *nt = entity_list.GetMob(ct->new_target); + if (nt) + { + SetTarget(nt); + if ((nt->IsClient() && !nt->CastToClient()->GetPVP()) || + (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || + (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) + nt->SendBuffsToClient(this); + } + else + { + SetTarget(nullptr); + SetHoTT(0); + UpdateXTargetType(TargetsTarget, nullptr); + + Group *g = GetGroup(); + + if (g && g->HasRole(this, RoleAssist)) + g->SetGroupAssistTarget(0); + + if (g && g->HasRole(this, RoleTank)) + g->SetGroupTankTarget(0); + + if (g && g->HasRole(this, RolePuller)) + g->SetGroupPullerTarget(0); + + return; + } + } + else + { + SetTarget(nullptr); + SetHoTT(0); + UpdateXTargetType(TargetsTarget, nullptr); + return; + } + + // HoTT + if (GetTarget() && GetTarget()->GetTarget()) + { + SetHoTT(GetTarget()->GetTarget()->GetID()); + UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); + } + else + { + SetHoTT(0); + UpdateXTargetType(TargetsTarget, nullptr); + } + + Group *g = GetGroup(); + + if (g && g->HasRole(this, RoleAssist)) + g->SetGroupAssistTarget(GetTarget()); + + if (g && g->HasRole(this, RoleTank)) + g->SetGroupTankTarget(GetTarget()); + + if (g && g->HasRole(this, RolePuller)) + g->SetGroupPullerTarget(GetTarget()); + + // For /target, send reject or success packet + if (app->GetOpcode() == OP_TargetCommand) { + if (GetTarget() && !GetTarget()->CastToMob()->IsInvisible(this) && (DistNoRoot(*GetTarget()) <= TARGETING_RANGE*TARGETING_RANGE || GetGM())) { + if (GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special + || GetTarget()->GetBodyType() == BT_NoTarget) + { + //Targeting something we shouldn't with /target + //but the client allows this without MQ so you don't flag it + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); + outapp->pBuffer[0] = 0x2f; + outapp->pBuffer[1] = 0x01; + outapp->pBuffer[4] = 0x0d; + if (GetTarget()) + { + SetTarget(nullptr); + } + QueuePacket(outapp); + safe_delete(outapp); + return; + } + + QueuePacket(app); + EQApplicationPacket hp_app; + GetTarget()->IsTargeted(1); + GetTarget()->CreateHPPacket(&hp_app); + QueuePacket(&hp_app, false); + } + else + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); + outapp->pBuffer[0] = 0x2f; + outapp->pBuffer[1] = 0x01; + outapp->pBuffer[4] = 0x0d; + if (GetTarget()) + { + SetTarget(nullptr); + } + QueuePacket(outapp); + safe_delete(outapp); + } + } + else + { + if (GetTarget()) + { + if (GetGM()) + { + GetTarget()->IsTargeted(1); + return; + } + else if (IsAssistExempted()) + { + GetTarget()->IsTargeted(1); + SetAssistExemption(false); + return; + } + else if (GetTarget()->IsClient()) + { + //make sure this client is in our raid/group + GetTarget()->IsTargeted(1); + return; + } + else if (GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special + || GetTarget()->GetBodyType() == BT_NoTarget) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "%s attempting to target something untargetable, %s bodytype: %i\n", + GetName(), GetTarget()->GetName(), (int)GetTarget()->GetBodyType()); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + SetTarget((Mob*)nullptr); + return; + } + else if (IsPortExempted()) + { + GetTarget()->IsTargeted(1); + return; + } + else if (IsSenseExempted()) + { + GetTarget()->IsTargeted(1); + SetSenseExemption(false); + return; + } + else if (IsXTarget(GetTarget())) + { + GetTarget()->IsTargeted(1); + return; + } + else if (GetBindSightTarget()) + { + if (GetBindSightTarget()->DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) + { + if (DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," + " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), + (zone->newzone_data.maxclip*zone->newzone_data.maxclip), + GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + SetTarget(nullptr); + return; + } + } + } + else if (DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," + " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), + (zone->newzone_data.maxclip*zone->newzone_data.maxclip), + GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + SetTarget(nullptr); + return; + } + + GetTarget()->IsTargeted(1); + } + } + return; +} + +void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app) +{ + Handle_OP_TargetCommand(app); +} + +void Client::Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(TaskHistoryRequest_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_TaskHistoryRequest expected %i got %i", + sizeof(TaskHistoryRequest_Struct), app->size); + DumpPacket(app); + return; + } + TaskHistoryRequest_Struct *ths = (TaskHistoryRequest_Struct*)app->pBuffer; + + if (RuleB(TaskSystem, EnableTaskSystem) && taskstate) + taskstate->SendTaskHistory(this, ths->TaskIndex); +} + +void Client::Handle_OP_Taunt(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClientTarget_Struct)) { + std::cout << "Wrong size on OP_Taunt. Got: " << app->size << ", Expected: " << sizeof(ClientTarget_Struct) << std::endl; + return; + } + + if (!p_timers.Expired(&database, pTimerTaunt, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerTaunt, TauntReuseTime - 1); + + if (GetTarget() == nullptr || !GetTarget()->IsNPC()) + return; + + Taunt(GetTarget()->CastToNPC(), false); + return; +} + +void Client::Handle_OP_TestBuff(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_TGB(const EQApplicationPacket *app) +{ + OPTGB(app); + return; +} + +void Client::Handle_OP_Track(const EQApplicationPacket *app) +{ + if (GetClass() != RANGER && GetClass() != DRUID && GetClass() != BARD) + return; + + if (GetSkill(SkillTracking) == 0) + SetSkill(SkillTracking, 1); + else + CheckIncreaseSkill(SkillTracking, nullptr, 15); + + if (!entity_list.MakeTrackPacket(this)) + LogFile->write(EQEMuLog::Error, "Unable to generate OP_Track packet requested by client."); + + return; +} + +void Client::Handle_OP_TrackTarget(const EQApplicationPacket *app) +{ + int PlayerClass = GetClass(); + + if ((PlayerClass != RANGER) && (PlayerClass != DRUID) && (PlayerClass != BARD)) + return; + + if (app->size != sizeof(TrackTarget_Struct)) + { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_TrackTarget: Expected: %i, Got: %i", + sizeof(TrackTarget_Struct), app->size); + return; + } + + TrackTarget_Struct *tts = (TrackTarget_Struct*)app->pBuffer; + + TrackingID = tts->EntityID; +} + +void Client::Handle_OP_TrackUnknown(const EQApplicationPacket *app) +{ + // size 0 send right after OP_Track + return; +} + +void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app) +{ + Mob* with = trade->With(); + trade->state = TradeAccepted; + + if (with && with->IsClient()) { + //finish trade... + // Have both accepted? + Client* other = with->CastToClient(); + other->QueuePacket(app); + + if (other->trade->state == trade->state) { + other->trade->state = TradeCompleting; + trade->state = TradeCompleting; + + // should we do this for NoDrop items as well? + if (CheckTradeLoreConflict(other) || other->CheckTradeLoreConflict(this)) { + Message_StringID(13, TRADE_CANCEL_LORE); + other->Message_StringID(13, TRADE_CANCEL_LORE); + this->FinishTrade(this); + other->FinishTrade(other); + other->trade->Reset(); + trade->Reset(); + } + else { + // Audit trade to database for both trade streams + other->trade->LogTrade(); + trade->LogTrade(); + + // start QS code + if (RuleB(QueryServ, PlayerLogTrades)) { + QSPlayerLogTrade_Struct event_entry; + std::list event_details; + + memset(&event_entry, 0, sizeof(QSPlayerLogTrade_Struct)); + + // Perform actual trade + this->FinishTrade(other, true, &event_entry, &event_details); + other->FinishTrade(this, false, &event_entry, &event_details); + + event_entry._detail_count = event_details.size(); + + ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogTrades, sizeof(QSPlayerLogTrade_Struct)+(sizeof(QSTradeItems_Struct)* event_entry._detail_count)); + QSPlayerLogTrade_Struct* qs_buf = (QSPlayerLogTrade_Struct*)qs_pack->pBuffer; + + memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogTrade_Struct)); + + int offset = 0; + + for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { + QSTradeItems_Struct* detail = reinterpret_cast(*iter); + qs_buf->items[offset] = *detail; + safe_delete(detail); + } + + event_details.clear(); + + qs_pack->Deflate(); + + if (worldserver.Connected()) + worldserver.SendPacket(qs_pack); + + safe_delete(qs_pack); + // end QS code + } + else { + this->FinishTrade(other); + other->FinishTrade(this); + } + + other->trade->Reset(); + trade->Reset(); + } + // All done + EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); + other->QueuePacket(outapp); + this->FastQueuePacket(&outapp); + } + } + // Trading with a Mob object that is not a Client. + else if (with) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); + QueuePacket(outapp); + safe_delete(outapp); + if (with->IsNPC()) { + // Audit trade to database for player trade stream + if (RuleB(QueryServ, PlayerLogHandins)) { + QSPlayerLogHandin_Struct event_entry; + std::list event_details; + + memset(&event_entry, 0, sizeof(QSPlayerLogHandin_Struct)); + + FinishTrade(with->CastToNPC(), false, &event_entry, &event_details); + + event_entry._detail_count = event_details.size(); + + ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogHandins, sizeof(QSPlayerLogHandin_Struct)+(sizeof(QSHandinItems_Struct)* event_entry._detail_count)); + QSPlayerLogHandin_Struct* qs_buf = (QSPlayerLogHandin_Struct*)qs_pack->pBuffer; + + memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogHandin_Struct)); + + int offset = 0; + + for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { + QSHandinItems_Struct* detail = reinterpret_cast(*iter); + qs_buf->items[offset] = *detail; + safe_delete(detail); + } + + event_details.clear(); + + qs_pack->Deflate(); + + if (worldserver.Connected()) + worldserver.SendPacket(qs_pack); + + safe_delete(qs_pack); + } + else { + FinishTrade(with->CastToNPC()); + } + } +#ifdef BOTS + // TODO: Log Bot trades + else if (with->IsBot()) + with->CastToBot()->FinishTrade(this, Bot::BotTradeClientNormal); +#endif + trade->Reset(); + } + + + return; +} + +void Client::Handle_OP_TradeBusy(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeBusy_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeBusy, size=%i, expected %i", app->size, sizeof(TradeBusy_Struct)); + return; + } + // Trade request recipient is cancelling the trade due to being busy + // Trade requester gets message "I'm busy right now" + // Send busy message on to trade initiator if client + TradeBusy_Struct* msg = (TradeBusy_Struct*)app->pBuffer; + Mob* tradee = entity_list.GetMob(msg->to_mob_id); + + if (tradee && tradee->IsClient()) { + tradee->CastToClient()->QueuePacket(app); + } + return; +} + +void Client::Handle_OP_Trader(const EQApplicationPacket *app) +{ + // Bazaar Trader: + // + // SoF sends 1 or more unhandled OP_Trader packets of size 96 when a trade has completed. + // I don't know what they are for (yet), but it doesn't seem to matter that we ignore them. + + _pkt(TRADING__PACKETS, app); + + uint32 max_items = 80; + + /* + if (GetClientVersion() >= EQClientRoF) + max_items = 200; + */ + + //Show Items + if (app->size == sizeof(Trader_ShowItems_Struct)) + { + Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)app->pBuffer; + + switch (sis->Code) + { + case BazaarTrader_EndTraderMode: { + Trader_EndTrader(); + break; + } + case BazaarTrader_EndTransaction: { + + Client* c = entity_list.GetClientByID(sis->TraderID); + if (c) + c->WithCustomer(0); + else + _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); + + break; + } + case BazaarTrader_ShowItems: { + Trader_ShowItems(); + break; + } + default: { + _log(TRADING__CLIENT, "Unhandled action code in OP_Trader ShowItems_Struct"); + break; + } + } + } + else if (app->size == sizeof(ClickTrader_Struct)) + { + if (Buyer) { + Trader_EndTrader(); + Message(13, "You cannot be a Trader and Buyer at the same time."); + return; + } + + ClickTrader_Struct* ints = (ClickTrader_Struct*)app->pBuffer; + + if (ints->Code == BazaarTrader_StartTraderMode) + { + GetItems_Struct* gis = GetTraderItems(); + + // Verify there are no NODROP or items with a zero price + bool TradeItemsValid = true; + + for (uint32 i = 0; i < max_items; i++) { + + if (gis->Items[i] == 0) break; + + if (ints->ItemCost[i] == 0) { + Message(13, "Item in Trader Satchel with no price. Unable to start trader mode"); + TradeItemsValid = false; + break; + } + const Item_Struct *Item = database.GetItem(gis->Items[i]); + + if (!Item) { + Message(13, "Unexpected error. Unable to start trader mode"); + TradeItemsValid = false; + break; + } + + if (Item->NoDrop == 0) { + Message(13, "NODROP Item in Trader Satchel. Unable to start trader mode"); + TradeItemsValid = false; + break; + } + } + + if (!TradeItemsValid) { + Trader_EndTrader(); + return; + } + + for (uint32 i = 0; i < max_items; i++) { + if (database.GetItem(gis->Items[i])) { + database.SaveTraderItem(this->CharacterID(), gis->Items[i], gis->SerialNumber[i], + gis->Charges[i], ints->ItemCost[i], i); + } + else { + //return; //sony doesnt memset so assume done on first bad item + break; + } + + } + safe_delete(gis); + + this->Trader_StartTrader(); + + if (GetClientVersion() >= EQClientRoF) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct)); + TraderStatus_Struct* tss = (TraderStatus_Struct*)outapp->pBuffer; + tss->Code = BazaarTrader_StartTraderMode2; + QueuePacket(outapp); + _pkt(TRADING__PACKETS, outapp); + safe_delete(outapp); + } + } + else if (app->size == sizeof(TraderStatus_Struct)) + { + TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer; + + if (tss->Code == BazaarTrader_ShowItems) + { + Trader_ShowItems(); + } + } + else { + _log(TRADING__CLIENT, "Client::Handle_OP_Trader: Unknown TraderStruct code of: %i\n", + ints->Code); + + LogFile->write(EQEMuLog::Error, "Unknown TraderStruct code of: %i\n", ints->Code); + } + } + + else if (app->size == sizeof(TraderPriceUpdate_Struct)) + { + HandleTraderPriceUpdate(app); + } + else { + _log(TRADING__CLIENT, "Unknown size for OP_Trader: %i\n", app->size); + LogFile->write(EQEMuLog::Error, "Unknown size for OP_Trader: %i\n", app->size); + DumpPacket(app); + return; + } + + return; +} + +void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) +{ + // Bazaar Trader: + // + // Client has elected to buy an item from a Trader + // + _pkt(TRADING__PACKETS, app); + + if (app->size == sizeof(TraderBuy_Struct)){ + + TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer; + + if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)){ + + BuyTraderItem(tbs, Trader, app); + } + else { + _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); + } + } + else { + _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Struct size mismatch"); + + } + return; +} + +void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequest, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); + return; + } + // Client requesting a trade session from an npc/client + // Trade session not started until OP_TradeRequestAck is sent + + BreakInvis(); + + // Pass trade request on to recipient + TradeRequest_Struct* msg = (TradeRequest_Struct*)app->pBuffer; + Mob* tradee = entity_list.GetMob(msg->to_mob_id); + + if (tradee && tradee->IsClient()) { + tradee->CastToClient()->QueuePacket(app); + } +#ifndef BOTS + else if (tradee && tradee->IsNPC()) { +#else + else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { +#endif + //npcs always accept + trade->Start(msg->to_mob_id); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); + TradeRequest_Struct* acc = (TradeRequest_Struct*)outapp->pBuffer; + acc->from_mob_id = msg->to_mob_id; + acc->to_mob_id = msg->from_mob_id; + FastQueuePacket(&outapp); + safe_delete(outapp); + } + return; +} + +void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequestAck, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); + return; + } + // Trade request recipient is acknowledging they are able to trade + // After this, the trade session has officially started + // Send ack on to trade initiator if client + TradeRequest_Struct* msg = (TradeRequest_Struct*)app->pBuffer; + Mob* tradee = entity_list.GetMob(msg->to_mob_id); + + if (tradee && tradee->IsClient()) { + trade->Start(msg->to_mob_id); + tradee->CastToClient()->QueuePacket(app); + } + return; +} + +void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) +{ + // Bazaar Trader: + // + // This is when a potential purchaser right clicks on this client who is in Trader mode to + // browse their goods. + // + _pkt(TRADING__PACKETS, app); + + TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer; + + if (app->size != sizeof(TraderClick_Struct)) { + + _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: Returning due to struct size mismatch"); + + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct)); + + TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer; + + Client* Customer = entity_list.GetClientByID(tcs->TraderID); + + if (Customer) + outtcs->Approval = Customer->WithCustomer(GetID()); + else { + _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" + " returned a nullptr pointer"); + return; + } + + outtcs->TraderID = tcs->TraderID; + + outtcs->Unknown008 = 0x3f800000; + + QueuePacket(outapp); + + _pkt(TRADING__PACKETS, outapp); + + if (outtcs->Approval) { + this->BulkSendTraderInventory(Customer->CharacterID()); + Customer->Trader_CustomerBrowsing(this); + } + else + Message_StringID(clientMessageYellow, TRADER_BUSY); + + safe_delete(outapp); + + return; +} + +void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app) +{ + if (app->size != sizeof(NewCombine_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for NewCombine_Struct: Expected: %i, Got: %i", + sizeof(NewCombine_Struct), app->size); + return; + } + /*if (m_tradeskill_object == nullptr) { + Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use"); + return; + }*/ + + //fixed this to work for non-world objects + + // Delegate to tradeskill object to perform combine + NewCombine_Struct* in_combine = (NewCombine_Struct*)app->pBuffer; + Object::HandleCombine(this, in_combine, m_tradeskill_object); + return; +} + +void Client::Handle_OP_Translocate(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(Translocate_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Translocate expected %i got %i", sizeof(Translocate_Struct), app->size); + DumpPacket(app); + return; + } + Translocate_Struct *its = (Translocate_Struct*)app->pBuffer; + + if (!PendingTranslocate) return; + + if ((RuleI(Spells, TranslocateTimeLimit) > 0) && (time(nullptr) > (TranslocateTime + RuleI(Spells, TranslocateTimeLimit)))) { + Message(13, "You did not accept the Translocate within the required time limit."); + PendingTranslocate = false; + return; + } + + if (its->Complete == 1) { + + int SpellID = PendingTranslocateData.SpellID; + int i = parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, SpellID, 0); + + if (i == 0) + { + // If the spell has a translocate to bind effect, AND we are already in the zone the client + // is bound in, use the GoToBind method. If we send OP_Translocate in this case, the client moves itself + // to the bind coords it has from the PlayerProfile, but with the X and Y reversed. I suspect they are + // reversed in the pp, and since spells like Gate are handled serverside, this has not mattered before. + if (((SpellID == 1422) || (SpellID == 1334) || (SpellID == 3243)) && + zone->GetZoneID() == PendingTranslocateData.ZoneID) + { + PendingTranslocate = false; + GoToBind(); + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Translocate, sizeof(Translocate_Struct)); + Translocate_Struct *ots = (Translocate_Struct*)outapp->pBuffer; + memcpy(ots, &PendingTranslocateData, sizeof(Translocate_Struct)); + + //Was sending the packet back to initiate client zone... + //but that could be abusable, so lets go through proper channels + MovePC(ots->ZoneID, 0, ots->x, ots->y, ots->z, GetHeading(), 0, ZoneSolicited); + } + } + + PendingTranslocate = false; +} + +void Client::Handle_OP_TributeItem(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeItem of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //player donates an item... + if (app->size != sizeof(TributeItem_Struct)) + printf("Error in OP_TributeItem. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + TributeItem_Struct* t = (TributeItem_Struct*)app->pBuffer; + + tribute_master_id = t->tribute_master_id; + //make sure they are dealing with a valid tribute master + Mob* tribmast = entity_list.GetMob(t->tribute_master_id); + if (!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) + return; + if (DistNoRoot(*tribmast) > USE_NPC_RANGE2) + return; + + t->tribute_points = TributeItem(t->slot, t->quantity); + + _log(TRIBUTE__OUT, "Sending tribute item reply with %d points", t->tribute_points); + _pkt(TRIBUTE__OUT, app); + + QueuePacket(app); + } + return; +} + +void Client::Handle_OP_TributeMoney(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeMoney of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //player donates money + if (app->size != sizeof(TributeMoney_Struct)) + printf("Error in OP_TributeMoney. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + TributeMoney_Struct* t = (TributeMoney_Struct*)app->pBuffer; + + tribute_master_id = t->tribute_master_id; + //make sure they are dealing with a valid tribute master + Mob* tribmast = entity_list.GetMob(t->tribute_master_id); + if (!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) + return; + if (DistNoRoot(*tribmast) > USE_NPC_RANGE2) + return; + + t->tribute_points = TributeMoney(t->platinum); + + _log(TRIBUTE__OUT, "Sending tribute money reply with %d points", t->tribute_points); + _pkt(TRIBUTE__OUT, app); + + QueuePacket(app); + } + return; +} + +void Client::Handle_OP_TributeNPC(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeNPC of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + return; +} + +void Client::Handle_OP_TributeToggle(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeToggle of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + if (app->size != sizeof(uint32)) + LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeToggle packet"); + else { + uint32 *val = (uint32 *)app->pBuffer; + ToggleTribute(*val ? true : false); + } + return; +} + +void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeUpdate of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //sent when the client changes their tribute settings... + if (app->size != sizeof(TributeInfo_Struct)) + LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeUpdate packet"); + else { + TributeInfo_Struct *t = (TributeInfo_Struct *)app->pBuffer; + ChangeTributeSettings(t); + } + return; +} + +void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app) +{ + if (app->size < sizeof(VeteranClaimRequest)) + { + LogFile->write(EQEMuLog::Debug, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", + app->size, sizeof(VeteranClaimRequest)); + DumpPacket(app); + return; + } + + VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer; + + if (vcr->claim_id == 0xFFFFFFFF) //request update packet + { + SendRewards(); + } + else //try to claim something! + { + if (!TryReward(vcr->claim_id)) + { + Message(13, "Your claim has been rejected."); + EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); + VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; + strcpy(cr->name, GetName()); + cr->claim_id = vcr->claim_id; + cr->reject_field = -1; + FastQueuePacket(&vetapp); + } + else + { + EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); + VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; + strcpy(cr->name, GetName()); + cr->claim_id = vcr->claim_id; + cr->reject_field = 0; + FastQueuePacket(&vetapp); + } + } +} + +void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(VoiceMacroIn_Struct)) { + + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_VoiceMacroIn expected %i got %i", + sizeof(VoiceMacroIn_Struct), app->size); + + DumpPacket(app); + + return; + } + + if (!RuleB(Chat, EnableVoiceMacros)) return; + + VoiceMacroIn_Struct* vmi = (VoiceMacroIn_Struct*)app->pBuffer; + + VoiceMacroReceived(vmi->Type, vmi->Target, vmi->MacroNumber); + +} + +void Client::Handle_OP_WearChange(const EQApplicationPacket *app) +{ + if (app->size != sizeof(WearChange_Struct)) { + std::cout << "Wrong size: OP_WearChange, size=" << app->size << ", expected " << sizeof(WearChange_Struct) << std::endl; + return; + } + + WearChange_Struct* wc = (WearChange_Struct*)app->pBuffer; + if (wc->spawn_id != GetID()) + return; + + // we could maybe ignore this and just send our own from moveitem + entity_list.QueueClients(this, app, true); + return; +} + +void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Who_All_Struct)) { + std::cout << "Wrong size on OP_WhoAll. Got: " << app->size << ", Expected: " << sizeof(Who_All_Struct) << std::endl; + return; + } + Who_All_Struct* whoall = (Who_All_Struct*)app->pBuffer; + + if (whoall->type == 0) // SoF only, for regular /who + entity_list.ZoneWho(this, whoall); + else + WhoAll(whoall); + return; +} + +void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) +{ + if (app->size != 1) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetAutoAddHaters, expected 1, got %i", app->size); + DumpPacket(app); + return; + } + + XTargetAutoAddHaters = app->ReadUInt8(0); +} + +void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app) +{ + if (app->size < 12) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetRequest, expected at least 12, got %i", app->size); + DumpPacket(app); + return; + } + + uint32 Unknown000 = app->ReadUInt32(0); + + if (Unknown000 != 1) + return; + + uint32 Slot = app->ReadUInt32(4); + + if (Slot >= XTARGET_HARDCAP) + return; + + XTargetType Type = (XTargetType)app->ReadUInt32(8); + + XTargets[Slot].Type = Type; + XTargets[Slot].ID = 0; + XTargets[Slot].Name[0] = 0; + + switch (Type) + { + case Empty: + case Auto: + { + break; + } + + case CurrentTargetPC: + { + char Name[65]; + + app->ReadString(Name, 12, 64); + Client *c = entity_list.GetClientByName(Name); + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, Name, 64); + } + SendXTargetPacket(Slot, c); + + break; + } + + case CurrentTargetNPC: + { + char Name[65]; + app->ReadString(Name, 12, 64); + Mob *m = entity_list.GetMob(Name); + if (m) + { + XTargets[Slot].ID = m->GetID(); + SendXTargetPacket(Slot, m); + break; + } + } + + case TargetsTarget: + { + if (GetTarget()) + UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); + else + UpdateXTargetType(TargetsTarget, nullptr); + + break; + } + + case GroupTank: + { + Group *g = GetGroup(); + + if (g) + { + Client *c = entity_list.GetClientByName(g->GetMainTankName()); + + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, g->GetMainTankName(), 64); + } + SendXTargetPacket(Slot, c); + } + break; + } + case GroupTankTarget: + { + Group *g = GetGroup(); + + if (g) + g->NotifyTankTarget(this); + + break; + } + + case GroupAssist: + { + Group *g = GetGroup(); + + if (g) + { + Client *c = entity_list.GetClientByName(g->GetMainAssistName()); + + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, g->GetMainAssistName(), 64); + } + SendXTargetPacket(Slot, c); + } + break; + } + + case GroupAssistTarget: + { + + Group *g = GetGroup(); + + if (g) + g->NotifyAssistTarget(this); + + break; + } + + case Puller: + { + Group *g = GetGroup(); + + if (g) + { + Client *c = entity_list.GetClientByName(g->GetPullerName()); + + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, g->GetPullerName(), 64); + } + SendXTargetPacket(Slot, c); + } + break; + } + + case PullerTarget: + { + + Group *g = GetGroup(); + + if (g) + g->NotifyPullerTarget(this); + + break; + } + + case GroupMarkTarget1: + case GroupMarkTarget2: + case GroupMarkTarget3: + { + Group *g = GetGroup(); + + if (g) + g->SendMarkedNPCsToMember(this); + + break; + } + + case RaidAssist1: + case RaidAssist2: + case RaidAssist3: + case RaidAssist1Target: + case RaidAssist2Target: + case RaidAssist3Target: + case RaidMarkTarget1: + case RaidMarkTarget2: + case RaidMarkTarget3: + { + // Not implemented yet. + break; + } + + case MyPet: + { + Mob *m = GetPet(); + if (m) + { + XTargets[Slot].ID = m->GetID(); + SendXTargetPacket(Slot, m); + + } + break; + } + case MyPetTarget: + { + Mob *m = GetPet(); + + if (m) + m = m->GetTarget(); + + if (m) + { + XTargets[Slot].ID = m->GetID(); + SendXTargetPacket(Slot, m); + + } + break; + } + + default: + LogFile->write(EQEMuLog::Debug, "Unhandled XTarget Type %i", Type); + break; + } + +} + +void Client::Handle_OP_YellForHelp(const EQApplicationPacket *app) +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_YellForHelp, 4); + *(uint32 *)outapp->pBuffer = GetID(); + entity_list.QueueCloseClients(this, outapp, true, 100.0); + safe_delete(outapp); + return; +} + +/* +void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) +{ + return; +} +*/ diff --git a/zone/client_packet.h b/zone/client_packet.h index 424a8f78f..7cf7ce06b 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -1,292 +1,294 @@ - void Handle_Connect_OP_SetDataRate(const EQApplicationPacket *app); - void Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app); - void Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app); - void Handle_Connect_OP_SendAATable(const EQApplicationPacket *app); - void Handle_Connect_OP_SendTributes(const EQApplicationPacket *app); - void Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app); - void Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app); + // connecting opcode handlers void Handle_Connect_0x3e33(const EQApplicationPacket *app); + void Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app); + void Handle_Connect_OP_ClientError(const EQApplicationPacket *app); + void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app); + void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app); void Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app); + void Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app); + void Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app); + void Handle_Connect_OP_SendAATable(const EQApplicationPacket *app); void Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app); + void Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app); + void Handle_Connect_OP_SendTributes(const EQApplicationPacket *app); + void Handle_Connect_OP_SetDataRate(const EQApplicationPacket *app); + void Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app); + void Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app); + void Handle_Connect_OP_TGB(const EQApplicationPacket *app); + void Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app); + void Handle_Connect_OP_WearChange(const EQApplicationPacket *app); void Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app); void Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app); - void Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app); - void Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app); - void Handle_Connect_OP_WearChange(const EQApplicationPacket *app); - void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app); - void Handle_Connect_OP_ClientError(const EQApplicationPacket *app); - void Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app); - void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app); - void Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app); - void Handle_Connect_OP_TGB(const EQApplicationPacket *app); - void Handle_OP_ClientUpdate(const EQApplicationPacket *app); - void Handle_OP_AutoAttack(const EQApplicationPacket *app); - void Handle_OP_AutoAttack2(const EQApplicationPacket *app); - void Handle_OP_Consent(const EQApplicationPacket *app); - void Handle_OP_ConsentDeny(const EQApplicationPacket *app); - void Handle_OP_TargetMouse(const EQApplicationPacket *app); - void Handle_OP_TargetCommand(const EQApplicationPacket *app); - void Handle_OP_Shielding(const EQApplicationPacket *app); - void Handle_OP_Jump(const EQApplicationPacket *app); - void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureRequest(const EQApplicationPacket *app); - void Handle_OP_LDoNButton(const EQApplicationPacket *app); - void Handle_OP_LeaveAdventure(const EQApplicationPacket *app); - void Handle_OP_Consume(const EQApplicationPacket *app); - void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app); - void Handle_OP_ConsiderCorpse(const EQApplicationPacket *app); - void Handle_OP_Consider(const EQApplicationPacket *app); - void Handle_OP_Begging(const EQApplicationPacket *app); - void Handle_OP_TestBuff(const EQApplicationPacket *app); - void Handle_OP_Surname(const EQApplicationPacket *app); - void Handle_OP_ClearSurname(const EQApplicationPacket *app); - void Handle_OP_YellForHelp(const EQApplicationPacket *app); - void Handle_OP_Assist(const EQApplicationPacket *app); - void Handle_OP_AssistGroup(const EQApplicationPacket *app); - void Handle_OP_GMTraining(const EQApplicationPacket *app); - void Handle_OP_GMEndTraining(const EQApplicationPacket *app); - void Handle_OP_GMTrainSkill(const EQApplicationPacket *app); - void Handle_OP_DuelResponse(const EQApplicationPacket *app); - void Handle_OP_DuelResponse2(const EQApplicationPacket *app); - void Handle_OP_RequestDuel(const EQApplicationPacket *app); - void Handle_OP_SpawnAppearance(const EQApplicationPacket *app); - void Handle_OP_BazaarInspect(const EQApplicationPacket *app); - void Handle_OP_Death(const EQApplicationPacket *app); - void Handle_OP_MoveCoin(const EQApplicationPacket *app); - void Handle_OP_ItemLinkClick(const EQApplicationPacket *app); - void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app); - void Handle_OP_MoveItem(const EQApplicationPacket *app); - void Handle_OP_Camp(const EQApplicationPacket *app); - void Handle_OP_Logout(const EQApplicationPacket *app); - void Handle_OP_SenseHeading(const EQApplicationPacket *app); - void Handle_OP_LDoNOpen(const EQApplicationPacket *app); - void Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app); - void Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app); - void Handle_OP_LDoNInspect(const EQApplicationPacket *app); - void Handle_OP_LDoNPickLock(const EQApplicationPacket *app); - void Handle_OP_FeignDeath(const EQApplicationPacket *app); - void Handle_OP_Sneak(const EQApplicationPacket *app); - void Handle_OP_Hide(const EQApplicationPacket *app); - void Handle_OP_ChannelMessage(const EQApplicationPacket *app); - void Handle_OP_WearChange(const EQApplicationPacket *app); - void Handle_OP_ZoneChange(const EQApplicationPacket *app); - void Handle_OP_DeleteSpawn(const EQApplicationPacket *app); - void Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app); - void Handle_OP_Save(const EQApplicationPacket *app); - void Handle_OP_WhoAllRequest(const EQApplicationPacket *app); - void Handle_OP_GMZoneRequest(const EQApplicationPacket *app); - void Handle_OP_GMZoneRequest2(const EQApplicationPacket *app); - void Handle_OP_EndLootRequest(const EQApplicationPacket *app); - void Handle_OP_LootRequest(const EQApplicationPacket *app); - void Handle_OP_Dye(const EQApplicationPacket *app); - void Handle_OP_LootItem(const EQApplicationPacket *app); - void Handle_OP_GuildDelete(const EQApplicationPacket *app); - void Handle_OP_GuildPublicNote(const EQApplicationPacket *app); - void Handle_OP_GetGuildsList(const EQApplicationPacket *app); - void Handle_OP_SetGuildMOTD(const EQApplicationPacket *app); - void Handle_OP_GuildPeace(const EQApplicationPacket *app); - void Handle_OP_GuildWar(const EQApplicationPacket *app); - void Handle_OP_GuildLeader(const EQApplicationPacket *app); - void Handle_OP_GuildDemote(const EQApplicationPacket *app); - void Handle_OP_GuildPromote(const EQApplicationPacket *app); - void Handle_OP_GuildInvite(const EQApplicationPacket *app); - void Handle_OP_GuildRemove(const EQApplicationPacket *app); - void Handle_OP_GetGuildMOTD(const EQApplicationPacket *app); - void Handle_OP_GuildManageBanker(const EQApplicationPacket *app); - void Handle_OP_GuildInviteAccept(const EQApplicationPacket *app); - void Handle_OP_ManaChange(const EQApplicationPacket *app); - void Handle_OP_MemorizeSpell(const EQApplicationPacket *app); - void Handle_OP_SwapSpell(const EQApplicationPacket *app); - void Handle_OP_CastSpell(const EQApplicationPacket *app); - void Handle_OP_DeleteItem(const EQApplicationPacket *app); - void Handle_OP_CombatAbility(const EQApplicationPacket *app); - void Handle_OP_Taunt(const EQApplicationPacket *app); - void Handle_OP_InstillDoubt(const EQApplicationPacket *app); - void Handle_OP_RezzAnswer(const EQApplicationPacket *app); - void Handle_OP_GMSummon(const EQApplicationPacket *app); - void Handle_OP_TradeRequest(const EQApplicationPacket *app); - void Handle_OP_TradeRequestAck(const EQApplicationPacket *app); - void Handle_OP_CancelTrade(const EQApplicationPacket *app); - void Handle_OP_TradeAcceptClick(const EQApplicationPacket *app); - void Handle_OP_BoardBoat(const EQApplicationPacket *app); - void Handle_OP_LeaveBoat(const EQApplicationPacket *app); - void Handle_OP_RandomReq(const EQApplicationPacket *app); - void Handle_OP_Buff(const EQApplicationPacket *app); - void Handle_OP_GMHideMe(const EQApplicationPacket *app); - void Handle_OP_GMNameChange(const EQApplicationPacket *app); - void Handle_OP_GMKill(const EQApplicationPacket *app); - void Handle_OP_GMLastName(const EQApplicationPacket *app); - void Handle_OP_GMToggle(const EQApplicationPacket *app); - void Handle_OP_LFGCommand(const EQApplicationPacket *app); - void Handle_OP_GMGoto(const EQApplicationPacket *app); - void Handle_OP_TraderShop(const EQApplicationPacket *app); - void Handle_OP_ShopRequest(const EQApplicationPacket *app); - void Handle_OP_BazaarSearch(const EQApplicationPacket *app); - void Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app); - void Handle_OP_ShopPlayerSell(const EQApplicationPacket *app); - void Handle_OP_ShopEnd(const EQApplicationPacket *app); -// void Handle_OP_CloseContainer(const EQApplicationPacket *app); - void Handle_OP_ClickObjectAction(const EQApplicationPacket *app); - void Handle_OP_ClickObject(const EQApplicationPacket *app); - void Handle_OP_RecipesFavorite(const EQApplicationPacket *app); - void Handle_OP_RecipesSearch(const EQApplicationPacket *app); - void Handle_OP_RecipeDetails(const EQApplicationPacket *app); - void Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app); - void Handle_OP_TradeSkillCombine(const EQApplicationPacket *app); - void Handle_OP_ItemName(const EQApplicationPacket *app); - void Handle_OP_AugmentItem(const EQApplicationPacket *app); - void Handle_OP_ClickDoor(const EQApplicationPacket *app); - void Handle_OP_CreateObject(const EQApplicationPacket *app); - void Handle_OP_FaceChange(const EQApplicationPacket *app); - void Handle_OP_GroupInvite(const EQApplicationPacket *app); - void Handle_OP_GroupInvite2(const EQApplicationPacket *app); - void Handle_OP_GroupAcknowledge(const EQApplicationPacket *app); - void Handle_OP_GroupCancelInvite(const EQApplicationPacket *app); - void Handle_OP_GroupFollow(const EQApplicationPacket *app); - void Handle_OP_GroupFollow2(const EQApplicationPacket *app); - void Handle_OP_GroupDisband(const EQApplicationPacket *app); - void Handle_OP_GroupDelete(const EQApplicationPacket *app); - void Handle_OP_GMEmoteZone(const EQApplicationPacket *app); - void Handle_OP_InspectRequest(const EQApplicationPacket *app); - void Handle_OP_InspectAnswer(const EQApplicationPacket *app); - void Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app); - void Handle_OP_Medding(const EQApplicationPacket *app); - void Handle_OP_DeleteSpell(const EQApplicationPacket *app); - void Handle_OP_PetitionBug(const EQApplicationPacket *app); - void Handle_OP_Bug(const EQApplicationPacket *app); - void Handle_OP_Petition(const EQApplicationPacket *app); - void Handle_OP_PetitionCheckIn(const EQApplicationPacket *app); - void Handle_OP_PetitionResolve(const EQApplicationPacket *app); - void Handle_OP_PetitionDelete(const EQApplicationPacket *app); - void Handle_OP_PetCommands(const EQApplicationPacket *app); - void Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app); - void Handle_OP_PetitionQue(const EQApplicationPacket *app); - void Handle_OP_PDeletePetition(const EQApplicationPacket *app); - void Handle_OP_PetitionCheckout(const EQApplicationPacket *app); - void Handle_OP_PetitionRefresh(const EQApplicationPacket *app); - void Handle_OP_ReadBook(const EQApplicationPacket *app); - void Handle_OP_Emote(const EQApplicationPacket *app); - void Handle_OP_Animation(const EQApplicationPacket *app); - void Handle_OP_SetServerFilter(const EQApplicationPacket *app); - void Handle_OP_GMDelCorpse(const EQApplicationPacket *app); - void Handle_OP_GMKick(const EQApplicationPacket *app); - void Handle_OP_GMServers(const EQApplicationPacket *app); - void Handle_OP_Illusion(const EQApplicationPacket *app); - void Handle_OP_GMBecomeNPC(const EQApplicationPacket *app); - void Handle_OP_Fishing(const EQApplicationPacket *app); - void Handle_OP_Forage(const EQApplicationPacket *app); - void Handle_OP_Mend(const EQApplicationPacket *app); - void Handle_OP_EnvDamage(const EQApplicationPacket *app); - void Handle_OP_Damage(const EQApplicationPacket *app); - void Handle_OP_AAAction(const EQApplicationPacket *app); - void Handle_OP_TraderBuy(const EQApplicationPacket *app); - void Handle_OP_Trader(const EQApplicationPacket *app); - void Handle_OP_GMFind(const EQApplicationPacket *app); - void Handle_OP_PickPocket(const EQApplicationPacket *app); - void Handle_OP_Bind_Wound(const EQApplicationPacket *app); - void Handle_OP_TrackTarget(const EQApplicationPacket *app); - void Handle_OP_Track(const EQApplicationPacket *app); - void Handle_OP_TrackUnknown(const EQApplicationPacket *app); + void Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app); + // connected opcode handlers void Handle_0x0193(const EQApplicationPacket *app); void Handle_0x01e7(const EQApplicationPacket *app); - void Handle_OP_ClientError(const EQApplicationPacket *app); - void Handle_OP_ReloadUI(const EQApplicationPacket *app); - void Handle_OP_TGB(const EQApplicationPacket *app); - void Handle_OP_Split(const EQApplicationPacket *app); - void Handle_OP_SenseTraps(const EQApplicationPacket *app); - void Handle_OP_DisarmTraps(const EQApplicationPacket *app); - void Handle_OP_OpenTributeMaster(const EQApplicationPacket *app); - void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app); - void Handle_OP_TributeItem(const EQApplicationPacket *app); - void Handle_OP_TributeMoney(const EQApplicationPacket *app); - void Handle_OP_SelectTribute(const EQApplicationPacket *app); - void Handle_OP_TributeUpdate(const EQApplicationPacket *app); - void Handle_OP_TributeToggle(const EQApplicationPacket *app); - void Handle_OP_TributeNPC(const EQApplicationPacket *app); - void Handle_OP_ConfirmDelete(const EQApplicationPacket *app); - void Handle_OP_CrashDump(const EQApplicationPacket *app); - void Handle_OP_ControlBoat(const EQApplicationPacket *app); - void Handle_OP_DumpName(const EQApplicationPacket *app); - void Handle_OP_SetRunMode(const EQApplicationPacket *app); - void Handle_OP_SafeFallSuccess(const EQApplicationPacket *app); - void Handle_OP_Heartbeat(const EQApplicationPacket *app); - void Handle_OP_SafePoint(const EQApplicationPacket *app); - void Handle_OP_FindPersonRequest(const EQApplicationPacket *app); - void Handle_OP_BankerChange(const EQApplicationPacket *app); - void Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app); - void Handle_OP_SetTitle(const EQApplicationPacket *app); - void Handle_OP_RequestTitles(const EQApplicationPacket *app); - void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app); - void Handle_OP_Ignore(const EQApplicationPacket *app); - void Handle_OP_LoadSpellSet(const EQApplicationPacket *app); - void Handle_OP_AutoFire(const EQApplicationPacket *app); - void Handle_OP_Rewind(const EQApplicationPacket *app); - void Handle_OP_RaidCommand(const EQApplicationPacket *app); - void Handle_OP_Translocate(const EQApplicationPacket *app); - void Handle_OP_Sacrifice(const EQApplicationPacket *app); + void Handle_OP_AAAction(const EQApplicationPacket *app); void Handle_OP_AcceptNewTask(const EQApplicationPacket *app); - void Handle_OP_CancelTask(const EQApplicationPacket *app); - void Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app); - void Handle_OP_KeyRing(const EQApplicationPacket *app); - void Handle_OP_FriendsWho(const EQApplicationPacket *app); - void Handle_OP_Bandolier(const EQApplicationPacket *app); - void Handle_OP_PopupResponse(const EQApplicationPacket *app); - void Handle_OP_PotionBelt(const EQApplicationPacket *app); - void Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app); - void Handle_OP_LFPCommand(const EQApplicationPacket *app); - void Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app); - void Handle_OP_Barter(const EQApplicationPacket *app); - void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app); - void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app); - void Handle_OP_ClearNPCMarks(const EQApplicationPacket *app); - void Handle_OP_DelegateAbility(const EQApplicationPacket *app); - void Handle_OP_ApplyPoison(const EQApplicationPacket *app); - void Handle_OP_AugmentInfo(const EQApplicationPacket *app); - void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app); - void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app); - void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app); + void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app); void Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app); - void Handle_OP_RespawnWindow(const EQApplicationPacket *app); - void Handle_OP_GroupUpdate(const EQApplicationPacket *app); - void Handle_OP_SetStartCity(const EQApplicationPacket *app); - void Handle_OP_Report(const EQApplicationPacket *app); - void Handle_OP_VetClaimRequest(const EQApplicationPacket *app); - void Handle_OP_GMSearchCorpse(const EQApplicationPacket *app); - void Handle_OP_GuildBank(const EQApplicationPacket *app); - void Handle_OP_GroupRoles(const EQApplicationPacket *app); - void Handle_OP_HideCorpse(const EQApplicationPacket *app); - void Handle_OP_TradeBusy(const EQApplicationPacket *app); - void Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app); - void Handle_OP_GuildStatus(const EQApplicationPacket *app); - void Handle_OP_BlockedBuffs(const EQApplicationPacket *app); - void Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app); - void Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app); - void Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app); - void Handle_OP_CorpseDrag(const EQApplicationPacket *app); - void Handle_OP_CorpseDrop(const EQApplicationPacket *app); - void Handle_OP_GroupMakeLeader(const EQApplicationPacket *app); - void Handle_OP_GuildCreate(const EQApplicationPacket *app); + void Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app); + void Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app); + void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app); + void Handle_OP_AdventureRequest(const EQApplicationPacket *app); + void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app); void Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app); - void Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app); void Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app); void Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app); void Handle_OP_AltCurrencySell(const EQApplicationPacket *app); - void Handle_OP_CrystalReclaim(const EQApplicationPacket *app); - void Handle_OP_CrystalCreate(const EQApplicationPacket *app); - void Handle_OP_LFGuild(const EQApplicationPacket *app); - void Handle_OP_XTargetRequest(const EQApplicationPacket *app); - void Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app); - void Handle_OP_ItemPreview(const EQApplicationPacket *app); - void Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app); - void Handle_OP_MercenaryHire(const EQApplicationPacket *app); - void Handle_OP_MercenaryCommand(const EQApplicationPacket *app); - void Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app); - void Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app); - void Handle_OP_MercenaryDismiss(const EQApplicationPacket *app); - void Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app); - void Handle_OP_OpenInventory(const EQApplicationPacket *app); - void Handle_OP_OpenContainer(const EQApplicationPacket *app); + void Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app); + void Handle_OP_Animation(const EQApplicationPacket *app); + void Handle_OP_ApplyPoison(const EQApplicationPacket *app); + void Handle_OP_Assist(const EQApplicationPacket *app); + void Handle_OP_AssistGroup(const EQApplicationPacket *app); + void Handle_OP_AugmentInfo(const EQApplicationPacket *app); + void Handle_OP_AugmentItem(const EQApplicationPacket *app); + void Handle_OP_AutoAttack(const EQApplicationPacket *app); + void Handle_OP_AutoAttack2(const EQApplicationPacket *app); + void Handle_OP_AutoFire(const EQApplicationPacket *app); + void Handle_OP_Bandolier(const EQApplicationPacket *app); + void Handle_OP_BankerChange(const EQApplicationPacket *app); + void Handle_OP_Barter(const EQApplicationPacket *app); + void Handle_OP_BazaarInspect(const EQApplicationPacket *app); + void Handle_OP_BazaarSearch(const EQApplicationPacket *app); + void Handle_OP_Begging(const EQApplicationPacket *app); + void Handle_OP_Bind_Wound(const EQApplicationPacket *app); + void Handle_OP_BlockedBuffs(const EQApplicationPacket *app); + void Handle_OP_BoardBoat(const EQApplicationPacket *app); + void Handle_OP_Buff(const EQApplicationPacket *app); + void Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app); + void Handle_OP_Bug(const EQApplicationPacket *app); + void Handle_OP_Camp(const EQApplicationPacket *app); + void Handle_OP_CancelTask(const EQApplicationPacket *app); + void Handle_OP_CancelTrade(const EQApplicationPacket *app); + void Handle_OP_CastSpell(const EQApplicationPacket *app); + void Handle_OP_ChannelMessage(const EQApplicationPacket *app); + void Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app); + void Handle_OP_ClearNPCMarks(const EQApplicationPacket *app); + void Handle_OP_ClearSurname(const EQApplicationPacket *app); + void Handle_OP_ClickDoor(const EQApplicationPacket *app); + void Handle_OP_ClickObject(const EQApplicationPacket *app); + void Handle_OP_ClickObjectAction(const EQApplicationPacket *app); + void Handle_OP_ClientError(const EQApplicationPacket *app); void Handle_OP_ClientTimeStamp(const EQApplicationPacket *app); + void Handle_OP_ClientUpdate(const EQApplicationPacket *app); +// void Handle_OP_CloseContainer(const EQApplicationPacket *app); + void Handle_OP_CombatAbility(const EQApplicationPacket *app); + void Handle_OP_ConfirmDelete(const EQApplicationPacket *app); + void Handle_OP_Consent(const EQApplicationPacket *app); + void Handle_OP_ConsentDeny(const EQApplicationPacket *app); + void Handle_OP_Consider(const EQApplicationPacket *app); + void Handle_OP_ConsiderCorpse(const EQApplicationPacket *app); + void Handle_OP_Consume(const EQApplicationPacket *app); + void Handle_OP_ControlBoat(const EQApplicationPacket *app); + void Handle_OP_CorpseDrag(const EQApplicationPacket *app); + void Handle_OP_CorpseDrop(const EQApplicationPacket *app); + void Handle_OP_CrashDump(const EQApplicationPacket *app); + void Handle_OP_CreateObject(const EQApplicationPacket *app); + void Handle_OP_CrystalCreate(const EQApplicationPacket *app); + void Handle_OP_CrystalReclaim(const EQApplicationPacket *app); + void Handle_OP_Damage(const EQApplicationPacket *app); + void Handle_OP_Death(const EQApplicationPacket *app); + void Handle_OP_DelegateAbility(const EQApplicationPacket *app); + void Handle_OP_DeleteItem(const EQApplicationPacket *app); + void Handle_OP_DeleteSpawn(const EQApplicationPacket *app); + void Handle_OP_DeleteSpell(const EQApplicationPacket *app); + void Handle_OP_DisarmTraps(const EQApplicationPacket *app); + void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app); + void Handle_OP_DuelResponse(const EQApplicationPacket *app); + void Handle_OP_DuelResponse2(const EQApplicationPacket *app); + void Handle_OP_DumpName(const EQApplicationPacket *app); + void Handle_OP_Dye(const EQApplicationPacket *app); + void Handle_OP_Emote(const EQApplicationPacket *app); + void Handle_OP_EndLootRequest(const EQApplicationPacket *app); + void Handle_OP_EnvDamage(const EQApplicationPacket *app); + void Handle_OP_FaceChange(const EQApplicationPacket *app); + void Handle_OP_FeignDeath(const EQApplicationPacket *app); + void Handle_OP_FindPersonRequest(const EQApplicationPacket *app); + void Handle_OP_Fishing(const EQApplicationPacket *app); + void Handle_OP_Forage(const EQApplicationPacket *app); + void Handle_OP_FriendsWho(const EQApplicationPacket *app); + void Handle_OP_GetGuildMOTD(const EQApplicationPacket *app); + void Handle_OP_GetGuildsList(const EQApplicationPacket *app); + void Handle_OP_GMBecomeNPC(const EQApplicationPacket *app); + void Handle_OP_GMDelCorpse(const EQApplicationPacket *app); + void Handle_OP_GMEmoteZone(const EQApplicationPacket *app); + void Handle_OP_GMEndTraining(const EQApplicationPacket *app); + void Handle_OP_GMFind(const EQApplicationPacket *app); + void Handle_OP_GMGoto(const EQApplicationPacket *app); + void Handle_OP_GMHideMe(const EQApplicationPacket *app); + void Handle_OP_GMKick(const EQApplicationPacket *app); + void Handle_OP_GMKill(const EQApplicationPacket *app); + void Handle_OP_GMLastName(const EQApplicationPacket *app); + void Handle_OP_GMNameChange(const EQApplicationPacket *app); + void Handle_OP_GMSearchCorpse(const EQApplicationPacket *app); + void Handle_OP_GMServers(const EQApplicationPacket *app); + void Handle_OP_GMSummon(const EQApplicationPacket *app); + void Handle_OP_GMToggle(const EQApplicationPacket *app); + void Handle_OP_GMTraining(const EQApplicationPacket *app); + void Handle_OP_GMTrainSkill(const EQApplicationPacket *app); + void Handle_OP_GMZoneRequest(const EQApplicationPacket *app); + void Handle_OP_GMZoneRequest2(const EQApplicationPacket *app); + void Handle_OP_GroupAcknowledge(const EQApplicationPacket *app); + void Handle_OP_GroupCancelInvite(const EQApplicationPacket *app); + void Handle_OP_GroupDelete(const EQApplicationPacket *app); + void Handle_OP_GroupDisband(const EQApplicationPacket *app); + void Handle_OP_GroupFollow(const EQApplicationPacket *app); + void Handle_OP_GroupFollow2(const EQApplicationPacket *app); + void Handle_OP_GroupInvite(const EQApplicationPacket *app); + void Handle_OP_GroupInvite2(const EQApplicationPacket *app); + void Handle_OP_GroupMakeLeader(const EQApplicationPacket *app); + void Handle_OP_GroupRoles(const EQApplicationPacket *app); + void Handle_OP_GroupUpdate(const EQApplicationPacket *app); + void Handle_OP_GuildBank(const EQApplicationPacket *app); + void Handle_OP_GuildCreate(const EQApplicationPacket *app); + void Handle_OP_GuildDelete(const EQApplicationPacket *app); + void Handle_OP_GuildDemote(const EQApplicationPacket *app); + void Handle_OP_GuildInvite(const EQApplicationPacket *app); + void Handle_OP_GuildInviteAccept(const EQApplicationPacket *app); + void Handle_OP_GuildLeader(const EQApplicationPacket *app); + void Handle_OP_GuildManageBanker(const EQApplicationPacket *app); + void Handle_OP_GuildPeace(const EQApplicationPacket *app); + void Handle_OP_GuildPromote(const EQApplicationPacket *app); + void Handle_OP_GuildPublicNote(const EQApplicationPacket *app); + void Handle_OP_GuildRemove(const EQApplicationPacket *app); + void Handle_OP_GuildStatus(const EQApplicationPacket *app); + void Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app); + void Handle_OP_GuildWar(const EQApplicationPacket *app); + void Handle_OP_Heartbeat(const EQApplicationPacket *app); + void Handle_OP_Hide(const EQApplicationPacket *app); + void Handle_OP_HideCorpse(const EQApplicationPacket *app); + void Handle_OP_Ignore(const EQApplicationPacket *app); + void Handle_OP_Illusion(const EQApplicationPacket *app); + void Handle_OP_InspectAnswer(const EQApplicationPacket *app); + void Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app); + void Handle_OP_InspectRequest(const EQApplicationPacket *app); + void Handle_OP_InstillDoubt(const EQApplicationPacket *app); + void Handle_OP_ItemLinkClick(const EQApplicationPacket *app); + void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app); + void Handle_OP_ItemName(const EQApplicationPacket *app); + void Handle_OP_ItemPreview(const EQApplicationPacket *app); + void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app); + void Handle_OP_Jump(const EQApplicationPacket *app); + void Handle_OP_KeyRing(const EQApplicationPacket *app); + void Handle_OP_LDoNButton(const EQApplicationPacket *app); + void Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app); + void Handle_OP_LDoNInspect(const EQApplicationPacket *app); + void Handle_OP_LDoNOpen(const EQApplicationPacket *app); + void Handle_OP_LDoNPickLock(const EQApplicationPacket *app); + void Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app); + void Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app); + void Handle_OP_LeaveAdventure(const EQApplicationPacket *app); + void Handle_OP_LeaveBoat(const EQApplicationPacket *app); + void Handle_OP_LFGCommand(const EQApplicationPacket *app); + void Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app); + void Handle_OP_LFGuild(const EQApplicationPacket *app); + void Handle_OP_LFPCommand(const EQApplicationPacket *app); + void Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app); + void Handle_OP_LoadSpellSet(const EQApplicationPacket *app); + void Handle_OP_Logout(const EQApplicationPacket *app); + void Handle_OP_LootItem(const EQApplicationPacket *app); + void Handle_OP_LootRequest(const EQApplicationPacket *app); + void Handle_OP_ManaChange(const EQApplicationPacket *app); + void Handle_OP_Medding(const EQApplicationPacket *app); + void Handle_OP_MemorizeSpell(const EQApplicationPacket *app); + void Handle_OP_Mend(const EQApplicationPacket *app); + void Handle_OP_MercenaryCommand(const EQApplicationPacket *app); + void Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app); + void Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app); + void Handle_OP_MercenaryDismiss(const EQApplicationPacket *app); + void Handle_OP_MercenaryHire(const EQApplicationPacket *app); + void Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app); + void Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app); + void Handle_OP_MoveCoin(const EQApplicationPacket *app); + void Handle_OP_MoveItem(const EQApplicationPacket *app); + void Handle_OP_OpenContainer(const EQApplicationPacket *app); + void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app); + void Handle_OP_OpenInventory(const EQApplicationPacket *app); + void Handle_OP_OpenTributeMaster(const EQApplicationPacket *app); + void Handle_OP_PDeletePetition(const EQApplicationPacket *app); + void Handle_OP_PetCommands(const EQApplicationPacket *app); + void Handle_OP_Petition(const EQApplicationPacket *app); + void Handle_OP_PetitionBug(const EQApplicationPacket *app); + void Handle_OP_PetitionCheckIn(const EQApplicationPacket *app); + void Handle_OP_PetitionCheckout(const EQApplicationPacket *app); + void Handle_OP_PetitionDelete(const EQApplicationPacket *app); + void Handle_OP_PetitionQue(const EQApplicationPacket *app); + void Handle_OP_PetitionRefresh(const EQApplicationPacket *app); + void Handle_OP_PetitionResolve(const EQApplicationPacket *app); + void Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app); + void Handle_OP_PickPocket(const EQApplicationPacket *app); + void Handle_OP_PopupResponse(const EQApplicationPacket *app); + void Handle_OP_PotionBelt(const EQApplicationPacket *app); + void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app); + void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app); + void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app); + void Handle_OP_RaidCommand(const EQApplicationPacket *app); + void Handle_OP_RandomReq(const EQApplicationPacket *app); + void Handle_OP_ReadBook(const EQApplicationPacket *app); + void Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app); + void Handle_OP_RecipeDetails(const EQApplicationPacket *app); + void Handle_OP_RecipesFavorite(const EQApplicationPacket *app); + void Handle_OP_RecipesSearch(const EQApplicationPacket *app); + void Handle_OP_ReloadUI(const EQApplicationPacket *app); + void Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app); + void Handle_OP_Report(const EQApplicationPacket *app); + void Handle_OP_RequestDuel(const EQApplicationPacket *app); + void Handle_OP_RequestTitles(const EQApplicationPacket *app); + void Handle_OP_RespawnWindow(const EQApplicationPacket *app); + void Handle_OP_Rewind(const EQApplicationPacket *app); + void Handle_OP_RezzAnswer(const EQApplicationPacket *app); + void Handle_OP_Sacrifice(const EQApplicationPacket *app); + void Handle_OP_SafeFallSuccess(const EQApplicationPacket *app); + void Handle_OP_SafePoint(const EQApplicationPacket *app); + void Handle_OP_Save(const EQApplicationPacket *app); + void Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app); + void Handle_OP_SelectTribute(const EQApplicationPacket *app); + void Handle_OP_SenseHeading(const EQApplicationPacket *app); + void Handle_OP_SenseTraps(const EQApplicationPacket *app); + void Handle_OP_SetGuildMOTD(const EQApplicationPacket *app); + void Handle_OP_SetRunMode(const EQApplicationPacket *app); + void Handle_OP_SetServerFilter(const EQApplicationPacket *app); + void Handle_OP_SetStartCity(const EQApplicationPacket *app); + void Handle_OP_SetTitle(const EQApplicationPacket *app); + void Handle_OP_Shielding(const EQApplicationPacket *app); + void Handle_OP_ShopEnd(const EQApplicationPacket *app); + void Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app); + void Handle_OP_ShopPlayerSell(const EQApplicationPacket *app); + void Handle_OP_ShopRequest(const EQApplicationPacket *app); + void Handle_OP_Sneak(const EQApplicationPacket *app); + void Handle_OP_SpawnAppearance(const EQApplicationPacket *app); + void Handle_OP_Split(const EQApplicationPacket *app); + void Handle_OP_Surname(const EQApplicationPacket *app); + void Handle_OP_SwapSpell(const EQApplicationPacket *app); + void Handle_OP_TargetCommand(const EQApplicationPacket *app); + void Handle_OP_TargetMouse(const EQApplicationPacket *app); + void Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app); + void Handle_OP_Taunt(const EQApplicationPacket *app); + void Handle_OP_TestBuff(const EQApplicationPacket *app); + void Handle_OP_TGB(const EQApplicationPacket *app); + void Handle_OP_Track(const EQApplicationPacket *app); + void Handle_OP_TrackTarget(const EQApplicationPacket *app); + void Handle_OP_TrackUnknown(const EQApplicationPacket *app); + void Handle_OP_TradeAcceptClick(const EQApplicationPacket *app); + void Handle_OP_TradeBusy(const EQApplicationPacket *app); + void Handle_OP_Trader(const EQApplicationPacket *app); + void Handle_OP_TraderBuy(const EQApplicationPacket *app); + void Handle_OP_TradeRequest(const EQApplicationPacket *app); + void Handle_OP_TradeRequestAck(const EQApplicationPacket *app); + void Handle_OP_TraderShop(const EQApplicationPacket *app); + void Handle_OP_TradeSkillCombine(const EQApplicationPacket *app); + void Handle_OP_Translocate(const EQApplicationPacket *app); + void Handle_OP_TributeItem(const EQApplicationPacket *app); + void Handle_OP_TributeMoney(const EQApplicationPacket *app); + void Handle_OP_TributeNPC(const EQApplicationPacket *app); + void Handle_OP_TributeToggle(const EQApplicationPacket *app); + void Handle_OP_TributeUpdate(const EQApplicationPacket *app); + void Handle_OP_VetClaimRequest(const EQApplicationPacket *app); + void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app); + void Handle_OP_WearChange(const EQApplicationPacket *app); + void Handle_OP_WhoAllRequest(const EQApplicationPacket *app); + void Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app); + void Handle_OP_XTargetRequest(const EQApplicationPacket *app); + void Handle_OP_YellForHelp(const EQApplicationPacket *app); + void Handle_OP_ZoneChange(const EQApplicationPacket *app); From ec8c46abfe3775bf5e08cff432b4bf28886b8779 Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 24 Sep 2014 19:46:53 -0700 Subject: [PATCH 157/368] I keep seeing a crash due to an invalidated iter during mob delete every few days, seeing if this fixes it. --- zone/entity.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 8d21262c6..243bf99f5 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -497,7 +497,6 @@ void EntityList::MobProcess() if (!it->second->Process()) { Mob *mob = it->second; uint16 tempid = it->first; - ++it; // we don't erase here because the destructor will if (mob->IsNPC()) { entity_list.RemoveNPC(mob->CastToNPC()->GetID()); } else if (mob->IsMerc()) { @@ -525,7 +524,12 @@ void EntityList::MobProcess() } entity_list.RemoveClient(mob->GetID()); } - entity_list.RemoveMob(tempid); + + if(entity_list.RemoveMob(tempid)) { + it = mob_list.begin(); + } else { + ++it; + } } else { ++it; } From a73ac9cfe85beef94c94e0f578e2bbe1718ea936 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 24 Sep 2014 23:35:10 -0400 Subject: [PATCH 158/368] Added helper function bool EQEmu::IsTradeskill(uint32 skill) Returns true if you pass a tradeskill to it, otherwise false --- changelog.txt | 1 + common/CMakeLists.txt | 3 ++- common/skills.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ common/skills.h | 5 +++++ world/client.cpp | 23 +++++------------------ 5 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 common/skills.cpp diff --git a/changelog.txt b/changelog.txt index f5ada28d5..fdc882932 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 09/24/2014 == Uleat: Re-ordered server opcodes and handlers to give them some predictability of location (I need this for the inventory re-enumeration.) +demonstar55: Added helper function bool EQEmu::IsTradeskill(uint32 skill) == 09/22/2014 == Akkadius: #resetaa now covers the function of #resetaa and #refundaa diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 6047e2712..65d5e9caf 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -55,8 +55,9 @@ SET(common_sources rulesys.cpp serverinfo.cpp shareddb.cpp + skills.cpp spdat.cpp - string_util.cpp + string_util.cpp struct_strategy.cpp tcp_connection.cpp tcp_server.cpp diff --git a/common/skills.cpp b/common/skills.cpp new file mode 100644 index 000000000..96b87c4a9 --- /dev/null +++ b/common/skills.cpp @@ -0,0 +1,40 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include "types.h" +#include "skills.h" + +bool EQEmu::IsTradeskill(uint32 skill) +{ + switch (skill) { + case SkillFishing: + case SkillMakePoison: + case SkillTinkering: + case SkillResearch: + case SkillAlchemy: + case SkillBaking: + case SkillTailoring: + case SkillBlacksmithing: + case SkillFletching: + case SkillBrewing: + case SkillPottery: + case SkillJewelryMaking: + return true; + default: + return false; + } +} diff --git a/common/skills.h b/common/skills.h index 88b05967f..15876cedc 100644 --- a/common/skills.h +++ b/common/skills.h @@ -260,4 +260,9 @@ typedef enum { #define HIGHEST_SKILL FRENZY */ +// for skill related helper functions +namespace EQEmu { + bool IsTradeskill(uint32 skill); +} + #endif diff --git a/world/client.cpp b/world/client.cpp index ca90b1f46..9120ce70b 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1790,28 +1790,15 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) return Charerrors == 0; } -void Client::SetClassStartingSkills( PlayerProfile_Struct *pp ) +void Client::SetClassStartingSkills(PlayerProfile_Struct *pp) { - for(uint32 i = 0; i <= HIGHEST_SKILL; ++i) { - if(pp->skills[i] == 0) { - if(i >= SkillSpecializeAbjure && i <= SkillSpecializeEvocation) { + for (uint32 i = 0; i <= HIGHEST_SKILL; ++i) { + if (pp->skills[i] == 0) { + if (i >= SkillSpecializeAbjure && i <= SkillSpecializeEvocation) continue; - } - if(i == SkillMakePoison || - i == SkillTinkering || - i == SkillResearch || - i == SkillAlchemy || - i == SkillBaking || - i == SkillTailoring || - i == SkillBlacksmithing || - i == SkillFletching || - i == SkillBrewing || - i == SkillPottery || - i == SkillJewelryMaking || - i == SkillBegging) { + if (EQEmu::IsTradeskill(i) || i == SkillBegging) continue; - } pp->skills[i] = database.GetSkillCap(pp->class_, (SkillUseTypes)i, 1); } From c0cbbf3a656f7d1f5b2ca259bad1993b218673f6 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 25 Sep 2014 03:14:43 -0400 Subject: [PATCH 159/368] World needs to load skill cap data for char creation --- world/net.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/world/net.cpp b/world/net.cpp index 3c4ab53c3..c7340333e 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -284,9 +284,11 @@ int main(int argc, char** argv) { database.ClearRaid(); database.ClearRaidDetails(); _log(WORLD__INIT, "Loading items.."); - if (!database.LoadItems()) { + if (!database.LoadItems()) _log(WORLD__INIT_ERR, "Error: Could not load item data. But ignoring"); - } + _log(WORLD__INIT, "Loading skill caps.."); + if (!database.LoadSkillCaps()) + _log(WORLD__INIT_ERR, "Error: Could not load skill cap data. But ignoring"); _log(WORLD__INIT, "Loading guilds.."); guild_mgr.LoadGuilds(); //rules: From aa021addc1928d8746ab4d96033fa724303c5433 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 25 Sep 2014 03:59:59 -0700 Subject: [PATCH 160/368] Fix for potion belt name loading. --- common/CMakeLists.txt | 1 + common/data_verification.h | 5 +++++ common/database.h | 6 ------ tests/data_verification_test.h | 14 ++++++++++++++ zone/client_packet.cpp | 7 +++++++ zone/lua_packet.cpp | 1 + zone/zonedb.cpp | 10 ++++++++-- 7 files changed, 36 insertions(+), 8 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 65d5e9caf..ac42f5e4e 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -103,6 +103,7 @@ SET(common_headers crash.h crc16.h crc32.h + data_verification.h database.h dbcore.h debug.h diff --git a/common/data_verification.h b/common/data_verification.h index d42f72aac..9da85a579 100644 --- a/common/data_verification.h +++ b/common/data_verification.h @@ -38,6 +38,11 @@ T ClampUpper(const T& value, const T& upper) { return std::min(value, upper); } +template +bool ValueWithin(const T& value, const T& lower, const T& upper) { + return value >= lower && value <= upper; +} + } #endif diff --git a/common/database.h b/common/database.h index c4d3af336..a24198ca4 100644 --- a/common/database.h +++ b/common/database.h @@ -26,12 +26,6 @@ #include "dbcore.h" #include "linked_list.h" #include "eq_packet_structs.h" -/*#include "eq_stream.h" -#include "guilds.h" -#include "misc_functions.h" -#include "mutex.h" -#include "item.h" -#include "extprofile.h"*/ #include #include #include diff --git a/tests/data_verification_test.h b/tests/data_verification_test.h index 1a151c7fa..b9eb210fa 100644 --- a/tests/data_verification_test.h +++ b/tests/data_verification_test.h @@ -29,6 +29,7 @@ public: TEST_ADD(DataVerificationTest::Clamp); TEST_ADD(DataVerificationTest::ClampUpper); TEST_ADD(DataVerificationTest::ClampLower); + TEST_ADD(DataVerificationTest::ValueWithin); } ~DataVerificationTest() { @@ -89,6 +90,19 @@ public: TEST_ASSERT_EQUALS(vi1, 500); TEST_ASSERT_EQUALS(vi2, 750); } + + void ValueWithin() { + float value_f = 500.0f; + int value_i = 500; + + TEST_ASSERT(EQEmu::ValueWithin(value_f, 0.0f, 1000.0f)); + TEST_ASSERT(!EQEmu::ValueWithin(value_f, 0.0f, 400.0f)); + TEST_ASSERT(!EQEmu::ValueWithin(value_f, 600.0f, 900.0f)); + + TEST_ASSERT(EQEmu::ValueWithin(value_i, 0, 1000)); + TEST_ASSERT(!EQEmu::ValueWithin(value_i, 0, 400)); + TEST_ASSERT(!EQEmu::ValueWithin(value_i, 600, 900)); + } }; #endif diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 62e31c075..0061efff4 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -48,6 +48,7 @@ #include "../common/guilds.h" #include "../common/rulesys.h" #include "../common/spdat.h" +#include "../common/data_verification.h" #include "petitions.h" #include "npc_ai.h" #include "../common/skills.h" @@ -10490,7 +10491,13 @@ void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) DumpPacket(app); return; } + MovePotionToBelt_Struct *mptbs = (MovePotionToBelt_Struct*)app->pBuffer; + if(!EQEmu::ValueWithin(mptbs->SlotNumber, 0U, 3U)) { + LogFile->write(EQEMuLog::Debug, "Client::Handle_OP_PotionBelt mptbs->SlotNumber out of range."); + return; + } + if (mptbs->Action == 0) { const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID); if (BaseItem) { diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index deff0f0f2..e16a85c6d 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -301,6 +301,7 @@ luabind::scope lua_register_packet() { .def("ReadFixedLengthString", &Lua_Packet::ReadFixedLengthString); } +//TODO: Reorder these to match emu_oplist.h again luabind::scope lua_register_packet_opcodes() { return luabind::class_("Opcode") .enum_("constants") diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 3728e3144..b49837704 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1178,8 +1178,14 @@ bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struc } for (auto row = results.begin(); row != results.end(); ++row) { i = atoi(row[0]); /* Potion belt slot number */ - pp->potionbelt.items[i].item_id = atoi(row[1]); - pp->potionbelt.items[i].icon = atoi(row[2]); + uint32 item_id = atoi(row[1]); + const Item_Struct *item = database.GetItem(item_id); + + if(item) { + pp->potionbelt.items[i].item_id = item_id; + pp->potionbelt.items[i].icon = atoi(row[2]); + strncpy(pp->potionbelt.items[i].item_name, item->Name, 64); + } } return true; } From c57292a9dd33f79980def3f50402b1bdfcf60e08 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 25 Sep 2014 14:54:40 -0700 Subject: [PATCH 161/368] Tired of this GLM warning (and we dont even use glm rotate grr) also reworked how I approached that peq entity process crash after thinking about it a bit --- CMakeLists.txt | 1 + zone/entity.cpp | 52 ++++++++++++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d0b6a632..8f9c5429b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,6 +289,7 @@ ADD_DEFINITIONS(-DLOG_LEVEL_DEBUG=${EQEMU_LOG_LEVEL_DEBUG}) ADD_DEFINITIONS(-DLOG_LEVEL_QUEST=${EQEMU_LOG_LEVEL_QUEST}) ADD_DEFINITIONS(-DLOG_LEVEL_COMMANDS=${EQEMU_LOG_LEVEL_COMMANDS}) ADD_DEFINITIONS(-DLOG_LEVEL_CRASH=${EQEMU_LOG_LEVEL_CRASH}) +ADD_DEFINITIONS(-DGLM_FORCE_RADIANS) IF(EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS) ADD_DEFINITIONS(-DRETRANSMIT_ACKED_PACKETS=true) diff --git a/zone/entity.cpp b/zone/entity.cpp index 243bf99f5..fd031d618 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -490,22 +490,36 @@ void EntityList::MobProcess() #endif auto it = mob_list.begin(); while (it != mob_list.end()) { - if (!it->second) { + uint16 id = it->first; + Mob *mob = it->second; + + size_t sz = mob_list.size(); + bool p_val = mob->Process(); + size_t a_sz = mob_list.size(); + + if(a_sz > sz) { + //increased size can potentially screw with iterators so reset it to current value + //if buckets are re-orderered we may skip a process here and there but since + //process happens so often it shouldn't matter much + it = mob_list.find(id); + ++it; + } else { ++it; - continue; } - if (!it->second->Process()) { - Mob *mob = it->second; - uint16 tempid = it->first; - if (mob->IsNPC()) { - entity_list.RemoveNPC(mob->CastToNPC()->GetID()); - } else if (mob->IsMerc()) { - entity_list.RemoveMerc(mob->CastToMerc()->GetID()); + + if(!p_val) { + if(mob->IsNPC()) { + entity_list.RemoveNPC(id); + } + else if(mob->IsMerc()) { + entity_list.RemoveMerc(id); #ifdef BOTS - } else if (mob->IsBot()) { - entity_list.RemoveBot(mob->CastToBot()->GetID()); + } + else if(mob->IsBot()) { + entity_list.RemoveBot(id); #endif - } else { + } + else { #ifdef _WINDOWS struct in_addr in; in.s_addr = mob->CastToClient()->GetIP(); @@ -513,25 +527,19 @@ void EntityList::MobProcess() #endif zone->StartShutdownTimer(); Group *g = GetGroupByMob(mob); - if (g) { + if(g) { LogFile->write(EQEMuLog::Error, "About to delete a client still in a group."); g->DelMember(mob); } Raid *r = entity_list.GetRaidByClient(mob->CastToClient()); - if (r) { + if(r) { LogFile->write(EQEMuLog::Error, "About to delete a client still in a raid."); r->MemberZoned(mob->CastToClient()); } - entity_list.RemoveClient(mob->GetID()); + entity_list.RemoveClient(id); } - if(entity_list.RemoveMob(tempid)) { - it = mob_list.begin(); - } else { - ++it; - } - } else { - ++it; + entity_list.RemoveMob(id); } } } From 039e321ddeedb74872fc0a0599daa1f47e9cf638 Mon Sep 17 00:00:00 2001 From: Uleat Date: Thu, 25 Sep 2014 18:48:58 -0400 Subject: [PATCH 162/368] More re-enumeration prep work --- common/misc_functions.cpp | 23 +++ common/misc_functions.h | 2 + common/patches/client62.cpp | 16 -- common/patches/client62_ops.h | 53 +++---- common/patches/rof_ops.h | 282 ++++++++++++++++----------------- common/patches/sod_ops.h | 198 ++++++++++++----------- common/patches/sof_ops.h | 164 ++++++++++--------- common/patches/titanium.cpp | 16 -- common/patches/titanium_ops.h | 79 +++++---- common/patches/underfoot_ops.h | 216 +++++++++++++------------ 10 files changed, 516 insertions(+), 533 deletions(-) diff --git a/common/misc_functions.cpp b/common/misc_functions.cpp index e7b581733..882cc61c7 100644 --- a/common/misc_functions.cpp +++ b/common/misc_functions.cpp @@ -387,3 +387,26 @@ float EQHtoFloat(int d) { return(360.0f - float((d * 360) >> 11)); } + +// returns a swapped-bit value for use in client translator and inventory functions +uint32 SwapBits21and22(uint32 mask) +{ + static const uint32 BIT21 = 1 << 21; + static const uint32 BIT22 = 1 << 22; + static const uint32 SWAPBITS = (BIT21 | BIT22); + + if ((bool)(mask & BIT21) != (bool)(mask & BIT22)) + mask ^= SWAPBITS; + + return mask; +} + +// returns an unset bit 22 value for use in client translators +uint32 Catch22(uint32 mask) +{ + static const uint32 KEEPBITS = ~(1 << 22); + + mask &= KEEPBITS; + + return mask; +} diff --git a/common/misc_functions.h b/common/misc_functions.h index c057af8d8..abc9747b5 100644 --- a/common/misc_functions.h +++ b/common/misc_functions.h @@ -102,6 +102,8 @@ int FloatToEQ13(float d); int NewFloatToEQ13(float d); int FloatToEQ19(float d); int FloatToEQH(float d); +uint32 SwapBits21and22(uint32 mask); +uint32 Catch22(uint32 mask); // macro to catch fp errors (provided by noudness) #define FCMP(a,b) (fabs(a-b) < FLT_EPSILON) diff --git a/common/patches/client62.cpp b/common/patches/client62.cpp index 10cc331ec..494049876 100644 --- a/common/patches/client62.cpp +++ b/common/patches/client62.cpp @@ -131,22 +131,6 @@ static inline uint32 Client62ToServerCorpseSlot(uint32 Client62Corpse) { // reserved } */ -/* -static inline uint32 RemovePowerSourceBit(uint32 slots) { // shouldn't need to add one..just grab the actual server reference, if so... - static const uint32 BIT21 = 1 << 21; - static const uint32 BIT22 = 1 << 22; - static const uint32 KEEPBITS = ~(BIT21 | BIT22); - - bool wearammo = slots & BIT22; - - slots &= KEEPBITS; - - if (wearammo) - slots |= BIT21; - - return slots; -} -*/ EAT_ENCODE(OP_ZoneServerReady) diff --git a/common/patches/client62_ops.h b/common/patches/client62_ops.h index 5f1a1b4a0..8f544ccfc 100644 --- a/common/patches/client62_ops.h +++ b/common/patches/client62_ops.h @@ -1,36 +1,35 @@ - -//list of packets we need to encode on the way out: +// out-going packets that require an ENCODE translation: +E(OP_Action) +E(OP_BazaarSearch) +E(OP_BecomeTrader) +E(OP_CharInventory) +E(OP_DeleteSpawn) +E(OP_GuildMemberLevelUpdate) +E(OP_GuildMemberList) +E(OP_Illusion) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_LeadershipExpUpdate) +E(OP_NewSpawn) +E(OP_OnLevelMessage) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_ReadBook) +E(OP_RespondAA) E(OP_SendAATable) E(OP_SendCharInfo) -E(OP_LeadershipExpUpdate) -E(OP_PlayerProfile) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ZoneEntry) -E(OP_ItemPacket) -E(OP_ItemLinkResponse) -E(OP_CharInventory) -E(OP_GuildMemberList) -E(OP_ZoneServerReady) -E(OP_GuildMemberLevelUpdate) -E(OP_ReadBook) -E(OP_Illusion) E(OP_Track) -E(OP_BazaarSearch) -E(OP_RespondAA) -E(OP_DeleteSpawn) E(OP_WearChange) -E(OP_Action) -E(OP_BecomeTrader) -E(OP_PetBuffWindow) -E(OP_OnLevelMessage) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) +E(OP_ZoneEntry) +E(OP_ZoneServerReady) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_WhoAllRequest) -D(OP_ReadBook) D(OP_FaceChange) +D(OP_ItemLinkClick) +D(OP_ReadBook) +D(OP_SetServerFilter) D(OP_WearChange) +D(OP_WhoAllRequest) #undef E #undef D diff --git a/common/patches/rof_ops.h b/common/patches/rof_ops.h index 9e3a91c7e..030c331d6 100644 --- a/common/patches/rof_ops.h +++ b/common/patches/rof_ops.h @@ -1,162 +1,160 @@ - -//list of packets we need to encode on the way out: - -E(OP_SendCharInfo) -E(OP_ZoneServerInfo) -E(OP_SendAATable) -E(OP_PlayerProfile) -E(OP_ZoneEntry) -E(OP_CharInventory) -E(OP_NewZone) -E(OP_SpawnDoor) -E(OP_GroundSpawn) -E(OP_SendZonepoints) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ItemLinkResponse) -E(OP_ItemPacket) -E(OP_GuildMemberList) -E(OP_Illusion) -E(OP_ManaChange) -E(OP_ClientUpdate) -E(OP_LeadershipExpUpdate) -E(OP_ExpansionInfo) -E(OP_LogServer) -E(OP_Damage) -E(OP_Buff) +// out-going packets that require an ENCODE translation: E(OP_Action) -E(OP_Consider) -E(OP_CancelTrade) -E(OP_ShopPlayerSell) -E(OP_DeleteItem) -E(OP_ItemVerifyReply) -E(OP_DeleteCharge) -E(OP_MoveItem) -//E(OP_OpenNewTasksWindow) -E(OP_BazaarSearch) -E(OP_Trader) -E(OP_TraderBuy) -E(OP_LootItem) -E(OP_TributeItem) -E(OP_SomeItemPacketMaybe) -E(OP_ReadBook) -E(OP_Stun) -E(OP_ZonePlayerToBind) E(OP_AdventureMerchantSell) -E(OP_RaidUpdate) -E(OP_RaidJoin) -E(OP_VetRewardsAvaliable) -E(OP_InspectRequest) -E(OP_GroupInvite) -E(OP_GroupFollow) -E(OP_GroupFollow2) -E(OP_GroupUpdate) -E(OP_GroupCancelInvite) -E(OP_WhoAllResponse) -E(OP_Track) -E(OP_ShopPlayerBuy) -E(OP_PetBuffWindow) -E(OP_OnLevelMessage) -E(OP_Barter) +E(OP_AltCurrency) +E(OP_AltCurrencySell) +E(OP_Animation) E(OP_ApplyPoison) +E(OP_Barter) +E(OP_BazaarSearch) +E(OP_BeginCast) +E(OP_BlockedBuffs) +E(OP_Buff) +E(OP_BuffCreate) +E(OP_CancelTrade) +E(OP_CastSpell) E(OP_ChannelMessage) -E(OP_GuildsList) +E(OP_CharInventory) +E(OP_ClickObjectAction) +E(OP_ClientUpdate) +E(OP_Consider) +E(OP_Damage) +E(OP_DeleteCharge) +E(OP_DeleteItem) +E(OP_DeleteSpawn) +E(OP_DisciplineUpdate) +E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) E(OP_DzExpeditionInfo) -E(OP_DzCompass) -E(OP_DzMemberList) E(OP_DzExpeditionList) -E(OP_DzLeaderStatus) E(OP_DzJoinExpeditionConfirm) -E(OP_TargetBuffs) -E(OP_BuffCreate) -E(OP_SpawnAppearance) -E(OP_RespondAA) -E(OP_DisciplineUpdate) -E(OP_AltCurrencySell) -E(OP_AltCurrency) -E(OP_RequestClientZoneChange) -E(OP_ZoneChange) -E(OP_WearChange) -E(OP_ShopRequest) -E(OP_CastSpell) -E(OP_InterruptCast) -E(OP_SendMembership) -E(OP_Animation) -E(OP_HPUpdate) -E(OP_BlockedBuffs) -E(OP_RemoveBlockedBuffs) -E(OP_DeleteSpawn) -E(OP_ClickObjectAction) -E(OP_RecipeAutoCombine) -E(OP_GMTrainSkillConfirm) -E(OP_SkillUpdate) -E(OP_TributeInfo) -E(OP_TaskHistoryReply) -E(OP_TaskDescription) -E(OP_SetGuildRank) -E(OP_MercenaryDataUpdate) -E(OP_MercenaryDataResponse) -E(OP_GuildMemberUpdate) +E(OP_DzLeaderStatus) +E(OP_DzMemberList) +E(OP_ExpansionInfo) E(OP_GMLastName) -E(OP_BeginCast) +E(OP_GMTrainSkillConfirm) +E(OP_GroundSpawn) +E(OP_GroupCancelInvite) +E(OP_GroupFollow) +E(OP_GroupFollow2) +E(OP_GroupInvite) +E(OP_GroupUpdate) +E(OP_GuildMemberList) +E(OP_GuildMemberUpdate) +E(OP_GuildsList) +E(OP_HPUpdate) +E(OP_Illusion) +E(OP_InspectRequest) +E(OP_InterruptCast) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_ItemVerifyReply) +E(OP_LeadershipExpUpdate) +E(OP_LogServer) +E(OP_LootItem) +E(OP_ManaChange) +E(OP_MercenaryDataResponse) +E(OP_MercenaryDataUpdate) +E(OP_MoveItem) +E(OP_NewSpawn) +E(OP_NewZone) +E(OP_OnLevelMessage) +//E(OP_OpenNewTasksWindow) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_RaidJoin) +E(OP_RaidUpdate) +E(OP_ReadBook) +E(OP_RecipeAutoCombine) +E(OP_RemoveBlockedBuffs) +E(OP_RequestClientZoneChange) +E(OP_RespondAA) E(OP_RezzRequest) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) -D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_ConsiderCorpse) -D(OP_Consider) -D(OP_ClientUpdate) -D(OP_MoveItem) -D(OP_WhoAllRequest) +E(OP_SendAATable) +E(OP_SendCharInfo) +E(OP_SendMembership) +E(OP_SendZonepoints) +E(OP_SetGuildRank) +E(OP_ShopPlayerBuy) +E(OP_ShopPlayerSell) +E(OP_ShopRequest) +E(OP_SkillUpdate) +E(OP_SomeItemPacketMaybe) +E(OP_SpawnAppearance) +E(OP_SpawnDoor) +E(OP_Stun) +E(OP_TargetBuffs) +E(OP_TaskDescription) +E(OP_TaskHistoryReply) +E(OP_Track) +E(OP_Trader) +E(OP_TraderBuy) +E(OP_TributeInfo) +E(OP_TributeItem) +E(OP_VetRewardsAvaliable) +E(OP_WearChange) +E(OP_WhoAllResponse) +E(OP_ZoneChange) +E(OP_ZoneEntry) +E(OP_ZonePlayerToBind) +E(OP_ZoneServerInfo) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_AltCurrencySell) +D(OP_AltCurrencySellSelection) +D(OP_ApplyPoison) +D(OP_AugmentInfo) +D(OP_AugmentItem) +D(OP_BazaarSearch) +D(OP_BlockedBuffs) D(OP_Buff) -D(OP_ShopPlayerSell) -D(OP_Consume) +D(OP_BuffRemoveRequest) D(OP_CastSpell) -D(OP_Save) -D(OP_ItemVerifyRequest) -D(OP_GroupInvite) -D(OP_GroupInvite2) +D(OP_ChannelMessage) +D(OP_CharacterCreate) +D(OP_ClientUpdate) +D(OP_Consider) +D(OP_ConsiderCorpse) +D(OP_Consume) +D(OP_Damage) +D(OP_DeleteItem) +D(OP_EnvDamage) +D(OP_FaceChange) +D(OP_FindPersonRequest) +D(OP_GMLastName) +D(OP_GroupCancelInvite) +D(OP_GroupDisband) D(OP_GroupFollow) D(OP_GroupFollow2) -D(OP_GroupDisband) -D(OP_GroupCancelInvite) -D(OP_FindPersonRequest) -D(OP_TraderBuy) -D(OP_LootItem) -D(OP_TributeItem) -D(OP_ReadBook) -D(OP_AugmentInfo) -D(OP_FaceChange) -D(OP_AdventureMerchantSell) -D(OP_TradeSkillCombine) -D(OP_RaidInvite) -D(OP_InspectRequest) -D(OP_ShopPlayerBuy) -D(OP_BazaarSearch) -D(OP_LoadSpellSet) -D(OP_ApplyPoison) -D(OP_Damage) -D(OP_EnvDamage) -D(OP_ChannelMessage) -D(OP_DeleteItem) -D(OP_AugmentItem) -D(OP_PetCommands) -D(OP_BuffRemoveRequest) -D(OP_AltCurrencySellSelection) -D(OP_AltCurrencySell) -D(OP_ZoneChange) -D(OP_ZoneEntry) -D(OP_ShopRequest) -D(OP_BlockedBuffs) -D(OP_RemoveBlockedBuffs) -D(OP_RecipeAutoCombine) +D(OP_GroupInvite) +D(OP_GroupInvite2) D(OP_GuildDemote) D(OP_GuildRemove) D(OP_GuildStatus) -D(OP_Trader) -D(OP_GMLastName) +D(OP_InspectRequest) +D(OP_ItemLinkClick) +D(OP_ItemVerifyRequest) +D(OP_LoadSpellSet) +D(OP_LootItem) +D(OP_MoveItem) +D(OP_PetCommands) +D(OP_RaidInvite) +D(OP_ReadBook) +D(OP_RecipeAutoCombine) +D(OP_RemoveBlockedBuffs) D(OP_RezzAnswer) +D(OP_Save) +D(OP_SetServerFilter) +D(OP_ShopPlayerBuy) +D(OP_ShopPlayerSell) +D(OP_ShopRequest) +D(OP_Trader) +D(OP_TraderBuy) +D(OP_TradeSkillCombine) +D(OP_TributeItem) +D(OP_WhoAllRequest) +D(OP_ZoneChange) +D(OP_ZoneEntry) #undef E #undef D diff --git a/common/patches/sod_ops.h b/common/patches/sod_ops.h index d7b87d514..792c9e5d3 100644 --- a/common/patches/sod_ops.h +++ b/common/patches/sod_ops.h @@ -1,117 +1,115 @@ - -//list of packets we need to encode on the way out: - -E(OP_SendCharInfo) -E(OP_ZoneServerInfo) -E(OP_SendAATable) -E(OP_PlayerProfile) -E(OP_ZoneEntry) -E(OP_CharInventory) -E(OP_NewZone) -E(OP_SpawnDoor) -E(OP_GroundSpawn) -E(OP_SendZonepoints) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ItemLinkResponse) -E(OP_ItemPacket) -E(OP_GuildMemberList) -E(OP_Illusion) -E(OP_ManaChange) -E(OP_ClientUpdate) -E(OP_LeadershipExpUpdate) -E(OP_ExpansionInfo) -E(OP_LogServer) -E(OP_Damage) -E(OP_Buff) +// out-going packets that require an ENCODE translation: E(OP_Action) -E(OP_Consider) -E(OP_CancelTrade) -E(OP_ShopPlayerSell) -E(OP_DeleteItem) -E(OP_ItemVerifyReply) -E(OP_DeleteCharge) -E(OP_MoveItem) -E(OP_OpenNewTasksWindow) -E(OP_BazaarSearch) -E(OP_Trader) -E(OP_TraderBuy) -E(OP_LootItem) -E(OP_TributeItem) -E(OP_SomeItemPacketMaybe) -E(OP_ReadBook) -E(OP_Stun) -E(OP_ZonePlayerToBind) E(OP_AdventureMerchantSell) -E(OP_RaidUpdate) -E(OP_RaidJoin) -E(OP_VetRewardsAvaliable) -E(OP_InspectRequest) -E(OP_GroupInvite) -E(OP_GroupFollow) -E(OP_GroupFollow2) -E(OP_GroupUpdate) -E(OP_GroupCancelInvite) -E(OP_WhoAllResponse) -E(OP_Track) -E(OP_ShopPlayerBuy) -E(OP_PetBuffWindow) -E(OP_OnLevelMessage) -E(OP_Barter) +E(OP_AltCurrencySell) E(OP_ApplyPoison) +E(OP_Barter) +E(OP_BazaarSearch) +E(OP_Buff) +E(OP_CancelTrade) +E(OP_CharInventory) +E(OP_ClientUpdate) +E(OP_Consider) +E(OP_Damage) +E(OP_DeleteCharge) +E(OP_DeleteItem) +E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) E(OP_DzExpeditionInfo) -E(OP_DzCompass) -E(OP_DzMemberList) E(OP_DzExpeditionList) -E(OP_DzLeaderStatus) E(OP_DzJoinExpeditionConfirm) -E(OP_TargetBuffs) -E(OP_AltCurrencySell) -E(OP_WearChange) +E(OP_DzLeaderStatus) +E(OP_DzMemberList) +E(OP_ExpansionInfo) +E(OP_GroundSpawn) +E(OP_GroupCancelInvite) +E(OP_GroupFollow) +E(OP_GroupFollow2) +E(OP_GroupInvite) +E(OP_GroupUpdate) +E(OP_GuildMemberList) +E(OP_Illusion) +E(OP_InspectRequest) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_ItemVerifyReply) +E(OP_LeadershipExpUpdate) +E(OP_LogServer) +E(OP_LootItem) +E(OP_ManaChange) E(OP_MercenaryDataResponse) E(OP_MercenaryDataUpdate) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) -D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_ConsiderCorpse) -D(OP_Consider) -D(OP_ClientUpdate) -D(OP_MoveItem) -D(OP_WhoAllRequest) +E(OP_MoveItem) +E(OP_NewSpawn) +E(OP_NewZone) +E(OP_OnLevelMessage) +E(OP_OpenNewTasksWindow) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_RaidJoin) +E(OP_RaidUpdate) +E(OP_ReadBook) +E(OP_SendAATable) +E(OP_SendCharInfo) +E(OP_SendZonepoints) +E(OP_ShopPlayerBuy) +E(OP_ShopPlayerSell) +E(OP_SomeItemPacketMaybe) +E(OP_SpawnDoor) +E(OP_Stun) +E(OP_TargetBuffs) +E(OP_Track) +E(OP_Trader) +E(OP_TraderBuy) +E(OP_TributeItem) +E(OP_VetRewardsAvaliable) +E(OP_WearChange) +E(OP_WhoAllResponse) +E(OP_ZoneEntry) +E(OP_ZonePlayerToBind) +E(OP_ZoneServerInfo) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_AltCurrencySell) +D(OP_AltCurrencySellSelection) +D(OP_ApplyPoison) +D(OP_AugmentInfo) +D(OP_AugmentItem) +D(OP_BazaarSearch) D(OP_Buff) -D(OP_ShopPlayerSell) -D(OP_Consume) +D(OP_Bug) D(OP_CastSpell) -D(OP_Save) -D(OP_ItemVerifyRequest) -D(OP_GroupInvite) -D(OP_GroupInvite2) +D(OP_CharacterCreate) +D(OP_ClientUpdate) +D(OP_Consider) +D(OP_ConsiderCorpse) +D(OP_Consume) +D(OP_DeleteItem) +D(OP_FaceChange) +D(OP_FindPersonRequest) +D(OP_GroupCancelInvite) +D(OP_GroupDisband) D(OP_GroupFollow) D(OP_GroupFollow2) -D(OP_GroupDisband) -D(OP_GroupCancelInvite) -D(OP_FindPersonRequest) -D(OP_TraderBuy) -D(OP_LootItem) -D(OP_TributeItem) -D(OP_ReadBook) -D(OP_AugmentInfo) -D(OP_FaceChange) -D(OP_AdventureMerchantSell) -D(OP_TradeSkillCombine) -D(OP_RaidInvite) +D(OP_GroupInvite) +D(OP_GroupInvite2) D(OP_InspectRequest) -D(OP_WearChange) -D(OP_ShopPlayerBuy) -D(OP_BazaarSearch) +D(OP_ItemLinkClick) +D(OP_ItemVerifyRequest) D(OP_LoadSpellSet) -D(OP_ApplyPoison) -D(OP_DeleteItem) -D(OP_AugmentItem) -D(OP_Bug) -D(OP_AltCurrencySellSelection) -D(OP_AltCurrencySell) +D(OP_LootItem) +D(OP_MoveItem) +D(OP_RaidInvite) +D(OP_ReadBook) +D(OP_Save) +D(OP_SetServerFilter) +D(OP_ShopPlayerBuy) +D(OP_ShopPlayerSell) +D(OP_TraderBuy) +D(OP_TradeSkillCombine) +D(OP_TributeItem) +D(OP_WearChange) +D(OP_WhoAllRequest) #undef E #undef D diff --git a/common/patches/sof_ops.h b/common/patches/sof_ops.h index ac8dd2008..f039c2189 100644 --- a/common/patches/sof_ops.h +++ b/common/patches/sof_ops.h @@ -1,100 +1,98 @@ - -//list of packets we need to encode on the way out: - -E(OP_SendCharInfo) -E(OP_ZoneServerInfo) -E(OP_SendAATable) -E(OP_PlayerProfile) -E(OP_ZoneEntry) -E(OP_CharInventory) -E(OP_NewZone) -E(OP_SpawnDoor) -E(OP_GroundSpawn) -E(OP_SendZonepoints) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ItemLinkResponse) -E(OP_ItemPacket) -E(OP_GuildMemberList) -E(OP_Illusion) -E(OP_ManaChange) -E(OP_ClientUpdate) -E(OP_LeadershipExpUpdate) -E(OP_ExpansionInfo) -E(OP_LogServer) -E(OP_Damage) -E(OP_Buff) +// out-going packets that require an ENCODE translation: E(OP_Action) -E(OP_Consider) -E(OP_CancelTrade) -E(OP_ShopPlayerSell) -E(OP_DeleteItem) -E(OP_ItemVerifyReply) -E(OP_DeleteCharge) -E(OP_MoveItem) -E(OP_OpenNewTasksWindow) -E(OP_BazaarSearch) -E(OP_Trader) -E(OP_TraderBuy) -E(OP_LootItem) -E(OP_TributeItem) -E(OP_SomeItemPacketMaybe) -E(OP_ReadBook) -E(OP_Stun) -E(OP_ZonePlayerToBind) E(OP_AdventureMerchantSell) -E(OP_RaidUpdate) -E(OP_RaidJoin) -E(OP_VetRewardsAvaliable) -E(OP_InspectRequest) -E(OP_Track) -E(OP_DeleteSpawn) +E(OP_AltCurrencySell) E(OP_ApplyPoison) +E(OP_BazaarSearch) +E(OP_BecomeTrader) +E(OP_Buff) +E(OP_CancelTrade) +E(OP_CharInventory) +E(OP_ClientUpdate) +E(OP_Consider) +E(OP_Damage) +E(OP_DeleteCharge) +E(OP_DeleteItem) +E(OP_DeleteSpawn) +E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) E(OP_DzExpeditionInfo) -E(OP_DzCompass) -E(OP_DzMemberList) E(OP_DzExpeditionList) -E(OP_DzLeaderStatus) E(OP_DzJoinExpeditionConfirm) -E(OP_BecomeTrader) -E(OP_PetBuffWindow) +E(OP_DzLeaderStatus) +E(OP_DzMemberList) +E(OP_ExpansionInfo) +E(OP_GroundSpawn) +E(OP_GuildMemberList) +E(OP_Illusion) +E(OP_InspectRequest) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_ItemVerifyReply) +E(OP_LeadershipExpUpdate) +E(OP_LogServer) +E(OP_LootItem) +E(OP_ManaChange) +E(OP_MoveItem) +E(OP_NewSpawn) +E(OP_NewZone) E(OP_OnLevelMessage) -E(OP_AltCurrencySell) +E(OP_OpenNewTasksWindow) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_RaidJoin) +E(OP_RaidUpdate) +E(OP_ReadBook) +E(OP_SendAATable) +E(OP_SendCharInfo) +E(OP_SendZonepoints) +E(OP_ShopPlayerSell) +E(OP_SomeItemPacketMaybe) +E(OP_SpawnDoor) +E(OP_Stun) +E(OP_Track) +E(OP_Trader) +E(OP_TraderBuy) +E(OP_TributeItem) +E(OP_VetRewardsAvaliable) E(OP_WearChange) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) -D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_ConsiderCorpse) -D(OP_Consider) -D(OP_ClientUpdate) -D(OP_MoveItem) -D(OP_WhoAllRequest) +E(OP_ZoneEntry) +E(OP_ZonePlayerToBind) +E(OP_ZoneServerInfo) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_AltCurrencySell) +D(OP_AltCurrencySellSelection) +D(OP_ApplyPoison) +D(OP_AugmentInfo) +D(OP_AugmentItem) D(OP_Buff) -D(OP_ShopPlayerSell) -D(OP_Consume) D(OP_CastSpell) -D(OP_Save) -D(OP_ItemVerifyRequest) +D(OP_CharacterCreate) +D(OP_ClientUpdate) +D(OP_Consider) +D(OP_ConsiderCorpse) +D(OP_Consume) +D(OP_DeleteItem) +D(OP_FaceChange) +D(OP_FindPersonRequest) D(OP_GroupFollow) D(OP_GroupFollow2) -D(OP_FindPersonRequest) -D(OP_TraderBuy) -D(OP_LootItem) -D(OP_TributeItem) -D(OP_ReadBook) -D(OP_AugmentInfo) -D(OP_FaceChange) -D(OP_AdventureMerchantSell) -D(OP_TradeSkillCombine) -D(OP_RaidInvite) D(OP_InspectRequest) +D(OP_ItemLinkClick) +D(OP_ItemVerifyRequest) +D(OP_LootItem) +D(OP_MoveItem) +D(OP_RaidInvite) +D(OP_ReadBook) +D(OP_Save) +D(OP_SetServerFilter) +D(OP_ShopPlayerSell) +D(OP_TraderBuy) +D(OP_TradeSkillCombine) +D(OP_TributeItem) D(OP_WearChange) -D(OP_ApplyPoison) -D(OP_DeleteItem) -D(OP_AugmentItem) -D(OP_AltCurrencySellSelection) -D(OP_AltCurrencySell) +D(OP_WhoAllRequest) #undef E #undef D diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index c79fa5d4f..77d87cb7d 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -133,22 +133,6 @@ static inline uint32 TitaniumToServerCorpseSlot(uint32 TitaniumCorpse) { // reserved } */ -/* -static inline uint32 RemovePowerSourceBit(uint32 slots) { // shouldn't need to add one..just grab the actual server reference, if so... - static const uint32 BIT21 = 1 << 21; - static const uint32 BIT22 = 1 << 22; - static const uint32 KEEPBITS = ~(BIT21 | BIT22); - - bool wearammo = slots & BIT22; - - slots &= KEEPBITS; - - if (wearammo) - slots |= BIT21; - - return slots; -} -*/ EAT_ENCODE(OP_ZoneServerReady) diff --git a/common/patches/titanium_ops.h b/common/patches/titanium_ops.h index ead6bcecc..40651a652 100644 --- a/common/patches/titanium_ops.h +++ b/common/patches/titanium_ops.h @@ -1,53 +1,52 @@ - -//list of packets we need to encode on the way out: -E(OP_SendCharInfo) -E(OP_SendAATable) -E(OP_LeadershipExpUpdate) -E(OP_PlayerProfile) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ZoneEntry) -E(OP_CharInventory) -E(OP_ItemLinkResponse) -E(OP_ItemPacket) +// out-going packets that require an ENCODE translation: +E(OP_Action) E(OP_BazaarSearch) -E(OP_GuildMemberList) -E(OP_ZoneServerReady) -E(OP_GuildMemberLevelUpdate) -E(OP_Trader) -E(OP_TraderBuy) -E(OP_ReadBook) -E(OP_Illusion) -E(OP_VetRewardsAvaliable) -E(OP_InspectRequest) -E(OP_InspectAnswer) -E(OP_Track) -E(OP_RespondAA) +E(OP_BecomeTrader) +E(OP_CharInventory) E(OP_DeleteSpawn) -E(OP_WearChange) +E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) E(OP_DzExpeditionInfo) -E(OP_DzCompass) -E(OP_DzMemberList) E(OP_DzExpeditionList) -E(OP_DzLeaderStatus) E(OP_DzJoinExpeditionConfirm) -E(OP_Action) -E(OP_BecomeTrader) -E(OP_PetBuffWindow) -E(OP_OnLevelMessage) +E(OP_DzLeaderStatus) +E(OP_DzMemberList) +E(OP_GuildMemberLevelUpdate) +E(OP_GuildMemberList) +E(OP_Illusion) +E(OP_InspectAnswer) +E(OP_InspectRequest) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_LeadershipExpUpdate) E(OP_LFGuild) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) +E(OP_OnLevelMessage) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_NewSpawn) +E(OP_ReadBook) +E(OP_RespondAA) +E(OP_SendCharInfo) +E(OP_SendAATable) +E(OP_Track) +E(OP_Trader) +E(OP_TraderBuy) +E(OP_VetRewardsAvaliable) +E(OP_WearChange) +E(OP_ZoneEntry) +E(OP_ZoneServerReady) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_TraderBuy) -D(OP_WhoAllRequest) -D(OP_ReadBook) D(OP_FaceChange) -D(OP_InspectRequest) D(OP_InspectAnswer) -D(OP_WearChange) +D(OP_InspectRequest) +D(OP_ItemLinkClick) D(OP_LFGuild) +D(OP_ReadBook) +D(OP_SetServerFilter) +D(OP_TraderBuy) +D(OP_WearChange) +D(OP_WhoAllRequest) #undef E #undef D diff --git a/common/patches/underfoot_ops.h b/common/patches/underfoot_ops.h index 7aa9caf50..28864301d 100644 --- a/common/patches/underfoot_ops.h +++ b/common/patches/underfoot_ops.h @@ -1,128 +1,126 @@ - -//list of packets we need to encode on the way out: - -E(OP_SendCharInfo) -E(OP_ZoneServerInfo) -E(OP_SendAATable) -E(OP_PlayerProfile) -E(OP_ZoneEntry) -E(OP_CharInventory) -E(OP_NewZone) -E(OP_SpawnDoor) -E(OP_GroundSpawn) -E(OP_SendZonepoints) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ItemLinkResponse) -E(OP_ItemPacket) -E(OP_GuildMemberList) -E(OP_Illusion) -E(OP_ManaChange) -E(OP_ClientUpdate) -E(OP_LeadershipExpUpdate) -E(OP_ExpansionInfo) -E(OP_LogServer) -E(OP_Damage) -E(OP_Buff) +// out-going packets that require an ENCODE translation: E(OP_Action) -E(OP_Consider) -E(OP_CancelTrade) -E(OP_ShopPlayerSell) -E(OP_DeleteItem) -E(OP_ItemVerifyReply) -E(OP_DeleteCharge) -E(OP_MoveItem) -E(OP_OpenNewTasksWindow) -E(OP_BazaarSearch) -E(OP_Trader) -E(OP_TraderBuy) -E(OP_LootItem) -E(OP_TributeItem) -E(OP_SomeItemPacketMaybe) -E(OP_ReadBook) -E(OP_Stun) -E(OP_ZonePlayerToBind) E(OP_AdventureMerchantSell) -E(OP_RaidUpdate) -E(OP_RaidJoin) -E(OP_VetRewardsAvaliable) -E(OP_InspectRequest) -E(OP_GroupInvite) -E(OP_GroupFollow) -E(OP_GroupFollow2) -E(OP_GroupUpdate) -E(OP_GroupCancelInvite) -E(OP_WhoAllResponse) -E(OP_Track) -E(OP_ShopPlayerBuy) -E(OP_PetBuffWindow) -E(OP_OnLevelMessage) -E(OP_Barter) +E(OP_AltCurrency) +E(OP_AltCurrencySell) E(OP_ApplyPoison) +E(OP_Barter) +E(OP_BazaarSearch) +E(OP_Buff) +E(OP_BuffCreate) +E(OP_CancelTrade) E(OP_ChannelMessage) -E(OP_GuildsList) +E(OP_CharInventory) +E(OP_ClientUpdate) +E(OP_Consider) +E(OP_Damage) +E(OP_DeleteCharge) +E(OP_DeleteItem) +E(OP_DisciplineUpdate) +E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) E(OP_DzExpeditionInfo) -E(OP_DzCompass) -E(OP_DzMemberList) E(OP_DzExpeditionList) -E(OP_DzLeaderStatus) E(OP_DzJoinExpeditionConfirm) -E(OP_TargetBuffs) -E(OP_BuffCreate) -E(OP_SpawnAppearance) -E(OP_RespondAA) -E(OP_DisciplineUpdate) -E(OP_AltCurrencySell) -E(OP_AltCurrency) -E(OP_WearChange) +E(OP_DzLeaderStatus) +E(OP_DzMemberList) +E(OP_ExpansionInfo) +E(OP_GroundSpawn) +E(OP_GroupCancelInvite) +E(OP_GroupFollow) +E(OP_GroupFollow2) +E(OP_GroupInvite) +E(OP_GroupUpdate) +E(OP_GuildMemberList) +E(OP_GuildsList) +E(OP_Illusion) +E(OP_InspectRequest) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_ItemVerifyReply) +E(OP_LeadershipExpUpdate) +E(OP_LogServer) +E(OP_LootItem) +E(OP_ManaChange) E(OP_MercenaryDataResponse) E(OP_MercenaryDataUpdate) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) -D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_ConsiderCorpse) -D(OP_Consider) -D(OP_ClientUpdate) -D(OP_MoveItem) -D(OP_WhoAllRequest) +E(OP_MoveItem) +E(OP_NewSpawn) +E(OP_NewZone) +E(OP_OnLevelMessage) +E(OP_OpenNewTasksWindow) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_RaidJoin) +E(OP_RaidUpdate) +E(OP_ReadBook) +E(OP_RespondAA) +E(OP_SendAATable) +E(OP_SendCharInfo) +E(OP_SendZonepoints) +E(OP_ShopPlayerBuy) +E(OP_ShopPlayerSell) +E(OP_SomeItemPacketMaybe) +E(OP_SpawnAppearance) +E(OP_SpawnDoor) +E(OP_Stun) +E(OP_TargetBuffs) +E(OP_Track) +E(OP_Trader) +E(OP_TraderBuy) +E(OP_TributeItem) +E(OP_VetRewardsAvaliable) +E(OP_WearChange) +E(OP_WhoAllResponse) +E(OP_ZoneEntry) +E(OP_ZonePlayerToBind) +E(OP_ZoneServerInfo) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_AltCurrencySell) +D(OP_AltCurrencySellSelection) +D(OP_ApplyPoison) +D(OP_AugmentInfo) +D(OP_AugmentItem) +D(OP_BazaarSearch) D(OP_Buff) -D(OP_ShopPlayerSell) -D(OP_Consume) +D(OP_BuffRemoveRequest) D(OP_CastSpell) -D(OP_Save) -D(OP_ItemVerifyRequest) -D(OP_GroupInvite) -D(OP_GroupInvite2) +D(OP_ChannelMessage) +D(OP_CharacterCreate) +D(OP_ClientUpdate) +D(OP_Consider) +D(OP_ConsiderCorpse) +D(OP_Consume) +D(OP_Damage) +D(OP_DeleteItem) +D(OP_EnvDamage) +D(OP_FaceChange) +D(OP_FindPersonRequest) +D(OP_GroupCancelInvite) +D(OP_GroupDisband) D(OP_GroupFollow) D(OP_GroupFollow2) -D(OP_GroupDisband) -D(OP_GroupCancelInvite) -D(OP_FindPersonRequest) -D(OP_TraderBuy) -D(OP_LootItem) -D(OP_TributeItem) -D(OP_ReadBook) -D(OP_AugmentInfo) -D(OP_FaceChange) -D(OP_AdventureMerchantSell) -D(OP_TradeSkillCombine) -D(OP_RaidInvite) +D(OP_GroupInvite) +D(OP_GroupInvite2) D(OP_InspectRequest) -D(OP_WearChange) -D(OP_ShopPlayerBuy) -D(OP_BazaarSearch) +D(OP_ItemLinkClick) +D(OP_ItemVerifyRequest) D(OP_LoadSpellSet) -D(OP_ApplyPoison) -D(OP_Damage) -D(OP_EnvDamage) -D(OP_ChannelMessage) -D(OP_DeleteItem) -D(OP_AugmentItem) +D(OP_LootItem) +D(OP_MoveItem) D(OP_PetCommands) -D(OP_BuffRemoveRequest) -D(OP_AltCurrencySellSelection) -D(OP_AltCurrencySell) +D(OP_RaidInvite) +D(OP_ReadBook) +D(OP_Save) +D(OP_SetServerFilter) +D(OP_ShopPlayerBuy) +D(OP_ShopPlayerSell) +D(OP_TraderBuy) +D(OP_TradeSkillCombine) +D(OP_TributeItem) +D(OP_WearChange) +D(OP_WhoAllRequest) #undef E #undef D From 723e5d536ab3edb59cfda7c1c24a234eb171e1ad Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 25 Sep 2014 21:20:59 -0400 Subject: [PATCH 163/368] EQEmu::IsTradeskill uint32 to SkillUseTypes --- common/skills.cpp | 2 +- common/skills.h | 2 +- world/client.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/skills.cpp b/common/skills.cpp index 96b87c4a9..2eefd49c8 100644 --- a/common/skills.cpp +++ b/common/skills.cpp @@ -18,7 +18,7 @@ #include "types.h" #include "skills.h" -bool EQEmu::IsTradeskill(uint32 skill) +bool EQEmu::IsTradeskill(SkillUseTypes skill) { switch (skill) { case SkillFishing: diff --git a/common/skills.h b/common/skills.h index 15876cedc..939223e56 100644 --- a/common/skills.h +++ b/common/skills.h @@ -262,7 +262,7 @@ typedef enum { // for skill related helper functions namespace EQEmu { - bool IsTradeskill(uint32 skill); + bool IsTradeskill(SkillUseTypes skill); } #endif diff --git a/world/client.cpp b/world/client.cpp index 9120ce70b..ad82f11fe 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1797,7 +1797,7 @@ void Client::SetClassStartingSkills(PlayerProfile_Struct *pp) if (i >= SkillSpecializeAbjure && i <= SkillSpecializeEvocation) continue; - if (EQEmu::IsTradeskill(i) || i == SkillBegging) + if (EQEmu::IsTradeskill((SkillUseTypes)i) || i == SkillBegging) continue; pp->skills[i] = database.GetSkillCap(pp->class_, (SkillUseTypes)i, 1); From 61b784e96e14b82a499eb5774bf71008bc7178ea Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 25 Sep 2014 21:35:17 -0400 Subject: [PATCH 164/368] Add tests for skills utilities --- tests/CMakeLists.txt | 1 + tests/main.cpp | 2 ++ tests/skills_util_test.h | 42 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 tests/skills_util_test.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 428e8b5b1..1c13ae26c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,6 +15,7 @@ SET(tests_headers ipc_mutex_test.h memory_mapped_file_test.h string_util_test.h + skills_util_test.h ) ADD_EXECUTABLE(tests ${tests_sources} ${tests_headers}) diff --git a/tests/main.cpp b/tests/main.cpp index 9d9b658f9..d64dfead4 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -28,6 +28,7 @@ #include "hextoi_32_64_test.h" #include "string_util_test.h" #include "data_verification_test.h" +#include "skills_util_test.h" int main() { try { @@ -42,6 +43,7 @@ int main() { tests.add(new hextoi_32_64_Test()); tests.add(new StringUtilTest()); tests.add(new DataVerificationTest()); + tests.add(new SkillsUtilsTest()); tests.run(*output, true); } catch(...) { return -1; diff --git a/tests/skills_util_test.h b/tests/skills_util_test.h new file mode 100644 index 000000000..63b5cde5a --- /dev/null +++ b/tests/skills_util_test.h @@ -0,0 +1,42 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __EQEMU_TESTS_SKILLS_UTILS_H +#define __EQEMU_TESTS_SKILLS_UTILS_H + +#include "cppunit/cpptest.h" +#include "../common/skills.h" + +class SkillsUtilsTest: public Test::Suite { + typedef void(SkillsUtilsTest::*TestFunction)(void); +public: + SkillsUtilsTest() { + TEST_ADD(SkillsUtilsTest::IsTradeskill); + } + + ~SkillsUtilsTest() { + } + + private: + void IsTradeskill() { + TEST_ASSERT(EQEmu::IsTradeskill(SkillPottery)); + TEST_ASSERT(!EQEmu::IsTradeskill(SkillParry)); + } +}; + +#endif From 0f12a740747be849961007013fde47786f4639bb Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 25 Sep 2014 21:40:31 -0400 Subject: [PATCH 165/368] Add bool EQEmu::IsSpecializedSkill(SkillUseTypes skill) --- common/skills.cpp | 16 ++++++++++++++++ common/skills.h | 1 + tests/skills_util_test.h | 6 ++++++ 3 files changed, 23 insertions(+) diff --git a/common/skills.cpp b/common/skills.cpp index 2eefd49c8..24a32196e 100644 --- a/common/skills.cpp +++ b/common/skills.cpp @@ -38,3 +38,19 @@ bool EQEmu::IsTradeskill(SkillUseTypes skill) return false; } } + +bool EQEmu::IsSpecializedSkill(SkillUseTypes skill) +{ + // this could be a simple if, but if this is more portable if any IDs change (probably won't) + // or any other specialized are added (also unlikely) + switch (skill) { + case SkillSpecializeAbjure: + case SkillSpecializeAlteration: + case SkillSpecializeConjuration: + case SkillSpecializeDivination: + case SkillSpecializeEvocation: + return true; + default: + return false; + } +} diff --git a/common/skills.h b/common/skills.h index 939223e56..b4064ab3f 100644 --- a/common/skills.h +++ b/common/skills.h @@ -263,6 +263,7 @@ typedef enum { // for skill related helper functions namespace EQEmu { bool IsTradeskill(SkillUseTypes skill); + bool IsSpecializedSkill(SkillUseTypes skill); } #endif diff --git a/tests/skills_util_test.h b/tests/skills_util_test.h index 63b5cde5a..96507256e 100644 --- a/tests/skills_util_test.h +++ b/tests/skills_util_test.h @@ -27,6 +27,7 @@ class SkillsUtilsTest: public Test::Suite { public: SkillsUtilsTest() { TEST_ADD(SkillsUtilsTest::IsTradeskill); + TEST_ADD(SkillsUtilsTest::IsSpecializedSkill); } ~SkillsUtilsTest() { @@ -37,6 +38,11 @@ public: TEST_ASSERT(EQEmu::IsTradeskill(SkillPottery)); TEST_ASSERT(!EQEmu::IsTradeskill(SkillParry)); } + + void IsSpecializedSkill() { + TEST_ASSERT(EQEmu::IsSpecializedSkill(SkillSpecializeConjuration)); + TEST_ASSERT(!EQEmu::IsSpecializedSkill(SkillConjuration)) + } }; #endif From 179400c7778d18570aa13da642187eca7bfe670d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 26 Sep 2014 00:44:51 -0400 Subject: [PATCH 166/368] Client::SetClassStartingSkills should now match live --- world/client.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index ad82f11fe..a373004e1 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1794,10 +1794,10 @@ void Client::SetClassStartingSkills(PlayerProfile_Struct *pp) { for (uint32 i = 0; i <= HIGHEST_SKILL; ++i) { if (pp->skills[i] == 0) { - if (i >= SkillSpecializeAbjure && i <= SkillSpecializeEvocation) - continue; - - if (EQEmu::IsTradeskill((SkillUseTypes)i) || i == SkillBegging) + // Skip specialized, tradeskills (fishing excluded), Alcohol Tolerance, and Bind Wound + if (EQEmu::IsSpecializedSkill((SkillUseTypes)i) || + (EQEmu::IsTradeskill((SkillUseTypes)i) && i != SkillFishing) || + i == SkillAlcoholTolerance || i == SkillBindWound) continue; pp->skills[i] = database.GetSkillCap(pp->class_, (SkillUseTypes)i, 1); From 31b46efcac53ed2d17d580c06afac9527fec7bfc Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 26 Sep 2014 06:56:42 -0400 Subject: [PATCH 167/368] Clean up of perl based NPC spell scaling functions. Added Perl - $npc->GetSpellFocusDMG(), $npc->GetSpellFocusHeal() --- zone/effects.cpp | 4 ++-- zone/npc.h | 8 +++++++ zone/perl_npc.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 10748c0e1..7822d2e5e 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -56,7 +56,7 @@ int32 NPC::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= target->GetFcDamageAmtIncoming(this, spell_id)/spells[spell_id].buffduration; } - value += dmg*SpellFocusDMG/100; + value += dmg*GetSpellFocusDMG()/100; if (AI_HasSpellsEffects()){ int16 chance = 0; @@ -275,7 +275,7 @@ int32 NPC::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { //Scale all NPC spell healing via SetSpellFocusHeal(value) - value += value*SpellFocusHeal/100; + value += value*GetSpellFocusHeal()/100; if (target) { value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); diff --git a/zone/npc.h b/zone/npc.h index b23522bee..228ce7cf3 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -387,6 +387,12 @@ public: inline void SetHealScale(float amt) { healscale = amt; } inline float GetHealScale() { return healscale; } + + inline void SetSpellFocusDMG(int32 NewSpellFocusDMG) {SpellFocusDMG = NewSpellFocusDMG;} + inline int32 GetSpellFocusDMG() const { return SpellFocusDMG;} + + inline void SetSpellFocusHeal(int32 NewSpellFocusHeal) {SpellFocusHeal = NewSpellFocusHeal;} + inline int32 GetSpellFocusHeal() const {return SpellFocusHeal;} uint32 GetSpawnKillCount(); int GetScore(); @@ -452,6 +458,8 @@ protected: uint32 npc_mana; float spellscale; float healscale; + int32 SpellFocusDMG; + int32 SpellFocusHeal; //pet crap: uint16 pet_spell_id; diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index dcc66b886..0ea8a5340 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1991,6 +1991,32 @@ XS(XS_NPC_SetSpellFocusDMG) XSRETURN_EMPTY; } +XS(XS_NPC_GetSpellFocusDMG); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSpellFocusDMG) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSpellFocusDMG(THIS)"); + { + NPC * THIS; + int32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->GetSpellFocusDMG(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + XS(XS_NPC_SetSpellFocusHeal); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_SetSpellFocusHeal) { @@ -2015,6 +2041,32 @@ XS(XS_NPC_SetSpellFocusHeal) XSRETURN_EMPTY; } +XS(XS_NPC_GetSpellFocusHeal); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSpellFocusHeal) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSpellFocusHeal(THIS)"); + { + NPC * THIS; + int32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->GetSpellFocusHeal(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + XS(XS_NPC_GetSlowMitigation); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_GetSlowMitigation) { @@ -2286,6 +2338,8 @@ XS(boot_NPC) newXSproto(strcpy(buf, "RemoveAISpell"), XS_NPC_RemoveSpellFromNPCList, file, "$$"); newXSproto(strcpy(buf, "SetSpellFocusDMG"), XS_NPC_SetSpellFocusDMG, file, "$$"); newXSproto(strcpy(buf, "SetSpellFocusHeal"), XS_NPC_SetSpellFocusHeal, file, "$$"); + newXSproto(strcpy(buf, "GetSpellFocusDMG"), XS_NPC_GetSpellFocusDMG, file, "$"); + newXSproto(strcpy(buf, "GetSpellFocusHeal"), XS_NPC_GetSpellFocusHeal, file, "$"); newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetAttackSpeed, file, "$"); newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetSlowMitigation, file, "$"); newXSproto(strcpy(buf, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$"); From 2497c719ee7472bfa86843844bedd5aaa474b3fa Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 26 Sep 2014 12:43:34 -0400 Subject: [PATCH 168/368] Rename Name variable in SendBazaarResults to temp_buffer fixes #253 --- zone/trading.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/zone/trading.cpp b/zone/trading.cpp index ec9e652cc..376d2cb14 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1861,7 +1861,7 @@ void Client::SendBazaarResults(uint32 TraderID, uint32 Class_, uint32 Race, uint int Action = BazaarSearchResults; uint32 Cost = 0; int32 SerialNumber = 0; - char Name[64] = {0}; + char temp_buffer[64] = {0}; int Count = 0; uint32 StatValue=0; @@ -1887,23 +1887,23 @@ void Client::SendBazaarResults(uint32 TraderID, uint32 Class_, uint32 Race, uint bool Stackable = atoi(row[10]); if(Stackable) { int Charges = atoi(row[9]); - sprintf(Name, "%s(%i)", row[7], Charges); + sprintf(temp_buffer, "%s(%i)", row[7], Charges); } else - sprintf(Name,"%s(%i)",row[7], Count); + sprintf(temp_buffer,"%s(%i)",row[7], Count); - memcpy(bufptr,&Name, strlen(Name)); + memcpy(bufptr,&temp_buffer, strlen(temp_buffer)); bufptr += 64; // Extra fields for SoD+ // if(Trader2) - sprintf(Name, "%s", Trader2->GetName()); + sprintf(temp_buffer, "%s", Trader2->GetName()); else - sprintf(Name, "Unknown"); + sprintf(temp_buffer, "Unknown"); - memcpy(bufptr,&Name, strlen(Name)); + memcpy(bufptr,&temp_buffer, strlen(temp_buffer)); bufptr += 64; From 837ce8ab4a3fffbc2cece765276930abe849861d Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 26 Sep 2014 20:51:42 -0400 Subject: [PATCH 169/368] Re-ordered client patch ENCODES and DECODES - snuck in a bitswap change for SoF+ item_struct::Slots translations --- common/patches/client62.cpp | 1951 +++---- common/patches/rof.cpp | 9677 +++++++++++++++++----------------- common/patches/sod.cpp | 6385 +++++++++++----------- common/patches/sof.cpp | 5586 ++++++++++---------- common/patches/titanium.cpp | 2799 +++++----- common/patches/underfoot.cpp | 7013 ++++++++++++------------ 6 files changed, 16915 insertions(+), 16496 deletions(-) diff --git a/common/patches/client62.cpp b/common/patches/client62.cpp index 494049876..65ebe3600 100644 --- a/common/patches/client62.cpp +++ b/common/patches/client62.cpp @@ -1,4 +1,3 @@ - #include "../debug.h" #include "client62.h" #include "../opcodemgr.h" @@ -13,1061 +12,1087 @@ #include "../clientversions.h" #include "client62_structs.h" -namespace Client62 { +namespace Client62 +{ + static const char *name = "6.2"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "6.2"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); -char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline uint32 ServerToClient62Slot(uint32 ServerSlot); + static inline uint32 ServerToClient62CorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 Client62ToServerSlot(uint32 Client62Slot); + static inline uint32 Client62ToServerCorpseSlot(uint32 Client62Corpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "client62_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "client62_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClient62; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClient62; + } #include "ss_define.h" +// ENCODE methods + EAT_ENCODE(OP_GuildMemberLevelUpdate); // added ; -/* -// Converts Server Slot IDs to Client62 Slot IDs for use in Encodes -static inline uint32 ServerToClient62Slot(uint32 ServerSlot) { - uint32 Client62Slot; - // reserved -} -*/ -/* -// Converts Client62 Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 Client62ToServerSlot(uint32 Client62Slot) { - uint32 ServerSlot; - // reserved -} -*/ -/* -// Converts Server Corpse Slot IDs to Client62 Corpse Slot IDs for use in Encodes -static inline uint32 ServerToClient62CorpseSlot(uint32 ServerCorpse) { - uint32 Client62Corpse; - // reserved -} -*/ -/* -// Converts Client62 Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 Client62ToServerCorpseSlot(uint32 Client62Corpse) { - uint32 ServerCorpse; - // reserved -} -*/ - - -EAT_ENCODE(OP_ZoneServerReady) -EAT_ENCODE(OP_GuildMemberLevelUpdate) - -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct); - int r; - for(r = 0; r < 10; r++) { - OUT(zone[r]); - OUT(eyecolor1[r]); - OUT(eyecolor2[r]); - OUT(hairstyle[r]); - OUT(primary[r]); - OUT(race[r]); - OUT(class_[r]); - OUT_str(name[r]); - OUT(gender[r]); - OUT(level[r]); - OUT(secondary[r]); - OUT(face[r]); - OUT(beard[r]); - int k; - for(k = 0; k < 9; k++) { - OUT(equip[r][k]); - OUT(cs_colors[r][k].color); - } - OUT(haircolor[r]); - OUT(gohome[r]); - OUT(deity[r]); - OUT(beardcolor[r]); - } - FINISH_ENCODE(); -} - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for SoF - // clientver 1 is for all clients and 2 is for 6.2 - if (emu->clientver <= 2 ) + EAT_ENCODE(OP_ZoneServerReady); // added ; + + ENCODE(OP_Action) { - OUT(id); - OUT(hotkey_sid); - OUT(hotkey_sid2); - OUT(title_sid); - OUT(desc_sid); - OUT(class_type); - OUT(cost); - OUT(seq); - OUT(current_level); - OUT(prereq_skill); - OUT(prereq_minpoints); + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); + + OUT(target); + OUT(source); + OUT(level); + OUT(instrument_mod); + OUT(sequence); OUT(type); - OUT(spellid); - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - OUT(unknown80[0]); - OUT(unknown80[1]); - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } + //OUT(damage); + OUT(spell); + OUT(buff_unknown); // if this is 4, a buff icon is made + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} - -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteSpawn) { - SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - OUT(spawn_id); - FINISH_ENCODE(); -} - -ENCODE(OP_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); - - uint32 r; - - memset(eq->unknown3224, 0xff, 448); - memset(eq->unknown3704, 0xff, 32); - -// OUT(checksum); - OUT(gender); - OUT(race); - OUT(class_); - OUT(level); - eq->level2 = emu->level; - - eq->bind_zone_id = emu->binds[0].zoneId; - eq->bind_x[0] = emu->binds[0].x; - eq->bind_y[0] = emu->binds[0].y; - eq->bind_z[0] = emu->binds[0].z; - eq->bind_heading[0] = emu->binds[0].heading; - //just making this up base on organization of struct: - eq->zone_safe_x = emu->binds[4].x; - eq->zone_safe_y = emu->binds[4].y; - eq->zone_safe_z = emu->binds[4].z; - eq->zone_safe_heading = emu->binds[4].heading; - - - OUT(deity); - OUT(intoxication); - OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); - OUT(abilitySlotRefresh); -// OUT(unknown0166[4]); - OUT(haircolor); - OUT(beardcolor); - OUT(eyecolor1); - OUT(eyecolor2); - OUT(hairstyle); - OUT(beard); - for(r = 0; r < 9; r++) { - OUT(item_material[r]); - OUT(item_tint[r].color); - } - for(r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { - OUT(aa_array[r].AA); - OUT(aa_array[r].value); - } - OUT(points); - OUT(mana); - OUT(cur_hp); - OUT(STR); - OUT(STA); - OUT(CHA); - OUT(DEX); - OUT(INT); - OUT(AGI); - OUT(WIS); - OUT(face); - OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); - OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - OUT(platinum_cursor); - OUT(gold_cursor); - OUT(silver_cursor); - OUT(copper_cursor); - - OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) - - OUT(toxicity); - OUT(thirst_level); - OUT(hunger_level); - for(r = 0; r < structs::BUFF_COUNT; r++) { - OUT(buffs[r].slotid); - OUT(buffs[r].level); - OUT(buffs[r].bard_modifier); - OUT(buffs[r].effect); - OUT(buffs[r].spellid); - OUT(buffs[r].duration); - OUT(buffs[r].counters); -// OUT(buffs[r].player_id); - } - for(r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { - OUT(disciplines.values[r]); - } -// OUT_array(recastTimers, structs::MAX_RECAST_TYPES); - OUT(endurance); - OUT(aapoints_spent); - OUT(aapoints); - for(r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) { - OUT_str(bandoliers[r].name); - uint32 k; - for(k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { - OUT(bandoliers[r].items[k].item_id); - OUT(bandoliers[r].items[k].icon); - OUT_str(bandoliers[r].items[k].item_name); - } - } - for(r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) { - OUT(potionbelt.items[r].item_id); - OUT(potionbelt.items[r].icon); - OUT_str(potionbelt.items[r].item_name); - } -// OUT(available_slots); - OUT_str(name); - OUT_str(last_name); - OUT(guild_id); - OUT(birthday); - OUT(lastlogin); - OUT(timePlayedMin); - OUT(pvp); - OUT(anon); - OUT(gm); - OUT(guildrank); - OUT(exp); - OUT_array(languages, structs::MAX_PP_LANGUAGE); - OUT(x); - OUT(y); - OUT(z); - OUT(heading); - OUT(platinum_bank); - OUT(gold_bank); - OUT(silver_bank); - OUT(copper_bank); - OUT(platinum_shared); - OUT(expansions); - OUT(autosplit); - OUT(zone_id); - OUT(zoneInstance); - for(r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { - OUT_str(groupMembers[r]); - } -// OUT_str(groupLeader); //this is prolly right after groupMembers, but I dont feel like checking. -// OUT(leadAAActive); - OUT(ldon_points_guk); - OUT(ldon_points_mir); - OUT(ldon_points_mmc); - OUT(ldon_points_ruj); - OUT(ldon_points_tak); - OUT(ldon_points_available); - OUT(tribute_time_remaining); - OUT(career_tribute_points); - OUT(tribute_points); - OUT(tribute_active); - for(r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { - OUT(tributes[r].tribute); - OUT(tributes[r].tier); - } - OUT(group_leadership_exp); - OUT(raid_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_points); - OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); - OUT(air_remaining); - OUT(entityid); - OUT(leadAAActive); - OUT(expAA); - OUT(currentRadCrystals); - OUT(careerRadCrystals); - OUT(currentEbonCrystals); - OUT(careerEbonCrystals); - OUT(groupAutoconsent); - OUT(raidAutoconsent); - OUT(guildAutoconsent); -// OUT(showhelm); - - //set the checksum... - CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct)-4); - - FINISH_ENCODE(); -} - -ENCODE(OP_Track) -{ - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + ENCODE(OP_BazaarSearch) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); - delete in; - return; + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if (SubAction != BazaarSearchResults) + { + dest->FastQueuePacket(&in, ack_req); + + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); + + if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); + + in->pBuffer = new unsigned char[in->size]; + + memset(in->pBuffer, 0, in->size); + + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++emu, ++eq) + { + OUT(Beginning.Action); + OUT(NumItems); + OUT(SerialNumber); + OUT(SellerID); + OUT(Cost); + OUT(ItemStat); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); } - in->size = sizeof(structs::Track_Struct) * EntryCount; - in->pBuffer = new unsigned char[in->size]; - structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++eq, ++emu) + ENCODE(OP_BecomeTrader) { - OUT(entityid); - OUT(padding002); - OUT(distance); + ENCODE_LENGTH_EXACT(BecomeTrader_Struct); + SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); + + OUT(ID); + OUT(Code); + + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_CharInventory) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; - dest->FastQueuePacket(&in, ack_req); -} + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; - - //determine and verify length - int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); - delete in; - return; - } - - //make the EQ struct. - in->size = sizeof(structs::Spawn_Struct)*entrycount; - in->pBuffer = new unsigned char[in->size]; - structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; - - //zero out the packet. We could avoid this memset by setting all fields (including unknowns) - //in the loop. - memset(in->pBuffer, 0, in->size); - - //do the transform... - int r; - int k; - for(r = 0; r < entrycount; r++, eq++, emu++) { - eq->gm = emu->gm; - eq->aaitle = emu->aaitle; - eq->anon = emu->anon; - eq->face = emu->face; - strcpy(eq->name, emu->name); - eq->deity = emu->deity; - eq->size = emu->size; - eq->NPC = emu->NPC; - eq->invis = emu->invis; - eq->haircolor = emu->haircolor; - eq->curHp = emu->curHp; - eq->max_hp = emu->max_hp; - eq->findable = emu->findable; - eq->deltaHeading = emu->deltaHeading; - eq->x = emu->x; - eq->y = emu->y; - eq->animation = emu->animation; - eq->z = emu->z; - eq->deltaY = emu->deltaY; - eq->deltaX = emu->deltaX; - eq->heading = emu->heading; - eq->deltaZ = emu->deltaZ; - eq->eyecolor1 = emu->eyecolor1; -// eq->showhelm = emu->showhelm; - eq->is_npc = emu->is_npc; - eq->hairstyle = emu->hairstyle; - eq->beard = emu->beard; - eq->level = emu->level; - eq->beardcolor = emu->beardcolor; - strcpy(eq->suffix, emu->suffix); - eq->petOwnerId = emu->petOwnerId; - eq->guildrank = emu->guildrank; - for(k = 0; k < 9; k++) { - eq->equipment[k] = emu->equipment[k]; - eq->colors[k].color = emu->colors[k].color; + int itemcount = in->size / sizeof(InternalSerializedItem_Struct); + if (itemcount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + delete in; + return; } - for(k = 0; k < 8; k++) { - eq->set_to_0xFF[k] = 0xFF; - } - eq->runspeed = emu->runspeed; - eq->afk = emu->afk; - eq->guildID = emu->guildID; - strcpy(eq->title, emu->title); - eq->helm = emu->helm; - eq->race = emu->race; - strcpy(eq->lastName, emu->lastName); - eq->walkspeed = emu->walkspeed; - eq->is_pet = emu->is_pet; - eq->light = emu->light; - eq->class_ = emu->class_; - eq->eyecolor2 = emu->eyecolor2; - eq->gender = emu->gender; - eq->bodytype = emu->bodytype; - eq->equip_chest2 = emu->equip_chest2; - eq->spawnId = emu->spawnId; - eq->lfg = emu->lfg; - eq->flymode = emu->flymode; - } + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; - //kill off the emu structure and send the eq packet. - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); -} + //do the transform... + int r; + std::string serial_string; + for (r = 0; r < itemcount; r++, eq++) { + uint32 length; + char *serialized = SerializeItem((ItemInst*)eq->inst, eq->slot_id, &length, 0); + if (serialized) { + serial_string.append(serialized, length + 1); + safe_delete_array(serialized); + } + else { + _log(NET__STRUCTS, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((const ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+5; // ItemPacketType + Serialization + \0 - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length+1); - - delete[] __emu_buffer; - safe_delete_array(serialized); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int itemcount = in->size / sizeof(InternalSerializedItem_Struct); - if(itemcount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); - delete in; - return; - } - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - //do the transform... - int r; - std::string serial_string; - for(r = 0; r < itemcount; r++, eq++) { - uint32 length; - char *serialized=SerializeItem((ItemInst*)eq->inst,eq->slot_id,&length,0); - if (serialized) { - serial_string.append(serialized,length+1); - safe_delete_array(serialized); - } else { - _log(NET__STRUCTS, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); } + in->size = serial_string.length(); + in->pBuffer = new unsigned char[in->size]; + memcpy(in->pBuffer, serial_string.c_str(), serial_string.length()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); } - in->size = serial_string.length(); - in->pBuffer = new unsigned char[in->size]; - memcpy(in->pBuffer,serial_string.c_str(),serial_string.length()); + ENCODE(OP_DeleteSpawn) + { + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); -} + OUT(spawn_id); -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; + FINISH_ENCODE(); + } - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; + ENCODE(OP_GuildMemberList) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count * sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. + uint8 *buffer; + buffer = in->pBuffer; - uint8 *buffer; - buffer = in->pBuffer; + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); - - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data ); - const char *emu_note = (emu_name + + const char *emu_note = (emu_name + emu->name_length + //skip name contents emu->count //skip string terminators ); - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { - //the order we set things here must match the struct + //the order we set things here must match the struct - //nice helper macro - /*#define SlideStructString(field, str) \ - strcpy(e->field, str.c_str()); \ - e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ + //nice helper macro + /*#define SlideStructString(field, str) \ + strcpy(e->field, str.c_str()); \ + e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ #define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - PutFieldN(rank); - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - PutFieldN(total_tribute); - PutFieldN(last_tribute); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + PutFieldN(rank); + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + PutFieldN(total_tribute); + PutFieldN(last_tribute); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); #undef SlideStructString #undef PutFieldN - e++; + e++; + } } - } - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ReadBook) { - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - BookText_Struct *emu_BookText_Struct = (BookText_Struct *)__emu_buffer; - - in->size = sizeof(structs::BookText_Struct) + strlen(emu_BookText_Struct->booktext); - - in->pBuffer = new unsigned char[in->size]; - - structs::BookText_Struct *eq_BookText_Struct = (structs::BookText_Struct*)in->pBuffer; - - eq_BookText_Struct->window = emu_BookText_Struct->window; - eq_BookText_Struct->type = emu_BookText_Struct->type; - strcpy(eq_BookText_Struct->booktext, emu_BookText_Struct->booktext); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - if(emu->race > 473){ - eq->race = 1; - } - else { - OUT(race); - } - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - - FINISH_ENCODE(); -} - -ENCODE(OP_BazaarSearch) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if(SubAction != BazaarSearchResults) - { + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); - - return; } - unsigned char *__emu_buffer = in->pBuffer; - - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); - - if(EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + ENCODE(OP_Illusion) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + + if (emu->race > 473) + eq->race = 1; + else + OUT(race); + + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + + FINISH_ENCODE(); } - in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); - in->pBuffer = new unsigned char[in->size]; + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } - memset(in->pBuffer, 0, in->size); - - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++emu, ++eq) + ENCODE(OP_ItemPacket) { - OUT(Beginning.Action); - OUT(NumItems); - OUT(SerialNumber); - OUT(SellerID); - OUT(Cost); - OUT(ItemStat); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); + + uint32 length; + char *serialized = SerializeItem((const ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); + + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 5; // ItemPacketType + Serialization + \0 + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length + 1); + + delete[] __emu_buffer; + safe_delete_array(serialized); + dest->FastQueuePacket(&in, ack_req); } - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_RespondAA) { - ENCODE_LENGTH_EXACT(AATable_Struct); - SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); - - unsigned int r; - for(r = 0; r < MAX_PP_AA_ARRAY; r++) { - OUT(aa_list[r].aa_skill); - OUT(aa_list[r].aa_value); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_WearChange) { - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); - OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - OUT(sequence); - OUT(type); - //OUT(damage); - OUT(spell); - OUT(buff_unknown); // if this is 4, a buff icon is made - FINISH_ENCODE(); -} - -ENCODE(OP_BecomeTrader) -{ - ENCODE_LENGTH_EXACT(BecomeTrader_Struct); - SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); - OUT(ID); - OUT(Code); - FINISH_ENCODE(); -} - -ENCODE(OP_PetBuffWindow) -{ - ENCODE_LENGTH_EXACT(PetBuff_Struct); - SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); - - OUT(petid); - OUT(buffcount); - - int EQBuffSlot = 0; - - for(uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) + ENCODE(OP_LeadershipExpUpdate) { - if(emu->spellid[EmuBuffSlot]) + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + OUT_str(Title); + OUT_str(Text); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + + eq->unknown4236 = 0x00000000; + eq->unknown4240 = 0xffffffff; + + FINISH_ENCODE(); + } + + ENCODE(OP_PetBuffWindow) + { + ENCODE_LENGTH_EXACT(PetBuff_Struct); + SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); + + OUT(petid); + OUT(buffcount); + + int EQBuffSlot = 0; + + for (uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) { - eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; - eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; + if (emu->spellid[EmuBuffSlot]) + { + eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; + eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; + } } + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} + ENCODE(OP_PlayerProfile) + { + SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - OUT_str(Title); - OUT_str(Text); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); + uint32 r; - eq->unknown4236 = 0x00000000; - eq->unknown4240 = 0xffffffff; + memset(eq->unknown3224, 0xff, 448); + memset(eq->unknown3704, 0xff, 32); - FINISH_ENCODE(); -} + // OUT(checksum); + OUT(gender); + OUT(race); + OUT(class_); + OUT(level); + eq->level2 = emu->level; -DECODE(OP_WearChange) { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - IN(spawn_id); - IN(material); - IN(color.color); - IN(wear_slot_id); - FINISH_DIRECT_DECODE(); -} + eq->bind_zone_id = emu->binds[0].zoneId; + eq->bind_x[0] = emu->binds[0].x; + eq->bind_y[0] = emu->binds[0].y; + eq->bind_z[0] = emu->binds[0].z; + eq->bind_heading[0] = emu->binds[0].heading; + //just making this up base on organization of struct: + eq->zone_safe_x = emu->binds[4].x; + eq->zone_safe_y = emu->binds[4].y; + eq->zone_safe_z = emu->binds[4].z; + eq->zone_safe_heading = emu->binds[4].heading; -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); - } - IN(link_hash); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 25; r++) { - IN(filters[r]); - } - emu->filters[25] = 1; - emu->filters[26] = 1; - emu->filters[27] = 1; - emu->filters[28] = 1; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - IN(class_); - IN(beardcolor); - IN(beard); - IN(haircolor); - IN(gender); - IN(race); - IN(start_zone); - IN(hairstyle); - IN(deity); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - emu->type = 3; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(window); - IN(type); - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - - FINISH_DIRECT_DECODE(); -} - -char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth) { - char *serialization = nullptr; - char *instance = nullptr; - const char *protection=(const char *)"\\\\\\\\\\"; - char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; - bool stackable=inst->IsStackable(); - uint32 merchant_slot=inst->GetMerchantSlot(); - int16 charges=inst->GetCharges(); - const Item_Struct *item=inst->GetItem(); - int i; - uint32 sub_length; - - MakeAnyLenString(&instance, - "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", - stackable ? charges : 1, - 0, - (merchant_slot==0) ? slot_id : merchant_slot, - inst->GetPrice(), - (merchant_slot==0) ? 1 : inst->GetMerchantCount(), - 0, - //merchant_slot, //instance ID, bullshit for now - // The 'Merchant Slot' needs to be some unique id for bazaar to work properly - (merchant_slot==0) ? inst->GetSerialNumber() : merchant_slot, - inst->IsInstNoDrop() ? 1 : 0, //not sure where this field is - (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? charges : 0) : charges), - 0 - ); - - for(i=0;i<10;i++) { - ItemInst *sub=inst->GetItem(i); - if (sub) { - sub_items[i]=SerializeItem(sub,0,&sub_length,depth+1); + OUT(deity); + OUT(intoxication); + OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); + OUT(abilitySlotRefresh); + // OUT(unknown0166[4]); + OUT(haircolor); + OUT(beardcolor); + OUT(eyecolor1); + OUT(eyecolor2); + OUT(hairstyle); + OUT(beard); + for (r = 0; r < 9; r++) { + OUT(item_material[r]); + OUT(item_tint[r].color); } + for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { + OUT(aa_array[r].AA); + OUT(aa_array[r].value); + } + OUT(points); + OUT(mana); + OUT(cur_hp); + OUT(STR); + OUT(STA); + OUT(CHA); + OUT(DEX); + OUT(INT); + OUT(AGI); + OUT(WIS); + OUT(face); + OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); + OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + OUT(platinum_cursor); + OUT(gold_cursor); + OUT(silver_cursor); + OUT(copper_cursor); + + OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) + + OUT(toxicity); + OUT(thirst_level); + OUT(hunger_level); + for (r = 0; r < structs::BUFF_COUNT; r++) { + OUT(buffs[r].slotid); + OUT(buffs[r].level); + OUT(buffs[r].bard_modifier); + OUT(buffs[r].effect); + OUT(buffs[r].spellid); + OUT(buffs[r].duration); + OUT(buffs[r].counters); + // OUT(buffs[r].player_id); + } + for (r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { + OUT(disciplines.values[r]); + } + // OUT_array(recastTimers, structs::MAX_RECAST_TYPES); + OUT(endurance); + OUT(aapoints_spent); + OUT(aapoints); + for (r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) { + OUT_str(bandoliers[r].name); + uint32 k; + for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { + OUT(bandoliers[r].items[k].item_id); + OUT(bandoliers[r].items[k].icon); + OUT_str(bandoliers[r].items[k].item_name); + } + } + for (r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) { + OUT(potionbelt.items[r].item_id); + OUT(potionbelt.items[r].icon); + OUT_str(potionbelt.items[r].item_name); + } + // OUT(available_slots); + OUT_str(name); + OUT_str(last_name); + OUT(guild_id); + OUT(birthday); + OUT(lastlogin); + OUT(timePlayedMin); + OUT(pvp); + OUT(anon); + OUT(gm); + OUT(guildrank); + OUT(exp); + OUT_array(languages, structs::MAX_PP_LANGUAGE); + OUT(x); + OUT(y); + OUT(z); + OUT(heading); + OUT(platinum_bank); + OUT(gold_bank); + OUT(silver_bank); + OUT(copper_bank); + OUT(platinum_shared); + OUT(expansions); + OUT(autosplit); + OUT(zone_id); + OUT(zoneInstance); + for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { + OUT_str(groupMembers[r]); + } + // OUT_str(groupLeader); //this is prolly right after groupMembers, but I dont feel like checking. + // OUT(leadAAActive); + OUT(ldon_points_guk); + OUT(ldon_points_mir); + OUT(ldon_points_mmc); + OUT(ldon_points_ruj); + OUT(ldon_points_tak); + OUT(ldon_points_available); + OUT(tribute_time_remaining); + OUT(career_tribute_points); + OUT(tribute_points); + OUT(tribute_active); + for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { + OUT(tributes[r].tribute); + OUT(tributes[r].tier); + } + OUT(group_leadership_exp); + OUT(raid_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_points); + OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); + OUT(air_remaining); + OUT(entityid); + OUT(leadAAActive); + OUT(expAA); + OUT(currentRadCrystals); + OUT(careerRadCrystals); + OUT(currentEbonCrystals); + OUT(careerEbonCrystals); + OUT(groupAutoconsent); + OUT(raidAutoconsent); + OUT(guildAutoconsent); + // OUT(showhelm); + + //set the checksum... + CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); + + FINISH_ENCODE(); } + ENCODE(OP_ReadBook) + { + EQApplicationPacket *in = *p; + *p = nullptr; - *length=MakeAnyLenString(&serialization, - "%.*s%s" // For leading quotes (and protection) if a subitem; - "%s" // Instance data - "%.*s\"" // Quotes (and protection, if needed) around static data - "%i" // item->ItemClass so we can do |%s instead of %s| + unsigned char *__emu_buffer = in->pBuffer; + + BookText_Struct *emu_BookText_Struct = (BookText_Struct *)__emu_buffer; + + in->size = sizeof(structs::BookText_Struct) + strlen(emu_BookText_Struct->booktext); + + in->pBuffer = new unsigned char[in->size]; + + structs::BookText_Struct *eq_BookText_Struct = (structs::BookText_Struct*)in->pBuffer; + + eq_BookText_Struct->window = emu_BookText_Struct->window; + eq_BookText_Struct->type = emu_BookText_Struct->type; + strcpy(eq_BookText_Struct->booktext, emu_BookText_Struct->booktext); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_RespondAA) + { + ENCODE_LENGTH_EXACT(AATable_Struct); + SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); + + unsigned int r; + for (r = 0; r < MAX_PP_AA_ARRAY; r++) { + OUT(aa_list[r].aa_skill); + OUT(aa_list[r].aa_value); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for SoF + // clientver 1 is for all clients and 2 is for 6.2 + if (emu->clientver <= 2) + { + OUT(id); + OUT(hotkey_sid); + OUT(hotkey_sid2); + OUT(title_sid); + OUT(desc_sid); + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + OUT(prereq_skill); + OUT(prereq_minpoints); + OUT(type); + OUT(spellid); + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + OUT(unknown80[0]); + OUT(unknown80[1]); + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct); + + int r; + for (r = 0; r < 10; r++) { + OUT(zone[r]); + OUT(eyecolor1[r]); + OUT(eyecolor2[r]); + OUT(hairstyle[r]); + OUT(primary[r]); + OUT(race[r]); + OUT(class_[r]); + OUT_str(name[r]); + OUT(gender[r]); + OUT(level[r]); + OUT(secondary[r]); + OUT(face[r]); + OUT(beard[r]); + int k; + for (k = 0; k < 9; k++) { + OUT(equip[r][k]); + OUT(cs_colors[r][k].color); + } + OUT(haircolor[r]); + OUT(gohome[r]); + OUT(deity[r]); + OUT(beardcolor[r]); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + in->size = sizeof(structs::Track_Struct) * EntryCount; + in->pBuffer = new unsigned char[in->size]; + structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++eq, ++emu) + { + OUT(entityid); + OUT(padding002); + OUT(distance); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZoneSpawns) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; + + //determine and verify length + int entrycount = in->size / sizeof(Spawn_Struct); + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + delete in; + return; + } + + //make the EQ struct. + in->size = sizeof(structs::Spawn_Struct)*entrycount; + in->pBuffer = new unsigned char[in->size]; + structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; + + //zero out the packet. We could avoid this memset by setting all fields (including unknowns) + //in the loop. + memset(in->pBuffer, 0, in->size); + + //do the transform... + int r; + int k; + for (r = 0; r < entrycount; r++, eq++, emu++) { + eq->gm = emu->gm; + eq->aaitle = emu->aaitle; + eq->anon = emu->anon; + eq->face = emu->face; + strcpy(eq->name, emu->name); + eq->deity = emu->deity; + eq->size = emu->size; + eq->NPC = emu->NPC; + eq->invis = emu->invis; + eq->haircolor = emu->haircolor; + eq->curHp = emu->curHp; + eq->max_hp = emu->max_hp; + eq->findable = emu->findable; + eq->deltaHeading = emu->deltaHeading; + eq->x = emu->x; + eq->y = emu->y; + eq->animation = emu->animation; + eq->z = emu->z; + eq->deltaY = emu->deltaY; + eq->deltaX = emu->deltaX; + eq->heading = emu->heading; + eq->deltaZ = emu->deltaZ; + eq->eyecolor1 = emu->eyecolor1; + // eq->showhelm = emu->showhelm; + eq->is_npc = emu->is_npc; + eq->hairstyle = emu->hairstyle; + eq->beard = emu->beard; + eq->level = emu->level; + eq->beardcolor = emu->beardcolor; + strcpy(eq->suffix, emu->suffix); + eq->petOwnerId = emu->petOwnerId; + eq->guildrank = emu->guildrank; + for (k = 0; k < 9; k++) { + eq->equipment[k] = emu->equipment[k]; + eq->colors[k].color = emu->colors[k].color; + } + for (k = 0; k < 8; k++) { + eq->set_to_0xFF[k] = 0xFF; + } + eq->runspeed = emu->runspeed; + eq->afk = emu->afk; + eq->guildID = emu->guildID; + strcpy(eq->title, emu->title); + eq->helm = emu->helm; + eq->race = emu->race; + strcpy(eq->lastName, emu->lastName); + eq->walkspeed = emu->walkspeed; + eq->is_pet = emu->is_pet; + eq->light = emu->light; + eq->class_ = emu->class_; + eq->eyecolor2 = emu->eyecolor2; + eq->gender = emu->gender; + eq->bodytype = emu->bodytype; + eq->equip_chest2 = emu->equip_chest2; + eq->spawnId = emu->spawnId; + eq->lfg = emu->lfg; + eq->flymode = emu->flymode; + } + + //kill off the emu structure and send the eq packet. + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + +// DECODE methods + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + IN(class_); + IN(beardcolor); + IN(beard); + IN(haircolor); + IN(gender); + IN(race); + IN(start_zone); + IN(hairstyle); + IN(deity); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + IN(link_hash); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ReadBook) + { + DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(window); + IN(type); + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 25; r++) { + IN(filters[r]); + } + emu->filters[25] = 1; + emu->filters[26] = 1; + emu->filters[27] = 1; + emu->filters[28] = 1; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + IN(spawn_id); + IN(material); + IN(color.color); + IN(wear_slot_id); + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + emu->type = 3; + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth) + { + char *serialization = nullptr; + char *instance = nullptr; + const char *protection = (const char *)"\\\\\\\\\\"; + char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + bool stackable = inst->IsStackable(); + uint32 merchant_slot = inst->GetMerchantSlot(); + int16 charges = inst->GetCharges(); + const Item_Struct *item = inst->GetItem(); + int i; + uint32 sub_length; + + MakeAnyLenString(&instance, + "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", + stackable ? charges : 1, + 0, + (merchant_slot == 0) ? slot_id : merchant_slot, + inst->GetPrice(), + (merchant_slot == 0) ? 1 : inst->GetMerchantCount(), + 0, + //merchant_slot, //instance ID, bullshit for now + // The 'Merchant Slot' needs to be some unique id for bazaar to work properly + (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot, + inst->IsInstNoDrop() ? 1 : 0, //not sure where this field is + (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? charges : 0) : charges), + 0 + ); + + for (i = 0; i<10; i++) { + ItemInst *sub = inst->GetItem(i); + if (sub) { + sub_items[i] = SerializeItem(sub, 0, &sub_length, depth + 1); + } + } + + + *length = MakeAnyLenString(&serialization, + "%.*s%s" // For leading quotes (and protection) if a subitem; + "%s" // Instance data + "%.*s\"" // Quotes (and protection, if needed) around static data + "%i" // item->ItemClass so we can do |%s instead of %s| #define I(field) "|%i" #define C(field) "|%s" #define S(field) "|%s" #define F(field) "|%f" #include "client62_itemfields.h" - "%.*s\"" // Quotes (and protection, if needed) around static data - "|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" // Sub items - "%.*s%s" // For trailing quotes (and protection) if a subitem; - ,depth ? depth-1 : 0,protection,(depth) ? "\"" : "" - ,instance - ,depth,protection - ,item->ItemClass + "%.*s\"" // Quotes (and protection, if needed) around static data + "|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" // Sub items + "%.*s%s" // For trailing quotes (and protection) if a subitem; + , depth ? depth - 1 : 0, protection, (depth) ? "\"" : "" + , instance + , depth, protection + , item->ItemClass #define I(field) ,item->field #define C(field) ,field #define S(field) ,item->field #define F(field) ,item->field #include "client62_itemfields.h" - ,depth,protection - ,sub_items[0] ? sub_items[0] : "" - ,sub_items[1] ? sub_items[1] : "" - ,sub_items[2] ? sub_items[2] : "" - ,sub_items[3] ? sub_items[3] : "" - ,sub_items[4] ? sub_items[4] : "" - ,sub_items[5] ? sub_items[5] : "" - ,sub_items[6] ? sub_items[6] : "" - ,sub_items[7] ? sub_items[7] : "" - ,sub_items[8] ? sub_items[8] : "" - ,sub_items[9] ? sub_items[9] : "" - ,(depth) ? depth-1 : 0,protection,(depth) ? "\"" : "" - ); + , depth, protection + , sub_items[0] ? sub_items[0] : "" + , sub_items[1] ? sub_items[1] : "" + , sub_items[2] ? sub_items[2] : "" + , sub_items[3] ? sub_items[3] : "" + , sub_items[4] ? sub_items[4] : "" + , sub_items[5] ? sub_items[5] : "" + , sub_items[6] ? sub_items[6] : "" + , sub_items[7] ? sub_items[7] : "" + , sub_items[8] ? sub_items[8] : "" + , sub_items[9] ? sub_items[9] : "" + , (depth) ? depth - 1 : 0, protection, (depth) ? "\"" : "" + ); - for(i=0;i<10;i++) { - if (sub_items[i]) - safe_delete_array(sub_items[i]); + for (i = 0; i < 10; i++) { + if (sub_items[i]) + safe_delete_array(sub_items[i]); + } + + safe_delete_array(instance); + + return serialization; } - safe_delete_array(instance); + static inline uint32 ServerToClient62Slot(uint32 ServerSlot) + { + //uint32 Client62Slot; + } + + static inline uint32 ServerToClient62CorpseSlot(uint32 ServerCorpse) + { + //uint32 Client62Corpse; + } - return serialization; + static inline uint32 Client62ToServerSlot(uint32 Client62Slot) + { + //uint32 ServerSlot; + } + + static inline uint32 Client62ToServerCorpseSlot(uint32 Client62Corpse) + { + //uint32 ServerCorpse; + } } - - -} //end namespace Client62 - - - - - - +// end namespace Client62 diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 9e81e8878..2643b909e 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -15,542 +15,1690 @@ #include #include -namespace RoF { +namespace RoF +{ + static const char *name = "RoF"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "RoF"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); -char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline structs::ItemSlotStruct ServerToRoFSlot(uint32 ServerSlot); + static inline structs::MainInvItemSlotStruct ServerToRoFMainInvSlot(uint32 ServerSlot); + static inline uint32 ServerToRoFCorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 RoFToServerSlot(structs::ItemSlotStruct RoFSlot); + static inline uint32 RoFToServerMainInvSlot(structs::MainInvItemSlotStruct RoFSlot); + static inline uint32 RoFToServerCorpseSlot(uint32 RoFCorpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "rof_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "rof_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClientRoF; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClientRoF; + } #include "ss_define.h" +// ENCODE methods + ENCODE(OP_Action) + { + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::ActionAlt_Struct); -// Converts Server Slot IDs to RoF Slot IDs for use in Encodes -static inline structs::ItemSlotStruct ServerToRoFSlot(uint32 ServerSlot) { - structs::ItemSlotStruct RoFSlot; - RoFSlot.SlotType = INVALID_INDEX; - RoFSlot.Unknown02 = NOT_USED; - RoFSlot.MainSlot = INVALID_INDEX; - RoFSlot.SubSlot = INVALID_INDEX; - RoFSlot.AugSlot = INVALID_INDEX; - RoFSlot.Unknown01 = NOT_USED; + OUT(target); + OUT(source); + OUT(level); + eq->unknown06 = 0; + eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; + eq->bard_focus_id = emu->bard_focus_id; + eq->knockback_angle = emu->sequence; + eq->unknown22 = 0; + OUT(type); + eq->damage = 0; + eq->unknown31 = 0; + OUT(spell); + eq->level2 = eq->level; + eq->effect_flag = emu->buff_unknown; + eq->unknown39 = 14; + eq->unknown43 = 0; + eq->unknown44 = 17; + eq->unknown45 = 0; + eq->unknown46 = -1; + eq->unknown50 = 0; + eq->unknown54 = 0; - uint32 TempSlot = 0; - - if (ServerSlot < 56 || ServerSlot == MainPowerSource) { // Main Inventory and Cursor - RoFSlot.SlotType = maps::MapPossessions; - RoFSlot.MainSlot = ServerSlot; - - if (ServerSlot == MainPowerSource) - RoFSlot.MainSlot = slots::MainPowerSource; - - else if (ServerSlot >= MainCursor) // Cursor and Extended Corpse Inventory - RoFSlot.MainSlot += 3; - - else if (ServerSlot >= MainAmmo) // (> 20) - RoFSlot.MainSlot += 1; + FINISH_ENCODE(); } - /*else if (ServerSlot < 51) { // Cursor Buffer - RoFSlot.SlotType = maps::MapLimbo; - RoFSlot.MainSlot = ServerSlot - 31; + ENCODE(OP_AdventureMerchantSell) + { + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToRoFMainInvSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); + } + + ENCODE(OP_AltCurrency) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *emu_buffer = in->pBuffer; + uint32 opcode = *((uint32*)emu_buffer); + + if (opcode == 8) { + AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(structs::AltCurrencyPopulate_Struct) + + sizeof(structs::AltCurrencyPopulateEntry_Struct) * populate->count); + structs::AltCurrencyPopulate_Struct *out_populate = (structs::AltCurrencyPopulate_Struct*)outapp->pBuffer; + + out_populate->opcode = populate->opcode; + out_populate->count = populate->count; + for (uint32 i = 0; i < populate->count; ++i) { + out_populate->entries[i].currency_number = populate->entries[i].currency_number; + out_populate->entries[i].unknown00 = populate->entries[i].unknown00; + out_populate->entries[i].currency_number2 = populate->entries[i].currency_number2; + out_populate->entries[i].item_id = populate->entries[i].item_id; + out_populate->entries[i].item_icon = populate->entries[i].item_icon; + out_populate->entries[i].stack_size = populate->entries[i].stack_size; + out_populate->entries[i].display = ((populate->entries[i].stack_size > 0) ? 1 : 0); + } + + dest->FastQueuePacket(&outapp, ack_req); + } + else { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyUpdate_Struct)); + memcpy(outapp->pBuffer, emu_buffer, sizeof(AltCurrencyUpdate_Struct)); + dest->FastQueuePacket(&outapp, ack_req); + } + + //dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_AltCurrencySell) + { + ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); + SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); + + OUT(merchant_entity_id); + eq->slot_id = ServerToRoFSlot(emu->slot_id); + OUT(charges); + OUT(cost); + + FINISH_ENCODE(); + } + + ENCODE(OP_Animation) + { + ENCODE_LENGTH_EXACT(Animation_Struct); + SETUP_DIRECT_ENCODE(Animation_Struct, structs::Animation_Struct); + + OUT(spawnid); + OUT(value); + OUT(action); + + FINISH_ENCODE(); + } + + ENCODE(OP_ApplyPoison) + { + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToRoFMainInvSlot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); + } + + ENCODE(OP_Barter) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + + uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + + if (SubAction != Barter_BuyerAppearance) + { + dest->FastQueuePacket(&in, ack_req); + + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + + in->size = 80; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + char Name[64]; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); + uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); + uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + VARSTRUCT_DECODE_STRING(Name, Buffer); + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + OutBuffer = (char *)in->pBuffer + 72; + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_BazaarSearch) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if (SubAction != BazaarSearchResults) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); + + if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + + in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); + in->pBuffer = new unsigned char[in->size]; + + memset(in->pBuffer, 0, in->size); + + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++emu, ++eq) + { + OUT(Beginning.Action); + OUT(SellerID); + memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); + OUT(NumItems); + OUT(ItemID); + OUT(SerialNumber); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(Cost); + OUT(ItemStat); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_BeginCast) + { + SETUP_DIRECT_ENCODE(BeginCast_Struct, structs::BeginCast_Struct); + + OUT(spell_id); + OUT(caster_id); + OUT(cast_time); + + FINISH_ENCODE(); + } + + ENCODE(OP_BlockedBuffs) + { + ENCODE_LENGTH_EXACT(BlockedBuffs_Struct); + SETUP_DIRECT_ENCODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); + + for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) + eq->SpellID[i] = emu->SpellID[i]; + + // -1 for the extra 10 added in RoF. We should really be encoding for the older clients, not RoF, but + // we can sort that out later. + + for (uint32 i = BLOCKED_BUFF_COUNT; i < structs::BLOCKED_BUFF_COUNT; ++i) + eq->SpellID[i] = -1; + + OUT(Count); + OUT(Pet); + OUT(Initialise); + OUT(Flags); + + FINISH_ENCODE(); + } + + ENCODE(OP_Buff) + { + ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); + SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Live); + + OUT(entityid); + eq->unknown004 = 2; + //eq->level = 80; + //eq->effect = 0; + OUT(level); + OUT(effect); + eq->unknown007 = 0; + eq->unknown008 = 1.0f; + OUT(spellid); + OUT(duration); + eq->playerId = 0x7cde; + OUT(slotid); + if (emu->bufffade == 1) + eq->bufffade = 1; + else + eq->bufffade = 2; + + // Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon + EQApplicationPacket *outapp = nullptr; + if (eq->bufffade == 1) + { + outapp = new EQApplicationPacket(OP_BuffCreate, 29); + outapp->WriteUInt32(emu->entityid); + outapp->WriteUInt32(0x0271); // Unk + outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ? + outapp->WriteUInt16(1); // 1 buff in this packet + outapp->WriteUInt32(emu->slotid); + outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove) + outapp->WriteUInt32(0); // Duration + outapp->WriteUInt32(0); // ? + outapp->WriteUInt8(0); // Caster name + outapp->WriteUInt8(0); // Terminating byte + } + FINISH_ENCODE(); + + if (outapp) + dest->FastQueuePacket(&outapp); // Send the OP_BuffCreate to remove the buff + } + + ENCODE(OP_BuffCreate) + { + SETUP_VAR_ENCODE(BuffIcon_Struct); + + uint32 sz = 12 + (17 * emu->count); + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); + + __packet->WriteUInt32(emu->entity_id); + __packet->WriteUInt32(0); // PlayerID ? + __packet->WriteUInt8(1); // 1 indicates all buffs on the player (0 to add or remove a single buff) + __packet->WriteUInt16(emu->count); + + for (uint16 i = 0; i < emu->count; ++i) + { + uint16 buffslot = emu->entries[i].buff_slot; + // Not sure if this is needs amending for RoF yet. + if (emu->entries[i].buff_slot >= 25) + { + buffslot += 17; + } + + __packet->WriteUInt32(buffslot); + __packet->WriteUInt32(emu->entries[i].spell_id); + __packet->WriteUInt32(emu->entries[i].tics_remaining); + __packet->WriteUInt32(0); // Unknown + __packet->WriteString(""); + } + __packet->WriteUInt8(0); // Unknown + + FINISH_ENCODE(); + } + + ENCODE(OP_CancelTrade) + { + ENCODE_LENGTH_EXACT(CancelTrade_Struct); + SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); + + OUT(fromid); + OUT(action); + + FINISH_ENCODE(); + } + + ENCODE(OP_CastSpell) + { + ENCODE_LENGTH_EXACT(CastSpell_Struct); + SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct); + + if (emu->slot == 10) + eq->slot = 13; + else + OUT(slot); + + OUT(spell_id); + eq->inventoryslot = ServerToRoFSlot(emu->inventoryslot); + //OUT(inventoryslot); + OUT(target_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_CharInventory) + { + //consume the packet + EQApplicationPacket *in = *p; + + *p = nullptr; + + if (in->size == 0) { + + in->size = 4; + in->pBuffer = new uchar[in->size]; + + *((uint32 *)in->pBuffer) = 0; + + dest->FastQueuePacket(&in, ack_req); + return; + } + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + + int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); + + if (ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + + delete in; + + return; + } + + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; + + in->pBuffer = new uchar[4]; + *(uint32 *)in->pBuffer = ItemCount; + in->size = 4; + + for (int r = 0; r < ItemCount; r++, eq++) { + + uint32 Length = 0; + + char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); + + if (Serialized) { + + uchar *OldBuffer = in->pBuffer; + in->pBuffer = new uchar[in->size + Length]; + memcpy(in->pBuffer, OldBuffer, in->size); + + safe_delete_array(OldBuffer); + + memcpy(in->pBuffer + in->size, Serialized, Length); + in->size += Length; + + safe_delete_array(Serialized); + } + else { + _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } + } + + delete[] __emu_buffer; + + //_log(NET__ERROR, "Sending inventory to client"); + //_hex(NET__ERROR, in->pBuffer, in->size); + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ClickObjectAction) + { + ENCODE_LENGTH_EXACT(ClickObjectAction_Struct); + SETUP_DIRECT_ENCODE(ClickObjectAction_Struct, structs::ClickObjectAction_Struct); + + OUT(drop_id); + eq->unknown04 = -1; + eq->unknown08 = -1; + OUT(type); + OUT(icon); + eq->unknown16 = 0; + OUT_str(object_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_ClientUpdate) + { + ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); + SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); + + OUT(spawn_id); + OUT(x_pos); + OUT(delta_x); + OUT(delta_y); + OUT(z_pos); + OUT(delta_heading); + OUT(y_pos); + OUT(delta_z); + OUT(animation); + OUT(heading); + + FINISH_ENCODE(); + } + + ENCODE(OP_Consider) + { + ENCODE_LENGTH_EXACT(Consider_Struct); + SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); + + OUT(playerid); + OUT(targetid); + OUT(faction); + OUT(level); + OUT(pvpcon); + + FINISH_ENCODE(); + } + + ENCODE(OP_Damage) + { + ENCODE_LENGTH_EXACT(CombatDamage_Struct); + SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + OUT(target); + OUT(source); + OUT(type); + OUT(spellid); + OUT(damage); + eq->sequence = emu->sequence; + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToRoFSlot(emu->from_slot); + eq->to_slot = ServerToRoFSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteSpawn) + { + ENCODE_LENGTH_EXACT(DeleteSpawn_Struct); + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); + + OUT(spawn_id); + eq->unknown04 = 1; // Observed + + FINISH_ENCODE(); + } + + ENCODE(OP_DisciplineUpdate) + { + ENCODE_LENGTH_EXACT(Disciplines_Struct); + SETUP_DIRECT_ENCODE(Disciplines_Struct, structs::Disciplines_Struct); + + memcpy(&eq->values, &emu->values, sizeof(Disciplines_Struct)); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzCompass) + { + SETUP_VAR_ENCODE(ExpeditionCompass_Struct); + ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); + + OUT(count); + + for (uint32 i = 0; i < emu->count; ++i) + { + OUT(entries[i].x); + OUT(entries[i].y); + OUT(entries[i].z); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionEndsWarning) + { + ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); + SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); + + OUT(minutes_remaining); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionInfo) + { + ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + + OUT(max_players); + eq->unknown004 = 785316192; + eq->unknown008 = 435601; + strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); + strncpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionList) + { + SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); + ss.write((const char*)&null_term, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzJoinExpeditionConfirm) + { + ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); + SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); + + strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); + strncpy(eq->player_name, emu->player_name, sizeof(eq->player_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzLeaderStatus) + { + SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->leader_name, strlen(emu->leader_name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//1 + ss.write((const char*)&client_id, sizeof(uint32)); + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzMemberList) + { + SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].status, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpansionInfo) + { + ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + + OUT(Expansions); + + FINISH_ENCODE(); + } + + ENCODE(OP_GMLastName) + { + ENCODE_LENGTH_EXACT(GMLastName_Struct); + SETUP_DIRECT_ENCODE(GMLastName_Struct, structs::GMLastName_Struct); + + OUT_str(name); + OUT_str(gmname); + OUT_str(lastname); + for (int i = 0; i<4; i++) + { + eq->unknown[i] = emu->unknown[i]; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_GMTrainSkillConfirm) + { + ENCODE_LENGTH_EXACT(GMTrainSkillConfirm_Struct); + SETUP_DIRECT_ENCODE(GMTrainSkillConfirm_Struct, structs::GMTrainSkillConfirm_Struct); + + OUT(SkillID); + OUT(Cost); + OUT(NewSkill); + OUT_str(TrainerName); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroundSpawn) + { + // We are not encoding the spawn_id field here, but it doesn't appear to matter. + // + EQApplicationPacket *in = *p; + *p = nullptr; + + Object_Struct *emu = (Object_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + in->size = strlen(emu->object_name) + sizeof(Object_Struct)-1; + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->object_name); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_id); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_instance); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); // Some unique id + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Same for all objects in the zone + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->heading); + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Normally 0, but seen (float)255.0 as well + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 1); // Need to add emu->size to struct + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); + VARSTRUCT_ENCODE_TYPE(int32, OutBuffer, emu->object_type); // Unknown, observed 0x00000014 + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_GroupCancelInvite) + { + ENCODE_LENGTH_EXACT(GroupCancel_Struct); + SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + OUT(toggle); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow2) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupInvite) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); + memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupUpdate) + { + //_log(NET__ERROR, "OP_GroupUpdate"); + EQApplicationPacket *in = *p; + GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; + + //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); + if ((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) + { + if ((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) + { + //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); + dest->FastQueuePacket(&outapp); + + // Make an empty GLAA packet to clear out their useable GLAAs + // + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + dest->FastQueuePacket(&outapp); + delete in; + return; + } + //if(gjs->action == groupActLeave) + // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + delete in; + return; + } + + if (in->size == sizeof(GroupUpdate2_Struct)) + { + // Group Update2 + //_log(NET__ERROR, "Struct is GroupUpdate2"); + + unsigned char *__emu_buffer = in->pBuffer; + GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*)__emu_buffer; + + //_log(NET__ERROR, "Yourname is %s", gu2->yourname); + + int MemberCount = 1; + int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; + + for (int i = 0; i < 5; ++i) + { + //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); + if (gu2->membername[i][0] != '\0') + { + PacketLength += (22 + strlen(gu2->membername[i]) + 1); + ++MemberCount; + } + } + + //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); + + char *Buffer = (char *)outapp->pBuffer; + + // Header + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); + + // Leader + // + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + + int MemberNumber = 1; + + for (int i = 0; i < 5; ++i) + { + if (gu2->membername[i][0] == '\0') + continue; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = gu2->NPCMarkerID; + memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); + + dest->FastQueuePacket(&outapp); + delete in; + + return; + + } + //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + ENCODE_LENGTH_EXACT(GroupJoin_Struct); + SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); + + memcpy(eq->membername, emu->membername, sizeof(eq->membername)); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = emu->NPCMarkerID; + + memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); + //_hex(NET__ERROR, __packet->pBuffer, __packet->size); + + FINISH_ENCODE(); + + dest->FastQueuePacket(&outapp); + } + + ENCODE(OP_GuildMemberList) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count*sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + // Guild ID + buffer += sizeof(uint32); + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header + emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data + ); + const char *emu_note = (emu_name + + emu->name_length + //skip name contents + emu->count //skip string terminators + ); + + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { + + //the order we set things here must match the struct + + //nice helper macro +#define SlideStructString(field, str) \ + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) + + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + + /* Translate older ranks to new values */ + switch (emu_e->rank) { + case 0: { e->rank = htonl(5); break; } // GUILD_MEMBER 0 + case 1: { e->rank = htonl(3); break; } // GUILD_OFFICER 1 + case 2: { e->rank = htonl(1); break; } // GUILD_LEADER 2 + default: { e->rank = htonl(emu_e->rank); break; } // GUILD_NONE + } + + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + e->unknown01 = 0; + PutFieldN(total_tribute); + PutFieldN(last_tribute); + e->unknown_one = htonl(1); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); + e->unknown_one2 = htonl(1); + e->unknown04 = 0; + +#undef SlideStructString +#undef PutFieldN + + e++; + } + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_GuildMemberUpdate) + { + SETUP_DIRECT_ENCODE(GuildMemberUpdate_Struct, structs::GuildMemberUpdate_Struct); + + OUT(GuildID); + memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); + OUT(ZoneID); + OUT(InstanceID); + OUT(LastSeen); + eq->Unknown76 = 0; + + FINISH_ENCODE(); + } + + ENCODE(OP_GuildsList) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + uint32 NumberOfGuilds = in->size / 64; + uint32 PacketSize = 68; // 64 x 0x00 + a uint32 that I am guessing is the highest guild ID in use. + + unsigned char *__emu_buffer = in->pBuffer; + + char *InBuffer = (char *)__emu_buffer; + + uint32 HighestGuildID = 0; + + for (unsigned int i = 0; i < NumberOfGuilds; ++i) + { + if (InBuffer[0]) + { + PacketSize += (5 + strlen(InBuffer)); + HighestGuildID = i - 1; + } + InBuffer += 64; + } + + PacketSize++; // Appears to be an extra 0x00 at the very end. + + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + + InBuffer = (char *)__emu_buffer; + + char *OutBuffer = (char *)in->pBuffer; + + // Init the first 64 bytes to zero, as per live. + // + memset(OutBuffer, 0, 64); + + OutBuffer += 64; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, HighestGuildID); + + for (unsigned int i = 0; i < NumberOfGuilds; ++i) + { + if (InBuffer[0]) + { + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, i - 1); + VARSTRUCT_ENCODE_STRING(OutBuffer, InBuffer); + } + InBuffer += 64; + } + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0x00); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_HPUpdate) + { + SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct, structs::SpawnHPUpdate_Struct); + + OUT(spawn_id); + OUT(cur_hp); + OUT(max_hp); + + FINISH_ENCODE(); + } + + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + OUT(race); + OUT(unknown006[0]); + OUT(unknown006[1]); + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + OUT(drakkin_heritage); + OUT(drakkin_tattoo); + OUT(drakkin_details); + eq->unknown316 = -1; // Observed + + FINISH_ENCODE(); + } + + /*ENCODE(OP_InspectAnswer) + { + ENCODE_LENGTH_EXACT(InspectResponse_Struct); + SETUP_DIRECT_ENCODE(InspectResponse_Struct, structs::InspectResponse_Struct); + + OUT(TargetID); + OUT(playerid); + + int r; + for (r = 0; r < 21; r++) { + strn0cpy(eq->itemnames[r], emu->itemnames[r], sizeof(eq->itemnames[r])); + } + // Swap last 2 slots for Arrow and Power Source + strn0cpy(eq->itemnames[21], emu->itemnames[22], sizeof(eq->itemnames[21])); + strn0cpy(eq->unknown_zero, emu->itemnames[21], sizeof(eq->unknown_zero)); + + int k; + for (k = 0; k < 21; k++) { + OUT(itemicons[k]); + } + // Swap last 2 slots for Arrow and Power Source + eq->itemicons[21] = emu->itemicons[22]; + eq->unknown_zero2 = emu->itemicons[21]; + strn0cpy(eq->text, emu->text, sizeof(eq->text)); + + FINISH_ENCODE(); }*/ - else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) { // (> 250 && < 341) - RoFSlot.SlotType = maps::MapPossessions; - TempSlot = ServerSlot - 1; - RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 2; - RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 2) * EmuConstants::ITEM_CONTAINER_SIZE); - - if (RoFSlot.MainSlot >= slots::MainGeneral9) // (> 30) - RoFSlot.MainSlot = slots::MainCursor; - } - - else if (ServerSlot >= EmuConstants::TRIBUTE_BEGIN && ServerSlot <= EmuConstants::TRIBUTE_END) { // Tribute - RoFSlot.SlotType = maps::MapTribute; - RoFSlot.MainSlot = ServerSlot - EmuConstants::TRIBUTE_BEGIN; - } - - else if (ServerSlot >= EmuConstants::BANK_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) { - RoFSlot.SlotType = maps::MapBank; - TempSlot = ServerSlot - EmuConstants::BANK_BEGIN; - RoFSlot.MainSlot = TempSlot; - - if (TempSlot > 30) { // (> 30) - RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; - RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); - } - } - - else if (ServerSlot >= EmuConstants::SHARED_BANK_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) { - RoFSlot.SlotType = maps::MapSharedBank; - TempSlot = ServerSlot - EmuConstants::SHARED_BANK_BEGIN; - RoFSlot.MainSlot = TempSlot; - - if (TempSlot > 30) { // (> 30) - RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; - RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); - } - } - - else if (ServerSlot >= EmuConstants::TRADE_BEGIN && ServerSlot <= EmuConstants::TRADE_BAGS_END) { - RoFSlot.SlotType = maps::MapTrade; - TempSlot = ServerSlot - EmuConstants::TRADE_BEGIN; - RoFSlot.MainSlot = TempSlot; - - if (TempSlot > 30) { - RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; - RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); - } - - /* - // OLD CODE: - if (TempSlot > 99) { - if (TempSlot > 100) - RoFSlot.MainSlot = int((TempSlot - 100) / 10); - - else - RoFSlot.MainSlot = 0; - - RoFSlot.SubSlot = TempSlot - (100 + RoFSlot.MainSlot); - } - */ - } - - else if (ServerSlot >= EmuConstants::WORLD_BEGIN && ServerSlot <= EmuConstants::WORLD_END) { - RoFSlot.SlotType = maps::MapWorld; - TempSlot = ServerSlot - EmuConstants::WORLD_BEGIN; - RoFSlot.MainSlot = TempSlot; - } - - _log(NET__ERROR, "Convert Server Slot %i to RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i", ServerSlot, RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01); - - return RoFSlot; -} - -// Converts RoF Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 RoFToServerSlot(structs::ItemSlotStruct RoFSlot) { - uint32 ServerSlot = INVALID_INDEX; - uint32 TempSlot = 0; - - if (RoFSlot.SlotType == maps::MapPossessions && RoFSlot.MainSlot < 57) { // Worn/Personal Inventory and Cursor (< 51) - if (RoFSlot.MainSlot == slots::MainPowerSource) - TempSlot = MainPowerSource; - - else if (RoFSlot.MainSlot >= slots::MainCursor) // Cursor and Extended Corpse Inventory - TempSlot = RoFSlot.MainSlot - 3; - - /*else if (RoFSlot.MainSlot == slots::MainGeneral9 || RoFSlot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF inventory/corpse slots - // Need to figure out what to do when we get these - - // The slot range of 0 - client_max is cross-utilized between player inventory and corpse inventory. - // In the case of RoF, player inventory is addressed as 0 - 33 and corpse inventory is addressed as 23 - 56. - // We 'could' assign the two new inventory slots as 9997 and 9998, and then work around their bag - // slot assignments, but doing so may disrupt our ability to utilize the corpse looting range properly. - - // For now, it's probably best to leave as-is and let this work itself out in the inventory rework. - }*/ - - else if (RoFSlot.MainSlot >= slots::MainAmmo) // Ammo and Main Inventory - TempSlot = RoFSlot.MainSlot - 1; - - else // Worn Slots - TempSlot = RoFSlot.MainSlot; - - if (RoFSlot.SubSlot >= SUB_BEGIN) // Bag Slots - TempSlot = ((TempSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; - - ServerSlot = TempSlot; - } - - else if (RoFSlot.SlotType == maps::MapBank) { - TempSlot = EmuConstants::BANK_BEGIN; - - if (RoFSlot.SubSlot >= SUB_BEGIN) - TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; - - else - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - } - - else if (RoFSlot.SlotType == maps::MapSharedBank) { - TempSlot = EmuConstants::SHARED_BANK_BEGIN; - - if (RoFSlot.SubSlot >= SUB_BEGIN) - TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; - - else - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - } - - else if (RoFSlot.SlotType == maps::MapTrade) { - TempSlot = EmuConstants::TRADE_BEGIN; - - if (RoFSlot.SubSlot >= SUB_BEGIN) - TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; - // OLD CODE: - //TempSlot += 100 + (RoFSlot.MainSlot * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot; - - else - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - } - - else if (RoFSlot.SlotType == maps::MapWorld) { - TempSlot = EmuConstants::WORLD_BEGIN; - - if (RoFSlot.MainSlot >= SUB_BEGIN) - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - } - - /*else if (RoFSlot.SlotType == maps::MapLimbo) { // Cursor Buffer - TempSlot = 31; - - if (RoFSlot.MainSlot >= 0) - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - }*/ - - _log(NET__ERROR, "Convert RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, ServerSlot); - - return ServerSlot; -} - -// Converts Server MainInv Slot IDs to RoF MainInv Slot IDs for use in Encodes -static inline structs::MainInvItemSlotStruct ServerToRoFMainInvSlot(uint32 ServerSlot) { - structs::MainInvItemSlotStruct RoFSlot; - RoFSlot.MainSlot = INVALID_INDEX; - RoFSlot.SubSlot = INVALID_INDEX; - RoFSlot.AugSlot = INVALID_INDEX; - RoFSlot.Unknown01 = NOT_USED; - - uint32 TempSlot = 0; - - if (ServerSlot < 56 || ServerSlot == MainPowerSource) { // (< 52) - RoFSlot.MainSlot = ServerSlot; - - if (ServerSlot == MainPowerSource) - RoFSlot.MainSlot = slots::MainPowerSource; - - else if (ServerSlot >= MainCursor) // Cursor and Extended Corpse Inventory - RoFSlot.MainSlot += 3; - - else if (ServerSlot >= MainAmmo) // Ammo and Personl Inventory - RoFSlot.MainSlot += 1; - - /*else if (ServerSlot >= MainCursor) { // Cursor - RoFSlot.MainSlot = slots::MainCursor; - - if (ServerSlot > 30) - RoFSlot.SubSlot = (ServerSlot + 3) - 33; - }*/ - } - - else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) { - TempSlot = ServerSlot - 1; - RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 2; - RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 2) * EmuConstants::ITEM_CONTAINER_SIZE); - } - - _log(NET__ERROR, "Convert Server Slot %i to RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i", ServerSlot, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01); - - return RoFSlot; -} - -// Converts RoF MainInv Slot IDs to Server MainInv Slot IDs for use in Decodes -static inline uint32 RoFToServerMainInvSlot(structs::MainInvItemSlotStruct RoFSlot) { - uint32 ServerSlot = INVALID_INDEX; - uint32 TempSlot = 0; - - if (RoFSlot.MainSlot < 57) { // Worn/Personal Inventory and Cursor (< 33) - if (RoFSlot.MainSlot == slots::MainPowerSource) - TempSlot = MainPowerSource; - - else if (RoFSlot.MainSlot >= slots::MainCursor) // Cursor and Extended Corpse Inventory - TempSlot = RoFSlot.MainSlot - 3; - - /*else if (RoFSlot.MainSlot == slots::MainGeneral9 || RoFSlot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF inventory slots - // Need to figure out what to do when we get these - - // Same as above - }*/ - - else if (RoFSlot.MainSlot >= slots::MainAmmo) // Main Inventory and Ammo Slots - TempSlot = RoFSlot.MainSlot - 1; - - else - TempSlot = RoFSlot.MainSlot; - - if (RoFSlot.SubSlot >= SUB_BEGIN) // Bag Slots - TempSlot = ((TempSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; - - ServerSlot = TempSlot; - } - - _log(NET__ERROR, "Convert RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, ServerSlot); - - return ServerSlot; -} - -/* -// Converts Server Corpse Slot IDs to RoF Corpse Slot IDs for use in Encodes -static inline uint32 ServerToRoFCorpseSlot(uint32 ServerCorpse) { - uint32 RoFCorpse; - // reserved -} -*/ -/* -// Converts RoF Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 RoFToServerCorpseSlot(uint32 RoFCorpse) { - uint32 ServerCorpse; - // reserved -} -*/ - - -ENCODE(OP_TaskHistoryReply) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - // First we need to calculate the length of the new packet - in->SetReadPosition(4); - uint32 ActivityCount = in->ReadUInt32(); - - uint32 Text1Length = 0; - uint32 Text2Length = 0; - uint32 Text3Length = 0; - - uint32 OutboundPacketSize = 8; - - for(uint32 i = 0; i < ActivityCount; ++i) + ENCODE(OP_InspectRequest) { - Text1Length = 0; - Text2Length = 0; - Text3Length = 0; + ENCODE_LENGTH_EXACT(Inspect_Struct); + SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - in->ReadUInt32(); // Activity type + OUT(TargetID); + OUT(PlayerID); - // Skip past Text1 - while(in->ReadUInt8()) - ++Text1Length; - - // Skip past Text2 - while(in->ReadUInt8()) - ++Text2Length; - - in->ReadUInt32(); - in->ReadUInt32(); - in->ReadUInt32(); - uint32 ZoneID = in->ReadUInt32(); - in->ReadUInt32(); - - // Skip past Text3 - while(in->ReadUInt8()) - ++Text3Length; - - char ZoneNumber[10]; - - sprintf(ZoneNumber, "%i", ZoneID); - - OutboundPacketSize += (24 + Text1Length + 1 + Text2Length + Text3Length + 1 + 7 + (strlen(ZoneNumber) * 2)); + FINISH_ENCODE(); } - in->SetReadPosition(0); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_TaskHistoryReply, OutboundPacketSize); - - outapp->WriteUInt32(in->ReadUInt32()); // Task index - outapp->WriteUInt32(in->ReadUInt32()); // Activity count - - for(uint32 i = 0; i < ActivityCount; ++i) + ENCODE(OP_InterruptCast) { - Text1Length = 0; - Text2Length = 0; - Text3Length = 0; + ENCODE_LENGTH_EXACT(InterruptCast_Struct); + SETUP_DIRECT_ENCODE(InterruptCast_Struct, structs::InterruptCast_Struct); - outapp->WriteUInt32(in->ReadUInt32()); // ActivityType + OUT(spawnid); + OUT(messageid); - // Copy Text1 - while(uint8 c = in->ReadUInt8()) - outapp->WriteUInt8(c); - - outapp->WriteUInt8(0); // Text1 has a null terminator - - uint32 CurrentPosition = in->GetReadPosition(); - - // Determine Length of Text2 - while(in->ReadUInt8()) - ++Text2Length; - - outapp->WriteUInt32(Text2Length); - - in->SetReadPosition(CurrentPosition); - - // Copy Text2 - while(uint8 c = in->ReadUInt8()) - outapp->WriteUInt8(c); - - outapp->WriteUInt32(in->ReadUInt32()); // Goalcount - in->ReadUInt32(); - in->ReadUInt32(); - uint32 ZoneID = in->ReadUInt32(); - in->ReadUInt32(); - - char ZoneNumber[10]; - - sprintf(ZoneNumber, "%i", ZoneID); - - outapp->WriteUInt32(2); - outapp->WriteUInt8(0x2d); // "-" - outapp->WriteUInt8(0x31); // "1" - - outapp->WriteUInt32(2); - outapp->WriteUInt8(0x2d); // "-" - outapp->WriteUInt8(0x31); // "1" - outapp->WriteString(ZoneNumber); - - outapp->WriteUInt32(0); - - // Copy Tex3t - while(uint8 c = in->ReadUInt8()) - outapp->WriteUInt8(c); - - outapp->WriteUInt8(0); // Text3 has a null terminator - - outapp->WriteUInt8(0x31); // "1" - outapp->WriteString(ZoneNumber); + FINISH_ENCODE(); } - delete in; + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } - dest->FastQueuePacket(&outapp, ack_req); -} + ENCODE(OP_ItemPacket) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; -ENCODE(OP_TaskDescription) -{ - EQApplicationPacket *in = *p; - *p = nullptr; + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_TaskDescription, in->size + 1); - // Set the Write pointer as we don't know what has been done with the packet before we get it. - in->SetReadPosition(0); - // Copy the header - for(int i = 0; i < 5; ++i) - outapp->WriteUInt32(in->ReadUInt32()); + uint32 length; + char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); - // Copy Title - while(uint8 c = in->ReadUInt8()) - outapp->WriteUInt8(c); - outapp->WriteUInt8(0); + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 4; + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length); - outapp->WriteUInt32(in->ReadUInt32()); // Duration - outapp->WriteUInt32(in->ReadUInt32()); // Unknown - uint32 StartTime = in->ReadUInt32(); - outapp->WriteUInt32(time(nullptr) - StartTime); // RoF has elapsed time here rather than starttime + delete[] __emu_buffer; + safe_delete_array(serialized); + dest->FastQueuePacket(&in, ack_req); + } - // Copy the rest of the packet verbatim - uint32 BytesLeftToCopy = in->size - in->GetReadPosition(); - memcpy(outapp->pBuffer + outapp->GetWritePosition(), in->pBuffer + in->GetReadPosition(), BytesLeftToCopy); + ENCODE(OP_ItemVerifyReply) + { + ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); + SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); - delete in; + eq->slot = ServerToRoFSlot(emu->slot); + OUT(spell); + OUT(target); - dest->FastQueuePacket(&outapp, ack_req); -} + FINISH_ENCODE(); + } -/* -ENCODE(OP_OpenNewTasksWindow) { + ENCODE(OP_LeadershipExpUpdate) + { + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LogServer) + { + ENCODE_LENGTH_EXACT(LogServer_Struct); + SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); + + strncpy(eq->worldshortname, emu->worldshortname, sizeof(eq->worldshortname)); + + //OUT(enablevoicemacros); // These two are lost, but must be one of the 1s in unknown[249] + //OUT(enablemail); + OUT(enable_pvp); + OUT(enable_FV); + + eq->unknown016 = 1; + eq->unknown020[0] = 1; + + eq->unknown249[0] = 1; + eq->unknown249[1] = 1; + eq->unknown249[8] = 1; + eq->unknown249[9] = 1; + eq->unknown249[12] = 1; + eq->unknown249[14] = 1; + eq->unknown249[15] = 1; + eq->unknown249[16] = 1; + + eq->unknown276[0] = 1.0f; + eq->unknown276[1] = 1.0f; + eq->unknown276[6] = 1.0f; + + FINISH_ENCODE(); + } + + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = emu->slot_id + 1; + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_ManaChange) + { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); + + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? + + FINISH_ENCODE(); + } + + ENCODE(OP_MercenaryDataResponse) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + + int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; + PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); + + for (r = 0; r < emu->MercTypeCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); + } + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Status); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk01); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MerchantSlot); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk02); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk03); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk04); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // MercName + for (k = 0; k < emu->Mercs[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); + } + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_MercenaryDataUpdate) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + + EQApplicationPacket *outapp; + + uint32 PacketSize = 0; + + // There are 2 different sized versions of this packet depending if a merc is hired or not + if (emu->MercStatus >= 0) + { + PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; + PacketSize += strlen(emu->MercData[r].MercName); // Null Terminator size already accounted for in the struct + } + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Status); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk01); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MerchantSlot); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk02); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].MercUnk03); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk04); + //VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // MercName + VARSTRUCT_ENCODE_STRING(Buffer, emu->MercData[r].MercName); + for (k = 0; k < emu->MercData[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); + } + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); // MercUnk05 + } + } + else + { + PacketSize += sizeof(structs::NoMercenaryHired_Struct); + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToRoFSlot(emu->from_slot); + eq->to_slot = ServerToRoFSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_NewZone) + { + SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); + + OUT_str(char_name); + OUT_str(zone_short_name); + OUT_str(zone_long_name); + OUT(ztype); + int r; + for (r = 0; r < 4; r++) { + OUT(fog_red[r]); + OUT(fog_green[r]); + OUT(fog_blue[r]); + OUT(fog_minclip[r]); + OUT(fog_maxclip[r]); + } + OUT(gravity); + OUT(time_type); + for (r = 0; r < 4; r++) { + OUT(rain_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(rain_duration[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_duration[r]); + } + for (r = 0; r < 32; r++) { + eq->unknown537[r] = 0xFF; //observed + } + OUT(sky); + OUT(zone_exp_multiplier); + OUT(safe_y); + OUT(safe_x); + OUT(safe_z); + OUT(max_z); + OUT(underworld); + OUT(minclip); + OUT(maxclip); + OUT_str(zone_short_name2); + OUT(zone_id); + OUT(zone_instance); + OUT(SuspendBuffs); + + eq->FogDensity = emu->fog_density; + + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown800 = -1; + eq->unknown844 = 600; + eq->unknown880 = 50; + eq->unknown884 = 10; + eq->unknown888 = 1; + eq->unknown889 = 0; + eq->unknown890 = 1; + eq->unknown891 = 0; + eq->unknown892 = 0; + eq->unknown893 = 0; + eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off + eq->unknown895 = 0; + eq->unknown896 = 180; + eq->unknown900 = 180; + eq->unknown904 = 180; + eq->unknown908 = 2; + eq->unknown912 = 2; + eq->unknown932 = -1; // Set from PoK Example + eq->unknown936 = -1; // Set from PoK Example + eq->unknown944 = 1.0; // Set from PoK Example + + FINISH_ENCODE(); + } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + // This packet is variable sized now, but forcing it to the old packet size for now. + eq->Title_Count = 128; + memcpy(eq->Title, emu->Title, sizeof(eq->Title)); + eq->Text_Count = 4096; + memcpy(eq->Text, emu->Text, sizeof(eq->Text)); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + OUT(NegativeID); + // These two field names are used if Buttons == 1. We should add an interface to them via Perl. + eq->ButtonName0_Count = 25; + OUT_str(ButtonName0); + eq->ButtonName1_Count = 25; + OUT_str(ButtonName1); + + FINISH_ENCODE(); + } + + /* + ENCODE(OP_OpenNewTasksWindow) + { AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; AvailableTaskData1_Struct* __emu_AvailableTaskData1; AvailableTaskData2_Struct* __emu_AvailableTaskData2; @@ -593,1260 +1741,1855 @@ ENCODE(OP_OpenNewTasksWindow) { for(uint32 i=0; i<__emu_AvailableTaskHeader->TaskCount; i++) { - __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; - __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; + __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; + __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; - __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; - // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen - // in RoF packets. Changing it to 0x3f000000 makes the title red. - __eq_AvailableTaskData1->unknown1 = 0x3f800000; - __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; - __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; + __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; + // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen + // in RoF packets. Changing it to 0x3f000000 makes the title red. + __eq_AvailableTaskData1->unknown1 = 0x3f800000; + __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; + __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; - __emu_Ptr += sizeof(AvailableTaskData1_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); + __emu_Ptr += sizeof(AvailableTaskData1_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); - strcpy(__eq_ptr, __emu_Ptr); // Title + strcpy(__eq_ptr, __emu_Ptr); // Title - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; - strcpy(__eq_ptr, __emu_Ptr); // Description + strcpy(__eq_ptr, __emu_Ptr); // Description - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; - __eq_ptr[0] = 0; - __eq_ptr += strlen(__eq_ptr) + 1; + __eq_ptr[0] = 0; + __eq_ptr += strlen(__eq_ptr) + 1; - __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; - __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; + __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; + __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; - __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; - __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; - __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; - __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; + __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; + __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; + __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; + __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; - __emu_Ptr += sizeof(AvailableTaskData2_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); + __emu_Ptr += sizeof(AvailableTaskData2_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); - strcpy(__eq_ptr, __emu_Ptr); // Unknown string + strcpy(__eq_ptr, __emu_Ptr); // Unknown string - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; - strcpy(__eq_ptr, __emu_Ptr); // Unknown string + strcpy(__eq_ptr, __emu_Ptr); // Unknown string - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; - __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; - __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; + __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; + __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; - __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; - __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; - __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; - __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; + __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; + __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; + __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; + __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; - __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); - __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); + __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); + __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); - strcpy(__eq_ptr, __emu_Ptr); // Unknown string + strcpy(__eq_ptr, __emu_Ptr); // Unknown string - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; } delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); -} -*/ - -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_VAR_ENCODE(CharacterSelect_Struct); - - - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; - - int char_count; - int namelen = 0; - for(char_count = 0; char_count < 10; char_count++) { - if(emu->name[char_count][0] == '\0') - break; - if(strcmp(emu->name[char_count], "") == 0) - break; - namelen += strlen(emu->name[char_count]); - } - - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; - - ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); - - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; - - eq->char_count = char_count; - //eq->total_chars = 10; - - unsigned char *bufptr = (unsigned char *) eq->entries; - int r; - for(r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - memcpy(eq2->name, emu->name[r], strlen(emu->name[r])+1); - } - //adjust for name. - bufptr += strlen(emu->name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->class_ = emu->class_[r]; - eq2->race = emu->race[r]; - eq2->level = emu->level[r]; - eq2->class_2 = emu->class_[r]; - eq2->race2 = emu->race[r]; - eq2->zone = emu->zone[r]; - eq2->instance = 0; - eq2->gender = emu->gender[r]; - eq2->face = emu->face[r]; - int k; - for(k = 0; k < _MaterialCount; k++) { - eq2->equip[k].equip0 = emu->equip[r][k]; - eq2->equip[k].equip1 = 0; - eq2->equip[k].equip2 = 0; - eq2->equip[k].itemid = 0; - eq2->equip[k].equip3 = emu->equip[r][k]; - eq2->equip[k].color.color = emu->cs_colors[r][k].color; - } - eq2->u15 = 0xff; - eq2->u19 = 0xFF; - eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; - eq2->drakkin_details = emu->drakkin_details[r]; - eq2->deity = emu->deity[r]; - eq2->primary = emu->primary[r]; - eq2->secondary = emu->secondary[r]; - eq2->haircolor = emu->haircolor[r]; - eq2->beardcolor = emu->beardcolor[r]; - eq2->eyecolor1 = emu->eyecolor1[r]; - eq2->eyecolor2 = emu->eyecolor2[r]; - eq2->hairstyle = emu->hairstyle[r]; - eq2->beard = emu->beard[r]; - eq2->char_enabled = 1; - eq2->tutorial = emu->tutorial[r]; - eq2->drakkin_heritage = emu->drakkin_heritage[r]; - eq2->unknown1 = 0; - eq2->gohome = emu->gohome[r]; - eq2->LastLogin = 1212696584; - eq2->unknown2 = 0; - } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); - } - - FINISH_ENCODE(); - -} - -ENCODE(OP_ZoneServerInfo) { - SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); - OUT_str(ip); - OUT(port); - FINISH_ENCODE(); -} - -ENCODE(OP_SendZonepoints) { - SETUP_VAR_ENCODE(ZonePoints); - ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); - - eq->count = emu->count; - for(uint32 i = 0; i < emu->count; ++i) - { - eq->zpe[i].iterator = emu->zpe[i].iterator; - eq->zpe[i].x = emu->zpe[i].x; - eq->zpe[i].y = emu->zpe[i].y; - eq->zpe[i].z = emu->zpe[i].z; - eq->zpe[i].heading = emu->zpe[i].heading; - eq->zpe[i].zoneid = emu->zpe[i].zoneid; - eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; - } - - FINISH_ENCODE(); -} - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for SoF - // clientver 1 is for all clients and 5 is for Live - if (emu->clientver <= 5 ) - { - OUT(id); - eq->unknown004 = 1; - //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->title_sid = emu->id - emu->current_level + 1; - //eq->desc_sid = emu->id - emu->current_level + 1; - eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?-1:(emu->sof_next_skill); - eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?-1:(emu->sof_next_skill); - eq->title_sid = emu->sof_next_skill; - eq->desc_sid = emu->sof_next_skill; - OUT(class_type); - OUT(cost); - OUT(seq); - OUT(current_level); - eq->unknown037 = 1; // Introduced during HoT - OUT(prereq_skill); - eq->unknown045 = 1; // New Mar 21 2012 - Seen 1 - OUT(prereq_minpoints); - eq->type = emu->sof_type; - OUT(spellid); - eq->unknown057 = 1; // Introduced during HoT - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - //eq->max_level = emu->sof_max_level; - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - eq->aa_expansion = emu->aa_expansion; - eq->special_category = emu->special_category; - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } - } - - _hex(NET__ERROR, eq, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - FINISH_ENCODE(); -} - -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - - -ENCODE(OP_PlayerProfile) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - PlayerProfile_Struct *emu = (PlayerProfile_Struct *) __emu_buffer; - - uint32 PacketSize = 40000; // Calculate this later - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PlayerProfile, PacketSize); - - outapp->WriteUInt32(0); // Checksum, we will update this later - outapp->WriteUInt32(0); // Checksum size, we will update this later - - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - - outapp->WriteUInt8(emu->gender); // Gender - outapp->WriteUInt32(emu->race); // Race - outapp->WriteUInt8(emu->class_); // Class - outapp->WriteUInt8(emu->level); // Level - outapp->WriteUInt8(emu->level); // Level1 - - - outapp->WriteUInt32(5); // Bind count - - for(int r = 0; r < 5; r++) - { - outapp->WriteUInt32(emu->binds[r].zoneId); - outapp->WriteFloat(emu->binds[r].x); - outapp->WriteFloat(emu->binds[r].y); - outapp->WriteFloat(emu->binds[r].z); - outapp->WriteFloat(emu->binds[r].heading); - } - - outapp->WriteUInt32(emu->deity); - outapp->WriteUInt32(emu->intoxication); - - outapp->WriteUInt32(10); // Unknown count - - for(int r = 0; r < 10; r++) - { - outapp->WriteUInt32(0); // Unknown - } - - outapp->WriteUInt32(22); // Equipment count - - for(int r = 0; r < 9; r++) - { - outapp->WriteUInt32(emu->item_material[r]); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - } - - // Write zeroes for the next 13 equipment slots - - for(int r = 0; r < 13; r++) - { - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(9); // Equipment2 count - - for(int r = 0; r < 9; r++) - { - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(9); // Tint Count - - for(int r = 0; r < 7; r++) - { - outapp->WriteUInt32(emu->item_tint[r].color); - } - // Write zeroes for extra two tint values - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - - outapp->WriteUInt32(9); // Tint2 Count - - for(int r = 0; r < 7; r++) - { - outapp->WriteUInt32(emu->item_tint[r].color); - } - // Write zeroes for extra two tint values - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - - - outapp->WriteUInt8(emu->haircolor); - outapp->WriteUInt8(emu->beardcolor); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt8(emu->eyecolor1); - outapp->WriteUInt8(emu->eyecolor2); - outapp->WriteUInt8(emu->hairstyle); - outapp->WriteUInt8(emu->beard); - outapp->WriteUInt8(emu->face); - - // Think there should be an extra byte before the drakkin stuff (referred to as oldface in client) - // Then one of the five bytes following the drakkin stuff needs removing. - - outapp->WriteUInt32(emu->drakkin_heritage); - outapp->WriteUInt32(emu->drakkin_tattoo); - outapp->WriteUInt32(emu->drakkin_details); - - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - - outapp->WriteFloat(5.0f); // Height ? - - outapp->WriteFloat(3.0f); // Unknown - outapp->WriteFloat(2.5f); // Unknown - outapp->WriteFloat(5.5f); // Unknown - - outapp->WriteUInt32(0); // Primary ? - outapp->WriteUInt32(0); // Secondary ? - - outapp->WriteUInt32(emu->points); // Unspent skill points - outapp->WriteUInt32(emu->mana); - outapp->WriteUInt32(emu->cur_hp); - - outapp->WriteUInt32(emu->STR); - outapp->WriteUInt32(emu->STA); - outapp->WriteUInt32(emu->CHA); - outapp->WriteUInt32(emu->DEX); - outapp->WriteUInt32(emu->INT); - outapp->WriteUInt32(emu->AGI); - outapp->WriteUInt32(emu->WIS); - - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - - outapp->WriteUInt32(300); // AA Count - - for(uint32 r = 0; r < MAX_PP_AA_ARRAY; r++) - { - outapp->WriteUInt32(emu->aa_array[r].AA); - outapp->WriteUInt32(emu->aa_array[r].value); - outapp->WriteUInt32(0); - } - - // Fill the other 60 AAs with zeroes - - for(uint32 r = 0; r < structs::MAX_PP_AA_ARRAY - MAX_PP_AA_ARRAY; r++) - { - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - } - - - outapp->WriteUInt32(structs::MAX_PP_SKILL); - - for(uint32 r = 0; r < structs::MAX_PP_SKILL; r++) - { - outapp->WriteUInt32(emu->skills[r]); - } - - // deprecated - // Write zeroes for the rest of the skills - /* - for(uint32 r = 0; r < structs::MAX_PP_SKILL - MAX_PP_SKILL; r++) - { - outapp->WriteUInt32(emu->skills[r]); } */ - outapp->WriteUInt32(25); // Unknown count - - for(uint32 r = 0; r < 25; r++) + ENCODE(OP_PetBuffWindow) { - outapp->WriteUInt32(0); // Unknown - } + // The format of the RoF packet is identical to the OP_BuffCreate packet. - outapp->WriteUInt32(structs::MAX_PP_DISCIPLINES); // Discipline count + SETUP_VAR_ENCODE(PetBuff_Struct); - for(uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) - { - outapp->WriteUInt32(emu->disciplines.values[r]); - } + uint32 sz = 12 + (17 * emu->buffcount); + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); - // Write zeroes for the rest of the disciplines - for(uint32 r = 0; r < structs::MAX_PP_DISCIPLINES - MAX_PP_DISCIPLINES; r++) - { - outapp->WriteUInt32(0); - } + __packet->WriteUInt32(emu->petid); + __packet->WriteUInt32(0); // PlayerID ? + __packet->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff) + __packet->WriteUInt16(emu->buffcount); - outapp->WriteUInt32(20); // Timestamp count - - for(uint32 r = 0; r < 20; r++) - { - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(MAX_RECAST_TYPES); // Timestamp count - - for(uint32 r = 0; r < MAX_RECAST_TYPES; r++) - { - outapp->WriteUInt32(emu->recastTimers[r]); - } - - outapp->WriteUInt32(100); // Timestamp2 count - - for(uint32 r = 0; r < 100; r++) - { - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(structs::MAX_PP_SPELLBOOK); // Spellbook slots - - for(uint32 r = 0; r < MAX_PP_SPELLBOOK; r++) - { - outapp->WriteUInt32(emu->spell_book[r]); - } - // zeroes for the rest of the spellbook slots - for(uint32 r = 0; r < structs::MAX_PP_SPELLBOOK - MAX_PP_SPELLBOOK; r++) - { - outapp->WriteUInt32(0xFFFFFFFFU); - } - - outapp->WriteUInt32(structs::MAX_PP_MEMSPELL); // Memorised spell slots - - for(uint32 r = 0; r < MAX_PP_MEMSPELL; r++) - { - outapp->WriteUInt32(emu->mem_spells[r]); - } - // zeroes for the rest of the slots - for(uint32 r = 0; r < structs::MAX_PP_MEMSPELL - MAX_PP_MEMSPELL; r++) - { - outapp->WriteUInt32(0xFFFFFFFFU); - } - - outapp->WriteUInt32(13); // Unknown count - - for(uint32 r = 0; r < 13; r++) - { - outapp->WriteUInt32(0); // Unknown - } - - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(structs::BUFF_COUNT); - - //*000*/ uint8 slotid; // badly named... seems to be 2 for a real buff, 0 otherwise - //*001*/ float unknown004; // Seen 1 for no buff - //*005*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages - //*009*/ uint32 unknown016; - //*013*/ uint8 bard_modifier; - //*014*/ uint32 duration; - //*018*/ uint8 level; - //*019*/ uint32 spellid; - //*023*/ uint32 counters; - //*027*/ uint8 unknown0028[53]; - //*080*/ - - for(uint32 r = 0; r < BUFF_COUNT; r++) - { - float instrument_mod = 0.0f; - uint8 slotid = emu->buffs[r].slotid; - uint32 player_id = emu->buffs[r].player_id;; - - if(emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0) + for (uint16 i = 0; i < BUFF_COUNT; ++i) { - instrument_mod = 1.0f + (emu->buffs[r].bard_modifier - 10) / 10.0f; - slotid = 2; - player_id = 0x000717fd; + if (emu->spellid[i]) + { + __packet->WriteUInt32(i); + __packet->WriteUInt32(emu->spellid[i]); + __packet->WriteUInt32(emu->ticsremaining[i]); + __packet->WriteUInt32(0); // Unknown + __packet->WriteString(""); + } } - else - { - slotid = 0; - } - outapp->WriteUInt8(0); // Had this as slot, but always appears to be 0 on live. - outapp->WriteFloat(instrument_mod); - outapp->WriteUInt32(player_id); - outapp->WriteUInt8(0); - outapp->WriteUInt32(emu->buffs[r].counters); - //outapp->WriteUInt8(emu->buffs[r].bard_modifier); - outapp->WriteUInt32(emu->buffs[r].duration); - outapp->WriteUInt8(emu->buffs[r].level); - outapp->WriteUInt32(emu->buffs[r].spellid); - outapp->WriteUInt32(slotid); // Only ever seen 2 - outapp->WriteUInt32(0); - outapp->WriteUInt8(0); - outapp->WriteUInt32(emu->buffs[r].counters); // Appears twice ? + __packet->WriteUInt8(0); // Unknown - for(uint32 j = 0; j < 44; ++j) - outapp->WriteUInt8(0); // Unknown + FINISH_ENCODE(); } - for(uint32 r = 0; r < structs::BUFF_COUNT - BUFF_COUNT; r++) + ENCODE(OP_PlayerProfile) { - // 80 bytes of zeroes - for(uint32 j = 0; j < 20; ++j) + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + PlayerProfile_Struct *emu = (PlayerProfile_Struct *)__emu_buffer; + + uint32 PacketSize = 40000; // Calculate this later + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PlayerProfile, PacketSize); + + outapp->WriteUInt32(0); // Checksum, we will update this later + outapp->WriteUInt32(0); // Checksum size, we will update this later + + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + + + outapp->WriteUInt8(emu->gender); // Gender + outapp->WriteUInt32(emu->race); // Race + outapp->WriteUInt8(emu->class_); // Class + outapp->WriteUInt8(emu->level); // Level + outapp->WriteUInt8(emu->level); // Level1 + + + outapp->WriteUInt32(5); // Bind count + + for (int r = 0; r < 5; r++) + { + outapp->WriteUInt32(emu->binds[r].zoneId); + outapp->WriteFloat(emu->binds[r].x); + outapp->WriteFloat(emu->binds[r].y); + outapp->WriteFloat(emu->binds[r].z); + outapp->WriteFloat(emu->binds[r].heading); + } + + outapp->WriteUInt32(emu->deity); + outapp->WriteUInt32(emu->intoxication); + + outapp->WriteUInt32(10); // Unknown count + + for (int r = 0; r < 10; r++) + { + outapp->WriteUInt32(0); // Unknown + } + + outapp->WriteUInt32(22); // Equipment count + + for (int r = 0; r < 9; r++) + { + outapp->WriteUInt32(emu->item_material[r]); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); outapp->WriteUInt32(0); - - } - - outapp->WriteUInt32(emu->platinum); - outapp->WriteUInt32(emu->gold); - outapp->WriteUInt32(emu->silver); - outapp->WriteUInt32(emu->copper); - - outapp->WriteUInt32(emu->platinum_cursor); - outapp->WriteUInt32(emu->gold_cursor); - outapp->WriteUInt32(emu->silver_cursor); - outapp->WriteUInt32(emu->copper_cursor); - - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt32(0); // This is the cooldown timer for the monk 'Mend' skill. Client will add 6 minutes to this value the first time the - // player logs in. After that it will honour whatever value we send here. - - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt32(emu->thirst_level); - outapp->WriteUInt32(emu->hunger_level); - - outapp->WriteUInt32(emu->aapoints_spent); - - outapp->WriteUInt32(5); // AA Points count ?? - outapp->WriteUInt32(1234); // AA Points assigned - outapp->WriteUInt32(0); // AA Points in General ? - outapp->WriteUInt32(0); // AA Points in Class ? - outapp->WriteUInt32(0); // AA Points in Archetype ? - outapp->WriteUInt32(0); // AA Points in Special ? - outapp->WriteUInt32(emu->aapoints); // AA Points unspent - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - - - outapp->WriteUInt32(structs::MAX_PLAYER_BANDOLIER); - - for(uint32 r = 0; r < EmuConstants::BANDOLIERS_COUNT; r++) - { - outapp->WriteString(emu->bandoliers[r].name); - - for(uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j) - { - outapp->WriteString(emu->bandoliers[r].items[j].item_name); - outapp->WriteUInt32(emu->bandoliers[r].items[j].item_id); - outapp->WriteUInt32(emu->bandoliers[r].items[j].icon); } - } - for(uint32 r = 0; r < structs::MAX_PLAYER_BANDOLIER - EmuConstants::BANDOLIERS_COUNT; r++) - { - outapp->WriteString(""); + // Write zeroes for the next 13 equipment slots - for(uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j) + for (int r = 0; r < 13; r++) + { + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(9); // Equipment2 count + + for (int r = 0; r < 9; r++) + { + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(9); // Tint Count + + for (int r = 0; r < 7; r++) + { + outapp->WriteUInt32(emu->item_tint[r].color); + } + // Write zeroes for extra two tint values + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + + outapp->WriteUInt32(9); // Tint2 Count + + for (int r = 0; r < 7; r++) + { + outapp->WriteUInt32(emu->item_tint[r].color); + } + // Write zeroes for extra two tint values + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + + outapp->WriteUInt8(emu->haircolor); + outapp->WriteUInt8(emu->beardcolor); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt8(emu->eyecolor1); + outapp->WriteUInt8(emu->eyecolor2); + outapp->WriteUInt8(emu->hairstyle); + outapp->WriteUInt8(emu->beard); + outapp->WriteUInt8(emu->face); + + // Think there should be an extra byte before the drakkin stuff (referred to as oldface in client) + // Then one of the five bytes following the drakkin stuff needs removing. + + outapp->WriteUInt32(emu->drakkin_heritage); + outapp->WriteUInt32(emu->drakkin_tattoo); + outapp->WriteUInt32(emu->drakkin_details); + + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + + outapp->WriteFloat(5.0f); // Height ? + + outapp->WriteFloat(3.0f); // Unknown + outapp->WriteFloat(2.5f); // Unknown + outapp->WriteFloat(5.5f); // Unknown + + outapp->WriteUInt32(0); // Primary ? + outapp->WriteUInt32(0); // Secondary ? + + outapp->WriteUInt32(emu->points); // Unspent skill points + outapp->WriteUInt32(emu->mana); + outapp->WriteUInt32(emu->cur_hp); + + outapp->WriteUInt32(emu->STR); + outapp->WriteUInt32(emu->STA); + outapp->WriteUInt32(emu->CHA); + outapp->WriteUInt32(emu->DEX); + outapp->WriteUInt32(emu->INT); + outapp->WriteUInt32(emu->AGI); + outapp->WriteUInt32(emu->WIS); + + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt32(300); // AA Count + + for (uint32 r = 0; r < MAX_PP_AA_ARRAY; r++) + { + outapp->WriteUInt32(emu->aa_array[r].AA); + outapp->WriteUInt32(emu->aa_array[r].value); + outapp->WriteUInt32(0); + } + + // Fill the other 60 AAs with zeroes + + for (uint32 r = 0; r < structs::MAX_PP_AA_ARRAY - MAX_PP_AA_ARRAY; r++) + { + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(structs::MAX_PP_SKILL); + + for (uint32 r = 0; r < structs::MAX_PP_SKILL; r++) + { + outapp->WriteUInt32(emu->skills[r]); + } + + // deprecated + // Write zeroes for the rest of the skills + /* + for(uint32 r = 0; r < structs::MAX_PP_SKILL - MAX_PP_SKILL; r++) + { + outapp->WriteUInt32(emu->skills[r]); + } + */ + + outapp->WriteUInt32(25); // Unknown count + + for (uint32 r = 0; r < 25; r++) + { + outapp->WriteUInt32(0); // Unknown + } + + outapp->WriteUInt32(structs::MAX_PP_DISCIPLINES); // Discipline count + + for (uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) + { + outapp->WriteUInt32(emu->disciplines.values[r]); + } + + // Write zeroes for the rest of the disciplines + for (uint32 r = 0; r < structs::MAX_PP_DISCIPLINES - MAX_PP_DISCIPLINES; r++) + { + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(20); // Timestamp count + + for (uint32 r = 0; r < 20; r++) + { + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(MAX_RECAST_TYPES); // Timestamp count + + for (uint32 r = 0; r < MAX_RECAST_TYPES; r++) + { + outapp->WriteUInt32(emu->recastTimers[r]); + } + + outapp->WriteUInt32(100); // Timestamp2 count + + for (uint32 r = 0; r < 100; r++) + { + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(structs::MAX_PP_SPELLBOOK); // Spellbook slots + + for (uint32 r = 0; r < MAX_PP_SPELLBOOK; r++) + { + outapp->WriteUInt32(emu->spell_book[r]); + } + // zeroes for the rest of the spellbook slots + for (uint32 r = 0; r < structs::MAX_PP_SPELLBOOK - MAX_PP_SPELLBOOK; r++) + { + outapp->WriteUInt32(0xFFFFFFFFU); + } + + outapp->WriteUInt32(structs::MAX_PP_MEMSPELL); // Memorised spell slots + + for (uint32 r = 0; r < MAX_PP_MEMSPELL; r++) + { + outapp->WriteUInt32(emu->mem_spells[r]); + } + // zeroes for the rest of the slots + for (uint32 r = 0; r < structs::MAX_PP_MEMSPELL - MAX_PP_MEMSPELL; r++) + { + outapp->WriteUInt32(0xFFFFFFFFU); + } + + outapp->WriteUInt32(13); // Unknown count + + for (uint32 r = 0; r < 13; r++) + { + outapp->WriteUInt32(0); // Unknown + } + + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(structs::BUFF_COUNT); + + //*000*/ uint8 slotid; // badly named... seems to be 2 for a real buff, 0 otherwise + //*001*/ float unknown004; // Seen 1 for no buff + //*005*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages + //*009*/ uint32 unknown016; + //*013*/ uint8 bard_modifier; + //*014*/ uint32 duration; + //*018*/ uint8 level; + //*019*/ uint32 spellid; + //*023*/ uint32 counters; + //*027*/ uint8 unknown0028[53]; + //*080*/ + + for (uint32 r = 0; r < BUFF_COUNT; r++) + { + float instrument_mod = 0.0f; + uint8 slotid = emu->buffs[r].slotid; + uint32 player_id = emu->buffs[r].player_id;; + + if (emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0) + { + instrument_mod = 1.0f + (emu->buffs[r].bard_modifier - 10) / 10.0f; + slotid = 2; + player_id = 0x000717fd; + } + else + { + slotid = 0; + } + outapp->WriteUInt8(0); // Had this as slot, but always appears to be 0 on live. + outapp->WriteFloat(instrument_mod); + outapp->WriteUInt32(player_id); + outapp->WriteUInt8(0); + outapp->WriteUInt32(emu->buffs[r].counters); + //outapp->WriteUInt8(emu->buffs[r].bard_modifier); + outapp->WriteUInt32(emu->buffs[r].duration); + outapp->WriteUInt8(emu->buffs[r].level); + outapp->WriteUInt32(emu->buffs[r].spellid); + outapp->WriteUInt32(slotid); // Only ever seen 2 + outapp->WriteUInt32(0); + outapp->WriteUInt8(0); + outapp->WriteUInt32(emu->buffs[r].counters); // Appears twice ? + + for (uint32 j = 0; j < 44; ++j) + outapp->WriteUInt8(0); // Unknown + } + + for (uint32 r = 0; r < structs::BUFF_COUNT - BUFF_COUNT; r++) + { + // 80 bytes of zeroes + for (uint32 j = 0; j < 20; ++j) + outapp->WriteUInt32(0); + + } + + outapp->WriteUInt32(emu->platinum); + outapp->WriteUInt32(emu->gold); + outapp->WriteUInt32(emu->silver); + outapp->WriteUInt32(emu->copper); + + outapp->WriteUInt32(emu->platinum_cursor); + outapp->WriteUInt32(emu->gold_cursor); + outapp->WriteUInt32(emu->silver_cursor); + outapp->WriteUInt32(emu->copper_cursor); + + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt32(0); // This is the cooldown timer for the monk 'Mend' skill. Client will add 6 minutes to this value the first time the + // player logs in. After that it will honour whatever value we send here. + + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt32(emu->thirst_level); + outapp->WriteUInt32(emu->hunger_level); + + outapp->WriteUInt32(emu->aapoints_spent); + + outapp->WriteUInt32(5); // AA Points count ?? + outapp->WriteUInt32(1234); // AA Points assigned + outapp->WriteUInt32(0); // AA Points in General ? + outapp->WriteUInt32(0); // AA Points in Class ? + outapp->WriteUInt32(0); // AA Points in Archetype ? + outapp->WriteUInt32(0); // AA Points in Special ? + outapp->WriteUInt32(emu->aapoints); // AA Points unspent + + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(structs::MAX_PLAYER_BANDOLIER); + + for (uint32 r = 0; r < EmuConstants::BANDOLIERS_COUNT; r++) + { + outapp->WriteString(emu->bandoliers[r].name); + + for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j) + { + outapp->WriteString(emu->bandoliers[r].items[j].item_name); + outapp->WriteUInt32(emu->bandoliers[r].items[j].item_id); + outapp->WriteUInt32(emu->bandoliers[r].items[j].icon); + } + } + + for (uint32 r = 0; r < structs::MAX_PLAYER_BANDOLIER - EmuConstants::BANDOLIERS_COUNT; r++) + { + outapp->WriteString(""); + + for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j) + { + outapp->WriteString(""); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + } + } + + outapp->WriteUInt32(structs::MAX_POTIONS_IN_BELT); + + for (uint32 r = 0; r < EmuConstants::POTION_BELT_SIZE; r++) + { + outapp->WriteString(emu->potionbelt.items[r].item_name); + outapp->WriteUInt32(emu->potionbelt.items[r].item_id); + outapp->WriteUInt32(emu->potionbelt.items[r].icon); + } + + for (uint32 r = 0; r < structs::MAX_POTIONS_IN_BELT - EmuConstants::POTION_BELT_SIZE; r++) { outapp->WriteString(""); outapp->WriteUInt32(0); outapp->WriteUInt32(0); } - } + outapp->WriteSInt32(-1); // Unknown; + outapp->WriteSInt32(123); // HP Total ? + outapp->WriteSInt32(234); // Endurance Total ? + outapp->WriteSInt32(345); // Mana Total ? - outapp->WriteUInt32(structs::MAX_POTIONS_IN_BELT); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - for(uint32 r = 0; r < EmuConstants::POTION_BELT_SIZE; r++) - { - outapp->WriteString(emu->potionbelt.items[r].item_name); - outapp->WriteUInt32(emu->potionbelt.items[r].item_id); - outapp->WriteUInt32(emu->potionbelt.items[r].icon); - } + outapp->WriteUInt32(20); // Unknown - Expansion count ? + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->endurance); + outapp->WriteUInt32(0); // Unknown - Observed 0x7cde - This is also seen in guild packets sent to this character. + outapp->WriteUInt32(0); // Unknown - Observed 0x64 - for(uint32 r = 0; r < structs::MAX_POTIONS_IN_BELT - EmuConstants::POTION_BELT_SIZE; r++) - { - outapp->WriteString(""); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - } + outapp->WriteUInt32(64); // Name Length - outapp->WriteSInt32(-1); // Unknown; - outapp->WriteSInt32(123); // HP Total ? - outapp->WriteSInt32(234); // Endurance Total ? - outapp->WriteSInt32(345); // Mana Total ? + uint32 CurrentPosition = outapp->GetWritePosition(); + outapp->WriteString(emu->name); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown + outapp->SetWritePosition(CurrentPosition + 64); - outapp->WriteUInt32(20); // Unknown - Expansion count ? + outapp->WriteUInt32(32); // Last Name Length - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(emu->endurance); - outapp->WriteUInt32(0); // Unknown - Observed 0x7cde - This is also seen in guild packets sent to this character. - outapp->WriteUInt32(0); // Unknown - Observed 0x64 + CurrentPosition = outapp->GetWritePosition(); - outapp->WriteUInt32(64); // Name Length + outapp->WriteString(emu->last_name); - uint32 CurrentPosition = outapp->GetWritePosition(); + outapp->SetWritePosition(CurrentPosition + 32); - outapp->WriteString(emu->name); + outapp->WriteUInt32(emu->birthday); + outapp->WriteUInt32(emu->birthday); // Account start date ? + outapp->WriteUInt32(emu->lastlogin); + outapp->WriteUInt32(emu->timePlayedMin); + outapp->WriteUInt32(emu->timeentitledonaccount); + outapp->WriteUInt32(0x0007ffff); // Expansion bitmask - outapp->SetWritePosition(CurrentPosition + 64); + outapp->WriteUInt32(structs::MAX_PP_LANGUAGE); - outapp->WriteUInt32(32); // Last Name Length + for (uint32 r = 0; r < MAX_PP_LANGUAGE; r++) + { + outapp->WriteUInt8(emu->languages[r]); + } - CurrentPosition = outapp->GetWritePosition(); + for (uint32 r = 0; r < structs::MAX_PP_LANGUAGE - MAX_PP_LANGUAGE; r++) + { + outapp->WriteUInt8(0); + } - outapp->WriteString(emu->last_name); + outapp->WriteUInt16(emu->zone_id); + outapp->WriteUInt16(emu->zoneInstance); - outapp->SetWritePosition(CurrentPosition + 32); + outapp->WriteFloat(emu->y); + outapp->WriteFloat(emu->x); + outapp->WriteFloat(emu->z); + outapp->WriteFloat(emu->heading); - outapp->WriteUInt32(emu->birthday); - outapp->WriteUInt32(emu->birthday); // Account start date ? - outapp->WriteUInt32(emu->lastlogin); - outapp->WriteUInt32(emu->timePlayedMin); - outapp->WriteUInt32(emu->timeentitledonaccount); - outapp->WriteUInt32(0x0007ffff); // Expansion bitmask + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(emu->pvp); + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(emu->gm); - outapp->WriteUInt32(structs::MAX_PP_LANGUAGE); + outapp->WriteUInt32(emu->guild_id); + outapp->WriteUInt8(0); // Unknown - observed 1 in a live packet. + outapp->WriteUInt32(0); // Unknown - observed 1 in a live packet. + outapp->WriteUInt8(0); // Unknown - observed 1 in a live packet. + outapp->WriteUInt32(0); // Unknown - for(uint32 r = 0; r < MAX_PP_LANGUAGE; r++) - { - outapp->WriteUInt8(emu->languages[r]); - } + outapp->WriteUInt64(emu->exp); + outapp->WriteUInt8(0); // Unknown - for(uint32 r = 0; r < structs::MAX_PP_LANGUAGE - MAX_PP_LANGUAGE; r++) - { - outapp->WriteUInt8(0); - } + outapp->WriteUInt32(emu->platinum_bank); + outapp->WriteUInt32(emu->gold_bank); + outapp->WriteUInt32(emu->silver_bank); + outapp->WriteUInt32(emu->copper_bank); - outapp->WriteUInt16(emu->zone_id); - outapp->WriteUInt16(emu->zoneInstance); - - outapp->WriteFloat(emu->y); - outapp->WriteFloat(emu->x); - outapp->WriteFloat(emu->z); - outapp->WriteFloat(emu->heading); - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(emu->pvp); - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(emu->gm); - - outapp->WriteUInt32(emu->guild_id); - outapp->WriteUInt8(0); // Unknown - observed 1 in a live packet. - outapp->WriteUInt32(0); // Unknown - observed 1 in a live packet. - outapp->WriteUInt8(0); // Unknown - observed 1 in a live packet. - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt64(emu->exp); - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(emu->platinum_bank); - outapp->WriteUInt32(emu->gold_bank); - outapp->WriteUInt32(emu->silver_bank); - outapp->WriteUInt32(emu->copper_bank); - - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt32(42); // The meaning of life ? - - for(uint32 r = 0; r < 42; r++) - { outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown - } + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(42); // The meaning of life ? - outapp->WriteUInt32(emu->career_tribute_points); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(emu->tribute_points); - outapp->WriteUInt32(0); // Unknown + for (uint32 r = 0; r < 42; r++) + { + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + } - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(EmuConstants::TRIBUTE_SIZE); + outapp->WriteUInt32(emu->career_tribute_points); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->tribute_points); + outapp->WriteUInt32(0); // Unknown - for(uint32 r = 0; r < EmuConstants::TRIBUTE_SIZE; r++) - { - outapp->WriteUInt32(emu->tributes[r].tribute); - outapp->WriteUInt32(emu->tributes[r].tier); - } - - outapp->WriteUInt32(10); // Guild Tribute Count ? - - for(uint32 r = 0; r < 10; r++) - { - outapp->WriteUInt32(0xffffffff); - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - // Block of 121 unknown bytes - for(uint32 r = 0; r < 121; r++) + outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(emu->currentRadCrystals); - outapp->WriteUInt32(emu->careerRadCrystals); - outapp->WriteUInt32(emu->currentEbonCrystals); - outapp->WriteUInt32(emu->careerEbonCrystals); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(EmuConstants::TRIBUTE_SIZE); - // Unknown String ? - outapp->WriteUInt32(64); // Unknown - for(uint32 r = 0; r < 64; r++) + for (uint32 r = 0; r < EmuConstants::TRIBUTE_SIZE; r++) + { + outapp->WriteUInt32(emu->tributes[r].tribute); + outapp->WriteUInt32(emu->tributes[r].tier); + } + + outapp->WriteUInt32(10); // Guild Tribute Count ? + + for (uint32 r = 0; r < 10; r++) + { + outapp->WriteUInt32(0xffffffff); + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + + // Block of 121 unknown bytes + for (uint32 r = 0; r < 121; r++) + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->currentRadCrystals); + outapp->WriteUInt32(emu->careerRadCrystals); + outapp->WriteUInt32(emu->currentEbonCrystals); + outapp->WriteUInt32(emu->careerEbonCrystals); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + + // Unknown String ? + outapp->WriteUInt32(64); // Unknown + for (uint32 r = 0; r < 64; r++) + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(0); // Unknown - - // Unknown String ? - outapp->WriteUInt32(64); // Unknown - for(uint32 r = 0; r < 64; r++) + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown - // Unknown String ? - outapp->WriteUInt32(64); // Unknown - for(uint32 r = 0; r < 64; r++) + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - // Block of 320 unknown bytes - for(uint32 r = 0; r < 320; r++) outapp->WriteUInt8(0); // Unknown - // Block of 343 unknown bytes - for(uint32 r = 0; r < 343; r++) + outapp->WriteUInt32(0); // Unknown + + // Unknown String ? + outapp->WriteUInt32(64); // Unknown + for (uint32 r = 0; r < 64; r++) + outapp->WriteUInt8(0); // Unknown + + // Unknown String ? + outapp->WriteUInt32(64); // Unknown + for (uint32 r = 0; r < 64; r++) + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(0); // Unknown + + // Block of 320 unknown bytes + for (uint32 r = 0; r < 320; r++) + outapp->WriteUInt8(0); // Unknown + + // Block of 343 unknown bytes + for (uint32 r = 0; r < 343; r++) + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt8(emu->leadAAActive); + + outapp->WriteUInt32(6); // Count ... of LDoN stats ? + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->ldon_points_guk); + outapp->WriteUInt32(emu->ldon_points_mir); + outapp->WriteUInt32(emu->ldon_points_mmc); + outapp->WriteUInt32(emu->ldon_points_ruj); + outapp->WriteUInt32(emu->ldon_points_tak); + + outapp->WriteUInt32(emu->ldon_points_available); + + outapp->WriteDouble(emu->group_leadership_exp); + outapp->WriteDouble(emu->raid_leadership_exp); + + outapp->WriteUInt32(emu->group_leadership_points); + outapp->WriteUInt32(emu->raid_leadership_points); + + outapp->WriteUInt32(64); // Group of 64 int32s follow Group/Raid Leadership abilities ? + + for (uint32 r = 0; r < MAX_LEADERSHIP_AA_ARRAY; r++) + outapp->WriteUInt32(emu->leader_abilities.ranks[r]); + + for (uint32 r = 0; r < 64 - MAX_LEADERSHIP_AA_ARRAY; r++) + outapp->WriteUInt32(0); // Unused/unsupported Leadership abilities + + outapp->WriteUInt32(emu->air_remaining); // ? + + // PVP Stats + + outapp->WriteUInt32(emu->PVPKills); + outapp->WriteUInt32(emu->PVPDeaths); + outapp->WriteUInt32(emu->PVPCurrentPoints); + outapp->WriteUInt32(emu->PVPCareerPoints); + outapp->WriteUInt32(emu->PVPBestKillStreak); + outapp->WriteUInt32(emu->PVPWorstDeathStreak); + outapp->WriteUInt32(emu->PVPCurrentKillStreak); + + // Last PVP Kill + + outapp->WriteString(emu->PVPLastKill.Name); + outapp->WriteUInt32(emu->PVPLastKill.Level); + outapp->WriteUInt32(emu->PVPLastKill.Race); + outapp->WriteUInt32(emu->PVPLastKill.Class); + outapp->WriteUInt32(emu->PVPLastKill.Zone); + outapp->WriteUInt32(emu->PVPLastKill.Time); + outapp->WriteUInt32(emu->PVPLastKill.Points); + + // Last PVP Death + + outapp->WriteString(emu->PVPLastDeath.Name); + outapp->WriteUInt32(emu->PVPLastDeath.Level); + outapp->WriteUInt32(emu->PVPLastDeath.Race); + outapp->WriteUInt32(emu->PVPLastDeath.Class); + outapp->WriteUInt32(emu->PVPLastDeath.Zone); + outapp->WriteUInt32(emu->PVPLastDeath.Time); + outapp->WriteUInt32(emu->PVPLastDeath.Points); + + outapp->WriteUInt32(emu->PVPNumberOfKillsInLast24Hours); + + // Last 50 Kills + outapp->WriteUInt32(50); + for (uint32 r = 0; r < 50; ++r) + { + outapp->WriteString(emu->PVPRecentKills[r].Name); + outapp->WriteUInt32(emu->PVPRecentKills[r].Level); + outapp->WriteUInt32(emu->PVPRecentKills[r].Race); + outapp->WriteUInt32(emu->PVPRecentKills[r].Class); + outapp->WriteUInt32(emu->PVPRecentKills[r].Zone); + outapp->WriteUInt32(emu->PVPRecentKills[r].Time); + outapp->WriteUInt32(emu->PVPRecentKills[r].Points); + } + + outapp->WriteUInt32(emu->expAA); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt8(emu->groupAutoconsent); + outapp->WriteUInt8(emu->raidAutoconsent); + outapp->WriteUInt8(emu->guildAutoconsent); + outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->level); // Level3 ? - outapp->WriteUInt8(emu->leadAAActive); + outapp->WriteUInt8(emu->showhelm); - outapp->WriteUInt32(6); // Count ... of LDoN stats ? - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(emu->ldon_points_guk); - outapp->WriteUInt32(emu->ldon_points_mir); - outapp->WriteUInt32(emu->ldon_points_mmc); - outapp->WriteUInt32(emu->ldon_points_ruj); - outapp->WriteUInt32(emu->ldon_points_tak); + outapp->WriteUInt32(emu->RestTimer); - outapp->WriteUInt32(emu->ldon_points_available); + outapp->WriteUInt32(1024); // Unknown Count - outapp->WriteDouble(emu->group_leadership_exp); - outapp->WriteDouble(emu->raid_leadership_exp); + // Block of 1024 unknown bytes + outapp->WriteUInt8(31); // Unknown - outapp->WriteUInt32(emu->group_leadership_points); - outapp->WriteUInt32(emu->raid_leadership_points); + for (uint32 r = 0; r < 1023; r++) + outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(64); // Group of 64 int32s follow Group/Raid Leadership abilities ? + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - for(uint32 r = 0; r < MAX_LEADERSHIP_AA_ARRAY; r++) - outapp->WriteUInt32(emu->leader_abilities.ranks[r]); + // Think we need 1 byte of padding at the end - for(uint32 r = 0; r < 64 - MAX_LEADERSHIP_AA_ARRAY; r++) - outapp->WriteUInt32(0); // Unused/unsupported Leadership abilities - - outapp->WriteUInt32(emu->air_remaining); // ? - - // PVP Stats - - outapp->WriteUInt32(emu->PVPKills); - outapp->WriteUInt32(emu->PVPDeaths); - outapp->WriteUInt32(emu->PVPCurrentPoints); - outapp->WriteUInt32(emu->PVPCareerPoints); - outapp->WriteUInt32(emu->PVPBestKillStreak); - outapp->WriteUInt32(emu->PVPWorstDeathStreak); - outapp->WriteUInt32(emu->PVPCurrentKillStreak); - - // Last PVP Kill - - outapp->WriteString(emu->PVPLastKill.Name); - outapp->WriteUInt32(emu->PVPLastKill.Level); - outapp->WriteUInt32(emu->PVPLastKill.Race); - outapp->WriteUInt32(emu->PVPLastKill.Class); - outapp->WriteUInt32(emu->PVPLastKill.Zone); - outapp->WriteUInt32(emu->PVPLastKill.Time); - outapp->WriteUInt32(emu->PVPLastKill.Points); - - // Last PVP Death - - outapp->WriteString(emu->PVPLastDeath.Name); - outapp->WriteUInt32(emu->PVPLastDeath.Level); - outapp->WriteUInt32(emu->PVPLastDeath.Race); - outapp->WriteUInt32(emu->PVPLastDeath.Class); - outapp->WriteUInt32(emu->PVPLastDeath.Zone); - outapp->WriteUInt32(emu->PVPLastDeath.Time); - outapp->WriteUInt32(emu->PVPLastDeath.Points); - - outapp->WriteUInt32(emu->PVPNumberOfKillsInLast24Hours); - - // Last 50 Kills - outapp->WriteUInt32(50); - for(uint32 r = 0; r < 50; ++r) - { - outapp->WriteString(emu->PVPRecentKills[r].Name); - outapp->WriteUInt32(emu->PVPRecentKills[r].Level); - outapp->WriteUInt32(emu->PVPRecentKills[r].Race); - outapp->WriteUInt32(emu->PVPRecentKills[r].Class); - outapp->WriteUInt32(emu->PVPRecentKills[r].Zone); - outapp->WriteUInt32(emu->PVPRecentKills[r].Time); - outapp->WriteUInt32(emu->PVPRecentKills[r].Points); - } - - - outapp->WriteUInt32(emu->expAA); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt8(emu->groupAutoconsent); - outapp->WriteUInt8(emu->raidAutoconsent); - outapp->WriteUInt8(emu->guildAutoconsent); - - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(emu->level); // Level3 ? - - outapp->WriteUInt8(emu->showhelm); - - outapp->WriteUInt32(emu->RestTimer); - - outapp->WriteUInt32(1024); // Unknown Count - - // Block of 1024 unknown bytes - outapp->WriteUInt8(31); // Unknown - - for(uint32 r = 0; r < 1023; r++) outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown + _log(NET__STRUCTS, "Player Profile Packet is %i bytes", outapp->GetWritePosition()); - // Think we need 1 byte of padding at the end + unsigned char *NewBuffer = new unsigned char[outapp->GetWritePosition()]; + memcpy(NewBuffer, outapp->pBuffer, outapp->GetWritePosition()); + safe_delete_array(outapp->pBuffer); + outapp->pBuffer = NewBuffer; + outapp->size = outapp->GetWritePosition(); + outapp->SetWritePosition(4); + outapp->WriteUInt32(outapp->size - 9); - outapp->WriteUInt8(0); // Unknown + CRC32::SetEQChecksum(outapp->pBuffer, outapp->size - 1, 8); + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - - _log(NET__STRUCTS, "Player Profile Packet is %i bytes", outapp->GetWritePosition()); - - unsigned char *NewBuffer = new unsigned char[outapp->GetWritePosition()]; - memcpy(NewBuffer, outapp->pBuffer, outapp->GetWritePosition()); - safe_delete_array(outapp->pBuffer); - outapp->pBuffer = NewBuffer; - outapp->size = outapp->GetWritePosition(); - outapp->SetWritePosition(4); - outapp->WriteUInt32(outapp->size - 9); - - - CRC32::SetEQChecksum(outapp->pBuffer, outapp->size - 1, 8); - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp, ack_req); - - delete in; - - return; - -} - -ENCODE(OP_NewZone) { - SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); - OUT_str(char_name); - OUT_str(zone_short_name); - OUT_str(zone_long_name); - OUT(ztype); - int r; - for(r = 0; r < 4; r++) { - OUT(fog_red[r]); - OUT(fog_green[r]); - OUT(fog_blue[r]); - OUT(fog_minclip[r]); - OUT(fog_maxclip[r]); - } - OUT(gravity); - OUT(time_type); - for(r = 0; r < 4; r++) { - OUT(rain_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(rain_duration[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_duration[r]); - } - for(r = 0; r < 32; r++) { - eq->unknown537[r] = 0xFF; //observed - } - OUT(sky); - OUT(zone_exp_multiplier); - OUT(safe_y); - OUT(safe_x); - OUT(safe_z); - OUT(max_z); - OUT(underworld); - OUT(minclip); - OUT(maxclip); - OUT_str(zone_short_name2); - OUT(zone_id); - OUT(zone_instance); - OUT(SuspendBuffs); - - eq->FogDensity = emu->fog_density; - - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown800 = -1; - eq->unknown844 = 600; - eq->unknown880 = 50; - eq->unknown884 = 10; - eq->unknown888 = 1; - eq->unknown889 = 0; - eq->unknown890 = 1; - eq->unknown891 = 0; - eq->unknown892 = 0; - eq->unknown893 = 0; - eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off - eq->unknown895 = 0; - eq->unknown896 = 180; - eq->unknown900 = 180; - eq->unknown904 = 180; - eq->unknown908 = 2; - eq->unknown912 = 2; - eq->unknown932 = -1; // Set from PoK Example - eq->unknown936 = -1; // Set from PoK Example - eq->unknown944 = 1.0; // Set from PoK Example - - FINISH_ENCODE(); -} - - -ENCODE(OP_Track) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) - { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + dest->FastQueuePacket(&outapp, ack_req); delete in; return; } - int PacketSize = 2; - - for(int i = 0; i < EntryCount; ++i, ++emu) - PacketSize += (12 + strlen(emu->name)); - - emu = (Track_Struct *) __emu_buffer; - - in->size = PacketSize; - in->pBuffer = new unsigned char[in->size]; - - char *Buffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); - - for(int i = 0; i < EntryCount; ++i, ++emu) + ENCODE(OP_RaidJoin) { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); - VARSTRUCT_ENCODE_STRING(Buffer, emu->name); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; + + general->action = 8; + general->parameter = 1; + strn0cpy(general->leader_name, raid_create->leader_name, 64); + strn0cpy(general->player_name, raid_create->leader_name, 64); + + dest->FastQueuePacket(&outapp_create); + delete[] __emu_buffer; } - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_PetBuffWindow) -{ - // The format of the RoF packet is identical to the OP_BuffCreate packet. - - SETUP_VAR_ENCODE(PetBuff_Struct); - - uint32 sz = 12 + (17 * emu->buffcount); - __packet->size = sz; - __packet->pBuffer = new unsigned char[sz]; - memset(__packet->pBuffer, 0, sz); - - __packet->WriteUInt32(emu->petid); - __packet->WriteUInt32(0); // PlayerID ? - __packet->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff) - __packet->WriteUInt16(emu->buffcount); - - for(uint16 i = 0; i < BUFF_COUNT; ++i) + ENCODE(OP_RaidUpdate) { - if(emu->spellid[i]) + EQApplicationPacket *inapp = *p; + *p = nullptr; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; + + if (raid_gen->action == 0) // raid add has longer length than other raid updates { - __packet->WriteUInt32(i); - __packet->WriteUInt32(emu->spellid[i]); - __packet->WriteUInt32(emu->ticsremaining[i]); - __packet->WriteUInt32(0); // Unknown - __packet->WriteString(""); + RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); + structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + + add_member->raidGen.action = in_add_member->raidGen.action; + add_member->raidGen.parameter = in_add_member->raidGen.parameter; + strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); + strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); + add_member->_class = in_add_member->_class; + add_member->level = in_add_member->level; + add_member->isGroupLeader = in_add_member->isGroupLeader; + add_member->flags[0] = in_add_member->flags[0]; + add_member->flags[1] = in_add_member->flags[1]; + add_member->flags[2] = in_add_member->flags[2]; + add_member->flags[3] = in_add_member->flags[3]; + add_member->flags[4] = in_add_member->flags[4]; + dest->FastQueuePacket(&outapp); + } + else + { + RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); + strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); + raid_general->action = in_raid_general->action; + raid_general->parameter = in_raid_general->parameter; + dest->FastQueuePacket(&outapp); + } + + delete[] __emu_buffer; + } + + ENCODE(OP_ReadBook) + { + ENCODE_LENGTH_ATLEAST(BookText_Struct); + SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); + + if (emu->window == 0xFF) + eq->window = 0xFFFFFFFF; + else + eq->window = emu->window; + OUT(type); + eq->invslot = 0; // Set to hard 0 since it's not required for the structure to work + strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); + + FINISH_ENCODE(); + } + + ENCODE(OP_RecipeAutoCombine) + { + ENCODE_LENGTH_EXACT(RecipeAutoCombine_Struct); + SETUP_DIRECT_ENCODE(RecipeAutoCombine_Struct, structs::RecipeAutoCombine_Struct); + + OUT(object_type); + OUT(some_id); + eq->container_slot = ServerToRoFSlot(emu->unknown1); + structs::ItemSlotStruct RoFSlot; + RoFSlot.SlotType = 8; // Observed + RoFSlot.Unknown02 = 0; + RoFSlot.MainSlot = 0xffff; + RoFSlot.SubSlot = 0xffff; + RoFSlot.AugSlot = 0xffff; + RoFSlot.Unknown01 = 0; + eq->unknown_slot = RoFSlot; + OUT(recipe_id); + OUT(reply_code); + + FINISH_ENCODE(); + } + + ENCODE(OP_RemoveBlockedBuffs) { ENCODE_FORWARD(OP_BlockedBuffs); } + + ENCODE(OP_RequestClientZoneChange) + { + ENCODE_LENGTH_EXACT(RequestClientZoneChange_Struct); + SETUP_DIRECT_ENCODE(RequestClientZoneChange_Struct, structs::RequestClientZoneChange_Struct); + + OUT(zone_id); + OUT(instance_id); + OUT(y); + OUT(x); + OUT(z); + OUT(heading); + eq->type = 0x0b; + eq->unknown004 = 0xffffffff; + eq->unknown172 = 0x0168b500; + + FINISH_ENCODE(); + } + + ENCODE(OP_RespondAA) + { + SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); + + eq->aa_spent = emu->aa_spent; + // These fields may need to be correctly populated at some point + eq->aapoints_assigned = emu->aa_spent + 1; + eq->aa_spent_general = 0; + eq->aa_spent_archetype = 0; + eq->aa_spent_class = 0; + eq->aa_spent_special = 0; + + for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) + { + eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; + eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; + eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_RezzRequest) + { + SETUP_DIRECT_ENCODE(Resurrect_Struct, structs::Resurrect_Struct); + + OUT(zone_id); + OUT(instance_id); + OUT(y); + OUT(x); + OUT(z); + OUT_str(your_name); + OUT_str(rezzer_name); + OUT(spellid); + OUT_str(corpse_name); + OUT(action); + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for SoF + // clientver 1 is for all clients and 5 is for Live + if (emu->clientver <= 5) + { + OUT(id); + eq->unknown004 = 1; + //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->title_sid = emu->id - emu->current_level + 1; + //eq->desc_sid = emu->id - emu->current_level + 1; + eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? -1 : (emu->sof_next_skill); + eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? -1 : (emu->sof_next_skill); + eq->title_sid = emu->sof_next_skill; + eq->desc_sid = emu->sof_next_skill; + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + eq->unknown037 = 1; // Introduced during HoT + OUT(prereq_skill); + eq->unknown045 = 1; // New Mar 21 2012 - Seen 1 + OUT(prereq_minpoints); + eq->type = emu->sof_type; + OUT(spellid); + eq->unknown057 = 1; // Introduced during HoT + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + //eq->max_level = emu->sof_max_level; + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + eq->aa_expansion = emu->aa_expansion; + eq->special_category = emu->special_category; + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + _hex(NET__ERROR, eq, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_VAR_ENCODE(CharacterSelect_Struct); + + //EQApplicationPacket *packet = *p; + //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + + int char_count; + int namelen = 0; + for (char_count = 0; char_count < 10; char_count++) { + if (emu->name[char_count][0] == '\0') + break; + if (strcmp(emu->name[char_count], "") == 0) + break; + namelen += strlen(emu->name[char_count]); + } + + int total_length = sizeof(structs::CharacterSelect_Struct) + + char_count * sizeof(structs::CharacterSelectEntry_Struct) + + namelen; + + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + + //unsigned char *eq_buffer = new unsigned char[total_length]; + //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + + eq->char_count = char_count; + //eq->total_chars = 10; + + unsigned char *bufptr = (unsigned char *)eq->entries; + int r; + for (r = 0; r < char_count; r++) { + { //pre-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); + } + //adjust for name. + bufptr += strlen(emu->name[r]); + { //post-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->class_ = emu->class_[r]; + eq2->race = emu->race[r]; + eq2->level = emu->level[r]; + eq2->class_2 = emu->class_[r]; + eq2->race2 = emu->race[r]; + eq2->zone = emu->zone[r]; + eq2->instance = 0; + eq2->gender = emu->gender[r]; + eq2->face = emu->face[r]; + int k; + for (k = 0; k < _MaterialCount; k++) { + eq2->equip[k].equip0 = emu->equip[r][k]; + eq2->equip[k].equip1 = 0; + eq2->equip[k].equip2 = 0; + eq2->equip[k].itemid = 0; + eq2->equip[k].equip3 = emu->equip[r][k]; + eq2->equip[k].color.color = emu->cs_colors[r][k].color; + } + eq2->u15 = 0xff; + eq2->u19 = 0xFF; + eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; + eq2->drakkin_details = emu->drakkin_details[r]; + eq2->deity = emu->deity[r]; + eq2->primary = emu->primary[r]; + eq2->secondary = emu->secondary[r]; + eq2->haircolor = emu->haircolor[r]; + eq2->beardcolor = emu->beardcolor[r]; + eq2->eyecolor1 = emu->eyecolor1[r]; + eq2->eyecolor2 = emu->eyecolor2[r]; + eq2->hairstyle = emu->hairstyle[r]; + eq2->beard = emu->beard[r]; + eq2->char_enabled = 1; + eq2->tutorial = emu->tutorial[r]; + eq2->drakkin_heritage = emu->drakkin_heritage[r]; + eq2->unknown1 = 0; + eq2->gohome = emu->gohome[r]; + eq2->LastLogin = 1212696584; + eq2->unknown2 = 0; + } + bufptr += sizeof(structs::CharacterSelectEntry_Struct); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendMembership) + { + ENCODE_LENGTH_EXACT(Membership_Struct); + SETUP_DIRECT_ENCODE(Membership_Struct, structs::Membership_Struct); + + eq->membership = emu->membership; + eq->races = emu->races; + eq->classes = emu->classes; + eq->entrysize = 22; + for (int i = 0; i<21; i++) + { + eq->entries[i] = emu->entries[i]; + } + eq->entries[21] = 0; + + FINISH_ENCODE(); + } + + ENCODE(OP_SendZonepoints) + { + SETUP_VAR_ENCODE(ZonePoints); + ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); + + eq->count = emu->count; + for (uint32 i = 0; i < emu->count; ++i) + { + eq->zpe[i].iterator = emu->zpe[i].iterator; + eq->zpe[i].x = emu->zpe[i].x; + eq->zpe[i].y = emu->zpe[i].y; + eq->zpe[i].z = emu->zpe[i].z; + eq->zpe[i].heading = emu->zpe[i].heading; + eq->zpe[i].zoneid = emu->zpe[i].zoneid; + eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SetGuildRank) + { + ENCODE_LENGTH_EXACT(GuildSetRank_Struct); + SETUP_DIRECT_ENCODE(GuildSetRank_Struct, structs::GuildSetRank_Struct); + + eq->GuildID = emu->Unknown00; + + /* Translate older ranks to new values */ + switch (emu->Rank) { + case 0: { eq->Rank = 5; break; } // GUILD_MEMBER 0 + case 1: { eq->Rank = 3; break; } // GUILD_OFFICER 1 + case 2: { eq->Rank = 1; break; } // GUILD_LEADER 2 + default: { eq->Rank = emu->Rank; break; } + } + + memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); + OUT(Banker); + eq->Unknown76 = 1; + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerBuy) + { + ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); + SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + OUT(npcid); + OUT(playerid); + OUT(itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToRoFMainInvSlot(emu->itemslot); + //OUT(itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopRequest) + { + ENCODE_LENGTH_EXACT(Merchant_Click_Struct); + SETUP_DIRECT_ENCODE(Merchant_Click_Struct, structs::Merchant_Click_Struct); + + OUT(npcid); + OUT(playerid); + OUT(command); + OUT(rate); + eq->unknown01 = 3; // Not sure what these values do yet, but list won't display without them + eq->unknown02 = 2592000; + + FINISH_ENCODE(); + } + + ENCODE(OP_SkillUpdate) + { + ENCODE_LENGTH_EXACT(SkillUpdate_Struct); + SETUP_DIRECT_ENCODE(SkillUpdate_Struct, structs::SkillUpdate_Struct); + + OUT(skillId); + OUT(value); + eq->unknown08 = 1; // Observed + eq->unknown09 = 80; // Observed + eq->unknown10 = 136; // Observed + eq->unknown11 = 54; // Observed + + FINISH_ENCODE(); + } + + ENCODE(OP_SomeItemPacketMaybe) + { + // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow + // and flying to the target. + // + + ENCODE_LENGTH_EXACT(Arrow_Struct); + SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); + + OUT(src_y); + OUT(src_x); + OUT(src_z); + OUT(velocity); + OUT(launch_angle); + OUT(tilt); + OUT(arc); + OUT(source_id); + OUT(target_id); + OUT(item_id); + + eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. + + OUT(item_type); + OUT(skill); + + strncpy(eq->model_name, emu->model_name, sizeof(eq->model_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_SpawnAppearance) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *emu_buffer = in->pBuffer; + + SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer; + + if (sas->type != AT_Size) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); + + ChangeSize_Struct *css = (ChangeSize_Struct *)outapp->pBuffer; + + css->EntityID = sas->spawn_id; + css->Size = (float)sas->parameter; + css->Unknown08 = 0; + css->Unknown12 = 1.0f; + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_SpawnDoor) + { + SETUP_VAR_ENCODE(Door_Struct); + int door_count = __packet->size / sizeof(Door_Struct); + int total_length = door_count * sizeof(structs::Door_Struct); + ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); + + int r; + for (r = 0; r < door_count; r++) { + strncpy(eq[r].name, emu[r].name, sizeof(eq[r].name)); + eq[r].xPos = emu[r].xPos; + eq[r].yPos = emu[r].yPos; + eq[r].zPos = emu[r].zPos; + eq[r].heading = emu[r].heading; + eq[r].incline = emu[r].incline; + eq[r].size = emu[r].size; + eq[r].doorId = emu[r].doorId; + eq[r].opentype = emu[r].opentype; + eq[r].state_at_spawn = emu[r].state_at_spawn; + eq[r].invert_state = emu[r].invert_state; + eq[r].door_param = emu[r].door_param; + eq[r].unknown0080 = 0; + eq[r].unknown0081 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0082 = 0; + eq[r].unknown0083 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0084 = 0; + eq[r].unknown0085 = 0; + eq[r].unknown0086 = 0; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_Stun) + { + ENCODE_LENGTH_EXACT(Stun_Struct); + SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); + + OUT(duration); + eq->unknown005 = 163; + eq->unknown006 = 67; + + FINISH_ENCODE(); + } + + ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); } + + ENCODE(OP_TaskDescription) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_TaskDescription, in->size + 1); + // Set the Write pointer as we don't know what has been done with the packet before we get it. + in->SetReadPosition(0); + // Copy the header + for (int i = 0; i < 5; ++i) + outapp->WriteUInt32(in->ReadUInt32()); + + // Copy Title + while (uint8 c = in->ReadUInt8()) + outapp->WriteUInt8(c); + outapp->WriteUInt8(0); + + outapp->WriteUInt32(in->ReadUInt32()); // Duration + outapp->WriteUInt32(in->ReadUInt32()); // Unknown + uint32 StartTime = in->ReadUInt32(); + outapp->WriteUInt32(time(nullptr) - StartTime); // RoF has elapsed time here rather than starttime + + // Copy the rest of the packet verbatim + uint32 BytesLeftToCopy = in->size - in->GetReadPosition(); + memcpy(outapp->pBuffer + outapp->GetWritePosition(), in->pBuffer + in->GetReadPosition(), BytesLeftToCopy); + + delete in; + dest->FastQueuePacket(&outapp, ack_req); + } + + ENCODE(OP_TaskHistoryReply) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + // First we need to calculate the length of the new packet + in->SetReadPosition(4); + uint32 ActivityCount = in->ReadUInt32(); + + uint32 Text1Length = 0; + uint32 Text2Length = 0; + uint32 Text3Length = 0; + + uint32 OutboundPacketSize = 8; + + for (uint32 i = 0; i < ActivityCount; ++i) + { + Text1Length = 0; + Text2Length = 0; + Text3Length = 0; + + in->ReadUInt32(); // Activity type + + // Skip past Text1 + while (in->ReadUInt8()) + ++Text1Length; + + // Skip past Text2 + while (in->ReadUInt8()) + ++Text2Length; + + in->ReadUInt32(); + in->ReadUInt32(); + in->ReadUInt32(); + uint32 ZoneID = in->ReadUInt32(); + in->ReadUInt32(); + + // Skip past Text3 + while (in->ReadUInt8()) + ++Text3Length; + + char ZoneNumber[10]; + + sprintf(ZoneNumber, "%i", ZoneID); + + OutboundPacketSize += (24 + Text1Length + 1 + Text2Length + Text3Length + 1 + 7 + (strlen(ZoneNumber) * 2)); + } + + in->SetReadPosition(0); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_TaskHistoryReply, OutboundPacketSize); + + outapp->WriteUInt32(in->ReadUInt32()); // Task index + outapp->WriteUInt32(in->ReadUInt32()); // Activity count + + for (uint32 i = 0; i < ActivityCount; ++i) + { + Text1Length = 0; + Text2Length = 0; + Text3Length = 0; + + outapp->WriteUInt32(in->ReadUInt32()); // ActivityType + + // Copy Text1 + while (uint8 c = in->ReadUInt8()) + outapp->WriteUInt8(c); + + outapp->WriteUInt8(0); // Text1 has a null terminator + + uint32 CurrentPosition = in->GetReadPosition(); + + // Determine Length of Text2 + while (in->ReadUInt8()) + ++Text2Length; + + outapp->WriteUInt32(Text2Length); + + in->SetReadPosition(CurrentPosition); + + // Copy Text2 + while (uint8 c = in->ReadUInt8()) + outapp->WriteUInt8(c); + + outapp->WriteUInt32(in->ReadUInt32()); // Goalcount + in->ReadUInt32(); + in->ReadUInt32(); + uint32 ZoneID = in->ReadUInt32(); + in->ReadUInt32(); + + char ZoneNumber[10]; + + sprintf(ZoneNumber, "%i", ZoneID); + + outapp->WriteUInt32(2); + outapp->WriteUInt8(0x2d); // "-" + outapp->WriteUInt8(0x31); // "1" + + outapp->WriteUInt32(2); + outapp->WriteUInt8(0x2d); // "-" + outapp->WriteUInt8(0x31); // "1" + outapp->WriteString(ZoneNumber); + + outapp->WriteUInt32(0); + + // Copy Tex3t + while (uint8 c = in->ReadUInt8()) + outapp->WriteUInt8(c); + + outapp->WriteUInt8(0); // Text3 has a null terminator + + outapp->WriteUInt8(0x31); // "1" + outapp->WriteString(ZoneNumber); + } + + delete in; + dest->FastQueuePacket(&outapp, ack_req); + } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + int PacketSize = 2; + + for (int i = 0; i < EntryCount; ++i, ++emu) + PacketSize += (12 + strlen(emu->name)); + + emu = (Track_Struct *)__emu_buffer; + + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + + char *Buffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); + + for (int i = 0; i < EntryCount; ++i, ++emu) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); + VARSTRUCT_ENCODE_STRING(Buffer, emu->name); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Trader) + { + if ((*p)->size == sizeof(ClickTrader_Struct)) + { + ENCODE_LENGTH_EXACT(ClickTrader_Struct); + SETUP_DIRECT_ENCODE(ClickTrader_Struct, structs::ClickTrader_Struct); + + eq->Code = emu->Code; + // Live actually has 200 items now, but 80 is the most our internal struct supports + for (uint32 i = 0; i < 200; i++) + { + strncpy(eq->items[i].SerialNumber, "0000000000000000", sizeof(eq->items[i].SerialNumber)); + eq->items[i].Unknown18 = 0; + if (i < 80) { + eq->ItemCost[i] = emu->ItemCost[i]; + } + else { + eq->ItemCost[i] = 0; + } + } + + FINISH_ENCODE(); + } + else if ((*p)->size == sizeof(Trader_ShowItems_Struct)) + { + ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct); + SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct); + + eq->Code = emu->Code; + strncpy(eq->SerialNumber, "0000000000000000", sizeof(eq->SerialNumber)); + eq->TraderID = emu->TraderID; + eq->Stacksize = 0; + eq->Price = 0; + + FINISH_ENCODE(); + } + else if ((*p)->size == sizeof(TraderStatus_Struct)) + { + ENCODE_LENGTH_EXACT(TraderStatus_Struct); + SETUP_DIRECT_ENCODE(TraderStatus_Struct, structs::TraderStatus_Struct); + + eq->Code = emu->Code; + + FINISH_ENCODE(); + } + else if ((*p)->size == sizeof(TraderBuy_Struct)) + { + ENCODE_FORWARD(OP_TraderBuy); } } - __packet->WriteUInt8(0); // Unknown - FINISH_ENCODE(); -} - -ENCODE(OP_Barter) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - - if(SubAction != Barter_BuyerAppearance) + ENCODE(OP_TraderBuy) { - dest->FastQueuePacket(&in, ack_req); + ENCODE_LENGTH_EXACT(TraderBuy_Struct); + SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); - return; - } - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = 80; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - char Name[64]; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); - uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); - uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - VARSTRUCT_DECODE_STRING(Name, Buffer); - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - OutBuffer = (char *)in->pBuffer + 72; - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - -} - -ENCODE(OP_BazaarSearch) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if(SubAction != BazaarSearchResults) - { - dest->FastQueuePacket(&in, ack_req); - - return; - } - - unsigned char *__emu_buffer = in->pBuffer; - - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); - - if(EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) - { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; - } - in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); - - in->pBuffer = new unsigned char[in->size]; - - memset(in->pBuffer, 0, in->size); - - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++emu, ++eq) - { - OUT(Beginning.Action); - OUT(SellerID); - memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); - OUT(NumItems); - OUT(ItemID); - OUT(SerialNumber); + OUT(Action); + OUT(Price); + OUT(TraderID); memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(Cost); - OUT(ItemStat); + OUT(ItemID); + OUT(Quantity); + OUT(AlreadySold); + + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_TributeInfo) + { + ENCODE_LENGTH_ATLEAST(TributeAbility_Struct); + SETUP_VAR_ENCODE(TributeAbility_Struct); + ALLOC_VAR_ENCODE(structs::TributeAbility_Struct, sizeof(structs::TributeAbility_Struct) + strlen(emu->name) + 1); - dest->FastQueuePacket(&in, ack_req); -} + OUT(tribute_id); + OUT(tier_count); -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) -{ + for (uint32 i = 0; i < MAX_TRIBUTE_TIERS; ++i) + { + eq->tiers[i].level = emu->tiers[i].level; + eq->tiers[i].tribute_item_id = emu->tiers[i].tribute_item_id; + eq->tiers[i].cost = emu->tiers[i].cost; + } + + eq->unknown128 = 0; + + strcpy(eq->name, emu->name); + + FINISH_ENCODE(); + } + + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToRoFSlot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_VetRewardsAvaliable) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + + uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); + *p = nullptr; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); + uchar *old_data = __emu_buffer; + uchar *data = outapp_create->pBuffer; + for (unsigned int i = 0; i < count; ++i) + { + structs::VeteranReward *vr = (structs::VeteranReward*)data; + InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + + vr->claim_count = ivr->claim_count; + vr->claim_id = ivr->claim_id; + vr->number_available = ivr->number_available; + for (int x = 0; x < 8; ++x) + { + vr->items[x].item_id = ivr->items[x].item_id; + strncpy(vr->items[x].item_name, ivr->items[x].item_name, sizeof(vr->items[x].item_name)); + vr->items[x].charges = ivr->items[x].charges; + } + + old_data += sizeof(InternalVeteranReward); + data += sizeof(structs::VeteranReward); + } + + dest->FastQueuePacket(&outapp_create); + delete inapp; + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(unknown06); + OUT(elite_material); + OUT(hero_forge_model); + OUT(unknown18); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_WhoAllResponse) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *InBuffer = (char *)in->pBuffer; + + WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; + + int Count = wars->playercount; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); + + char *OutBuffer = (char *)outapp->pBuffer; + + // The struct fields were moved around a bit, so adjust values before copying + wars->unknown44[0] = Count; + wars->unknown52 = 0; + + memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); + + OutBuffer += sizeof(WhoAllReturnStruct); + InBuffer += sizeof(WhoAllReturnStruct); + + for (int i = 0; i < Count; ++i) + { + uint32 x; + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + InBuffer += 4; + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); + + char Name[64]; + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + for (int j = 0; j < 7; ++j) + { + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + delete in; + } + + ENCODE(OP_ZoneChange) + { + ENCODE_LENGTH_EXACT(ZoneChange_Struct); + SETUP_DIRECT_ENCODE(ZoneChange_Struct, structs::ZoneChange_Struct); + + memcpy(eq->char_name, emu->char_name, sizeof(emu->char_name)); + OUT(zoneID); + OUT(instanceID); + OUT(y); + OUT(x); + OUT(z) + OUT(zone_reason); + OUT(success); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZonePlayerToBind) + { + ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); + + ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; + structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; + unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; + structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; + + zph->x = zps->x; + zph->y = zps->y; + zph->z = zps->z; + zph->heading = zps->heading; + zph->bind_zone_id = 0; + zph->bind_instance_id = zps->bind_instance_id; + strncpy(zph->zone_name, zps->zone_name, sizeof(zph->zone_name)); + + zpf->unknown021 = 1; + zpf->unknown022 = 0; + zpf->unknown023 = 0; + zpf->unknown024 = 0; + + ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); + ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); + + delete[] buffer1; + delete[] buffer2; + delete[](*p)->pBuffer; + + (*p)->pBuffer = new unsigned char[ss.str().size()]; + (*p)->size = ss.str().size(); + + memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); + dest->FastQueuePacket(&(*p)); + } + + ENCODE(OP_ZoneServerInfo) + { + SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); + + OUT_str(ip); + OUT(port); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneSpawns) + { //consume the packet EQApplicationPacket *in = *p; *p = nullptr; //store away the emu struct unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } - //_log(NET__STRUCTS, "Spawn name is [%s]", emu->name); - emu = (Spawn_Struct *) __emu_buffer; + emu = (Spawn_Struct *)__emu_buffer; //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); - char *Buffer = (char *) in->pBuffer, *BufferStart; - + char *Buffer = (char *)in->pBuffer, *BufferStart; int r; int k; - for(r = 0; r < entrycount; r++, emu++) { + for (r = 0; r < entrycount; r++, emu++) { int PacketSize = 206; @@ -1856,14 +3599,14 @@ ENCODE(OP_ZoneSpawns) emu->title[0] = 0; emu->suffix[0] = 0; - if(strlen(emu->title)) + if (strlen(emu->title)) PacketSize += strlen(emu->title) + 1; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) PacketSize += strlen(emu->suffix) + 1; bool ShowName = 1; - if(emu->bodytype >= 66) + if (emu->bodytype >= 66) { emu->race = 127; emu->bodytype = 11; @@ -1872,11 +3615,11 @@ ENCODE(OP_ZoneSpawns) } float SpawnSize = emu->size; - if(!((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) + if (!((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) { PacketSize += 60; - if(emu->size == 0) + if (emu->size == 0) { emu->size = 6; SpawnSize = 6; @@ -1885,13 +3628,13 @@ ENCODE(OP_ZoneSpawns) else PacketSize += 216; - if(SpawnSize == 0) + if (SpawnSize == 0) { SpawnSize = 3; } EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); - Buffer = (char *) outapp->pBuffer; + Buffer = (char *)outapp->pBuffer; BufferStart = Buffer; VARSTRUCT_ENCODE_STRING(Buffer, emu->name); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId); @@ -1924,10 +3667,10 @@ ENCODE(OP_ZoneSpawns) uint8 OtherData = 0; - if(strlen(emu->title)) + if (strlen(emu->title)) OtherData = OtherData | 16; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) OtherData = OtherData | 32; VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); @@ -1962,10 +3705,9 @@ ENCODE(OP_ZoneSpawns) VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->runspeed); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->race); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // ShowEQ calls this 'Holding' VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->deity); - if(emu->NPC) + if (emu->NPC) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); @@ -1976,10 +3718,10 @@ ENCODE(OP_ZoneSpawns) /* Translate older ranks to new values */ switch (emu->guildrank) { - case 0: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 5); break; } // GUILD_MEMBER 0 - case 1: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 3); break; } // GUILD_OFFICER 1 - case 2: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); break; } // GUILD_LEADER 2 - default: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); break; } // + case 0: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 5); break; } // GUILD_MEMBER 0 + case 1: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 3); break; } // GUILD_OFFICER 1 + case 2: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); break; } // GUILD_LEADER 2 + default: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); break; } // } } @@ -2005,9 +3747,9 @@ ENCODE(OP_ZoneSpawns) VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown18 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown19 - if((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) + if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { - for(k = 0; k < 9; ++k) + for (k = 0; k < 9; ++k) { { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); @@ -2016,7 +3758,7 @@ ENCODE(OP_ZoneSpawns) structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; - for(k = 0; k < 9; k++) { + for (k = 0; k < 9; k++) { Equipment[k].equip0 = emu->equipment[k]; Equipment[k].equip1 = 0; Equipment[k].equip2 = 0; @@ -2047,7 +3789,6 @@ ENCODE(OP_ZoneSpawns) VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); } - structs::Spawn_Struct_Position *Position = (structs::Spawn_Struct_Position*)Buffer; Position->deltaX = emu->deltaX; @@ -2062,12 +3803,12 @@ ENCODE(OP_ZoneSpawns) Buffer += sizeof(structs::Spawn_Struct_Position); - if(strlen(emu->title)) + if (strlen(emu->title)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->title); } - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); } @@ -2079,1989 +3820,620 @@ ENCODE(OP_ZoneSpawns) VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // 29 zero bytes follow Buffer += 29; - if(Buffer != (BufferStart + PacketSize)) + if (Buffer != (BufferStart + PacketSize)) { _log(NET__ERROR, "SPAWN ENCODE LOGIC PROBLEM: Buffer pointer is now %i from end", Buffer - (BufferStart + PacketSize)); } //_log(NET__ERROR, "Sending zone spawn for %s packet is %i bytes", emu->name, outapp->size); //_hex(NET__ERROR, outapp->pBuffer, outapp->size); dest->FastQueuePacket(&outapp, ack_req); - } - - delete in; -} - -ENCODE(OP_MercenaryDataResponse) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; - PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); - - for(r = 0; r < emu->MercTypeCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); - } - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Status); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk01); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MerchantSlot); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk02); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk03); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk04); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // MercName - for(k = 0; k < emu->Mercs[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); } - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_MercenaryDataUpdate) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - EQApplicationPacket *outapp; - - uint32 PacketSize = 0; - - // There are 2 different sized versions of this packet depending if a merc is hired or not - if (emu->MercStatus >= 0) - { - PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; - PacketSize += strlen(emu->MercData[r].MercName); // Null Terminator size already accounted for in the struct - } - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Status); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk01); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MerchantSlot); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk02); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].MercUnk03); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk04); - //VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // MercName - VARSTRUCT_ENCODE_STRING(Buffer,emu->MercData[r].MercName); - for(k = 0; k < emu->MercData[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); - } - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); // MercUnk05 - } - } - else - { - PacketSize += sizeof(structs::NoMercenaryHired_Struct); - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+4; - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length); - - delete[] __emu_buffer; - safe_delete_array(serialized); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - - *p = nullptr; - - if(in->size == 0) { - - in->size = 4; - - in->pBuffer = new uchar[in->size]; - - *((uint32 *) in->pBuffer) = 0; - - dest->FastQueuePacket(&in, ack_req); - - return; - } - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); - - if(ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); delete in; - - return; } - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - in->pBuffer = new uchar[4]; - - *(uint32 *)in->pBuffer = ItemCount; - - in->size = 4; - - for(int r = 0; r < ItemCount; r++, eq++) { - - uint32 Length = 0; - - char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); - - if(Serialized) { - - uchar *OldBuffer = in->pBuffer; - - in->pBuffer = new uchar[in->size + Length]; - - memcpy(in->pBuffer, OldBuffer, in->size); - - safe_delete_array(OldBuffer); - - memcpy(in->pBuffer + in->size, Serialized, Length); - - in->size += Length; - - safe_delete_array(Serialized); - - } - else { - _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); - } - } - - delete[] __emu_buffer; - - //_log(NET__ERROR, "Sending inventory to client"); - - //_hex(NET__ERROR, in->pBuffer, in->size); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; - - - - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. - - uint8 *buffer; - buffer = in->pBuffer; - - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; - - // Guild ID - buffer += sizeof(uint32); - - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); - - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header - emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data - ); - const char *emu_note = (emu_name + - emu->name_length + //skip name contents - emu->count //skip string terminators - ); - - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { - - //the order we set things here must match the struct - -//nice helper macro -#define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) - - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - - /* Translate older ranks to new values */ - switch (emu_e->rank) { - case 0: { e->rank = htonl(5); break; } // GUILD_MEMBER 0 - case 1: { e->rank = htonl(3); break; } // GUILD_OFFICER 1 - case 2: { e->rank = htonl(1); break; } // GUILD_LEADER 2 - default: { e->rank = htonl(emu_e->rank); break; } // GUILD_NONE - } - - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - e->unknown01 = 0; - PutFieldN(total_tribute); - PutFieldN(last_tribute); - e->unknown_one = htonl(1); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); - e->unknown_one2 = htonl(1); - e->unknown04 = 0; - - -#undef SlideStructString -#undef PutFieldN - - e++; - } - } - - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SpawnDoor) { - SETUP_VAR_ENCODE(Door_Struct); - int door_count = __packet->size/sizeof(Door_Struct); - int total_length = door_count * sizeof(structs::Door_Struct); - ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); - int r; - for(r = 0; r < door_count; r++) { - strncpy(eq[r].name, emu[r].name, sizeof(eq[r].name)); - eq[r].xPos = emu[r].xPos; - eq[r].yPos = emu[r].yPos; - eq[r].zPos = emu[r].zPos; - eq[r].heading = emu[r].heading; - eq[r].incline = emu[r].incline; - eq[r].size = emu[r].size; - eq[r].doorId = emu[r].doorId; - eq[r].opentype = emu[r].opentype; - eq[r].state_at_spawn = emu[r].state_at_spawn; - eq[r].invert_state = emu[r].invert_state; - eq[r].door_param = emu[r].door_param; - eq[r].unknown0080 = 0; - eq[r].unknown0081 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0082 = 0; - eq[r].unknown0083 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0084 = 0; - eq[r].unknown0085 = 0; - eq[r].unknown0086 = 0; - } - FINISH_ENCODE(); -} - -ENCODE(OP_GroundSpawn) -{ - - // We are not encoding the spawn_id field here, but it doesn't appear to matter. - // - EQApplicationPacket *in = *p; - *p = nullptr; - - Object_Struct *emu = (Object_Struct *) in->pBuffer; - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = strlen(emu->object_name) + sizeof(Object_Struct) - 1; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->object_name); - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_id); - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_instance); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); // Some unique id - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Same for all objects in the zone - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->heading); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Normally 0, but seen (float)255.0 as well - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 1); // Need to add emu->size to struct - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); - VARSTRUCT_ENCODE_TYPE(int32, OutBuffer, emu->object_type); // Unknown, observed 0x00000014 - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ClickObjectAction) { - ENCODE_LENGTH_EXACT(ClickObjectAction_Struct); - SETUP_DIRECT_ENCODE(ClickObjectAction_Struct, structs::ClickObjectAction_Struct); - OUT(drop_id); - eq->unknown04 = -1; - eq->unknown08 = -1; - OUT(type); - OUT(icon); - eq->unknown16 = 0; - OUT_str(object_name); - FINISH_ENCODE(); -} - -ENCODE(OP_SendMembership) { - ENCODE_LENGTH_EXACT(Membership_Struct); - SETUP_DIRECT_ENCODE(Membership_Struct, structs::Membership_Struct); - - eq->membership = emu->membership; - eq->races = emu->races; - eq->classes = emu->classes; - eq->entrysize = 22; - for (int i=0; i<21; i++) +// DECODE methods + DECODE(OP_AdventureMerchantSell) { - eq->entries[i] = emu->entries[i]; - } - eq->entries[21] = 0; + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - FINISH_ENCODE(); -} + IN(npcid); + emu->slot = RoFToServerMainInvSlot(eq->slot); + IN(charges); + IN(sell_price); -ENCODE(OP_GMTrainSkillConfirm) { - ENCODE_LENGTH_EXACT(GMTrainSkillConfirm_Struct); - SETUP_DIRECT_ENCODE(GMTrainSkillConfirm_Struct, structs::GMTrainSkillConfirm_Struct); - OUT(SkillID); - OUT(Cost); - OUT(NewSkill); - OUT_str(TrainerName); - FINISH_ENCODE(); -} - -ENCODE(OP_GMLastName) { - ENCODE_LENGTH_EXACT(GMLastName_Struct); - SETUP_DIRECT_ENCODE(GMLastName_Struct, structs::GMLastName_Struct); - OUT_str(name); - OUT_str(gmname); - OUT_str(lastname); - for (int i=0; i<4; i++) - { - eq->unknown[i] = emu->unknown[i]; - } - FINISH_ENCODE(); -} - -ENCODE(OP_SkillUpdate) { - ENCODE_LENGTH_EXACT(SkillUpdate_Struct); - SETUP_DIRECT_ENCODE(SkillUpdate_Struct, structs::SkillUpdate_Struct); - OUT(skillId); - OUT(value); - eq->unknown08 = 1; // Observed - eq->unknown09 = 80; // Observed - eq->unknown10 = 136; // Observed - eq->unknown11 = 54; // Observed - FINISH_ENCODE(); -} - -ENCODE(OP_ManaChange) { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? - FINISH_ENCODE(); -} - -ENCODE(OP_RequestClientZoneChange) { - ENCODE_LENGTH_EXACT(RequestClientZoneChange_Struct); - SETUP_DIRECT_ENCODE(RequestClientZoneChange_Struct, structs::RequestClientZoneChange_Struct); - OUT(zone_id); - OUT(instance_id); - OUT(y); - OUT(x); - OUT(z); - OUT(heading); - eq->type = 0x0b; - eq->unknown004 = 0xffffffff; - eq->unknown172 = 0x0168b500; - FINISH_ENCODE(); -} - -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - - // This packet is variable sized now, but forcing it to the old packet size for now. - eq->Title_Count = 128; - memcpy(eq->Title, emu->Title, sizeof(eq->Title)); - eq->Text_Count = 4096; - memcpy(eq->Text, emu->Text, sizeof(eq->Text)); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); - OUT(NegativeID); - // These two field names are used if Buttons == 1. We should add an interface to them via Perl. - eq->ButtonName0_Count = 25; - OUT_str(ButtonName0); - eq->ButtonName1_Count = 25; - OUT_str(ButtonName1); - - FINISH_ENCODE(); -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - OUT(race); - OUT(unknown006[0]); - OUT(unknown006[1]); - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - OUT(drakkin_heritage); - OUT(drakkin_tattoo); - OUT(drakkin_details); - eq->unknown316 = -1; // Observed - - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerBuy) -{ - ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); - SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - OUT(npcid); - OUT(playerid); - OUT(itemslot); - OUT(quantity); - OUT(price); - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteSpawn) -{ - ENCODE_LENGTH_EXACT(DeleteSpawn_Struct); - SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - OUT(spawn_id); - eq->unknown04 = 1; // Observed - - FINISH_ENCODE(); -} - - -ENCODE(OP_ClientUpdate) { - ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); - SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); - OUT(spawn_id); - OUT(x_pos); - OUT(delta_x); - OUT(delta_y); - OUT(z_pos); - OUT(delta_heading); - OUT(y_pos); - OUT(delta_z); - OUT(animation); - OUT(heading); - FINISH_ENCODE(); -} - -ENCODE(OP_ExpansionInfo) { - ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); - OUT(Expansions); - FINISH_ENCODE(); -} - -ENCODE(OP_LogServer) { - ENCODE_LENGTH_EXACT(LogServer_Struct); - SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); - strncpy(eq->worldshortname, emu->worldshortname, sizeof(eq->worldshortname)); - - //OUT(enablevoicemacros); // These two are lost, but must be one of the 1s in unknown[249] - //OUT(enablemail); - OUT(enable_pvp); - OUT(enable_FV); - - eq->unknown016 = 1; - eq->unknown020[0] = 1; - - eq->unknown249[0] = 1; - eq->unknown249[1] = 1; - eq->unknown249[8] = 1; - eq->unknown249[9] = 1; - eq->unknown249[12] = 1; - eq->unknown249[14] = 1; - eq->unknown249[15] = 1; - eq->unknown249[16] = 1; - - eq->unknown276[0] = 1.0f; - eq->unknown276[1] = 1.0f; - eq->unknown276[6] = 1.0f; - - FINISH_ENCODE(); -} - -ENCODE(OP_Animation) { - ENCODE_LENGTH_EXACT(Animation_Struct); - SETUP_DIRECT_ENCODE(Animation_Struct, structs::Animation_Struct); - OUT(spawnid); - OUT(value); - OUT(action); - FINISH_ENCODE(); -} - -ENCODE(OP_Damage) { - ENCODE_LENGTH_EXACT(CombatDamage_Struct); - SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); - OUT(target); - OUT(source); - OUT(type); - OUT(spellid); - OUT(damage); - eq->sequence = emu->sequence; - FINISH_ENCODE(); -} - -ENCODE(OP_Consider) { - ENCODE_LENGTH_EXACT(Consider_Struct); - SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); - OUT(playerid); - OUT(targetid); - OUT(faction); - OUT(level); - OUT(pvpcon); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::ActionAlt_Struct); - OUT(target); - OUT(source); - OUT(level); - eq->unknown06 = 0; - eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; - eq->bard_focus_id = emu->bard_focus_id; - eq->knockback_angle = emu->sequence; - eq->unknown22 = 0; - OUT(type); - eq->damage = 0; - eq->unknown31 = 0; - OUT(spell); - eq->level2 = eq->level; - eq->effect_flag = emu->buff_unknown; - eq->unknown39 = 14; - eq->unknown43 = 0; - eq->unknown44 = 17; - eq->unknown45 = 0; - eq->unknown46 = -1; - eq->unknown50 = 0; - eq->unknown54 = 0; - FINISH_ENCODE(); -} - -ENCODE(OP_Buff) { - ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); - SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Live); - OUT(entityid); - eq->unknown004 = 2; - //eq->level = 80; - //eq->effect = 0; - OUT(level); - OUT(effect); - eq->unknown007 = 0; - eq->unknown008 = 1.0f; - OUT(spellid); - OUT(duration); - eq->playerId = 0x7cde; - OUT(slotid); - if(emu->bufffade == 1) - eq->bufffade = 1; - else - eq->bufffade = 2; - - // Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon - EQApplicationPacket *outapp = nullptr; - if(eq->bufffade == 1) - { - outapp = new EQApplicationPacket(OP_BuffCreate, 29); - outapp->WriteUInt32(emu->entityid); - outapp->WriteUInt32(0x0271); // Unk - outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ? - outapp->WriteUInt16(1); // 1 buff in this packet - outapp->WriteUInt32(emu->slotid); - outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove) - outapp->WriteUInt32(0); // Duration - outapp->WriteUInt32(0); // ? - outapp->WriteUInt8(0); // Caster name - outapp->WriteUInt8(0); // Terminating byte - } - FINISH_ENCODE(); - - if(outapp) - dest->FastQueuePacket(&outapp); // Send the OP_BuffCreate to remove the buff -} - -ENCODE(OP_CancelTrade) { - ENCODE_LENGTH_EXACT(CancelTrade_Struct); - SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); - OUT(fromid); - OUT(action); - FINISH_ENCODE(); -} - -ENCODE(OP_InterruptCast) { - ENCODE_LENGTH_EXACT(InterruptCast_Struct); - SETUP_DIRECT_ENCODE(InterruptCast_Struct, structs::InterruptCast_Struct); - OUT(spawnid); - OUT(messageid); - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerSell) { - ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); - SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - OUT(npcid); - eq->itemslot = ServerToRoFMainInvSlot(emu->itemslot); - //OUT(itemslot); - OUT(quantity); - OUT(price); - FINISH_ENCODE(); -} - -ENCODE(OP_ApplyPoison) { - ENCODE_LENGTH_EXACT(ApplyPoison_Struct); - SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - eq->inventorySlot = ServerToRoFMainInvSlot(emu->inventorySlot); - OUT(success); - FINISH_ENCODE(); -} - -ENCODE(OP_RecipeAutoCombine) { - ENCODE_LENGTH_EXACT(RecipeAutoCombine_Struct); - SETUP_DIRECT_ENCODE(RecipeAutoCombine_Struct, structs::RecipeAutoCombine_Struct); - OUT(object_type); - OUT(some_id); - eq->container_slot = ServerToRoFSlot(emu->unknown1); - structs::ItemSlotStruct RoFSlot; - RoFSlot.SlotType = 8; // Observed - RoFSlot.Unknown02 = 0; - RoFSlot.MainSlot = 0xffff; - RoFSlot.SubSlot = 0xffff; - RoFSlot.AugSlot = 0xffff; - RoFSlot.Unknown01 = 0; - eq->unknown_slot = RoFSlot; - OUT(recipe_id); - OUT(reply_code); - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteItem) { - ENCODE_LENGTH_EXACT(DeleteItem_Struct); - SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - eq->from_slot = ServerToRoFSlot(emu->from_slot); - eq->to_slot = ServerToRoFSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } -ENCODE(OP_MoveItem) { - ENCODE_LENGTH_EXACT(MoveItem_Struct); - SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); - - eq->from_slot = ServerToRoFSlot(emu->from_slot); - eq->to_slot = ServerToRoFSlot(emu->to_slot); - OUT(number_in_stack); - FINISH_ENCODE(); -} - -ENCODE(OP_ItemVerifyReply) { - ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); - SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); - - eq->slot = ServerToRoFSlot(emu->slot); - OUT(spell); - OUT(target); - - FINISH_ENCODE(); -} - -ENCODE(OP_Trader) { - - if((*p)->size == sizeof(ClickTrader_Struct)) - { - ENCODE_LENGTH_EXACT(ClickTrader_Struct); - SETUP_DIRECT_ENCODE(ClickTrader_Struct, structs::ClickTrader_Struct); - - eq->Code = emu->Code; - // Live actually has 200 items now, but 80 is the most our internal struct supports - for (uint32 i = 0; i < 200; i++) - { - strncpy(eq->items[i].SerialNumber, "0000000000000000", sizeof(eq->items[i].SerialNumber)); - eq->items[i].Unknown18 = 0; - if (i < 80) { - eq->ItemCost[i] = emu->ItemCost[i]; - } else { - eq->ItemCost[i] = 0; - } - } - FINISH_ENCODE(); - } - else if((*p)->size == sizeof(Trader_ShowItems_Struct)) - { - ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct); - SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct); - eq->Code = emu->Code; - strncpy(eq->SerialNumber, "0000000000000000", sizeof(eq->SerialNumber)); - eq->TraderID = emu->TraderID; - eq->Stacksize = 0; - eq->Price = 0; - FINISH_ENCODE(); - } - else if((*p)->size == sizeof(TraderStatus_Struct)) - { - ENCODE_LENGTH_EXACT(TraderStatus_Struct); - SETUP_DIRECT_ENCODE(TraderStatus_Struct, structs::TraderStatus_Struct); - eq->Code = emu->Code; - FINISH_ENCODE(); - } - else if((*p)->size == sizeof(TraderBuy_Struct)) - { - ENCODE_FORWARD(OP_TraderBuy); - } -} - -ENCODE(OP_TraderBuy) { - - ENCODE_LENGTH_EXACT(TraderBuy_Struct); - SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); - - OUT(Action); - OUT(Price); - OUT(TraderID); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(ItemID); - OUT(Quantity); - OUT(AlreadySold); - - FINISH_ENCODE(); -} - -ENCODE(OP_LootItem) { - - ENCODE_LENGTH_EXACT(LootingItem_Struct); - SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); - OUT(lootee); - OUT(looter); - eq->slot_id = emu->slot_id + 1; - OUT(auto_loot); - - FINISH_ENCODE(); -} - -ENCODE(OP_TributeItem) { - ENCODE_LENGTH_EXACT(TributeItem_Struct); - SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); - - eq->slot = ServerToRoFSlot(emu->slot); - OUT(quantity); - OUT(tribute_master_id); - OUT(tribute_points); - - FINISH_ENCODE(); -} - -ENCODE(OP_SomeItemPacketMaybe) { - // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow - // and flying to the target. - // - - ENCODE_LENGTH_EXACT(Arrow_Struct); - SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); - - OUT(src_y); - OUT(src_x); - OUT(src_z); - OUT(velocity); - OUT(launch_angle); - OUT(tilt); - OUT(arc); - OUT(source_id); - OUT(target_id); - OUT(item_id); - - eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. - - OUT(item_type); - OUT(skill); - - strncpy(eq->model_name, emu->model_name, sizeof(eq->model_name)); - - FINISH_ENCODE(); -} - -ENCODE(OP_ReadBook) { - - ENCODE_LENGTH_ATLEAST(BookText_Struct); - SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); - - if(emu->window == 0xFF) - eq->window = 0xFFFFFFFF; - else - eq->window = emu->window; - OUT(type); - eq->invslot = 0; // Set to hard 0 since it's not required for the structure to work - strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); - FINISH_ENCODE(); -} - -ENCODE(OP_Stun) { - - ENCODE_LENGTH_EXACT(Stun_Struct); - SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); - OUT(duration); - eq->unknown005 = 163; - eq->unknown006 = 67; - - FINISH_ENCODE(); -} - -ENCODE(OP_ZonePlayerToBind) -{ - ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); - ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; - structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; - unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; - structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; - - zph->x = zps->x; - zph->y = zps->y; - zph->z = zps->z; - zph->heading = zps->heading; - zph->bind_zone_id = 0; - zph->bind_instance_id = zps->bind_instance_id; - strncpy(zph->zone_name, zps->zone_name, sizeof(zph->zone_name)); - - zpf->unknown021 = 1; - zpf->unknown022 = 0; - zpf->unknown023 = 0; - zpf->unknown024 = 0; - - ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); - ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); - - delete[] buffer1; - delete[] buffer2; - delete[] (*p)->pBuffer; - - (*p)->pBuffer = new unsigned char[ss.str().size()]; - (*p)->size = ss.str().size(); - - memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); - dest->FastQueuePacket(&(*p)); -} - -ENCODE(OP_AdventureMerchantSell) { - ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); - SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - eq->unknown000 = 1; - OUT(npcid); - eq->slot = ServerToRoFMainInvSlot(emu->slot); - OUT(charges); - OUT(sell_price); - - FINISH_ENCODE(); -} - -ENCODE(OP_RaidUpdate) -{ - EQApplicationPacket *inapp = *p; - *p = nullptr; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; - - if(raid_gen->action == 0) // raid add has longer length than other raid updates - { - RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); - structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; - - add_member->raidGen.action = in_add_member->raidGen.action; - add_member->raidGen.parameter = in_add_member->raidGen.parameter; - strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); - strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); - add_member->_class = in_add_member->_class; - add_member->level= in_add_member->level; - add_member->isGroupLeader = in_add_member->isGroupLeader; - add_member->flags[0] = in_add_member->flags[0]; - add_member->flags[1] = in_add_member->flags[1]; - add_member->flags[2] = in_add_member->flags[2]; - add_member->flags[3] = in_add_member->flags[3]; - add_member->flags[4] = in_add_member->flags[4]; - dest->FastQueuePacket(&outapp); - } - else - { - RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); - strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); - raid_general->action = in_raid_general->action; - raid_general->parameter = in_raid_general->parameter; - dest->FastQueuePacket(&outapp); - } - delete[] __emu_buffer; -} - -ENCODE(OP_RaidJoin) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; - - general->action = 8; - general->parameter = 1; - strn0cpy(general->leader_name, raid_create->leader_name, 64); - strn0cpy(general->player_name, raid_create->leader_name, 64); - - dest->FastQueuePacket(&outapp_create); - delete[] __emu_buffer; -} - -ENCODE(OP_VetRewardsAvaliable) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - - uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for(unsigned int i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; - - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for(int x = 0; x < 8; ++x) - { - vr->items[x].item_id = ivr->items[x].item_id; - strncpy(vr->items[x].item_name, ivr->items[x].item_name, sizeof(vr->items[x].item_name)); - vr->items[x].charges = ivr->items[x].charges; - } - - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); + FINISH_DIRECT_DECODE(); } - dest->FastQueuePacket(&outapp_create); - delete inapp; -} - -ENCODE(OP_WhoAllResponse) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *InBuffer = (char *)in->pBuffer; - - WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; - - int Count = wars->playercount; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); - - char *OutBuffer = (char *)outapp->pBuffer; - - // The struct fields were moved around a bit, so adjust values before copying - wars->unknown44[0] = Count; - wars->unknown52 = 0; - - memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); - - OutBuffer += sizeof(WhoAllReturnStruct); - InBuffer += sizeof(WhoAllReturnStruct); - - for(int i = 0; i < Count; ++i) + DECODE(OP_AltCurrencySell) { - uint32 x; + DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + IN(merchant_entity_id); + emu->slot_id = RoFToServerSlot(eq->slot_id); + IN(charges); + IN(cost); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AltCurrencySellSelection) + { + DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); + + IN(merchant_entity_id); + emu->slot_id = RoFToServerSlot(eq->slot_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = RoFToServerMainInvSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentInfo) + { + DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); + SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + IN(itemid); + IN(window); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = RoFToServerSlot(eq->container_slot); + emu->augment_slot = RoFToServerSlot(eq->augment_slot); + emu->container_index = eq->container_index; + emu->augment_index = eq->augment_index; + emu->dest_inst_id = eq->dest_inst_id; + emu->augment_action = eq->augment_action; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_BazaarSearch) + { + char *Buffer = (char *)__packet->pBuffer; + + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if ((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) + return; + + SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); + MEMSET_IN(structs::NewBazaarInspect_Struct); + + IN(Beginning.Action); + memcpy(emu->Name, eq->Name, sizeof(emu->Name)); + IN(SerialNumber); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_BlockedBuffs) + { + DECODE_LENGTH_EXACT(structs::BlockedBuffs_Struct); + SETUP_DIRECT_DECODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); + + for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) + emu->SpellID[i] = eq->SpellID[i]; + + IN(Count); + IN(Pet); + IN(Initialise); + IN(Flags); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Buff) + { + DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct_Live); + SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Live); + + IN(entityid); + //IN(slot); + IN(level); + IN(effect); + IN(spellid); + IN(duration); + IN(slotid); + IN(bufffade); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_BuffRemoveRequest) + { + // This is to cater for the fact that short buff box buffs start at 30 as opposed to 25 in prior clients. + // + DECODE_LENGTH_EXACT(structs::BuffRemoveRequest_Struct); + SETUP_DIRECT_DECODE(BuffRemoveRequest_Struct, structs::BuffRemoveRequest_Struct); + + emu->SlotID = (eq->SlotID < 42) ? eq->SlotID : (eq->SlotID - 17); + + IN(EntityID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + if (eq->slot == 13) + emu->slot = 10; + else + IN(slot); + + IN(spell_id); + emu->inventoryslot = RoFToServerSlot(eq->inventoryslot); + //IN(inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ChannelMessage) + { + unsigned char *__eq_buffer = __packet->pBuffer; + + char *InBuffer = (char *)__eq_buffer; + + char Sender[64]; + char Target[64]; + + VARSTRUCT_DECODE_STRING(Sender, InBuffer); + VARSTRUCT_DECODE_STRING(Target, InBuffer); InBuffer += 4; - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); - char Name[64]; + uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + InBuffer += 5; - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + __packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; - for(int j = 0; j < 7; ++j) - { - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); - } + strn0cpy(emu->targetname, Target, sizeof(emu->targetname)); + strn0cpy(emu->sender, Target, sizeof(emu->sender)); + emu->language = Language; + emu->chan_num = Channel; + emu->skill_in_language = Skill; + strcpy(emu->message, InBuffer); - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + delete[] __eq_buffer; } - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - delete in; -} + IN(gender); + IN(race); + IN(class_); + IN(deity); -ENCODE(OP_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - FINISH_ENCODE(); -} + if (RuleB(World, EnableTutorialButton) && eq->tutorial) + emu->start_zone = RuleI(World, TutorialZoneID); + else + emu->start_zone = eq->start_zone; -/*ENCODE(OP_InspectAnswer) { - ENCODE_LENGTH_EXACT(InspectResponse_Struct); - SETUP_DIRECT_ENCODE(InspectResponse_Struct, structs::InspectResponse_Struct); + IN(haircolor); + IN(beard); + IN(beardcolor); + IN(hairstyle); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + //IN(tutorial); - OUT(TargetID); - OUT(playerid); + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ClientUpdate) + { + // for some odd reason, there is an extra byte on the end of this on occasion.. + DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); + SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); + + IN(spawn_id); + IN(sequence); + IN(x_pos); + IN(y_pos); + IN(z_pos); + IN(heading); + IN(delta_x); + IN(delta_y); + IN(delta_z); + IN(delta_heading); + IN(animation); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Consider) + { + DECODE_LENGTH_EXACT(structs::Consider_Struct); + SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); + + IN(playerid); + IN(targetid); + IN(faction); + IN(level); + //emu->cur_hp = 1; + //emu->max_hp = 2; + //emu->pvpcon = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = RoFToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Damage) + { + DECODE_LENGTH_EXACT(structs::CombatDamage_Struct); + SETUP_DIRECT_DECODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + IN(target); + IN(source); + IN(type); + IN(spellid); + IN(damage); + emu->sequence = eq->sequence; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = RoFToServerSlot(eq->from_slot); + emu->to_slot = RoFToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_EnvDamage) + { + DECODE_LENGTH_EXACT(structs::EnvDamage2_Struct); + SETUP_DIRECT_DECODE(EnvDamage2_Struct, structs::EnvDamage2_Struct); + + IN(id); + IN(damage); + IN(dmgtype); + emu->constant = 0xFFFF; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FindPersonRequest) + { + DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); + SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); + + IN(npc_id); + IN(client_pos.x); + IN(client_pos.y); + IN(client_pos.z); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GMLastName) + { + DECODE_LENGTH_EXACT(structs::GMLastName_Struct); + SETUP_DIRECT_DECODE(GMLastName_Struct, structs::GMLastName_Struct); + + memcpy(emu->name, eq->name, sizeof(emu->name)); + memcpy(emu->gmname, eq->gmname, sizeof(emu->gmname)); + memcpy(emu->lastname, eq->lastname, sizeof(emu->lastname)); + for (int i = 0; i<4; i++) + { + emu->unknown[i] = eq->unknown[i]; + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupCancelInvite) + { + DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); + SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + IN(toggle); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupDisband) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_Disband"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow2) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupInvite"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); + memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite2) + { + //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); + DECODE_FORWARD(OP_GroupInvite); + } + + DECODE(OP_GuildDemote) + { + DECODE_LENGTH_EXACT(structs::GuildDemoteStruct); + SETUP_DIRECT_DECODE(GuildDemoteStruct, structs::GuildDemoteStruct); + + strn0cpy(emu->target, eq->target, sizeof(emu->target)); + strn0cpy(emu->name, eq->name, sizeof(emu->name)); + // IN(rank); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GuildRemove) + { + DECODE_LENGTH_EXACT(structs::GuildCommand_Struct); + SETUP_DIRECT_DECODE(GuildCommand_Struct, structs::GuildCommand_Struct); + + strn0cpy(emu->othername, eq->othername, sizeof(emu->othername)); + strn0cpy(emu->myname, eq->myname, sizeof(emu->myname)); + IN(guildeqid); + IN(officer); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GuildStatus) + { + DECODE_LENGTH_EXACT(structs::GuildStatus_Struct); + SETUP_DIRECT_DECODE(GuildStatus_Struct, structs::GuildStatus_Struct); + + memcpy(emu->Name, eq->Name, sizeof(emu->Name)); + + FINISH_DIRECT_DECODE(); + } + + /*DECODE(OP_InspectAnswer) + { + DECODE_LENGTH_EXACT(structs::InspectResponse_Struct); + SETUP_DIRECT_DECODE(InspectResponse_Struct, structs::InspectResponse_Struct); + + IN(TargetID); + IN(playerid); int r; for (r = 0; r < 21; r++) { - strn0cpy(eq->itemnames[r], emu->itemnames[r], sizeof(eq->itemnames[r])); + strn0cpy(emu->itemnames[r], eq->itemnames[r], sizeof(emu->itemnames[r])); } // Swap last 2 slots for Arrow and Power Source - strn0cpy(eq->itemnames[21], emu->itemnames[22], sizeof(eq->itemnames[21])); - strn0cpy(eq->unknown_zero, emu->itemnames[21], sizeof(eq->unknown_zero)); + strn0cpy(emu->itemnames[22], eq->itemnames[21], sizeof(emu->itemnames[22])); + strn0cpy(emu->itemnames[21], eq->unknown_zero, sizeof(emu->itemnames[21])); + strn0cpy(emu->unknown_zero, eq->unknown_zero, sizeof(emu->unknown_zero)); int k; for (k = 0; k < 21; k++) { - OUT(itemicons[k]); + IN(itemicons[k]); } // Swap last 2 slots for Arrow and Power Source - eq->itemicons[21] = emu->itemicons[22]; - eq->unknown_zero2 = emu->itemicons[21]; - strn0cpy(eq->text, emu->text, sizeof(eq->text)); - - FINISH_ENCODE(); -}*/ - -ENCODE(OP_GroupInvite) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); - memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow2) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupCancelInvite) -{ - ENCODE_LENGTH_EXACT(GroupCancel_Struct); - SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - OUT(toggle); - FINISH_ENCODE(); -} - -ENCODE(OP_SetGuildRank) -{ - ENCODE_LENGTH_EXACT(GuildSetRank_Struct); - SETUP_DIRECT_ENCODE(GuildSetRank_Struct, structs::GuildSetRank_Struct); - eq->GuildID = emu->Unknown00; - - /* Translate older ranks to new values */ - switch (emu->Rank) { - case 0: { eq->Rank = 5; break; } // GUILD_MEMBER 0 - case 1: { eq->Rank = 3; break; } // GUILD_OFFICER 1 - case 2: { eq->Rank = 1; break; } // GUILD_LEADER 2 - default: { eq->Rank = emu->Rank; break; } - } - - memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); - OUT(Banker); - eq->Unknown76 = 1; - FINISH_ENCODE(); -} - - -ENCODE(OP_GroupUpdate) -{ - //_log(NET__ERROR, "OP_GroupUpdate"); - EQApplicationPacket *in = *p; - - GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; - - //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); - if((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) - { - if((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) - { - //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); - dest->FastQueuePacket(&outapp); - - // Make an empty GLAA packet to clear out their useable GLAAs - // - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - dest->FastQueuePacket(&outapp); - - delete in; - - return; - } - //if(gjs->action == groupActLeave) - // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - delete in; - return; - - - } - - if(in->size == sizeof(GroupUpdate2_Struct)) - { - // Group Update2 - //_log(NET__ERROR, "Struct is GroupUpdate2"); - - unsigned char *__emu_buffer = in->pBuffer; - GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*) __emu_buffer; - - //_log(NET__ERROR, "Yourname is %s", gu2->yourname); - - int MemberCount = 1; - - int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; - - for(int i = 0; i < 5; ++i) - { - //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); - if(gu2->membername[i][0] != '\0') - { - PacketLength += (22 + strlen(gu2->membername[i]) + 1); - ++MemberCount; - } - } - - //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); - - char *Buffer = (char *)outapp->pBuffer; - - // Header - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); - - // Leader - // - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - - int MemberNumber = 1; - - for(int i = 0; i < 5; ++i) - { - if(gu2->membername[i][0] == '\0') - continue; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - } - - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = gu2->NPCMarkerID; - memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); - - dest->FastQueuePacket(&outapp); - delete in; - - return; - - } - //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - ENCODE_LENGTH_EXACT(GroupJoin_Struct); - SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); - - memcpy(eq->membername, emu->membername, sizeof(eq->membername)); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = emu->NPCMarkerID; - - memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); - //_hex(NET__ERROR, __packet->pBuffer, __packet->size); - FINISH_ENCODE(); - - dest->FastQueuePacket(&outapp); -} - -ENCODE(OP_ChannelMessage) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - ChannelMessage_Struct *emu = (ChannelMessage_Struct *) in->pBuffer; - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildsList) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - uint32 NumberOfGuilds = in->size / 64; - - uint32 PacketSize = 68; // 64 x 0x00 + a uint32 that I am guessing is the highest guild ID in use. - - unsigned char *__emu_buffer = in->pBuffer; - - char *InBuffer = (char *)__emu_buffer; - - uint32 HighestGuildID = 0; - - for(unsigned int i = 0; i < NumberOfGuilds; ++i) - { - if(InBuffer[0]) - { - PacketSize += (5 + strlen(InBuffer)); - HighestGuildID = i - 1; - } - InBuffer += 64; - } - - PacketSize++; // Appears to be an extra 0x00 at the very end. - - in->size = PacketSize; - - in->pBuffer = new unsigned char[in->size]; - - InBuffer = (char *)__emu_buffer; - - char *OutBuffer = (char *)in->pBuffer; - - // Init the first 64 bytes to zero, as per live. - // - memset(OutBuffer, 0, 64); - - OutBuffer += 64; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, HighestGuildID); - - for(unsigned int i = 0; i < NumberOfGuilds; ++i) - { - if(InBuffer[0]) - { - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, i - 1); - VARSTRUCT_ENCODE_STRING(OutBuffer, InBuffer); - } - InBuffer += 64; - } - - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0x00); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_DzExpeditionEndsWarning) -{ - ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); - SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - OUT(minutes_remaining); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionInfo) -{ - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - OUT(max_players); - eq->unknown004 = 785316192; - eq->unknown008 = 435601; - strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); - strncpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); - FINISH_ENCODE(); -} - -ENCODE(OP_DzCompass) -{ - SETUP_VAR_ENCODE(ExpeditionCompass_Struct); - ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - OUT(count); - - for(uint32 i = 0; i < emu->count; ++i) - { - OUT(entries[i].x); - OUT(entries[i].y); - OUT(entries[i].z); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_DzMemberList) -{ - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].status, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionList) -{ - SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); - ss.write((const char*)&null_term, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzLeaderStatus) -{ - SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->leader_name, strlen(emu->leader_name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//1 - ss.write((const char*)&client_id, sizeof(uint32)); - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzJoinExpeditionConfirm) -{ - ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); - SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); - strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); - strncpy(eq->player_name, emu->player_name, sizeof(eq->player_name)); - FINISH_ENCODE(); -} - -ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); } -ENCODE(OP_BuffCreate) -{ - SETUP_VAR_ENCODE(BuffIcon_Struct); - - uint32 sz = 12 + (17 * emu->count); - __packet->size = sz; - __packet->pBuffer = new unsigned char[sz]; - memset(__packet->pBuffer, 0, sz); - - __packet->WriteUInt32(emu->entity_id); - __packet->WriteUInt32(0); // PlayerID ? - __packet->WriteUInt8(1); // 1 indicates all buffs on the player (0 to add or remove a single buff) - __packet->WriteUInt16(emu->count); - - for(uint16 i = 0; i < emu->count; ++i) - { - uint16 buffslot = emu->entries[i].buff_slot; - // Not sure if this is needs amending for RoF yet. - if(emu->entries[i].buff_slot >= 25) - { - buffslot += 17; - } - - __packet->WriteUInt32(buffslot); - __packet->WriteUInt32(emu->entries[i].spell_id); - __packet->WriteUInt32(emu->entries[i].tics_remaining); - __packet->WriteUInt32(0); // Unknown - __packet->WriteString(""); - } - __packet->WriteUInt8(0); // Unknown - - FINISH_ENCODE(); -} - -ENCODE(OP_ZoneChange) -{ - ENCODE_LENGTH_EXACT(ZoneChange_Struct); - SETUP_DIRECT_ENCODE(ZoneChange_Struct, structs::ZoneChange_Struct); - - memcpy(eq->char_name, emu->char_name, sizeof(emu->char_name)); - OUT(zoneID); - OUT(instanceID); - OUT(y); - OUT(x); - OUT(z) - OUT(zone_reason); - OUT(success); - FINISH_ENCODE(); -} - -ENCODE(OP_WearChange) -{ - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(unknown06); - OUT(elite_material); - OUT(hero_forge_model); - OUT(unknown18); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -ENCODE(OP_SpawnAppearance) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *emu_buffer = in->pBuffer; - - SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer; - - if(sas->type != AT_Size) - { - dest->FastQueuePacket(&in, ack_req); - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); - - ChangeSize_Struct *css = (ChangeSize_Struct *)outapp->pBuffer; - - css->EntityID = sas->spawn_id; - css->Size = (float)sas->parameter; - css->Unknown08 = 0; - css->Unknown12 = 1.0f; - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_CastSpell) -{ - ENCODE_LENGTH_EXACT(CastSpell_Struct); - SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct); - if(emu->slot == 10) - { - eq->slot = 13; - } - else - { - OUT(slot); - } - OUT(spell_id); - eq->inventoryslot = ServerToRoFSlot(emu->inventoryslot); - //OUT(inventoryslot); - OUT(target_id); - FINISH_ENCODE(); -} - -ENCODE(OP_ShopRequest) -{ - ENCODE_LENGTH_EXACT(Merchant_Click_Struct); - SETUP_DIRECT_ENCODE(Merchant_Click_Struct, structs::Merchant_Click_Struct); - OUT(npcid); - OUT(playerid); - OUT(command); - OUT(rate); - eq->unknown01 = 3; // Not sure what these values do yet, but list won't display without them - eq->unknown02 = 2592000; - FINISH_ENCODE(); -} - -ENCODE(OP_DisciplineUpdate) -{ - ENCODE_LENGTH_EXACT(Disciplines_Struct); - SETUP_DIRECT_ENCODE(Disciplines_Struct, structs::Disciplines_Struct); - - memcpy(&eq->values, &emu->values, sizeof(Disciplines_Struct)); - - FINISH_ENCODE(); -} - -ENCODE(OP_RespondAA) { - SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); - - eq->aa_spent = emu->aa_spent; - // These fields may need to be correctly populated at some point - eq->aapoints_assigned = emu->aa_spent + 1; - eq->aa_spent_general = 0; - eq->aa_spent_archetype = 0; - eq->aa_spent_class = 0; - eq->aa_spent_special = 0; - - for(uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) - { - eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; - eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; - eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; - } - - FINISH_ENCODE(); -} - -ENCODE(OP_AltCurrencySell) -{ - ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); - SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - - OUT(merchant_entity_id); - eq->slot_id = ServerToRoFSlot(emu->slot_id); - OUT(charges); - OUT(cost); - FINISH_ENCODE(); -} - -ENCODE(OP_AltCurrency) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *emu_buffer = in->pBuffer; - uint32 opcode = *((uint32*)emu_buffer); - - if(opcode == 8) { - AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(structs::AltCurrencyPopulate_Struct) - + sizeof(structs::AltCurrencyPopulateEntry_Struct) * populate->count); - structs::AltCurrencyPopulate_Struct *out_populate = (structs::AltCurrencyPopulate_Struct*)outapp->pBuffer; - - out_populate->opcode = populate->opcode; - out_populate->count = populate->count; - for(uint32 i = 0; i < populate->count; ++i) { - out_populate->entries[i].currency_number = populate->entries[i].currency_number; - out_populate->entries[i].unknown00 = populate->entries[i].unknown00; - out_populate->entries[i].currency_number2 = populate->entries[i].currency_number2; - out_populate->entries[i].item_id = populate->entries[i].item_id; - out_populate->entries[i].item_icon = populate->entries[i].item_icon; - out_populate->entries[i].stack_size = populate->entries[i].stack_size; - out_populate->entries[i].display = ((populate->entries[i].stack_size > 0) ? 1 : 0); - } - - dest->FastQueuePacket(&outapp, ack_req); - } else { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyUpdate_Struct)); - memcpy(outapp->pBuffer, emu_buffer, sizeof(AltCurrencyUpdate_Struct)); - dest->FastQueuePacket(&outapp, ack_req); - } - - //dest->FastQueuePacket(&outapp, ack_req); - delete in; -} - -ENCODE(OP_HPUpdate) -{ - SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct, structs::SpawnHPUpdate_Struct); - OUT(spawn_id); - OUT(cur_hp); - OUT(max_hp); - FINISH_ENCODE(); -} - -ENCODE(OP_RemoveBlockedBuffs) { ENCODE_FORWARD(OP_BlockedBuffs); } - -ENCODE(OP_BlockedBuffs) -{ - ENCODE_LENGTH_EXACT(BlockedBuffs_Struct); - SETUP_DIRECT_ENCODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); - - for(uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) - eq->SpellID[i] = emu->SpellID[i]; - - // -1 for the extra 10 added in RoF. We should really be encoding for the older clients, not RoF, but - // we can sort that out later. - - for(uint32 i = BLOCKED_BUFF_COUNT; i < structs::BLOCKED_BUFF_COUNT; ++i) - eq->SpellID[i] = -1; - - OUT(Count); - OUT(Pet); - OUT(Initialise); - OUT(Flags); - - FINISH_ENCODE(); -} - -ENCODE(OP_TributeInfo) -{ - ENCODE_LENGTH_ATLEAST(TributeAbility_Struct); - SETUP_VAR_ENCODE(TributeAbility_Struct); - - ALLOC_VAR_ENCODE(structs::TributeAbility_Struct, sizeof(structs::TributeAbility_Struct) + strlen(emu->name) + 1); - - OUT(tribute_id); - OUT(tier_count); - - for(uint32 i = 0; i < MAX_TRIBUTE_TIERS; ++i) - { - eq->tiers[i].level = emu->tiers[i].level; - eq->tiers[i].tribute_item_id = emu->tiers[i].tribute_item_id; - eq->tiers[i].cost = emu->tiers[i].cost; - } - - eq->unknown128 = 0; - - strcpy(eq->name, emu->name); - - FINISH_ENCODE(); -} - -ENCODE(OP_GuildMemberUpdate) -{ - SETUP_DIRECT_ENCODE(GuildMemberUpdate_Struct, structs::GuildMemberUpdate_Struct); - - OUT(GuildID); - memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); - OUT(ZoneID); - OUT(InstanceID); - OUT(LastSeen); - eq->Unknown76 = 0; - FINISH_ENCODE(); -} - -ENCODE(OP_BeginCast) -{ - SETUP_DIRECT_ENCODE(BeginCast_Struct, structs::BeginCast_Struct); - - OUT(spell_id); - OUT(caster_id); - OUT(cast_time); - - FINISH_ENCODE(); -} - -ENCODE(OP_RezzRequest) -{ - SETUP_DIRECT_ENCODE(Resurrect_Struct, structs::Resurrect_Struct); - - OUT(zone_id); - OUT(instance_id); - OUT(y); - OUT(x); - OUT(z); - OUT_str(your_name); - OUT_str(rezzer_name); - OUT(spellid); - OUT_str(corpse_name); - OUT(action); - - FINISH_ENCODE(); -} - -DECODE(OP_BuffRemoveRequest) -{ - // This is to cater for the fact that short buff box buffs start at 30 as opposed to 25 in prior clients. - // - DECODE_LENGTH_EXACT(structs::BuffRemoveRequest_Struct); - SETUP_DIRECT_DECODE(BuffRemoveRequest_Struct, structs::BuffRemoveRequest_Struct); - - emu->SlotID = (eq->SlotID < 42 ) ? eq->SlotID : (eq->SlotID - 17); - - IN(EntityID); + emu->itemicons[22] = eq->itemicons[21]; + emu->itemicons[21] = eq->unknown_zero2; + emu->unknown_zero2 = eq->unknown_zero2; + strn0cpy(emu->text, eq->text, sizeof(emu->text)); + //emu->unknown1772 = 0; FINISH_DIRECT_DECODE(); -} + }*/ -DECODE(OP_PetCommands) -{ - DECODE_LENGTH_EXACT(structs::PetCommand_Struct); - SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); - - switch(eq->command) + DECODE(OP_InspectRequest) { + DECODE_LENGTH_EXACT(structs::Inspect_Struct); + SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); + + IN(TargetID); + IN(PlayerID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + // Max Augs is now 6, but no code to support that many yet + IN(link_hash); + IN(icon); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemVerifyRequest) + { + DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); + SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); + + emu->slot = RoFToServerSlot(eq->slot); + IN(target); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LoadSpellSet) + { + DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); + SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); + + for (unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) + { + if (eq->spell[i] == 0) + emu->spell[i] = 0xFFFFFFFF; + else + emu->spell[i] = eq->spell[i]; + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = eq->slot_id - 1; + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + //_log(NET__ERROR, "Moved item from %u to %u", eq->from_slot.MainSlot, eq->to_slot.MainSlot); + _log(NET__ERROR, "MoveItem SlotType from %i to %i, MainSlot from %i to %i, SubSlot from %i to %i, AugSlot from %i to %i, Unknown01 from %i to %i, Number %u", eq->from_slot.SlotType, eq->to_slot.SlotType, eq->from_slot.MainSlot, eq->to_slot.MainSlot, eq->from_slot.SubSlot, eq->to_slot.SubSlot, eq->from_slot.AugSlot, eq->to_slot.AugSlot, eq->from_slot.Unknown01, eq->to_slot.Unknown01, eq->number_in_stack); + emu->from_slot = RoFToServerSlot(eq->from_slot); + emu->to_slot = RoFToServerSlot(eq->to_slot); + IN(number_in_stack); + + _hex(NET__ERROR, eq, sizeof(structs::MoveItem_Struct)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_PetCommands) + { + DECODE_LENGTH_EXACT(structs::PetCommand_Struct); + SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); + + switch (eq->command) + { case 0x00: emu->command = 0x04; // Health break; @@ -4100,1312 +4472,1075 @@ DECODE(OP_PetCommands) break; default: emu->command = eq->command; - } - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySellSelection) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); - IN(merchant_entity_id); - emu->slot_id = RoFToServerSlot(eq->slot_id); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySell) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - IN(merchant_entity_id); - emu->slot_id = RoFToServerSlot(eq->slot_id); - IN(charges); - IN(cost); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopRequest) { - DECODE_LENGTH_EXACT(structs::Merchant_Click_Struct); - SETUP_DIRECT_DECODE(Merchant_Click_Struct, structs::Merchant_Click_Struct); - IN(npcid); - IN(playerid); - IN(command); - IN(rate); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GuildRemove) -{ - DECODE_LENGTH_EXACT(structs::GuildCommand_Struct); - SETUP_DIRECT_DECODE(GuildCommand_Struct, structs::GuildCommand_Struct); - strn0cpy(emu->othername, eq->othername, sizeof(emu->othername)); - strn0cpy(emu->myname, eq->myname, sizeof(emu->myname)); - IN(guildeqid); - IN(officer); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GuildDemote) -{ - DECODE_LENGTH_EXACT(structs::GuildDemoteStruct); - SETUP_DIRECT_DECODE(GuildDemoteStruct, structs::GuildDemoteStruct); - strn0cpy(emu->target, eq->target, sizeof(emu->target)); - strn0cpy(emu->name, eq->name, sizeof(emu->name)); - // IN(rank); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_BazaarSearch) -{ - char *Buffer = (char *)__packet->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) - return; - - SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); - MEMSET_IN(structs::NewBazaarInspect_Struct); - IN(Beginning.Action); - memcpy(emu->Name, eq->Name, sizeof(emu->Name)); - IN(SerialNumber); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_InspectRequest) { - DECODE_LENGTH_EXACT(structs::Inspect_Struct); - SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); - IN(TargetID); - IN(PlayerID); - FINISH_DIRECT_DECODE(); -} - -/*DECODE(OP_InspectAnswer) { - DECODE_LENGTH_EXACT(structs::InspectResponse_Struct); - SETUP_DIRECT_DECODE(InspectResponse_Struct, structs::InspectResponse_Struct); - - IN(TargetID); - IN(playerid); - - int r; - for (r = 0; r < 21; r++) { - strn0cpy(emu->itemnames[r], eq->itemnames[r], sizeof(emu->itemnames[r])); - } - // Swap last 2 slots for Arrow and Power Source - strn0cpy(emu->itemnames[22], eq->itemnames[21], sizeof(emu->itemnames[22])); - strn0cpy(emu->itemnames[21], eq->unknown_zero, sizeof(emu->itemnames[21])); - strn0cpy(emu->unknown_zero, eq->unknown_zero, sizeof(emu->unknown_zero)); - - int k; - for (k = 0; k < 21; k++) { - IN(itemicons[k]); - } - // Swap last 2 slots for Arrow and Power Source - emu->itemicons[22] = eq->itemicons[21]; - emu->itemicons[21] = eq->unknown_zero2; - emu->unknown_zero2 = eq->unknown_zero2; - strn0cpy(emu->text, eq->text, sizeof(emu->text)); - //emu->unknown1772 = 0; - - FINISH_DIRECT_DECODE(); -}*/ - -DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); - - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AdventureMerchantSell) { - DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); - SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - IN(npcid); - emu->slot = RoFToServerMainInvSlot(eq->slot); - IN(charges); - IN(sell_price); - - FINISH_DIRECT_DECODE(); -} - - -DECODE(OP_ApplyPoison) { - DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); - SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - - emu->inventorySlot = RoFToServerMainInvSlot(eq->inventorySlot); - IN(success); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemVerifyRequest) { - DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); - SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); - - emu->slot = RoFToServerSlot(eq->slot); - IN(target); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Consume) { - DECODE_LENGTH_EXACT(structs::Consume_Struct); - SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); - - emu->slot = RoFToServerSlot(eq->slot); - IN(auto_consumed); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CastSpell) { - DECODE_LENGTH_EXACT(structs::CastSpell_Struct); - SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); - - if(eq->slot == 13) - { - emu->slot = 10; - } - else - { - IN(slot); - } - IN(spell_id); - emu->inventoryslot = RoFToServerSlot(eq->inventoryslot); - //IN(inventoryslot); - IN(target_id); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_DeleteItem) -{ - DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); - SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - emu->from_slot = RoFToServerSlot(eq->from_slot); - emu->to_slot = RoFToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_MoveItem) -{ - DECODE_LENGTH_EXACT(structs::MoveItem_Struct); - SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - - //_log(NET__ERROR, "Moved item from %u to %u", eq->from_slot.MainSlot, eq->to_slot.MainSlot); - _log(NET__ERROR, "MoveItem SlotType from %i to %i, MainSlot from %i to %i, SubSlot from %i to %i, AugSlot from %i to %i, Unknown01 from %i to %i, Number %u", eq->from_slot.SlotType, eq->to_slot.SlotType, eq->from_slot.MainSlot, eq->to_slot.MainSlot, eq->from_slot.SubSlot, eq->to_slot.SubSlot, eq->from_slot.AugSlot, eq->to_slot.AugSlot, eq->from_slot.Unknown01, eq->to_slot.Unknown01, eq->number_in_stack); - emu->from_slot = RoFToServerSlot(eq->from_slot); - emu->to_slot = RoFToServerSlot(eq->to_slot); - IN(number_in_stack); - - _hex(NET__ERROR, eq, sizeof(structs::MoveItem_Struct)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); - } - // Max Augs is now 6, but no code to support that many yet - IN(link_hash); - IN(icon); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 29; r++) { - // Size 40 in RoF - IN(filters[r]); - } - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } -DECODE(OP_Consider) { - DECODE_LENGTH_EXACT(structs::Consider_Struct); - SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); - IN(playerid); - IN(targetid); - IN(faction); - IN(level); - //emu->cur_hp = 1; - //emu->max_hp = 2; - //emu->pvpcon = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerBuy) -{ - DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); - SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - - IN(npcid); - IN(playerid); - IN(itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ClientUpdate) { - // for some odd reason, there is an extra byte on the end of this on occasion.. - DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); - SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); - IN(spawn_id); - IN(sequence); - IN(x_pos); - IN(y_pos); - IN(z_pos); - IN(heading); - IN(delta_x); - IN(delta_y); - IN(delta_z); - IN(delta_heading); - IN(animation); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - - IN(gender); - IN(race); - IN(class_); - IN(deity); - - if(RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - - IN(haircolor); - IN(beard); - IN(beardcolor); - IN(hairstyle); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - //IN(tutorial); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - IN(guildid); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupInvite2) -{ - //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); - DECODE_FORWARD(OP_GroupInvite); -} - -DECODE(OP_GroupInvite) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupInvite"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); - memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow2) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupDisband) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_Disband"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupCancelInvite) -{ - DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); - SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - IN(toggle); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GMLastName) -{ - DECODE_LENGTH_EXACT(structs::GMLastName_Struct); - SETUP_DIRECT_DECODE(GMLastName_Struct, structs::GMLastName_Struct); - memcpy(emu->name, eq->name, sizeof(emu->name)); - memcpy(emu->gmname, eq->gmname, sizeof(emu->gmname)); - memcpy(emu->lastname, eq->lastname, sizeof(emu->lastname)); - for (int i=0; i<4; i++) - { - emu->unknown[i] = eq->unknown[i]; - } - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Buff) { - DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct_Live); - SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Live); - IN(entityid); - //IN(slot); - IN(level); - IN(effect); - IN(spellid); - IN(duration); - IN(slotid); - IN(bufffade); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerSell) { - DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); - SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - - IN(npcid); - emu->itemslot = RoFToServerMainInvSlot(eq->itemslot); - //IN(itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Save) { - DECODE_LENGTH_EXACT(structs::Save_Struct); - SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); - memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FindPersonRequest) { - DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); - SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); - IN(npc_id); - IN(client_pos.x); - IN(client_pos.y); - IN(client_pos.z); - FINISH_DIRECT_DECODE(); -} - - -DECODE(OP_Trader) { - uint32 psize = __packet->size; - if(psize == sizeof(structs::ClickTrader_Struct)) - { - DECODE_LENGTH_EXACT(structs::ClickTrader_Struct); - SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::ClickTrader_Struct); - MEMSET_IN(ClickTrader_Struct); - - emu->Code = eq->Code; - // Live actually has 200 items now, but 80 is the most our internal struct supports - for (uint32 i = 0; i < 80; i++) - { - emu->SerialNumber[i] = 0; // eq->SerialNumber[i]; - emu->ItemCost[i] = eq->ItemCost[i]; } - FINISH_DIRECT_DECODE(); - } - else if(psize == sizeof(structs::Trader_ShowItems_Struct)) - { - DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct); - SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct); - MEMSET_IN(Trader_ShowItems_Struct); - - emu->Code = eq->Code; - emu->TraderID = eq->TraderID; FINISH_DIRECT_DECODE(); } - else if(psize == sizeof(structs::TraderStatus_Struct)) - { - DECODE_LENGTH_EXACT(structs::TraderStatus_Struct); - SETUP_DIRECT_DECODE(TraderStatus_Struct, structs::TraderStatus_Struct); - MEMSET_IN(TraderStatus_Struct); - emu->Code = eq->Code; + DECODE(OP_RaidInvite) + { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); FINISH_DIRECT_DECODE(); } -} -DECODE(OP_TraderBuy) -{ - DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); - SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); - MEMSET_IN(TraderBuy_Struct); - - IN(Action); - IN(Price); - IN(TraderID); - memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); - IN(ItemID); - IN(Quantity); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LootItem) { - DECODE_LENGTH_EXACT(structs::LootingItem_Struct); - SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); - IN(lootee); - IN(looter); - emu->slot_id = eq->slot_id - 1; - IN(auto_loot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TributeItem) { - DECODE_LENGTH_EXACT(structs::TributeItem_Struct); - SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); - - emu->slot = RoFToServerSlot(eq->slot); - IN(quantity); - IN(tribute_master_id); - IN(tribute_points); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_EXACT(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(type); - emu->invslot = 0; // Set to hard 0 since it's not required for the structure to work - emu->window = (uint8) eq->window; - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TradeSkillCombine) { - DECODE_LENGTH_EXACT(structs::NewCombine_Struct); - SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - - int16 slot_id = RoFToServerSlot(eq->container_slot); - if (slot_id == 4000) { - slot_id = legacy::SLOT_TRADESKILL; // 1000 - } - emu->container_slot = slot_id; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RecipeAutoCombine) { - DECODE_LENGTH_EXACT(structs::RecipeAutoCombine_Struct); - SETUP_DIRECT_DECODE(RecipeAutoCombine_Struct, structs::RecipeAutoCombine_Struct); - - IN(object_type); - IN(some_id); - emu->unknown1 = RoFToServerSlot(eq->container_slot); - IN(recipe_id); - IN(reply_code); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentItem) { - DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); - SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); - - emu->container_slot = RoFToServerSlot(eq->container_slot); - emu->augment_slot = RoFToServerSlot(eq->augment_slot); - emu->container_index = eq->container_index; - emu->augment_index = eq->augment_index; - emu->dest_inst_id = eq->dest_inst_id; - emu->augment_action = eq->augment_action; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentInfo) { - DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); - SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); - - IN(itemid); - IN(window); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LoadSpellSet) -{ - DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); - SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); - - for(unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) + DECODE(OP_ReadBook) { - if (eq->spell[i] == 0) - emu->spell[i] = 0xFFFFFFFF; + DECODE_LENGTH_EXACT(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(type); + emu->invslot = 0; // Set to hard 0 since it's not required for the structure to work + emu->window = (uint8)eq->window; + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_RecipeAutoCombine) + { + DECODE_LENGTH_EXACT(structs::RecipeAutoCombine_Struct); + SETUP_DIRECT_DECODE(RecipeAutoCombine_Struct, structs::RecipeAutoCombine_Struct); + + IN(object_type); + IN(some_id); + emu->unknown1 = RoFToServerSlot(eq->container_slot); + IN(recipe_id); + IN(reply_code); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_RemoveBlockedBuffs) { DECODE_FORWARD(OP_BlockedBuffs); } + + DECODE(OP_RezzAnswer) + { + DECODE_LENGTH_EXACT(structs::Resurrect_Struct); + SETUP_DIRECT_DECODE(Resurrect_Struct, structs::Resurrect_Struct); + + IN(zone_id); + IN(instance_id); + IN(y); + IN(x); + IN(z); + memcpy(emu->your_name, eq->your_name, sizeof(emu->your_name)); + memcpy(emu->rezzer_name, eq->rezzer_name, sizeof(emu->rezzer_name)); + IN(spellid); + memcpy(emu->corpse_name, eq->corpse_name, sizeof(emu->corpse_name)); + IN(action); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Save) + { + DECODE_LENGTH_EXACT(structs::Save_Struct); + SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); + + memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + // Size 40 in RoF + IN(filters[r]); + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerBuy) + { + DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); + SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + IN(npcid); + IN(playerid); + IN(itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = RoFToServerMainInvSlot(eq->itemslot); + //IN(itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopRequest) + { + DECODE_LENGTH_EXACT(structs::Merchant_Click_Struct); + SETUP_DIRECT_DECODE(Merchant_Click_Struct, structs::Merchant_Click_Struct); + + IN(npcid); + IN(playerid); + IN(command); + IN(rate); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Trader) + { + uint32 psize = __packet->size; + if (psize == sizeof(structs::ClickTrader_Struct)) + { + DECODE_LENGTH_EXACT(structs::ClickTrader_Struct); + SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::ClickTrader_Struct); + MEMSET_IN(ClickTrader_Struct); + + emu->Code = eq->Code; + // Live actually has 200 items now, but 80 is the most our internal struct supports + for (uint32 i = 0; i < 80; i++) + { + emu->SerialNumber[i] = 0; // eq->SerialNumber[i]; + emu->ItemCost[i] = eq->ItemCost[i]; + } + + FINISH_DIRECT_DECODE(); + } + else if (psize == sizeof(structs::Trader_ShowItems_Struct)) + { + DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct); + SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct); + MEMSET_IN(Trader_ShowItems_Struct); + + emu->Code = eq->Code; + emu->TraderID = eq->TraderID; + + FINISH_DIRECT_DECODE(); + } + else if (psize == sizeof(structs::TraderStatus_Struct)) + { + DECODE_LENGTH_EXACT(structs::TraderStatus_Struct); + SETUP_DIRECT_DECODE(TraderStatus_Struct, structs::TraderStatus_Struct); + MEMSET_IN(TraderStatus_Struct); + + emu->Code = eq->Code; + + FINISH_DIRECT_DECODE(); + } + } + + DECODE(OP_TraderBuy) + { + DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); + SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); + MEMSET_IN(TraderBuy_Struct); + + IN(Action); + IN(Price); + IN(TraderID); + memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); + IN(ItemID); + IN(Quantity); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + int16 slot_id = RoFToServerSlot(eq->container_slot); + if (slot_id == 4000) { + slot_id = legacy::SLOT_TRADESKILL; // 1000 + } + emu->container_slot = slot_id; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = RoFToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + IN(guildid); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ZoneChange) + { + DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); + SETUP_DIRECT_DECODE(ZoneChange_Struct, structs::ZoneChange_Struct); + + memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); + IN(zoneID); + IN(instanceID); + IN(y); + IN(x); + IN(z) + IN(zone_reason); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ZoneEntry) + { + DECODE_LENGTH_EXACT(structs::ClientZoneEntry_Struct); + SETUP_DIRECT_DECODE(ClientZoneEntry_Struct, structs::ClientZoneEntry_Struct); + + memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + uint32 NextItemInstSerialNumber = 1; + uint32 MaxInstances = 2000000000; + + static inline int32 GetNextItemInstSerialNumber() + { + if (NextItemInstSerialNumber >= MaxInstances) + NextItemInstSerialNumber = 1; else - emu->spell[i] = eq->spell[i]; + NextItemInstSerialNumber++; + + return NextItemInstSerialNumber; } - FINISH_DIRECT_DECODE(); -} -DECODE(OP_Damage) { - DECODE_LENGTH_EXACT(structs::CombatDamage_Struct); - SETUP_DIRECT_DECODE(CombatDamage_Struct, structs::CombatDamage_Struct); - IN(target); - IN(source); - IN(type); - IN(spellid); - IN(damage); - emu->sequence = eq->sequence; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_EnvDamage) { - DECODE_LENGTH_EXACT(structs::EnvDamage2_Struct); - SETUP_DIRECT_DECODE(EnvDamage2_Struct, structs::EnvDamage2_Struct); - IN(id); - IN(damage); - IN(dmgtype); - emu->constant = 0xFFFF; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ZoneChange) -{ - DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); - SETUP_DIRECT_DECODE(ZoneChange_Struct, structs::ZoneChange_Struct); - memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); - IN(zoneID); - IN(instanceID); - IN(y); - IN(x); - IN(z) - IN(zone_reason); - IN(success); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ChannelMessage) -{ - unsigned char *__eq_buffer = __packet->pBuffer; - - char *InBuffer = (char *)__eq_buffer; - - char Sender[64]; - char Target[64]; - - VARSTRUCT_DECODE_STRING(Sender, InBuffer); - VARSTRUCT_DECODE_STRING(Target, InBuffer); - - InBuffer += 4; - - uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - - InBuffer += 5; - - uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - - __packet->size = sizeof(ChannelMessage_Struct) + strlen(InBuffer) + 1; - __packet->pBuffer = new unsigned char[__packet->size]; - ChannelMessage_Struct *emu = (ChannelMessage_Struct *) __packet->pBuffer; - - strn0cpy(emu->targetname, Target, sizeof(emu->targetname)); - strn0cpy(emu->sender, Target, sizeof(emu->sender)); - emu->language = Language; - emu->chan_num = Channel; - emu->skill_in_language = Skill; - strcpy(emu->message, InBuffer); - - delete [] __eq_buffer; -} - -DECODE(OP_ZoneEntry) -{ - DECODE_LENGTH_EXACT(structs::ClientZoneEntry_Struct); - SETUP_DIRECT_DECODE(ClientZoneEntry_Struct, structs::ClientZoneEntry_Struct); - memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RemoveBlockedBuffs) { DECODE_FORWARD(OP_BlockedBuffs); } - -DECODE(OP_BlockedBuffs) -{ - DECODE_LENGTH_EXACT(structs::BlockedBuffs_Struct); - SETUP_DIRECT_DECODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); - - for(uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) - emu->SpellID[i] = eq->SpellID[i]; - - IN(Count); - IN(Pet); - IN(Initialise); - IN(Flags); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GuildStatus) -{ - DECODE_LENGTH_EXACT(structs::GuildStatus_Struct); - SETUP_DIRECT_DECODE(GuildStatus_Struct, structs::GuildStatus_Struct); - - memcpy(emu->Name, eq->Name, sizeof(emu->Name)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RezzAnswer) -{ - DECODE_LENGTH_EXACT(structs::Resurrect_Struct); - SETUP_DIRECT_DECODE(Resurrect_Struct, structs::Resurrect_Struct); - - IN(zone_id); - IN(instance_id); - IN(y); - IN(x); - IN(z); - memcpy(emu->your_name, eq->your_name, sizeof(emu->your_name)); - memcpy(emu->rezzer_name, eq->rezzer_name, sizeof(emu->rezzer_name)); - IN(spellid); - memcpy(emu->corpse_name, eq->corpse_name, sizeof(emu->corpse_name)); - IN(action); - - FINISH_DIRECT_DECODE(); -} - -uint32 NextItemInstSerialNumber = 1; -uint32 MaxInstances = 2000000000; - -static inline int32 GetNextItemInstSerialNumber() { - - if(NextItemInstSerialNumber >= MaxInstances) - NextItemInstSerialNumber = 1; - else - NextItemInstSerialNumber++; - - return NextItemInstSerialNumber; -} - - -char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { - uint8 null_term = 0; - bool stackable = inst->IsStackable(); - uint32 merchant_slot = inst->GetMerchantSlot(); - uint32 charges = inst->GetCharges(); - if (!stackable && charges > 254) - charges = 0xFFFFFFFF; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - const Item_Struct *item = inst->GetItem(); - //_log(NET__ERROR, "Serialize called for: %s", item->Name); - - RoF::structs::ItemSerializationHeader hdr; - - //sprintf(hdr.unknown000, "06e0002Y1W00"); - - snprintf( hdr.unknown000, sizeof(hdr.unknown000), "%012d", item->ID ); - - hdr.stacksize = stackable ? charges : 1; - hdr.unknown004 = 0; - - structs::ItemSlotStruct slot_id = ServerToRoFSlot(slot_id_in); - - hdr.slot_type = (merchant_slot == 0) ? slot_id.SlotType : 9; // 9 is merchant 20 is reclaim items? - hdr.main_slot = (merchant_slot == 0) ? slot_id.MainSlot : merchant_slot; - hdr.sub_slot = (merchant_slot == 0) ? slot_id.SubSlot : 0xffff; - hdr.unknown013 = (merchant_slot == 0) ? slot_id.AugSlot : 0xffff; - //hdr.unknown013 = 0xffff; - hdr.price = inst->GetPrice(); - hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); - //hdr.merchant_slot = (merchant_slot == 0) ? 1 : 0xffffffff; - hdr.unknown020 = 0; - hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; - hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); - hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); - hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; - hdr.unknown044 = 0; - hdr.unknown048 = 0; - hdr.unknown052 = 0; - hdr.unknown056 = 0; - hdr.unknown060 = 0; - hdr.unknown061 = 0; - hdr.unknown062 = 0; - hdr.unknowna1 = 0xffffffff; - hdr.unknowna2 = 0; - hdr.unknown063 = 0; - hdr.unknowna3 = 0; - hdr.unknowna4 = 0xffffffff; - hdr.unknowna5 = 0; - hdr.ItemClass = item->ItemClass; - - ss.write((const char*)&hdr, sizeof(RoF::structs::ItemSerializationHeader)); - - - if(strlen(item->Name) > 0) + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { - ss.write(item->Name, strlen(item->Name)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + uint8 null_term = 0; + bool stackable = inst->IsStackable(); + uint32 merchant_slot = inst->GetMerchantSlot(); + uint32 charges = inst->GetCharges(); + if (!stackable && charges > 254) + charges = 0xFFFFFFFF; - if(strlen(item->Lore) > 0) - { - ss.write(item->Lore, strlen(item->Lore)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - if(strlen(item->IDFile) > 0) - { - ss.write(item->IDFile, strlen(item->IDFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + const Item_Struct *item = inst->GetItem(); + //_log(NET__ERROR, "Serialize called for: %s", item->Name); - ss.write((const char*)&null_term, sizeof(uint8)); - //_log(NET__ERROR, "ItemBody struct is %i bytes", sizeof(RoF::structs::ItemBodyStruct)); - RoF::structs::ItemBodyStruct ibs; - memset(&ibs, 0, sizeof(RoF::structs::ItemBodyStruct)); + RoF::structs::ItemSerializationHeader hdr; - uint32 adjusted_slots = item->Slots; + //sprintf(hdr.unknown000, "06e0002Y1W00"); - // Conversions for Ammo and Power Source Slots - if(item->Slots & (1 << 21) & (1 << 22)) - { - // Do nothing - } - else - { - if(item->Slots & (1 << 21)) // Ammo Slot from Database + snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%012d", item->ID); + + hdr.stacksize = stackable ? charges : 1; + hdr.unknown004 = 0; + + structs::ItemSlotStruct slot_id = ServerToRoFSlot(slot_id_in); + + hdr.slot_type = (merchant_slot == 0) ? slot_id.SlotType : 9; // 9 is merchant 20 is reclaim items? + hdr.main_slot = (merchant_slot == 0) ? slot_id.MainSlot : merchant_slot; + hdr.sub_slot = (merchant_slot == 0) ? slot_id.SubSlot : 0xffff; + hdr.unknown013 = (merchant_slot == 0) ? slot_id.AugSlot : 0xffff; + //hdr.unknown013 = 0xffff; + hdr.price = inst->GetPrice(); + hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); + //hdr.merchant_slot = (merchant_slot == 0) ? 1 : 0xffffffff; + hdr.unknown020 = 0; + hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; + hdr.unknown028 = 0; + hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); + hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; + hdr.unknown044 = 0; + hdr.unknown048 = 0; + hdr.unknown052 = 0; + hdr.unknown056 = 0; + hdr.unknown060 = 0; + hdr.unknown061 = 0; + hdr.unknown062 = 0; + hdr.unknowna1 = 0xffffffff; + hdr.unknowna2 = 0; + hdr.unknown063 = 0; + hdr.unknowna3 = 0; + hdr.unknowna4 = 0xffffffff; + hdr.unknowna5 = 0; + hdr.ItemClass = item->ItemClass; + + ss.write((const char*)&hdr, sizeof(RoF::structs::ItemSerializationHeader)); + + if (strlen(item->Name) > 0) { - adjusted_slots -= (1 << 21); // Ammo Slot in Titanium - adjusted_slots += (1 << 22); // Ammo Slot in SoF + ss.write(item->Name, strlen(item->Name)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); } - if(item->Slots & (1 << 22)) // Power Source Slot from Database + if (strlen(item->Lore) > 0) { - adjusted_slots -= (1 << 22); // Non Existant Worn Slot in Titanium - adjusted_slots += (1 << 21); // Power Source Slot in SoF + ss.write(item->Lore, strlen(item->Lore)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); } - } - ibs.id = item->ID; - ibs.weight = item->Weight; - ibs.norent = item->NoRent; - ibs.nodrop = item->NoDrop; - ibs.attune = item->Attuneable; - ibs.size = item->Size; - ibs.slots = adjusted_slots; - ibs.price = item->Price; - ibs.icon = item->Icon; - ibs.unknown1 = 1; - ibs.unknown2 = 1; - ibs.BenefitFlag = item->BenefitFlag; - ibs.tradeskills = item->Tradeskills; - ibs.CR = item->CR; - ibs.DR = item->DR; - ibs.PR = item->PR; - ibs.MR = item->MR; - ibs.FR = item->FR; - ibs.SVCorruption = item->SVCorruption; - ibs.AStr = item->AStr; - ibs.ASta = item->ASta; - ibs.AAgi = item->AAgi; - ibs.ADex = item->ADex; - ibs.ACha = item->ACha; - ibs.AInt = item->AInt; - ibs.AWis = item->AWis; + if (strlen(item->IDFile) > 0) + { + ss.write(item->IDFile, strlen(item->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ibs.HP = item->HP; - ibs.Mana = item->Mana; - ibs.Endur = item->Endur; - ibs.AC = item->AC; - ibs.regen = item->Regen; - ibs.mana_regen = item->ManaRegen; - ibs.end_regen = item->EnduranceRegen; - ibs.Classes = item->Classes; - ibs.Races = item->Races; - ibs.Deity = item->Deity; - ibs.SkillModValue = item->SkillModValue; - ibs.SkillModMax = 0xffffffff; - ibs.SkillModType = (int8)(item->SkillModType); - ibs.SkillModExtra = 0; - ibs.BaneDmgRace = item->BaneDmgRace; - ibs.BaneDmgBody = item->BaneDmgBody; - ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; - ibs.BaneDmgAmt = item->BaneDmgAmt; - ibs.Magic = item->Magic; - ibs.CastTime_ = item->CastTime_; - ibs.ReqLevel = item->ReqLevel; - if(item->ReqLevel > 100) - ibs.ReqLevel = 100; - ibs.RecLevel = item->RecLevel; - if(item->RecLevel > 100) - ibs.RecLevel = 100; - ibs.RecSkill = item->RecSkill; - ibs.BardType = item->BardType; - ibs.BardValue = item->BardValue; - ibs.Light = item->Light; - ibs.Delay = item->Delay; - ibs.ElemDmgType = item->ElemDmgType; - ibs.ElemDmgAmt = item->ElemDmgAmt; - ibs.Range = item->Range; - ibs.Damage = item->Damage; - ibs.Color = item->Color; - ibs.Prestige = 0; - ibs.ItemType = item->ItemType; - ibs.Material = item->Material; - ibs.unknown7 = 0; - ibs.EliteMaterial = item->EliteMaterial; - ibs.unknown_RoF3 = 0; - ibs.unknown_RoF4 = 0; - ibs.SellRate = item->SellRate; - ibs.CombatEffects = item->CombatEffects; - ibs.Shielding = item->Shielding; - ibs.StunResist = item->StunResist; - ibs.StrikeThrough = item->StrikeThrough; - ibs.ExtraDmgSkill = item->ExtraDmgSkill; - ibs.ExtraDmgAmt = item->ExtraDmgAmt; - ibs.SpellShield = item->SpellShield; - ibs.Avoidance = item->Avoidance; - ibs.Accuracy = item->Accuracy; - ibs.FactionAmt1 = item->FactionAmt1; - ibs.FactionMod1 = item->FactionMod1; - ibs.FactionAmt2 = item->FactionAmt2; - ibs.FactionMod2 = item->FactionMod2; - ibs.FactionAmt3 = item->FactionAmt3; - ibs.FactionMod3 = item->FactionMod3; - ibs.FactionAmt4 = item->FactionAmt4; - ibs.FactionMod4 = item->FactionMod4; - - ss.write((const char*)&ibs, sizeof(RoF::structs::ItemBodyStruct)); - - //charm text - if(strlen(item->CharmFile) > 0) - { - ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + //_log(NET__ERROR, "ItemBody struct is %i bytes", sizeof(RoF::structs::ItemBodyStruct)); + RoF::structs::ItemBodyStruct ibs; + memset(&ibs, 0, sizeof(RoF::structs::ItemBodyStruct)); - //_log(NET__ERROR, "ItemBody secondary struct is %i bytes", sizeof(RoF::structs::ItemSecondaryBodyStruct)); - RoF::structs::ItemSecondaryBodyStruct isbs; - memset(&isbs, 0, sizeof(RoF::structs::ItemSecondaryBodyStruct)); + ibs.id = item->ID; + ibs.weight = item->Weight; + ibs.norent = item->NoRent; + ibs.nodrop = item->NoDrop; + ibs.attune = item->Attuneable; + ibs.size = item->Size; + ibs.slots = SwapBits21and22(item->Slots); + ibs.price = item->Price; + ibs.icon = item->Icon; + ibs.unknown1 = 1; + ibs.unknown2 = 1; + ibs.BenefitFlag = item->BenefitFlag; + ibs.tradeskills = item->Tradeskills; + ibs.CR = item->CR; + ibs.DR = item->DR; + ibs.PR = item->PR; + ibs.MR = item->MR; + ibs.FR = item->FR; + ibs.SVCorruption = item->SVCorruption; + ibs.AStr = item->AStr; + ibs.ASta = item->ASta; + ibs.AAgi = item->AAgi; + ibs.ADex = item->ADex; + ibs.ACha = item->ACha; + ibs.AInt = item->AInt; + ibs.AWis = item->AWis; - isbs.augtype = item->AugType; - isbs.augdistiller = 65535; - isbs.augrestrict = item->AugRestrict; - + ibs.HP = item->HP; + ibs.Mana = item->Mana; + ibs.Endur = item->Endur; + ibs.AC = item->AC; + ibs.regen = item->Regen; + ibs.mana_regen = item->ManaRegen; + ibs.end_regen = item->EnduranceRegen; + ibs.Classes = item->Classes; + ibs.Races = item->Races; + ibs.Deity = item->Deity; + ibs.SkillModValue = item->SkillModValue; + ibs.SkillModMax = 0xffffffff; + ibs.SkillModType = (int8)(item->SkillModType); + ibs.SkillModExtra = 0; + ibs.BaneDmgRace = item->BaneDmgRace; + ibs.BaneDmgBody = item->BaneDmgBody; + ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; + ibs.BaneDmgAmt = item->BaneDmgAmt; + ibs.Magic = item->Magic; + ibs.CastTime_ = item->CastTime_; + ibs.ReqLevel = item->ReqLevel; + if (item->ReqLevel > 100) + ibs.ReqLevel = 100; + ibs.RecLevel = item->RecLevel; + if (item->RecLevel > 100) + ibs.RecLevel = 100; + ibs.RecSkill = item->RecSkill; + ibs.BardType = item->BardType; + ibs.BardValue = item->BardValue; + ibs.Light = item->Light; + ibs.Delay = item->Delay; + ibs.ElemDmgType = item->ElemDmgType; + ibs.ElemDmgAmt = item->ElemDmgAmt; + ibs.Range = item->Range; + ibs.Damage = item->Damage; + ibs.Color = item->Color; + ibs.Prestige = 0; + ibs.ItemType = item->ItemType; + ibs.Material = item->Material; + ibs.unknown7 = 0; + ibs.EliteMaterial = item->EliteMaterial; + ibs.unknown_RoF3 = 0; + ibs.unknown_RoF4 = 0; + ibs.SellRate = item->SellRate; + ibs.CombatEffects = item->CombatEffects; + ibs.Shielding = item->Shielding; + ibs.StunResist = item->StunResist; + ibs.StrikeThrough = item->StrikeThrough; + ibs.ExtraDmgSkill = item->ExtraDmgSkill; + ibs.ExtraDmgAmt = item->ExtraDmgAmt; + ibs.SpellShield = item->SpellShield; + ibs.Avoidance = item->Avoidance; + ibs.Accuracy = item->Accuracy; + ibs.FactionAmt1 = item->FactionAmt1; + ibs.FactionMod1 = item->FactionMod1; + ibs.FactionAmt2 = item->FactionAmt2; + ibs.FactionMod2 = item->FactionMod2; + ibs.FactionAmt3 = item->FactionAmt3; + ibs.FactionMod3 = item->FactionMod3; + ibs.FactionAmt4 = item->FactionAmt4; + ibs.FactionMod4 = item->FactionMod4; - for(int x = AUG_BEGIN; x < EmuConstants::ITEM_COMMON_SIZE; ++x) - { - isbs.augslots[x].type = item->AugSlotType[x]; - isbs.augslots[x].visible = item->AugSlotVisible[x]; - isbs.augslots[x].unknown = item->AugSlotUnk2[x]; - } + ss.write((const char*)&ibs, sizeof(RoF::structs::ItemBodyStruct)); - // Increased to 6 max aug slots - isbs.augslots[5].type = 0; - isbs.augslots[5].visible = 1; - isbs.augslots[5].unknown = 0; + //charm text + if (strlen(item->CharmFile) > 0) + { + ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - isbs.ldonpoint_type = item->PointType; - isbs.ldontheme = item->LDoNTheme; - isbs.ldonprice = item->LDoNPrice; - isbs.ldonsellbackrate = item->LDoNSellBackRate; - isbs.ldonsold = item->LDoNSold; + //_log(NET__ERROR, "ItemBody secondary struct is %i bytes", sizeof(RoF::structs::ItemSecondaryBodyStruct)); + RoF::structs::ItemSecondaryBodyStruct isbs; + memset(&isbs, 0, sizeof(RoF::structs::ItemSecondaryBodyStruct)); - isbs.bagtype = item->BagType; - isbs.bagslots = item->BagSlots; - isbs.bagsize = item->BagSize; - isbs.wreduction = item->BagWR; + isbs.augtype = item->AugType; + isbs.augdistiller = 65535; + isbs.augrestrict = item->AugRestrict; - isbs.book = item->Book; - isbs.booktype = item->BookType; + for (int x = AUG_BEGIN; x < EmuConstants::ITEM_COMMON_SIZE; ++x) + { + isbs.augslots[x].type = item->AugSlotType[x]; + isbs.augslots[x].visible = item->AugSlotVisible[x]; + isbs.augslots[x].unknown = item->AugSlotUnk2[x]; + } - ss.write((const char*)&isbs, sizeof(RoF::structs::ItemSecondaryBodyStruct)); + // Increased to 6 max aug slots + isbs.augslots[5].type = 0; + isbs.augslots[5].visible = 1; + isbs.augslots[5].unknown = 0; - if(strlen(item->Filename) > 0) - { - ss.write((const char*)item->Filename, strlen(item->Filename)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + isbs.ldonpoint_type = item->PointType; + isbs.ldontheme = item->LDoNTheme; + isbs.ldonprice = item->LDoNPrice; + isbs.ldonsellbackrate = item->LDoNSellBackRate; + isbs.ldonsold = item->LDoNSold; - //_log(NET__ERROR, "ItemBody tertiary struct is %i bytes", sizeof(RoF::structs::ItemTertiaryBodyStruct)); - RoF::structs::ItemTertiaryBodyStruct itbs; - memset(&itbs, 0, sizeof(RoF::structs::ItemTertiaryBodyStruct)); + isbs.bagtype = item->BagType; + isbs.bagslots = item->BagSlots; + isbs.bagsize = item->BagSize; + isbs.wreduction = item->BagWR; - itbs.loregroup = item->LoreGroup; - itbs.artifact = item->ArtifactFlag; - itbs.summonedflag = item->SummonedFlag; - itbs.favor = item->Favor; - itbs.fvnodrop = item->FVNoDrop; - itbs.dotshield = item->DotShielding; - itbs.atk = item->Attack; - itbs.haste = item->Haste; - itbs.damage_shield = item->DamageShield; - itbs.guildfavor = item->GuildFavor; - itbs.augdistil = item->AugDistiller; - itbs.unknown3 = 0xffffffff; - itbs.unknown4 = 0; - itbs.no_pet = item->NoPet; - itbs.unknown5 = 0; + isbs.book = item->Book; + isbs.booktype = item->BookType; - itbs.potion_belt_enabled = item->PotionBelt; - itbs.potion_belt_slots = item->PotionBeltSlots; - itbs.stacksize = stackable ? item->StackSize : 0; - itbs.no_transfer = item->NoTransfer; - itbs.expendablearrow = item->ExpendableArrow; + ss.write((const char*)&isbs, sizeof(RoF::structs::ItemSecondaryBodyStruct)); - itbs.unknown8 = 0; - itbs.unknown9 = 0; - itbs.unknown10 = 0; - itbs.unknown11 = 0; - itbs.unknown12 = 0; - itbs.unknown13 = 0; - itbs.unknown14 = 0; + if (strlen(item->Filename) > 0) + { + ss.write((const char*)item->Filename, strlen(item->Filename)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ss.write((const char*)&itbs, sizeof(RoF::structs::ItemTertiaryBodyStruct)); + //_log(NET__ERROR, "ItemBody tertiary struct is %i bytes", sizeof(RoF::structs::ItemTertiaryBodyStruct)); + RoF::structs::ItemTertiaryBodyStruct itbs; + memset(&itbs, 0, sizeof(RoF::structs::ItemTertiaryBodyStruct)); - // Effect Structures Broken down to allow variable length strings for effect names - int32 effect_unknown = 0; + itbs.loregroup = item->LoreGroup; + itbs.artifact = item->ArtifactFlag; + itbs.summonedflag = item->SummonedFlag; + itbs.favor = item->Favor; + itbs.fvnodrop = item->FVNoDrop; + itbs.dotshield = item->DotShielding; + itbs.atk = item->Attack; + itbs.haste = item->Haste; + itbs.damage_shield = item->DamageShield; + itbs.guildfavor = item->GuildFavor; + itbs.augdistil = item->AugDistiller; + itbs.unknown3 = 0xffffffff; + itbs.unknown4 = 0; + itbs.no_pet = item->NoPet; + itbs.unknown5 = 0; - //_log(NET__ERROR, "ItemBody Click effect struct is %i bytes", sizeof(RoF::structs::ClickEffectStruct)); - RoF::structs::ClickEffectStruct ices; - memset(&ices, 0, sizeof(RoF::structs::ClickEffectStruct)); + itbs.potion_belt_enabled = item->PotionBelt; + itbs.potion_belt_slots = item->PotionBeltSlots; + itbs.stacksize = stackable ? item->StackSize : 0; + itbs.no_transfer = item->NoTransfer; + itbs.expendablearrow = item->ExpendableArrow; - ices.effect = item->Click.Effect; - ices.level2 = item->Click.Level2; - ices.type = item->Click.Type; - ices.level = item->Click.Level; - ices.max_charges = item->MaxCharges; - ices.cast_time = item->CastTime; - ices.recast = item->RecastDelay; - ices.recast_type = item->RecastType; + itbs.unknown8 = 0; + itbs.unknown9 = 0; + itbs.unknown10 = 0; + itbs.unknown11 = 0; + itbs.unknown12 = 0; + itbs.unknown13 = 0; + itbs.unknown14 = 0; - ss.write((const char*)&ices, sizeof(RoF::structs::ClickEffectStruct)); + ss.write((const char*)&itbs, sizeof(RoF::structs::ItemTertiaryBodyStruct)); - if(strlen(item->ClickName) > 0) - { - ss.write((const char*)item->ClickName, strlen(item->ClickName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + // Effect Structures Broken down to allow variable length strings for effect names + int32 effect_unknown = 0; - ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 + //_log(NET__ERROR, "ItemBody Click effect struct is %i bytes", sizeof(RoF::structs::ClickEffectStruct)); + RoF::structs::ClickEffectStruct ices; + memset(&ices, 0, sizeof(RoF::structs::ClickEffectStruct)); - //_log(NET__ERROR, "ItemBody proc effect struct is %i bytes", sizeof(RoF::structs::ProcEffectStruct)); - RoF::structs::ProcEffectStruct ipes; - memset(&ipes, 0, sizeof(RoF::structs::ProcEffectStruct)); + ices.effect = item->Click.Effect; + ices.level2 = item->Click.Level2; + ices.type = item->Click.Type; + ices.level = item->Click.Level; + ices.max_charges = item->MaxCharges; + ices.cast_time = item->CastTime; + ices.recast = item->RecastDelay; + ices.recast_type = item->RecastType; - ipes.effect = item->Proc.Effect; - ipes.level2 = item->Proc.Level2; - ipes.type = item->Proc.Type; - ipes.level = item->Proc.Level; - ipes.procrate = item->ProcRate; + ss.write((const char*)&ices, sizeof(RoF::structs::ClickEffectStruct)); - ss.write((const char*)&ipes, sizeof(RoF::structs::ProcEffectStruct)); + if (strlen(item->ClickName) > 0) + { + ss.write((const char*)item->ClickName, strlen(item->ClickName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - if(strlen(item->ProcName) > 0) - { - ss.write((const char*)item->ProcName, strlen(item->ProcName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 + //_log(NET__ERROR, "ItemBody proc effect struct is %i bytes", sizeof(RoF::structs::ProcEffectStruct)); + RoF::structs::ProcEffectStruct ipes; + memset(&ipes, 0, sizeof(RoF::structs::ProcEffectStruct)); - //_log(NET__ERROR, "ItemBody worn effect struct is %i bytes", sizeof(RoF::structs::WornEffectStruct)); - RoF::structs::WornEffectStruct iwes; - memset(&iwes, 0, sizeof(RoF::structs::WornEffectStruct)); + ipes.effect = item->Proc.Effect; + ipes.level2 = item->Proc.Level2; + ipes.type = item->Proc.Type; + ipes.level = item->Proc.Level; + ipes.procrate = item->ProcRate; - iwes.effect = item->Worn.Effect; - iwes.level2 = item->Worn.Level2; - iwes.type = item->Worn.Type; - iwes.level = item->Worn.Level; + ss.write((const char*)&ipes, sizeof(RoF::structs::ProcEffectStruct)); - ss.write((const char*)&iwes, sizeof(RoF::structs::WornEffectStruct)); + if (strlen(item->ProcName) > 0) + { + ss.write((const char*)item->ProcName, strlen(item->ProcName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - if(strlen(item->WornName) > 0) - { - ss.write((const char*)item->WornName, strlen(item->WornName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + //_log(NET__ERROR, "ItemBody worn effect struct is %i bytes", sizeof(RoF::structs::WornEffectStruct)); + RoF::structs::WornEffectStruct iwes; + memset(&iwes, 0, sizeof(RoF::structs::WornEffectStruct)); - RoF::structs::WornEffectStruct ifes; - memset(&ifes, 0, sizeof(RoF::structs::WornEffectStruct)); + iwes.effect = item->Worn.Effect; + iwes.level2 = item->Worn.Level2; + iwes.type = item->Worn.Type; + iwes.level = item->Worn.Level; - ifes.effect = item->Focus.Effect; - ifes.level2 = item->Focus.Level2; - ifes.type = item->Focus.Type; - ifes.level = item->Focus.Level; + ss.write((const char*)&iwes, sizeof(RoF::structs::WornEffectStruct)); - ss.write((const char*)&ifes, sizeof(RoF::structs::WornEffectStruct)); + if (strlen(item->WornName) > 0) + { + ss.write((const char*)item->WornName, strlen(item->WornName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - if(strlen(item->FocusName) > 0) - { - ss.write((const char*)item->FocusName, strlen(item->FocusName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + RoF::structs::WornEffectStruct ifes; + memset(&ifes, 0, sizeof(RoF::structs::WornEffectStruct)); - RoF::structs::WornEffectStruct ises; - memset(&ises, 0, sizeof(RoF::structs::WornEffectStruct)); + ifes.effect = item->Focus.Effect; + ifes.level2 = item->Focus.Level2; + ifes.type = item->Focus.Type; + ifes.level = item->Focus.Level; - ises.effect = item->Scroll.Effect; - ises.level2 = item->Scroll.Level2; - ises.type = item->Scroll.Type; - ises.level = item->Scroll.Level; + ss.write((const char*)&ifes, sizeof(RoF::structs::WornEffectStruct)); - ss.write((const char*)&ises, sizeof(RoF::structs::WornEffectStruct)); + if (strlen(item->FocusName) > 0) + { + ss.write((const char*)item->FocusName, strlen(item->FocusName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - if(strlen(item->ScrollName) > 0) - { - ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + RoF::structs::WornEffectStruct ises; + memset(&ises, 0, sizeof(RoF::structs::WornEffectStruct)); - // Bard Effect? - RoF::structs::WornEffectStruct ibes; - memset(&ibes, 0, sizeof(RoF::structs::WornEffectStruct)); + ises.effect = item->Scroll.Effect; + ises.level2 = item->Scroll.Level2; + ises.type = item->Scroll.Type; + ises.level = item->Scroll.Level; - ibes.effect = 0xffffffff; - ibes.level2 = 0; - ibes.type = 0; - ibes.level = 0; - //ibes.unknown6 = 0xffffffff; + ss.write((const char*)&ises, sizeof(RoF::structs::WornEffectStruct)); - ss.write((const char*)&ibes, sizeof(RoF::structs::WornEffectStruct)); + if (strlen(item->ScrollName) > 0) + { + ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - /* - if(strlen(item->BardName) > 0) - { + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + // Bard Effect? + RoF::structs::WornEffectStruct ibes; + memset(&ibes, 0, sizeof(RoF::structs::WornEffectStruct)); + + ibes.effect = 0xffffffff; + ibes.level2 = 0; + ibes.type = 0; + ibes.level = 0; + //ibes.unknown6 = 0xffffffff; + + ss.write((const char*)&ibes, sizeof(RoF::structs::WornEffectStruct)); + + /* + if(strlen(item->BardName) > 0) + { ss.write((const char*)item->BardName, strlen(item->BardName)); ss.write((const char*)&null_term, sizeof(uint8)); - } - else */ + } + else */ ss.write((const char*)&null_term, sizeof(uint8)); - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - // End of Effects + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + // End of Effects - //_log(NET__ERROR, "ItemBody Quaternary effect struct is %i bytes", sizeof(RoF::structs::ItemQuaternaryBodyStruct)); - RoF::structs::ItemQuaternaryBodyStruct iqbs; - memset(&iqbs, 0, sizeof(RoF::structs::ItemQuaternaryBodyStruct)); + //_log(NET__ERROR, "ItemBody Quaternary effect struct is %i bytes", sizeof(RoF::structs::ItemQuaternaryBodyStruct)); + RoF::structs::ItemQuaternaryBodyStruct iqbs; + memset(&iqbs, 0, sizeof(RoF::structs::ItemQuaternaryBodyStruct)); - iqbs.scriptfileid = item->ScriptFileID; - iqbs.quest_item = item->QuestItemFlag; - iqbs.Power = 0; - iqbs.Purity = item->Purity; - iqbs.unknown16 = 0; - iqbs.BackstabDmg = item->BackstabDmg; - iqbs.DSMitigation = item->DSMitigation; - iqbs.HeroicStr = item->HeroicStr; - iqbs.HeroicInt = item->HeroicInt; - iqbs.HeroicWis = item->HeroicWis; - iqbs.HeroicAgi = item->HeroicAgi; - iqbs.HeroicDex = item->HeroicDex; - iqbs.HeroicSta = item->HeroicSta; - iqbs.HeroicCha = item->HeroicCha; - iqbs.HeroicMR = item->HeroicMR; - iqbs.HeroicFR = item->HeroicFR; - iqbs.HeroicCR = item->HeroicCR; - iqbs.HeroicDR = item->HeroicDR; - iqbs.HeroicPR = item->HeroicPR; - iqbs.HeroicSVCorrup = item->HeroicSVCorrup; - iqbs.HealAmt = item->HealAmt; - iqbs.SpellDmg = item->SpellDmg; - iqbs.clairvoyance = item->Clairvoyance; - iqbs.unknown28 = 0; - iqbs.unknown30 = 0; - iqbs.unknown39 = 1; + iqbs.scriptfileid = item->ScriptFileID; + iqbs.quest_item = item->QuestItemFlag; + iqbs.Power = 0; + iqbs.Purity = item->Purity; + iqbs.unknown16 = 0; + iqbs.BackstabDmg = item->BackstabDmg; + iqbs.DSMitigation = item->DSMitigation; + iqbs.HeroicStr = item->HeroicStr; + iqbs.HeroicInt = item->HeroicInt; + iqbs.HeroicWis = item->HeroicWis; + iqbs.HeroicAgi = item->HeroicAgi; + iqbs.HeroicDex = item->HeroicDex; + iqbs.HeroicSta = item->HeroicSta; + iqbs.HeroicCha = item->HeroicCha; + iqbs.HeroicMR = item->HeroicMR; + iqbs.HeroicFR = item->HeroicFR; + iqbs.HeroicCR = item->HeroicCR; + iqbs.HeroicDR = item->HeroicDR; + iqbs.HeroicPR = item->HeroicPR; + iqbs.HeroicSVCorrup = item->HeroicSVCorrup; + iqbs.HealAmt = item->HealAmt; + iqbs.SpellDmg = item->SpellDmg; + iqbs.clairvoyance = item->Clairvoyance; + iqbs.unknown28 = 0; + iqbs.unknown30 = 0; + iqbs.unknown39 = 1; - iqbs.subitem_count = 0; + iqbs.subitem_count = 0; - char *SubSerializations[10]; // + char *SubSerializations[10]; // - uint32 SubLengths[10]; + uint32 SubLengths[10]; - for(int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { - SubSerializations[x] = nullptr; + SubSerializations[x] = nullptr; - const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); - if(subitem) { + if (subitem) { - int SubSlotNumber; + int SubSlotNumber; - iqbs.subitem_count++; + iqbs.subitem_count++; - if(slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? - //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); - SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); - else if(slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) - //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); - else if(slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) - //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); - else - SubSlotNumber = slot_id_in; // ??????? + if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? + //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); + else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) + //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); + else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) + //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); + else + SubSlotNumber = slot_id_in; // ??????? + + /* + // TEST CODE: + SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); + */ + + SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + } + } + + ss.write((const char*)&iqbs, sizeof(RoF::structs::ItemQuaternaryBodyStruct)); + + for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + + if (SubSerializations[x]) { + + ss.write((const char*)&x, sizeof(uint32)); + + ss.write(SubSerializations[x], SubLengths[x]); + + safe_delete_array(SubSerializations[x]); + } + } + + char* item_serial = new char[ss.tellp()]; + memset(item_serial, 0, ss.tellp()); + memcpy(item_serial, ss.str().c_str(), ss.tellp()); + + *length = ss.tellp(); + return item_serial; + } + + static inline structs::ItemSlotStruct ServerToRoFSlot(uint32 ServerSlot) + { + structs::ItemSlotStruct RoFSlot; + RoFSlot.SlotType = INVALID_INDEX; + RoFSlot.Unknown02 = NOT_USED; + RoFSlot.MainSlot = INVALID_INDEX; + RoFSlot.SubSlot = INVALID_INDEX; + RoFSlot.AugSlot = INVALID_INDEX; + RoFSlot.Unknown01 = NOT_USED; + + uint32 TempSlot = 0; + + if (ServerSlot < 56 || ServerSlot == MainPowerSource) { // Main Inventory and Cursor + RoFSlot.SlotType = maps::MapPossessions; + RoFSlot.MainSlot = ServerSlot; + + if (ServerSlot == MainPowerSource) + RoFSlot.MainSlot = slots::MainPowerSource; + + else if (ServerSlot >= MainCursor) // Cursor and Extended Corpse Inventory + RoFSlot.MainSlot += 3; + + else if (ServerSlot >= MainAmmo) // (> 20) + RoFSlot.MainSlot += 1; + } + + /*else if (ServerSlot < 51) { // Cursor Buffer + RoFSlot.SlotType = maps::MapLimbo; + RoFSlot.MainSlot = ServerSlot - 31; + }*/ + + else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) { // (> 250 && < 341) + RoFSlot.SlotType = maps::MapPossessions; + TempSlot = ServerSlot - 1; + RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 2; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 2) * EmuConstants::ITEM_CONTAINER_SIZE); + + if (RoFSlot.MainSlot >= slots::MainGeneral9) // (> 30) + RoFSlot.MainSlot = slots::MainCursor; + } + + else if (ServerSlot >= EmuConstants::TRIBUTE_BEGIN && ServerSlot <= EmuConstants::TRIBUTE_END) { // Tribute + RoFSlot.SlotType = maps::MapTribute; + RoFSlot.MainSlot = ServerSlot - EmuConstants::TRIBUTE_BEGIN; + } + + else if (ServerSlot >= EmuConstants::BANK_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) { + RoFSlot.SlotType = maps::MapBank; + TempSlot = ServerSlot - EmuConstants::BANK_BEGIN; + RoFSlot.MainSlot = TempSlot; + + if (TempSlot > 30) { // (> 30) + RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); + } + } + + else if (ServerSlot >= EmuConstants::SHARED_BANK_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) { + RoFSlot.SlotType = maps::MapSharedBank; + TempSlot = ServerSlot - EmuConstants::SHARED_BANK_BEGIN; + RoFSlot.MainSlot = TempSlot; + + if (TempSlot > 30) { // (> 30) + RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); + } + } + + else if (ServerSlot >= EmuConstants::TRADE_BEGIN && ServerSlot <= EmuConstants::TRADE_BAGS_END) { + RoFSlot.SlotType = maps::MapTrade; + TempSlot = ServerSlot - EmuConstants::TRADE_BEGIN; + RoFSlot.MainSlot = TempSlot; + + if (TempSlot > 30) { + RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); + } /* - // TEST CODE: - SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); + // OLD CODE: + if (TempSlot > 99) { + if (TempSlot > 100) + RoFSlot.MainSlot = int((TempSlot - 100) / 10); + + else + RoFSlot.MainSlot = 0; + + RoFSlot.SubSlot = TempSlot - (100 + RoFSlot.MainSlot); + } */ - - SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); } + + else if (ServerSlot >= EmuConstants::WORLD_BEGIN && ServerSlot <= EmuConstants::WORLD_END) { + RoFSlot.SlotType = maps::MapWorld; + TempSlot = ServerSlot - EmuConstants::WORLD_BEGIN; + RoFSlot.MainSlot = TempSlot; + } + + _log(NET__ERROR, "Convert Server Slot %i to RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i", ServerSlot, RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01); + + return RoFSlot; } - ss.write((const char*)&iqbs, sizeof(RoF::structs::ItemQuaternaryBodyStruct)); + static inline structs::MainInvItemSlotStruct ServerToRoFMainInvSlot(uint32 ServerSlot) + { + structs::MainInvItemSlotStruct RoFSlot; + RoFSlot.MainSlot = INVALID_INDEX; + RoFSlot.SubSlot = INVALID_INDEX; + RoFSlot.AugSlot = INVALID_INDEX; + RoFSlot.Unknown01 = NOT_USED; - for(int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + uint32 TempSlot = 0; - if(SubSerializations[x]) { + if (ServerSlot < 56 || ServerSlot == MainPowerSource) { // (< 52) + RoFSlot.MainSlot = ServerSlot; - ss.write((const char*)&x, sizeof(uint32)); + if (ServerSlot == MainPowerSource) + RoFSlot.MainSlot = slots::MainPowerSource; - ss.write(SubSerializations[x], SubLengths[x]); + else if (ServerSlot >= MainCursor) // Cursor and Extended Corpse Inventory + RoFSlot.MainSlot += 3; - safe_delete_array(SubSerializations[x]); + else if (ServerSlot >= MainAmmo) // Ammo and Personl Inventory + RoFSlot.MainSlot += 1; + + /*else if (ServerSlot >= MainCursor) { // Cursor + RoFSlot.MainSlot = slots::MainCursor; + + if (ServerSlot > 30) + RoFSlot.SubSlot = (ServerSlot + 3) - 33; + }*/ } + + else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) { + TempSlot = ServerSlot - 1; + RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 2; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 2) * EmuConstants::ITEM_CONTAINER_SIZE); + } + + _log(NET__ERROR, "Convert Server Slot %i to RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i", ServerSlot, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01); + + return RoFSlot; } - char* item_serial = new char[ss.tellp()]; - memset(item_serial, 0, ss.tellp()); - memcpy(item_serial, ss.str().c_str(), ss.tellp()); + static inline uint32 ServerToRoFCorpseSlot(uint32 ServerCorpse) + { + //uint32 RoFCorpse; + } - *length = ss.tellp(); - return item_serial; + static inline uint32 RoFToServerSlot(structs::ItemSlotStruct RoFSlot) + { + uint32 ServerSlot = INVALID_INDEX; + uint32 TempSlot = 0; + + if (RoFSlot.SlotType == maps::MapPossessions && RoFSlot.MainSlot < 57) { // Worn/Personal Inventory and Cursor (< 51) + if (RoFSlot.MainSlot == slots::MainPowerSource) + TempSlot = MainPowerSource; + + else if (RoFSlot.MainSlot >= slots::MainCursor) // Cursor and Extended Corpse Inventory + TempSlot = RoFSlot.MainSlot - 3; + + /*else if (RoFSlot.MainSlot == slots::MainGeneral9 || RoFSlot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF inventory/corpse slots + // Need to figure out what to do when we get these + + // The slot range of 0 - client_max is cross-utilized between player inventory and corpse inventory. + // In the case of RoF, player inventory is addressed as 0 - 33 and corpse inventory is addressed as 23 - 56. + // We 'could' assign the two new inventory slots as 9997 and 9998, and then work around their bag + // slot assignments, but doing so may disrupt our ability to utilize the corpse looting range properly. + + // For now, it's probably best to leave as-is and let this work itself out in the inventory rework. + }*/ + + else if (RoFSlot.MainSlot >= slots::MainAmmo) // Ammo and Main Inventory + TempSlot = RoFSlot.MainSlot - 1; + + else // Worn Slots + TempSlot = RoFSlot.MainSlot; + + if (RoFSlot.SubSlot >= SUB_BEGIN) // Bag Slots + TempSlot = ((TempSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; + + ServerSlot = TempSlot; + } + + else if (RoFSlot.SlotType == maps::MapBank) { + TempSlot = EmuConstants::BANK_BEGIN; + + if (RoFSlot.SubSlot >= SUB_BEGIN) + TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; + + else + TempSlot += RoFSlot.MainSlot; + + ServerSlot = TempSlot; + } + + else if (RoFSlot.SlotType == maps::MapSharedBank) { + TempSlot = EmuConstants::SHARED_BANK_BEGIN; + + if (RoFSlot.SubSlot >= SUB_BEGIN) + TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; + + else + TempSlot += RoFSlot.MainSlot; + + ServerSlot = TempSlot; + } + + else if (RoFSlot.SlotType == maps::MapTrade) { + TempSlot = EmuConstants::TRADE_BEGIN; + + if (RoFSlot.SubSlot >= SUB_BEGIN) + TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; + // OLD CODE: + //TempSlot += 100 + (RoFSlot.MainSlot * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot; + + else + TempSlot += RoFSlot.MainSlot; + + ServerSlot = TempSlot; + } + + else if (RoFSlot.SlotType == maps::MapWorld) { + TempSlot = EmuConstants::WORLD_BEGIN; + + if (RoFSlot.MainSlot >= SUB_BEGIN) + TempSlot += RoFSlot.MainSlot; + + ServerSlot = TempSlot; + } + + /*else if (RoFSlot.SlotType == maps::MapLimbo) { // Cursor Buffer + TempSlot = 31; + + if (RoFSlot.MainSlot >= 0) + TempSlot += RoFSlot.MainSlot; + + ServerSlot = TempSlot; + }*/ + + _log(NET__ERROR, "Convert RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, ServerSlot); + + return ServerSlot; + } + + static inline uint32 RoFToServerMainInvSlot(structs::MainInvItemSlotStruct RoFSlot) + { + uint32 ServerSlot = INVALID_INDEX; + uint32 TempSlot = 0; + + if (RoFSlot.MainSlot < 57) { // Worn/Personal Inventory and Cursor (< 33) + if (RoFSlot.MainSlot == slots::MainPowerSource) + TempSlot = MainPowerSource; + + else if (RoFSlot.MainSlot >= slots::MainCursor) // Cursor and Extended Corpse Inventory + TempSlot = RoFSlot.MainSlot - 3; + + /*else if (RoFSlot.MainSlot == slots::MainGeneral9 || RoFSlot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF inventory slots + // Need to figure out what to do when we get these + + // Same as above + }*/ + + else if (RoFSlot.MainSlot >= slots::MainAmmo) // Main Inventory and Ammo Slots + TempSlot = RoFSlot.MainSlot - 1; + + else + TempSlot = RoFSlot.MainSlot; + + if (RoFSlot.SubSlot >= SUB_BEGIN) // Bag Slots + TempSlot = ((TempSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; + + ServerSlot = TempSlot; + } + + _log(NET__ERROR, "Convert RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, ServerSlot); + + return ServerSlot; + } + + static inline uint32 RoFToServerCorpseSlot(uint32 RoFCorpse) + { + //uint32 ServerCorpse; + } } - -} //end namespace RoF +// end namespace RoF diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index db2f1f989..69ec9f1cb 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1,4 +1,3 @@ - #include "../debug.h" #include "sod.h" #include "../opcodemgr.h" @@ -16,991 +15,2274 @@ #include #include -namespace SoD { +namespace SoD +{ + static const char *name = "SoD"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "SoD"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); -char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline uint32 ServerToSoDSlot(uint32 ServerSlot); + static inline uint32 ServerToSoDCorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 SoDToServerSlot(uint32 SoDSlot); + static inline uint32 SoDToServerCorpseSlot(uint32 SoDCorpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "sod_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "sod_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClientSoD; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClientSoD; + } #include "ss_define.h" - -// Converts Server Slot IDs to SoD Slot IDs for use in Encodes -static inline uint32 ServerToSoDSlot(uint32 ServerSlot) { - uint32 SoDSlot = 0; - - if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots - SoDSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) - SoDSlot = ServerSlot + 11; - - else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) - SoDSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) - SoDSlot = ServerSlot + 1; - - else if (ServerSlot == MainPowerSource) - SoDSlot = slots::MainPowerSource; - - else - SoDSlot = ServerSlot; - - return SoDSlot; -} - -// Converts SoD Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 SoDToServerSlot(uint32 SoDSlot) { - uint32 ServerSlot = 0; - - if(SoDSlot >= slots::MainAmmo && SoDSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots - ServerSlot = SoDSlot - 1; - - else if(SoDSlot >= consts::GENERAL_BAGS_BEGIN && SoDSlot <= consts::CURSOR_BAG_END) - ServerSlot = SoDSlot - 11; - - else if(SoDSlot >= consts::BANK_BAGS_BEGIN && SoDSlot <= consts::BANK_BAGS_END) - ServerSlot = SoDSlot - 1; - - else if(SoDSlot >= consts::SHARED_BANK_BAGS_BEGIN && SoDSlot <= consts::SHARED_BANK_BAGS_END) - ServerSlot = SoDSlot - 1; - - else if(SoDSlot == slots::MainPowerSource) - ServerSlot = MainPowerSource; - - else - ServerSlot = SoDSlot; - - return ServerSlot; -} - -/* -// Converts Server Corpse Slot IDs to SoD Corpse Slot IDs for use in Encodes -static inline uint32 ServerToSoDCorpseSlot(uint32 ServerCorpse) { - uint32 SoDCorpse; - // reserved -} -*/ -/* -// Converts SoD Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 SoDToServerCorpseSlot(uint32 SoDCorpse) { - uint32 ServerCorpse; - // reserved -} -*/ - - -ENCODE(OP_OpenNewTasksWindow) { - - AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; - AvailableTaskData1_Struct* __emu_AvailableTaskData1; - AvailableTaskData2_Struct* __emu_AvailableTaskData2; - AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; - - structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; - structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; - structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; - structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; - - // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. - // - in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); - - in->pBuffer = new unsigned char[in->size]; - - unsigned char *__eq_buffer = in->pBuffer; - - __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; - - char *__eq_ptr, *__emu_Ptr; - - // Copy Header - // - // - - __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; - __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; - __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; - - __emu_Ptr = (char *) __emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); - __eq_ptr = (char *) __eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); - - for(uint32 i=0; i<__emu_AvailableTaskHeader->TaskCount; i++) { - - __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; - __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; - - __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; - // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen - // in Live packets. Changing it to 0x3f000000 makes the title red. - __eq_AvailableTaskData1->unknown1 = 0x3f800000; - __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; - __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; - - __emu_Ptr += sizeof(AvailableTaskData1_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Title - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Description - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __eq_ptr[0] = 0; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; - __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; - - __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; - __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; - __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; - __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; - - __emu_Ptr += sizeof(AvailableTaskData2_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; - __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; - - __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; - __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; - __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; - __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; - - __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); - __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_VAR_ENCODE(CharacterSelect_Struct); - - - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; - - int char_count; - int namelen = 0; - for(char_count = 0; char_count < 10; char_count++) { - if(emu->name[char_count][0] == '\0') - break; - if(strcmp(emu->name[char_count], "") == 0) - break; - namelen += strlen(emu->name[char_count]); - } - - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; - - ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); - - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; - - eq->char_count = char_count; - eq->total_chars = 10; - - unsigned char *bufptr = (unsigned char *) eq->entries; - int r; - for(r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->level = emu->level[r]; - eq2->hairstyle = emu->hairstyle[r]; - eq2->gender = emu->gender[r]; - memcpy(eq2->name, emu->name[r], strlen(emu->name[r])+1); - } - //adjust for name. - bufptr += strlen(emu->name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->beard = emu->beard[r]; - eq2->haircolor = emu->haircolor[r]; - eq2->face = emu->face[r]; - int k; - for(k = 0; k < _MaterialCount; k++) { - eq2->equip[k].equip0 = emu->equip[r][k]; - eq2->equip[k].equip1 = 0; - eq2->equip[k].itemid = 0; - eq2->equip[k].color.color = emu->cs_colors[r][k].color; - } - eq2->primary = emu->primary[r]; - eq2->secondary = emu->secondary[r]; - eq2->tutorial = emu->tutorial[r]; // was u15 - eq2->u15 = 0xff; - eq2->deity = emu->deity[r]; - eq2->zone = emu->zone[r]; - eq2->u19 = 0xFF; - eq2->race = emu->race[r]; - eq2->gohome = emu->gohome[r]; - eq2->class_ = emu->class_[r]; - eq2->eyecolor1 = emu->eyecolor1[r]; - eq2->beardcolor = emu->beardcolor[r]; - eq2->eyecolor2 = emu->eyecolor2[r]; - eq2->drakkin_heritage = emu->drakkin_heritage[r]; - eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; - eq2->drakkin_details = emu->drakkin_details[r]; - } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); - } - - FINISH_ENCODE(); - -} - -ENCODE(OP_ZoneServerInfo) { - SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); - OUT_str(ip); - OUT(port); - FINISH_ENCODE(); - - //this is SUCH bullshit to be doing from down here. but the - // new client requires us to close immediately following this - // packet, so do it. - //dest->Close(); -} - -//hack hack hack -ENCODE(OP_SendZonepoints) { - ENCODE_LENGTH_ATLEAST(ZonePoints); - - SETUP_VAR_ENCODE(ZonePoints); - ALLOC_VAR_ENCODE(structs::ZonePoints, __packet->size); - - memcpy(eq, emu, __packet->size); - - FINISH_ENCODE(); -// unknown0xxx[24]; - //this is utter crap... the client is waiting for this - //certain 0 length opcode to come after the reqclientspawn - //stuff... so this is a dirty way to put it in there. - // this needs to be done better - - //EQApplicationPacket hack_test(OP_PetitionUnCheckout, 0); - //dest->QueuePacket(&hack_test); - -} - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for SoF - // clientver 1 is for all clients and 5 is for SoD - if (emu->clientver <= 5 ) +// ENCODE methods + ENCODE(OP_Action) { - OUT(id); - eq->unknown004 = 1; - //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->title_sid = emu->id - emu->current_level + 1; - //eq->desc_sid = emu->id - emu->current_level + 1; - eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->sof_next_skill); - eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->sof_next_skill); - eq->title_sid = emu->sof_next_skill; - eq->desc_sid = emu->sof_next_skill; - OUT(class_type); + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); + + OUT(target); + OUT(source); + OUT(level); + OUT(instrument_mod); + eq->sequence = emu->sequence; + OUT(type); + //OUT(damage); + OUT(spell); + eq->level2 = emu->level; + OUT(buff_unknown); // if this is 4, a buff icon is made + //eq->unknown0036 = -1; + //eq->unknown0040 = -1; + //eq->unknown0044 = -1; + + FINISH_ENCODE(); + } + + ENCODE(OP_AdventureMerchantSell) + { + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToSoDSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); + } + + ENCODE(OP_AltCurrencySell) + { + ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); + SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); + + OUT(merchant_entity_id); + eq->slot_id = ServerToSoDSlot(emu->slot_id); + OUT(charges); OUT(cost); - OUT(seq); - OUT(current_level); - OUT(prereq_skill); - OUT(prereq_minpoints); - eq->type = emu->sof_type; - OUT(spellid); - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - //eq->max_level = emu->sof_max_level; - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - eq->aa_expansion = emu->aa_expansion; - eq->special_category = emu->special_category; - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - -ENCODE(OP_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); - - uint32 r; - - eq->available_slots=0xffffffff; - memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); - memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); - -// OUT(checksum); - OUT(gender); - OUT(race); - OUT(class_); -// OUT(unknown00016); - OUT(level); - eq->level1 = emu->level; -// OUT(unknown00022[2]); - for(r = 0; r < 5; r++) { - OUT(binds[r].zoneId); - OUT(binds[r].x); - OUT(binds[r].y); - OUT(binds[r].z); - OUT(binds[r].heading); - } - OUT(deity); - OUT(intoxication); - OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); - OUT(abilitySlotRefresh); - OUT(points); // Relocation Test -// OUT(unknown0166[4]); - OUT(haircolor); - OUT(beardcolor); - OUT(eyecolor1); - OUT(eyecolor2); - OUT(hairstyle); - OUT(beard); -// OUT(unknown00178[10]); - for(r = 0; r < 9; r++) { - eq->equipment[r].equip0 = emu->item_material[r]; - eq->equipment[r].equip1 = 0; - eq->equipment[r].itemId = 0; - //eq->colors[r].color = emu->colors[r].color; - } - for(r = 0; r < 7; r++) { - OUT(item_tint[r].color); - } -// OUT(unknown00224[48]); - //NOTE: new client supports 300 AAs, our internal rep/PP - //only supports 240.. - for(r = 0; r < MAX_PP_AA_ARRAY; r++) { - OUT(aa_array[r].AA); - OUT(aa_array[r].value); - } -// OUT(unknown02220[4]); - OUT(mana); - OUT(cur_hp); - OUT(STR); - OUT(STA); - OUT(CHA); - OUT(AGI); - OUT(INT); - OUT(DEX); - OUT(WIS); - OUT(face); -// OUT(unknown02264[47]); - OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); -// OUT(unknown4184[128]); - OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); -// OUT(unknown04396[32]); - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - OUT(platinum_cursor); - OUT(gold_cursor); - OUT(silver_cursor); - OUT(copper_cursor); - - OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) - -// OUT(unknown04760[236]); - OUT(toxicity); - OUT(thirst_level); - OUT(hunger_level); - for(r = 0; r < structs::BUFF_COUNT; r++) { - OUT(buffs[r].slotid); - OUT(buffs[r].level); - OUT(buffs[r].bard_modifier); - OUT(buffs[r].effect); - OUT(buffs[r].spellid); - OUT(buffs[r].duration); - OUT(buffs[r].counters); - OUT(buffs[r].player_id); - } - for(r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { - OUT(disciplines.values[r]); - } - OUT_array(recastTimers, structs::MAX_RECAST_TYPES); -// OUT(unknown08124[360]); - OUT(endurance); - OUT(aapoints_spent); - OUT(aapoints); -// OUT(unknown06160[4]); - //NOTE: new client supports 20 bandoliers, our internal rep - //only supports 4.. - for(r = 0; r < 4; r++) { - OUT_str(bandoliers[r].name); - uint32 k; - for(k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { - OUT(bandoliers[r].items[k].item_id); - OUT(bandoliers[r].items[k].icon); - OUT_str(bandoliers[r].items[k].item_name); - } - } -// OUT(unknown07444[5120]); - for(r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { - OUT(potionbelt.items[r].item_id); - OUT(potionbelt.items[r].icon); - OUT_str(potionbelt.items[r].item_name); - } -// OUT(unknown12852[8]); -// OUT(unknown12864[76]); - OUT_str(name); - OUT_str(last_name); - OUT(guild_id); - OUT(birthday); - OUT(lastlogin); - OUT(timePlayedMin); - OUT(pvp); - OUT(anon); - OUT(gm); - OUT(guildrank); - OUT(guildbanker); -// OUT(unknown13054[12]); - OUT(exp); -// OUT(unknown13072[8]); - OUT(timeentitledonaccount); - OUT_array(languages, structs::MAX_PP_LANGUAGE); -// OUT(unknown13109[7]); - OUT(y); //reversed x and y - OUT(x); - OUT(z); - OUT(heading); -// OUT(unknown13132[4]); - OUT(platinum_bank); - OUT(gold_bank); - OUT(silver_bank); - OUT(copper_bank); - OUT(platinum_shared); -// OUT(unknown13156[84]); - //OUT(expansions); - eq->expansions = 16383; -// OUT(unknown13244[12]); - OUT(autosplit); -// OUT(unknown13260[16]); - OUT(zone_id); - OUT(zoneInstance); - for(r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { - OUT_str(groupMembers[r]); - } - strcpy(eq->groupLeader, emu->groupMembers[0]); -// OUT_str(groupLeader); -// OUT(unknown13728[660]); - OUT(entityid); - OUT(leadAAActive); -// OUT(unknown14392[4]); - OUT(ldon_points_guk); - OUT(ldon_points_mir); - OUT(ldon_points_mmc); - OUT(ldon_points_ruj); - OUT(ldon_points_tak); - OUT(ldon_points_available); -// OUT(unknown14420[132]); - OUT(tribute_time_remaining); - OUT(career_tribute_points); -// OUT(unknown7208); - OUT(tribute_points); -// OUT(unknown7216); - OUT(tribute_active); - for(r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { - OUT(tributes[r].tribute); - OUT(tributes[r].tier); - } -// OUT(unknown14616[8]); - OUT(group_leadership_exp); -// OUT(unknown14628); - OUT(raid_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_points); - OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); -// OUT(unknown14772[128]); - OUT(air_remaining); - OUT(PVPKills); - OUT(PVPDeaths); - OUT(PVPCurrentPoints); - OUT(PVPCareerPoints); - OUT(PVPBestKillStreak); - OUT(PVPWorstDeathStreak); - OUT(PVPCurrentKillStreak); -// OUT(unknown17892[4580]); - OUT(expAA); -// OUT(unknown19516[40]); - OUT(currentRadCrystals); - OUT(careerRadCrystals); - OUT(currentEbonCrystals); - OUT(careerEbonCrystals); - OUT(groupAutoconsent); - OUT(raidAutoconsent); - OUT(guildAutoconsent); -// OUT(unknown19575[5]); - eq->level3 = emu->level; - eq->showhelm = emu->showhelm; - OUT(RestTimer); -// OUT(unknown19584[4]); -// OUT(unknown19588); - - -const uint8 bytes[] = { -0xa3,0x02,0x00,0x00,0x95,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x00,0x00,0x00, -0x19,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, -0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1F,0x85,0xEB,0x3E,0x33,0x33,0x33,0x3F, -0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; - - memcpy(eq->unknown12864, bytes, sizeof(bytes)); - - - - //set the checksum... - CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct)-4); - - FINISH_ENCODE(); -} - -ENCODE(OP_NewZone) { - SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); - OUT_str(char_name); - OUT_str(zone_short_name); - OUT_str(zone_long_name); - OUT(ztype); - int r; - for(r = 0; r < 4; r++) { - OUT(fog_red[r]); - OUT(fog_green[r]); - OUT(fog_blue[r]); - OUT(fog_minclip[r]); - OUT(fog_maxclip[r]); - } - OUT(gravity); - OUT(time_type); - for(r = 0; r < 4; r++) { - OUT(rain_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(rain_duration[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_duration[r]); - } - for(r = 0; r < 32; r++) { - eq->unknown537[r] = 0xFF; //observed - } - OUT(sky); - OUT(zone_exp_multiplier); - OUT(safe_y); - OUT(safe_x); - OUT(safe_z); - OUT(max_z); - OUT(underworld); - OUT(minclip); - OUT(maxclip); - OUT_str(zone_short_name2); - OUT(zone_id); - OUT(zone_instance); - OUT(SuspendBuffs); - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown800 = -1; - eq->unknown844 = 600; - eq->unknown880 = 50; - eq->unknown884 = 10; - eq->unknown888 = 1; - eq->unknown889 = 0; - eq->unknown890 = 1; - eq->unknown891 = 0; - eq->unknown892 = 0; - eq->unknown893 = 0; - eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off - eq->unknown895 = 0; - eq->unknown896 = 180; - eq->unknown900 = 180; - eq->unknown904 = 180; - eq->unknown908 = 2; - eq->unknown912 = 2; - eq->FogDensity = emu->fog_density; - - FINISH_ENCODE(); -} - - -ENCODE(OP_Track) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + ENCODE(OP_ApplyPoison) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); - delete in; - return; + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToSoDSlot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); } - int PacketSize = 2; - - for(int i = 0; i < EntryCount; ++i, ++emu) - PacketSize += (12 + strlen(emu->name)); - - emu = (Track_Struct *) __emu_buffer; - - in->size = PacketSize; - in->pBuffer = new unsigned char[in->size]; - - char *Buffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); - - for(int i = 0; i < EntryCount; ++i, ++emu) + ENCODE(OP_Barter) { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); - VARSTRUCT_ENCODE_STRING(Buffer, emu->name); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); - } + EQApplicationPacket *in = *p; + *p = nullptr; - delete[] __emu_buffer; + char *Buffer = (char *)in->pBuffer; + uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_PetBuffWindow) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - PetBuff_Struct *emu = (PetBuff_Struct *) __emu_buffer; - - int PacketSize = 7 + (emu->buffcount * 13); - - in->size = PacketSize; - - in->pBuffer = new unsigned char[in->size]; - - char *Buffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount); - - for(unsigned int i = 0; i < BUFF_COUNT; ++i) - { - if(emu->spellid[i]) + if (SubAction != Barter_BuyerAppearance) { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff. + dest->FastQueuePacket(&in, ack_req); + + return; } - } - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); - delete[] __emu_buffer; + unsigned char *__emu_buffer = in->pBuffer; + in->size = 80; + in->pBuffer = new unsigned char[in->size]; + char *OutBuffer = (char *)in->pBuffer; + char Name[64]; - dest->FastQueuePacket(&in, ack_req); -} + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); + uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); + uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + VARSTRUCT_DECODE_STRING(Name, Buffer); + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + OutBuffer = (char *)in->pBuffer + 72; + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); -ENCODE(OP_Barter) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - - if(SubAction != Barter_BuyerAppearance) - { + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); - - return; } - unsigned char *__emu_buffer = in->pBuffer; - - in->size = 80; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - char Name[64]; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); - uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); - uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - VARSTRUCT_DECODE_STRING(Name, Buffer); - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - OutBuffer = (char *)in->pBuffer + 72; - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - -} - -ENCODE(OP_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - FINISH_ENCODE(); -} - -DECODE(OP_InspectRequest) { - DECODE_LENGTH_EXACT(structs::Inspect_Struct); - SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); - IN(TargetID); - IN(PlayerID); - FINISH_DIRECT_DECODE(); -} - -ENCODE(OP_BazaarSearch) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if(SubAction != BazaarSearchResults) + ENCODE(OP_BazaarSearch) { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if (SubAction != BazaarSearchResults) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); + + if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + + in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); + in->pBuffer = new unsigned char[in->size]; + memset(in->pBuffer, 0, in->size); + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++emu, ++eq) + { + OUT(Beginning.Action); + OUT(SellerID); + memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); + OUT(NumItems); + OUT(ItemID); + OUT(SerialNumber); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(Cost); + OUT(ItemStat); + } + + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); - - return; } - unsigned char *__emu_buffer = in->pBuffer; - - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); - - if(EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + ENCODE(OP_Buff) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; + ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); + SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); + + OUT(entityid); + OUT(slot); + OUT(level); + OUT(effect); + //eq->unknown7 = 10; + OUT(spellid); + OUT(duration); + OUT(slotid); + OUT(bufffade); + + FINISH_ENCODE(); } - in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); - in->pBuffer = new unsigned char[in->size]; - - memset(in->pBuffer, 0, in->size); - - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++emu, ++eq) + ENCODE(OP_CancelTrade) { - OUT(Beginning.Action); - OUT(SellerID); - memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); - OUT(NumItems); - OUT(ItemID); - OUT(SerialNumber); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(Cost); - OUT(ItemStat); + ENCODE_LENGTH_EXACT(CancelTrade_Struct); + SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); + + OUT(fromid); + OUT(action); + + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_CharInventory) + { + //consume the packet + EQApplicationPacket *in = *p; - dest->FastQueuePacket(&in, ack_req); -} + *p = nullptr; -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) { + if (in->size == 0) { + + in->size = 4; + + in->pBuffer = new uchar[in->size]; + + *((uint32 *)in->pBuffer) = 0; + + dest->FastQueuePacket(&in, ack_req); + + return; + } + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + + int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); + + if (ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + + delete in; + return; + } + + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; + in->pBuffer = new uchar[4]; + *(uint32 *)in->pBuffer = ItemCount; + in->size = 4; + + for (int r = 0; r < ItemCount; r++, eq++) { + + uint32 Length = 0; + char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); + + if (Serialized) { + + uchar *OldBuffer = in->pBuffer; + in->pBuffer = new uchar[in->size + Length]; + memcpy(in->pBuffer, OldBuffer, in->size); + + safe_delete_array(OldBuffer); + + memcpy(in->pBuffer + in->size, Serialized, Length); + in->size += Length; + + safe_delete_array(Serialized); + + } + else { + _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } + } + + delete[] __emu_buffer; + + //_log(NET__ERROR, "Sending inventory to client"); + //_hex(NET__ERROR, in->pBuffer, in->size); + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ClientUpdate) + { + ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); + SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); + + OUT(spawn_id); + OUT(x_pos); + OUT(delta_x); + OUT(delta_y); + OUT(z_pos); + OUT(delta_heading); + OUT(y_pos); + OUT(delta_z); + OUT(animation); + OUT(heading); + + FINISH_ENCODE(); + } + + ENCODE(OP_Consider) + { + ENCODE_LENGTH_EXACT(Consider_Struct); + SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); + + OUT(playerid); + OUT(targetid); + OUT(faction); + OUT(level); + OUT(pvpcon); + + FINISH_ENCODE(); + } + + ENCODE(OP_Damage) + { + ENCODE_LENGTH_EXACT(CombatDamage_Struct); + SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + OUT(target); + OUT(source); + OUT(type); + OUT(spellid); + OUT(damage); + eq->sequence = emu->sequence; + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToSoDSlot(emu->from_slot); + eq->to_slot = ServerToSoDSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzCompass) + { + SETUP_VAR_ENCODE(ExpeditionCompass_Struct); + ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); + + OUT(count); + + for (uint32 i = 0; i < emu->count; ++i) + { + OUT(entries[i].x); + OUT(entries[i].y); + OUT(entries[i].z); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionEndsWarning) + { + ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); + SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); + + OUT(minutes_remaining); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionInfo) + { + ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + + OUT(max_players); + eq->unknown004 = 785316192; + eq->unknown008 = 435601; + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->leader_name, emu->leader_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionList) + { + SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); + ss.write((const char*)&null_term, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzJoinExpeditionConfirm) + { + ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); + SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); + + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->player_name, emu->player_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzLeaderStatus) + { + SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->leader_name, strlen(emu->leader_name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//1 + ss.write((const char*)&client_id, sizeof(uint32)); + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzMemberList) + { + SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].status, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpansionInfo) + { + ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + + OUT(Expansions); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroundSpawn) + { + ENCODE_LENGTH_EXACT(Object_Struct); + SETUP_DIRECT_ENCODE(Object_Struct, structs::Object_Struct); + + OUT(drop_id); + OUT(zone_id); + OUT(zone_instance); + OUT(heading); + OUT(x); + OUT(y); + OUT(z); + OUT_str(object_name); + OUT(object_type); + OUT(spawn_id); + + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown020 = 0; + eq->unknown024 = 0; + eq->size = 1; //This forces all objects to standard size for now + eq->unknown088 = 0; + memset(eq->unknown096, 0xFF, sizeof(eq->unknown096)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupCancelInvite) + { + ENCODE_LENGTH_EXACT(GroupCancel_Struct); + SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + OUT(toggle); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow2) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupInvite) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); + memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupUpdate) + { + //_log(NET__ERROR, "OP_GroupUpdate"); + EQApplicationPacket *in = *p; + GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; + + //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); + if ((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) + { + if ((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) + { + //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); + dest->FastQueuePacket(&outapp); + + // Make an empty GLAA packet to clear out their useable GLAAs + // + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + dest->FastQueuePacket(&outapp); + delete in; + return; + } + //if(gjs->action == groupActLeave) + // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + delete in; + return; + } + + if (in->size == sizeof(GroupUpdate2_Struct)) + { + // Group Update2 + //_log(NET__ERROR, "Struct is GroupUpdate2"); + + unsigned char *__emu_buffer = in->pBuffer; + GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*)__emu_buffer; + + //_log(NET__ERROR, "Yourname is %s", gu2->yourname); + + int MemberCount = 1; + int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; + + for (int i = 0; i < 5; ++i) + { + //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); + if (gu2->membername[i][0] != '\0') + { + PacketLength += (22 + strlen(gu2->membername[i]) + 1); + ++MemberCount; + } + } + + //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); + char *Buffer = (char *)outapp->pBuffer; + + // Header + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); + + // Leader + // + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + + int MemberNumber = 1; + + for (int i = 0; i < 5; ++i) + { + if (gu2->membername[i][0] == '\0') + continue; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = gu2->NPCMarkerID; + memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); + + dest->FastQueuePacket(&outapp); + delete in; + return; + } + + //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + ENCODE_LENGTH_EXACT(GroupJoin_Struct); + SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); + + memcpy(eq->membername, emu->membername, sizeof(eq->membername)); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = emu->NPCMarkerID; + + memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); + //_hex(NET__ERROR, __packet->pBuffer, __packet->size); + + FINISH_ENCODE(); + + dest->FastQueuePacket(&outapp); + } + + ENCODE(OP_GuildMemberList) + { //consume the packet EQApplicationPacket *in = *p; *p = nullptr; //store away the emu struct unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count*sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header + emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data + ); + const char *emu_note = (emu_name + + emu->name_length + //skip name contents + emu->count //skip string terminators + ); + + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { + + //the order we set things here must match the struct + + //nice helper macro + /*#define SlideStructString(field, str) \ + strcpy(e->field, str.c_str()); \ + e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ +#define SlideStructString(field, str) \ + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) + + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + PutFieldN(rank); + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + PutFieldN(total_tribute); + PutFieldN(last_tribute); + e->unknown_one = htonl(1); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); +#undef SlideStructString +#undef PutFieldN + + e++; + } + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + OUT(race); + OUT(unknown006[0]); + OUT(unknown006[1]); + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + OUT(drakkin_heritage); + OUT(drakkin_tattoo); + OUT(drakkin_details); + + FINISH_ENCODE(); + } + + ENCODE(OP_InspectRequest) + { + ENCODE_LENGTH_EXACT(Inspect_Struct); + SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); + + OUT(TargetID); + OUT(PlayerID); + + FINISH_ENCODE(); + } + + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } + + ENCODE(OP_ItemPacket) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); + + uint32 length; + char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); + + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 4; + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length); + + delete[] __emu_buffer; + safe_delete_array(serialized); + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ItemVerifyReply) + { + ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); + SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); + + eq->slot = ServerToSoDSlot(emu->slot); + OUT(spell); + OUT(target); + + FINISH_ENCODE(); + } + + ENCODE(OP_LeadershipExpUpdate) + { + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LogServer) + { + ENCODE_LENGTH_EXACT(LogServer_Struct); + SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); + + strcpy(eq->worldshortname, emu->worldshortname); + + OUT(enablevoicemacros); + OUT(enablemail); + OUT(enable_pvp); + OUT(enable_FV); + + // These next two need to be set like this for the Tutorial Button to work. + eq->unknown263[0] = 0; + eq->unknown263[2] = 1; + + FINISH_ENCODE(); + } + + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = emu->slot_id + 1; + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_ManaChange) + { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); + + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? + + FINISH_ENCODE(); + } + + ENCODE(OP_MercenaryDataResponse) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + + int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; + PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - 4) * emu->MercCount; + + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); + for (r = 0; r < emu->MercTypeCount; r++) + { + if (emu->MercTypeCount > 0) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); + } + } + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + if (emu->MercCount) + { + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); + if (emu->Mercs[r].StanceCount > 0) + { + for (k = 0; k < emu->Mercs[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); + } + } + } + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + // This packet does not appear to exist in SoD, but leaving it here just in case + ENCODE(OP_MercenaryDataUpdate) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + EQApplicationPacket *outapp; + uint32 PacketSize = 0; + + // There are 2 different sized versions of this packet depending if a merc is hired or not + if (emu->MercStatus >= 0) + { + PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; + } + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); + for (k = 0; k < emu->MercData[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); + } + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk05); + } + } + else + { + PacketSize += sizeof(structs::NoMercenaryHired_Struct); + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToSoDSlot(emu->from_slot); + eq->to_slot = ServerToSoDSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_NewZone) + { + SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); + + OUT_str(char_name); + OUT_str(zone_short_name); + OUT_str(zone_long_name); + OUT(ztype); + int r; + for (r = 0; r < 4; r++) { + OUT(fog_red[r]); + OUT(fog_green[r]); + OUT(fog_blue[r]); + OUT(fog_minclip[r]); + OUT(fog_maxclip[r]); + } + OUT(gravity); + OUT(time_type); + for (r = 0; r < 4; r++) { + OUT(rain_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(rain_duration[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_duration[r]); + } + for (r = 0; r < 32; r++) { + eq->unknown537[r] = 0xFF; //observed + } + OUT(sky); + OUT(zone_exp_multiplier); + OUT(safe_y); + OUT(safe_x); + OUT(safe_z); + OUT(max_z); + OUT(underworld); + OUT(minclip); + OUT(maxclip); + OUT_str(zone_short_name2); + OUT(zone_id); + OUT(zone_instance); + OUT(SuspendBuffs); + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown800 = -1; + eq->unknown844 = 600; + eq->unknown880 = 50; + eq->unknown884 = 10; + eq->unknown888 = 1; + eq->unknown889 = 0; + eq->unknown890 = 1; + eq->unknown891 = 0; + eq->unknown892 = 0; + eq->unknown893 = 0; + eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off + eq->unknown895 = 0; + eq->unknown896 = 180; + eq->unknown900 = 180; + eq->unknown904 = 180; + eq->unknown908 = 2; + eq->unknown912 = 2; + eq->FogDensity = emu->fog_density; + + FINISH_ENCODE(); + } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + memcpy(eq->Title, emu->Title, sizeof(eq->Title)); + memcpy(eq->Text, emu->Text, sizeof(eq->Text)); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + OUT(NegativeID); + // These two field names are used if Buttons == 1. + OUT_str(ButtonName0); + OUT_str(ButtonName1); + + FINISH_ENCODE(); + } + + ENCODE(OP_OpenNewTasksWindow) + { + AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; + AvailableTaskData1_Struct* __emu_AvailableTaskData1; + AvailableTaskData2_Struct* __emu_AvailableTaskData2; + AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; + + structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; + structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; + structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; + structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; + + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + + __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; + + // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. + // + in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); + in->pBuffer = new unsigned char[in->size]; + unsigned char *__eq_buffer = in->pBuffer; + __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; + char *__eq_ptr, *__emu_Ptr; + + // Copy Header + // + // + + __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; + __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; + __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; + + __emu_Ptr = (char *)__emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); + __eq_ptr = (char *)__eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); + + for (uint32 i = 0; i<__emu_AvailableTaskHeader->TaskCount; i++) { + + __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; + __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; + + __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; + // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen + // in Live packets. Changing it to 0x3f000000 makes the title red. + __eq_AvailableTaskData1->unknown1 = 0x3f800000; + __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; + __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; + + __emu_Ptr += sizeof(AvailableTaskData1_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Title + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Description + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __eq_ptr[0] = 0; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; + __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; + + __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; + __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; + __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; + __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; + + __emu_Ptr += sizeof(AvailableTaskData2_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; + __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; + + __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; + __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; + __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; + __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; + + __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); + __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_PetBuffWindow) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + PetBuff_Struct *emu = (PetBuff_Struct *)__emu_buffer; + int PacketSize = 7 + (emu->buffcount * 13); + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + char *Buffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount); + + for (unsigned int i = 0; i < BUFF_COUNT; ++i) + { + if (emu->spellid[i]) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff. + } + } + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_PlayerProfile) + { + SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); + + uint32 r; + + eq->available_slots = 0xffffffff; + memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); + memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); + + // OUT(checksum); + OUT(gender); + OUT(race); + OUT(class_); + // OUT(unknown00016); + OUT(level); + eq->level1 = emu->level; + // OUT(unknown00022[2]); + for (r = 0; r < 5; r++) { + OUT(binds[r].zoneId); + OUT(binds[r].x); + OUT(binds[r].y); + OUT(binds[r].z); + OUT(binds[r].heading); + } + OUT(deity); + OUT(intoxication); + OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); + OUT(abilitySlotRefresh); + OUT(points); // Relocation Test + // OUT(unknown0166[4]); + OUT(haircolor); + OUT(beardcolor); + OUT(eyecolor1); + OUT(eyecolor2); + OUT(hairstyle); + OUT(beard); + // OUT(unknown00178[10]); + for (r = 0; r < 9; r++) { + eq->equipment[r].equip0 = emu->item_material[r]; + eq->equipment[r].equip1 = 0; + eq->equipment[r].itemId = 0; + //eq->colors[r].color = emu->colors[r].color; + } + for (r = 0; r < 7; r++) { + OUT(item_tint[r].color); + } + // OUT(unknown00224[48]); + //NOTE: new client supports 300 AAs, our internal rep/PP + //only supports 240.. + for (r = 0; r < MAX_PP_AA_ARRAY; r++) { + OUT(aa_array[r].AA); + OUT(aa_array[r].value); + } + // OUT(unknown02220[4]); + OUT(mana); + OUT(cur_hp); + OUT(STR); + OUT(STA); + OUT(CHA); + OUT(AGI); + OUT(INT); + OUT(DEX); + OUT(WIS); + OUT(face); + // OUT(unknown02264[47]); + OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); + // OUT(unknown4184[128]); + OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); + // OUT(unknown04396[32]); + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + OUT(platinum_cursor); + OUT(gold_cursor); + OUT(silver_cursor); + OUT(copper_cursor); + + OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) + + // OUT(unknown04760[236]); + OUT(toxicity); + OUT(thirst_level); + OUT(hunger_level); + for (r = 0; r < structs::BUFF_COUNT; r++) { + OUT(buffs[r].slotid); + OUT(buffs[r].level); + OUT(buffs[r].bard_modifier); + OUT(buffs[r].effect); + OUT(buffs[r].spellid); + OUT(buffs[r].duration); + OUT(buffs[r].counters); + OUT(buffs[r].player_id); + } + for (r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { + OUT(disciplines.values[r]); + } + OUT_array(recastTimers, structs::MAX_RECAST_TYPES); + // OUT(unknown08124[360]); + OUT(endurance); + OUT(aapoints_spent); + OUT(aapoints); + // OUT(unknown06160[4]); + //NOTE: new client supports 20 bandoliers, our internal rep + //only supports 4.. + for (r = 0; r < 4; r++) { + OUT_str(bandoliers[r].name); + uint32 k; + for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { + OUT(bandoliers[r].items[k].item_id); + OUT(bandoliers[r].items[k].icon); + OUT_str(bandoliers[r].items[k].item_name); + } + } + // OUT(unknown07444[5120]); + for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { + OUT(potionbelt.items[r].item_id); + OUT(potionbelt.items[r].icon); + OUT_str(potionbelt.items[r].item_name); + } + // OUT(unknown12852[8]); + // OUT(unknown12864[76]); + OUT_str(name); + OUT_str(last_name); + OUT(guild_id); + OUT(birthday); + OUT(lastlogin); + OUT(timePlayedMin); + OUT(pvp); + OUT(anon); + OUT(gm); + OUT(guildrank); + OUT(guildbanker); + // OUT(unknown13054[12]); + OUT(exp); + // OUT(unknown13072[8]); + OUT(timeentitledonaccount); + OUT_array(languages, structs::MAX_PP_LANGUAGE); + // OUT(unknown13109[7]); + OUT(y); //reversed x and y + OUT(x); + OUT(z); + OUT(heading); + // OUT(unknown13132[4]); + OUT(platinum_bank); + OUT(gold_bank); + OUT(silver_bank); + OUT(copper_bank); + OUT(platinum_shared); + // OUT(unknown13156[84]); + //OUT(expansions); + eq->expansions = 16383; + // OUT(unknown13244[12]); + OUT(autosplit); + // OUT(unknown13260[16]); + OUT(zone_id); + OUT(zoneInstance); + for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { + OUT_str(groupMembers[r]); + } + strcpy(eq->groupLeader, emu->groupMembers[0]); + // OUT_str(groupLeader); + // OUT(unknown13728[660]); + OUT(entityid); + OUT(leadAAActive); + // OUT(unknown14392[4]); + OUT(ldon_points_guk); + OUT(ldon_points_mir); + OUT(ldon_points_mmc); + OUT(ldon_points_ruj); + OUT(ldon_points_tak); + OUT(ldon_points_available); + // OUT(unknown14420[132]); + OUT(tribute_time_remaining); + OUT(career_tribute_points); + // OUT(unknown7208); + OUT(tribute_points); + // OUT(unknown7216); + OUT(tribute_active); + for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { + OUT(tributes[r].tribute); + OUT(tributes[r].tier); + } + // OUT(unknown14616[8]); + OUT(group_leadership_exp); + // OUT(unknown14628); + OUT(raid_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_points); + OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); + // OUT(unknown14772[128]); + OUT(air_remaining); + OUT(PVPKills); + OUT(PVPDeaths); + OUT(PVPCurrentPoints); + OUT(PVPCareerPoints); + OUT(PVPBestKillStreak); + OUT(PVPWorstDeathStreak); + OUT(PVPCurrentKillStreak); + // OUT(unknown17892[4580]); + OUT(expAA); + // OUT(unknown19516[40]); + OUT(currentRadCrystals); + OUT(careerRadCrystals); + OUT(currentEbonCrystals); + OUT(careerEbonCrystals); + OUT(groupAutoconsent); + OUT(raidAutoconsent); + OUT(guildAutoconsent); + // OUT(unknown19575[5]); + eq->level3 = emu->level; + eq->showhelm = emu->showhelm; + OUT(RestTimer); + // OUT(unknown19584[4]); + // OUT(unknown19588); + + const uint8 bytes[] = { + 0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + memcpy(eq->unknown12864, bytes, sizeof(bytes)); + + //set the checksum... + CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); + + FINISH_ENCODE(); + } + + ENCODE(OP_RaidJoin) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; + + general->action = 8; + general->parameter = 1; + strn0cpy(general->leader_name, raid_create->leader_name, 64); + strn0cpy(general->player_name, raid_create->leader_name, 64); + + dest->FastQueuePacket(&outapp_create); + delete[] __emu_buffer; + } + + ENCODE(OP_RaidUpdate) + { + EQApplicationPacket *inapp = *p; + *p = nullptr; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; + + if (raid_gen->action == 0) // raid add has longer length than other raid updates + { + RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); + structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + + add_member->raidGen.action = in_add_member->raidGen.action; + add_member->raidGen.parameter = in_add_member->raidGen.parameter; + strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); + strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); + add_member->_class = in_add_member->_class; + add_member->level = in_add_member->level; + add_member->isGroupLeader = in_add_member->isGroupLeader; + add_member->flags[0] = in_add_member->flags[0]; + add_member->flags[1] = in_add_member->flags[1]; + add_member->flags[2] = in_add_member->flags[2]; + add_member->flags[3] = in_add_member->flags[3]; + add_member->flags[4] = in_add_member->flags[4]; + dest->FastQueuePacket(&outapp); + } + else + { + RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); + strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); + raid_general->action = in_raid_general->action; + raid_general->parameter = in_raid_general->parameter; + dest->FastQueuePacket(&outapp); + } + + delete[] __emu_buffer; + } + + ENCODE(OP_ReadBook) + { + ENCODE_LENGTH_ATLEAST(BookText_Struct); + SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); + + if (emu->window == 0xFF) + eq->window = 0xFFFFFFFF; + else + eq->window = emu->window; + OUT(type); + eq->invslot = ServerToSoDSlot(emu->invslot); + strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for SoF + // clientver 1 is for all clients and 5 is for SoD + if (emu->clientver <= 5) + { + OUT(id); + eq->unknown004 = 1; + //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->title_sid = emu->id - emu->current_level + 1; + //eq->desc_sid = emu->id - emu->current_level + 1; + eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->title_sid = emu->sof_next_skill; + eq->desc_sid = emu->sof_next_skill; + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + OUT(prereq_skill); + OUT(prereq_minpoints); + eq->type = emu->sof_type; + OUT(spellid); + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + //eq->max_level = emu->sof_max_level; + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + eq->aa_expansion = emu->aa_expansion; + eq->special_category = emu->special_category; + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_VAR_ENCODE(CharacterSelect_Struct); + + //EQApplicationPacket *packet = *p; + //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + + int char_count; + int namelen = 0; + for (char_count = 0; char_count < 10; char_count++) { + if (emu->name[char_count][0] == '\0') + break; + if (strcmp(emu->name[char_count], "") == 0) + break; + namelen += strlen(emu->name[char_count]); + } + + int total_length = sizeof(structs::CharacterSelect_Struct) + + char_count * sizeof(structs::CharacterSelectEntry_Struct) + + namelen; + + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + + //unsigned char *eq_buffer = new unsigned char[total_length]; + //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + + eq->char_count = char_count; + eq->total_chars = 10; + + unsigned char *bufptr = (unsigned char *)eq->entries; + int r; + for (r = 0; r < char_count; r++) { + { //pre-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->level = emu->level[r]; + eq2->hairstyle = emu->hairstyle[r]; + eq2->gender = emu->gender[r]; + memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); + } + //adjust for name. + bufptr += strlen(emu->name[r]); + { //post-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->beard = emu->beard[r]; + eq2->haircolor = emu->haircolor[r]; + eq2->face = emu->face[r]; + int k; + for (k = 0; k < _MaterialCount; k++) { + eq2->equip[k].equip0 = emu->equip[r][k]; + eq2->equip[k].equip1 = 0; + eq2->equip[k].itemid = 0; + eq2->equip[k].color.color = emu->cs_colors[r][k].color; + } + eq2->primary = emu->primary[r]; + eq2->secondary = emu->secondary[r]; + eq2->tutorial = emu->tutorial[r]; // was u15 + eq2->u15 = 0xff; + eq2->deity = emu->deity[r]; + eq2->zone = emu->zone[r]; + eq2->u19 = 0xFF; + eq2->race = emu->race[r]; + eq2->gohome = emu->gohome[r]; + eq2->class_ = emu->class_[r]; + eq2->eyecolor1 = emu->eyecolor1[r]; + eq2->beardcolor = emu->beardcolor[r]; + eq2->eyecolor2 = emu->eyecolor2[r]; + eq2->drakkin_heritage = emu->drakkin_heritage[r]; + eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; + eq2->drakkin_details = emu->drakkin_details[r]; + } + bufptr += sizeof(structs::CharacterSelectEntry_Struct); + } + + FINISH_ENCODE(); + } + + //hack hack hack + ENCODE(OP_SendZonepoints) + { + ENCODE_LENGTH_ATLEAST(ZonePoints); + SETUP_VAR_ENCODE(ZonePoints); + ALLOC_VAR_ENCODE(structs::ZonePoints, __packet->size); + + memcpy(eq, emu, __packet->size); + + FINISH_ENCODE(); + // unknown0xxx[24]; + //this is utter crap... the client is waiting for this + //certain 0 length opcode to come after the reqclientspawn + //stuff... so this is a dirty way to put it in there. + // this needs to be done better + + //EQApplicationPacket hack_test(OP_PetitionUnCheckout, 0); + //dest->QueuePacket(&hack_test); + } + + ENCODE(OP_ShopPlayerBuy) + { + ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); + SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + OUT(npcid); + OUT(playerid); + OUT(itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToSoDSlot(emu->itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_SomeItemPacketMaybe) + { + // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow + // and flying to the target. + // + + ENCODE_LENGTH_EXACT(Arrow_Struct); + SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); + + OUT(src_y); + OUT(src_x); + OUT(src_z); + OUT(velocity); + OUT(launch_angle); + OUT(tilt); + OUT(arc); + OUT(source_id); + OUT(target_id); + OUT(item_id); + + eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. + + OUT(item_type); + OUT(skill); + + strcpy(eq->model_name, emu->model_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_SpawnDoor) + { + SETUP_VAR_ENCODE(Door_Struct); + int door_count = __packet->size / sizeof(Door_Struct); + int total_length = door_count * sizeof(structs::Door_Struct); + ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); + + int r; + for (r = 0; r < door_count; r++) { + strcpy(eq[r].name, emu[r].name); + eq[r].xPos = emu[r].xPos; + eq[r].yPos = emu[r].yPos; + eq[r].zPos = emu[r].zPos; + eq[r].heading = emu[r].heading; + eq[r].incline = emu[r].incline; + eq[r].size = emu[r].size; + eq[r].doorId = emu[r].doorId; + eq[r].opentype = emu[r].opentype; + eq[r].state_at_spawn = emu[r].state_at_spawn; + eq[r].invert_state = emu[r].invert_state; + eq[r].door_param = emu[r].door_param; + eq[r].unknown0076 = 0; + eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0078 = 0; + eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0080 = 0; + eq[r].unknown0081 = 0; + eq[r].unknown0082 = 0; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_Stun) + { + ENCODE_LENGTH_EXACT(Stun_Struct); + SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); + OUT(duration); + eq->unknown005 = 163; + eq->unknown006 = 67; + + FINISH_ENCODE(); + } + + ENCODE(OP_TargetBuffs) + { + SETUP_VAR_ENCODE(BuffIcon_Struct); + + uint32 sz = 7 + (13 * emu->count); + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); + + uchar *ptr = __packet->pBuffer; + *((uint32*)ptr) = emu->entity_id; + ptr += sizeof(uint32); + + *((uint16*)ptr) = emu->count; + ptr += sizeof(uint16); + + for (uint16 i = 0; i < emu->count; ++i) + { + *((uint32*)ptr) = emu->entries[i].buff_slot; + ptr += sizeof(uint32); + *((uint32*)ptr) = emu->entries[i].spell_id; + ptr += sizeof(uint32); + *((uint32*)ptr) = emu->entries[i].tics_remaining; + ptr += sizeof(uint32); + ptr += 1; + } + /*std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint8 write_var8 = 1; + ss.write((const char*)&emu->entity_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint16)); + write_var8 = 0; + for(uint16 i = 0; i < emu->count; ++i) + { + ss.write((const char*)&emu->entries[i].buff_slot, sizeof(uint32)); + ss.write((const char*)&emu->entries[i].spell_id, sizeof(uint32)); + ss.write((const char*)&emu->entries[i].tics_remaining, sizeof(uint32)); + ss.write((const char*)&write_var8, sizeof(uint8)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + */ + + FINISH_ENCODE(); + } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + int PacketSize = 2; + + for (int i = 0; i < EntryCount; ++i, ++emu) + PacketSize += (12 + strlen(emu->name)); + + emu = (Track_Struct *)__emu_buffer; + + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + + char *Buffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); + + for (int i = 0; i < EntryCount; ++i, ++emu) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); + VARSTRUCT_ENCODE_STRING(Buffer, emu->name); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Trader) + { + if ((*p)->size != sizeof(TraderBuy_Struct)) { + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; + } + + ENCODE_FORWARD(OP_TraderBuy); + } + + ENCODE(OP_TraderBuy) + { + ENCODE_LENGTH_EXACT(TraderBuy_Struct); + SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); + + OUT(Action); + OUT(Price); + OUT(TraderID); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(ItemID); + OUT(Quantity); + OUT(AlreadySold); + + FINISH_ENCODE(); + } + + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToSoDSlot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_VetRewardsAvaliable) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + + uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); + *p = nullptr; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); + uchar *old_data = __emu_buffer; + uchar *data = outapp_create->pBuffer; + for (unsigned int i = 0; i < count; ++i) + { + structs::VeteranReward *vr = (structs::VeteranReward*)data; + InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + + vr->claim_count = ivr->claim_count; + vr->claim_id = ivr->claim_id; + vr->number_available = ivr->number_available; + for (int x = 0; x < 8; ++x) + { + vr->items[x].item_id = ivr->items[x].item_id; + strcpy(vr->items[x].item_name, ivr->items[x].item_name); + vr->items[x].charges = ivr->items[x].charges; + } + + old_data += sizeof(InternalVeteranReward); + data += sizeof(structs::VeteranReward); + } + + dest->FastQueuePacket(&outapp_create); + delete inapp; + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(unknown06); + OUT(elite_material); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_WhoAllResponse) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *InBuffer = (char *)in->pBuffer; + + WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; + + int Count = wars->playercount; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); + + char *OutBuffer = (char *)outapp->pBuffer; + + memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); + + OutBuffer += sizeof(WhoAllReturnStruct); + InBuffer += sizeof(WhoAllReturnStruct); + + for (int i = 0; i < Count; ++i) + { + uint32 x; + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + InBuffer += 4; + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); + + char Name[64]; + + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + for (int j = 0; j < 7; ++j) + { + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + delete in; + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZonePlayerToBind) + { + ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); + + ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; + structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; + unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; + structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; + + zph->x = zps->x; + zph->y = zps->y; + zph->z = zps->z; + zph->heading = zps->heading; + zph->bind_zone_id = zps->bind_zone_id; + zph->bind_instance_id = zps->bind_instance_id; + strcpy(zph->zone_name, zps->zone_name); + + zpf->unknown021 = 1; + zpf->unknown022 = 0; + zpf->unknown023 = 0; + zpf->unknown024 = 0; + + ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); + ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); + + delete[] buffer1; + delete[] buffer2; + delete[](*p)->pBuffer; + + (*p)->pBuffer = new unsigned char[ss.str().size()]; + (*p)->size = ss.str().size(); + + memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); + dest->FastQueuePacket(&(*p)); + } + + ENCODE(OP_ZoneServerInfo) + { + SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); + + OUT_str(ip); + OUT(port); + + FINISH_ENCODE(); + + //this is SUCH bullshit to be doing from down here. but the + // new client requires us to close immediately following this + // packet, so do it. + //dest->Close(); + } + + ENCODE(OP_ZoneSpawns) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } - //_log(NET__STRUCTS, "Spawn name is [%s]", emu->name); - emu = (Spawn_Struct *) __emu_buffer; + emu = (Spawn_Struct *)__emu_buffer; //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); - char *Buffer = (char *) in->pBuffer; - + char *Buffer = (char *)in->pBuffer; int r; int k; - for(r = 0; r < entrycount; r++, emu++) { + for (r = 0; r < entrycount; r++, emu++) { int PacketSize = sizeof(structs::Spawn_Struct); PacketSize += strlen(emu->name); PacketSize += strlen(emu->lastName); - if(strlen(emu->title)) + if (strlen(emu->title)) PacketSize += strlen(emu->title) + 1; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) PacketSize += strlen(emu->suffix) + 1; - if(emu->DestructibleObject) + if (emu->DestructibleObject) { PacketSize = PacketSize - 4; // No bodytype PacketSize += 53; // Fixed portion @@ -1010,7 +2292,7 @@ ENCODE(OP_ZoneSpawns) { } bool ShowName = 1; - if(emu->bodytype >= 66) + if (emu->bodytype >= 66) { emu->race = 127; emu->bodytype = 11; @@ -1019,30 +2301,30 @@ ENCODE(OP_ZoneSpawns) { } float SpawnSize = emu->size; - if(!((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) + if (!((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) { PacketSize -= (sizeof(structs::EquipStruct) * 9); - if(emu->size == 0) + if (emu->size == 0) { emu->size = 6; SpawnSize = 6; } } - if(SpawnSize == 0) + if (SpawnSize == 0) { SpawnSize = 3; } EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); - Buffer = (char *) outapp->pBuffer; + Buffer = (char *)outapp->pBuffer; VARSTRUCT_ENCODE_STRING(Buffer, emu->name); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(float, Buffer, 10); // was int and 0x41200000 } @@ -1073,10 +2355,10 @@ ENCODE(OP_ZoneSpawns) { Bitfields->showname = ShowName; - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x1d600000); - Buffer = Buffer -4; + Buffer = Buffer - 4; } Bitfields->ispet = emu->is_pet; @@ -1085,18 +2367,18 @@ ENCODE(OP_ZoneSpawns) { uint8 OtherData = 0; - if(strlen(emu->title)) + if (strlen(emu->title)) OtherData = OtherData | 0x04; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) OtherData = OtherData | 0x08; - if(emu->DestructibleObject) + if (emu->DestructibleObject) OtherData = OtherData | 0xd1; // Live has 0xe1 for OtherData VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); } @@ -1106,7 +2388,7 @@ ENCODE(OP_ZoneSpawns) { } VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleModel); VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleName2); @@ -1138,15 +2420,15 @@ ENCODE(OP_ZoneSpawns) { /* if(emu->bodytype >=66) { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname } else { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname }*/ - if(!emu->DestructibleObject) + if (!emu->DestructibleObject) { // Setting this next field to zero will cause a crash. Looking at ShowEQ, if it is zero, the bodytype field is not // present. Will sort that out later. @@ -1170,7 +2452,7 @@ ENCODE(OP_ZoneSpawns) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_details); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->deity); - if(emu->NPC) + if (emu->NPC) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); @@ -1216,9 +2498,9 @@ ENCODE(OP_ZoneSpawns) { Buffer += sizeof(structs::Spawn_Struct_Position); - if((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu ->race == 130) || (emu->race == 330) || (emu->race == 522)) + if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { - for(k = 0; k < 9; ++k) + for (k = 0; k < 9; ++k) { { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); @@ -1241,11 +2523,11 @@ ENCODE(OP_ZoneSpawns) { } - if((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) + if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; - for(k = 0; k < 9; k++) { + for (k = 0; k < 9; k++) { Equipment[k].equip0 = emu->equipment[k]; Equipment[k].equip1 = 0; Equipment[k].itemId = 0; @@ -1253,12 +2535,12 @@ ENCODE(OP_ZoneSpawns) { Buffer += (sizeof(structs::EquipStruct) * 9); } - if(strlen(emu->title)) + if (strlen(emu->title)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->title); } - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); } @@ -1269,2294 +2551,1077 @@ ENCODE(OP_ZoneSpawns) { Buffer += 24; // Unknown; dest->FastQueuePacket(&outapp, ack_req); - } - - delete in; -} - -ENCODE(OP_MercenaryDataResponse) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; - - PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - 4) * emu->MercCount; - - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); - for(r = 0; r < emu->MercTypeCount; r++) - { - if(emu->MercTypeCount > 0) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); } - } - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - if(emu->MercCount) - { - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); - if(emu->Mercs[r].StanceCount > 0) - { - for(k = 0; k < emu->Mercs[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); - } - } - } - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -// This packet does not appear to exist in SoD, but leaving it here just in case -ENCODE(OP_MercenaryDataUpdate) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - EQApplicationPacket *outapp; - - uint32 PacketSize = 0; - - // There are 2 different sized versions of this packet depending if a merc is hired or not - if (emu->MercStatus >= 0) - { - PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; - } - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); - for(k = 0; k < emu->MercData[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); - } - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk05); - } - } - else - { - PacketSize += sizeof(structs::NoMercenaryHired_Struct); - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+4; - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length); - - delete[] __emu_buffer; - safe_delete_array(serialized); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - - *p = nullptr; - - if(in->size == 0) { - - in->size = 4; - - in->pBuffer = new uchar[in->size]; - - *((uint32 *) in->pBuffer) = 0; - - dest->FastQueuePacket(&in, ack_req); - - return; - } - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); - - if(ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); delete in; - - return; } - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - in->pBuffer = new uchar[4]; - - *(uint32 *)in->pBuffer = ItemCount; - - in->size = 4; - - for(int r = 0; r < ItemCount; r++, eq++) { - - uint32 Length = 0; - - char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); - - if(Serialized) { - - uchar *OldBuffer = in->pBuffer; - - in->pBuffer = new uchar[in->size + Length]; - - memcpy(in->pBuffer, OldBuffer, in->size); - - safe_delete_array(OldBuffer); - - memcpy(in->pBuffer + in->size, Serialized, Length); - - in->size += Length; - - safe_delete_array(Serialized); - - } - else { - _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); - } - } - - delete[] __emu_buffer; - - //_log(NET__ERROR, "Sending inventory to client"); - - //_hex(NET__ERROR, in->pBuffer, in->size); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; - - - - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. - - uint8 *buffer; - buffer = in->pBuffer; - - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; - - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); - - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header - emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data - ); - const char *emu_note = (emu_name + - emu->name_length + //skip name contents - emu->count //skip string terminators - ); - - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { - - //the order we set things here must match the struct - -//nice helper macro -/*#define SlideStructString(field, str) \ - strcpy(e->field, str.c_str()); \ - e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ -#define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) - - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - PutFieldN(rank); - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - PutFieldN(total_tribute); - PutFieldN(last_tribute); - e->unknown_one = htonl(1); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); - - -#undef SlideStructString -#undef PutFieldN - - e++; - } - } - - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SpawnDoor) { - SETUP_VAR_ENCODE(Door_Struct); - int door_count = __packet->size/sizeof(Door_Struct); - int total_length = door_count * sizeof(structs::Door_Struct); - ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); - int r; - for(r = 0; r < door_count; r++) { - strcpy(eq[r].name, emu[r].name); - eq[r].xPos = emu[r].xPos; - eq[r].yPos = emu[r].yPos; - eq[r].zPos = emu[r].zPos; - eq[r].heading = emu[r].heading; - eq[r].incline = emu[r].incline; - eq[r].size = emu[r].size; - eq[r].doorId = emu[r].doorId; - eq[r].opentype = emu[r].opentype; - eq[r].state_at_spawn = emu[r].state_at_spawn; - eq[r].invert_state = emu[r].invert_state; - eq[r].door_param = emu[r].door_param; - eq[r].unknown0076 = 0; - eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0078 = 0; - eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0080 = 0; - eq[r].unknown0081 = 0; - eq[r].unknown0082 = 0; - } - FINISH_ENCODE(); -} - -ENCODE(OP_GroundSpawn) { - ENCODE_LENGTH_EXACT(Object_Struct); - SETUP_DIRECT_ENCODE(Object_Struct, structs::Object_Struct); - OUT(drop_id); - OUT(zone_id); - OUT(zone_instance); - OUT(heading); - OUT(x); - OUT(y); - OUT(z); - OUT_str(object_name); - OUT(object_type); - OUT(spawn_id); - - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown020 = 0; - eq->unknown024 = 0; - eq->size = 1; //This forces all objects to standard size for now - eq->unknown088 = 0; - memset(eq->unknown096, 0xFF, sizeof(eq->unknown096)); - FINISH_ENCODE(); -} - -ENCODE(OP_ManaChange) { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? - FINISH_ENCODE(); -} - -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - memcpy(eq->Title, emu->Title, sizeof(eq->Title)); - memcpy(eq->Text, emu->Text, sizeof(eq->Text)); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); - OUT(NegativeID); - // These two field names are used if Buttons == 1. - OUT_str(ButtonName0); - OUT_str(ButtonName1); - FINISH_ENCODE(); -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - OUT(race); - OUT(unknown006[0]); - OUT(unknown006[1]); - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - OUT(drakkin_heritage); - OUT(drakkin_tattoo); - OUT(drakkin_details); - - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerBuy) -{ - ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); - SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - OUT(npcid); - OUT(playerid); - OUT(itemslot); - OUT(quantity); - OUT(price); - - FINISH_ENCODE(); -} - -ENCODE(OP_ClientUpdate) { - ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); - SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); - OUT(spawn_id); - OUT(x_pos); - OUT(delta_x); - OUT(delta_y); - OUT(z_pos); - OUT(delta_heading); - OUT(y_pos); - OUT(delta_z); - OUT(animation); - OUT(heading); - FINISH_ENCODE(); -} - -ENCODE(OP_ExpansionInfo) { - ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); - OUT(Expansions); - FINISH_ENCODE(); -} - -ENCODE(OP_LogServer) { - ENCODE_LENGTH_EXACT(LogServer_Struct); - SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); - strcpy(eq->worldshortname, emu->worldshortname); - - OUT(enablevoicemacros); - OUT(enablemail); - OUT(enable_pvp); - OUT(enable_FV); - - // These next two need to be set like this for the Tutorial Button to work. - eq->unknown263[0] = 0; - eq->unknown263[2] = 1; - - FINISH_ENCODE(); -} - -ENCODE(OP_Damage) { - ENCODE_LENGTH_EXACT(CombatDamage_Struct); - SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); - OUT(target); - OUT(source); - OUT(type); - OUT(spellid); - OUT(damage); - eq->sequence = emu->sequence; - FINISH_ENCODE(); -} - -ENCODE(OP_Consider) { - ENCODE_LENGTH_EXACT(Consider_Struct); - SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); - OUT(playerid); - OUT(targetid); - OUT(faction); - OUT(level); - OUT(pvpcon); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); - OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - eq->sequence = emu->sequence; - OUT(type); - //OUT(damage); - OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1; - FINISH_ENCODE(); -} - -ENCODE(OP_Buff) { - ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); - SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); - OUT(entityid); - OUT(slot); - OUT(level); - OUT(effect); - //eq->unknown7 = 10; - OUT(spellid); - OUT(duration); - OUT(slotid); - OUT(bufffade); - FINISH_ENCODE(); -} - -ENCODE(OP_CancelTrade) { - ENCODE_LENGTH_EXACT(CancelTrade_Struct); - SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); - OUT(fromid); - OUT(action); - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerSell) { - ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); - SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - OUT(npcid); - eq->itemslot = ServerToSoDSlot(emu->itemslot); - OUT(quantity); - OUT(price); - FINISH_ENCODE(); -} - -ENCODE(OP_ApplyPoison) { - ENCODE_LENGTH_EXACT(ApplyPoison_Struct); - SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - eq->inventorySlot = ServerToSoDSlot(emu->inventorySlot); - OUT(success); - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteItem) { - ENCODE_LENGTH_EXACT(DeleteItem_Struct); - SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - eq->from_slot = ServerToSoDSlot(emu->from_slot); - eq->to_slot = ServerToSoDSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } -ENCODE(OP_MoveItem) { - ENCODE_LENGTH_EXACT(MoveItem_Struct); - SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); - - eq->from_slot = ServerToSoDSlot(emu->from_slot); - eq->to_slot = ServerToSoDSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_ItemVerifyReply) { - ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); - SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); - - eq->slot = ServerToSoDSlot(emu->slot); - OUT(spell); - OUT(target); - - FINISH_ENCODE(); -} - -ENCODE(OP_Trader) { - - if((*p)->size != sizeof(TraderBuy_Struct)) { - EQApplicationPacket *in = *p; - *p = nullptr; - dest->FastQueuePacket(&in, ack_req); - return; - } - ENCODE_FORWARD(OP_TraderBuy); -} - -ENCODE(OP_TraderBuy) { - - ENCODE_LENGTH_EXACT(TraderBuy_Struct); - SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); - - OUT(Action); - OUT(Price); - OUT(TraderID); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(ItemID); - OUT(Quantity); - OUT(AlreadySold); - - FINISH_ENCODE(); -} - -ENCODE(OP_LootItem) { - - ENCODE_LENGTH_EXACT(LootingItem_Struct); - SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); - OUT(lootee); - OUT(looter); - eq->slot_id = emu->slot_id + 1; - OUT(auto_loot); - - FINISH_ENCODE(); -} - -ENCODE(OP_TributeItem) { - ENCODE_LENGTH_EXACT(TributeItem_Struct); - SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); - - eq->slot = ServerToSoDSlot(emu->slot); - OUT(quantity); - OUT(tribute_master_id); - OUT(tribute_points); - - FINISH_ENCODE(); -} - -ENCODE(OP_SomeItemPacketMaybe) { - // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow - // and flying to the target. - // - - ENCODE_LENGTH_EXACT(Arrow_Struct); - SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); - - OUT(src_y); - OUT(src_x); - OUT(src_z); - OUT(velocity); - OUT(launch_angle); - OUT(tilt); - OUT(arc); - OUT(source_id); - OUT(target_id); - OUT(item_id); - - eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. - - OUT(item_type); - OUT(skill); - - strcpy(eq->model_name, emu->model_name); - - FINISH_ENCODE(); -} - -ENCODE(OP_ReadBook) { - - ENCODE_LENGTH_ATLEAST(BookText_Struct); - SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); - - if(emu->window == 0xFF) - eq->window = 0xFFFFFFFF; - else - eq->window = emu->window; - OUT(type); - eq->invslot = ServerToSoDSlot(emu->invslot); - strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); - FINISH_ENCODE(); -} - -ENCODE(OP_Stun) { - - ENCODE_LENGTH_EXACT(Stun_Struct); - SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); - OUT(duration); - eq->unknown005 = 163; - eq->unknown006 = 67; - - FINISH_ENCODE(); -} - -ENCODE(OP_ZonePlayerToBind) -{ - ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); - ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; - structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; - unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; - structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; - - zph->x = zps->x; - zph->y = zps->y; - zph->z = zps->z; - zph->heading = zps->heading; - zph->bind_zone_id = zps->bind_zone_id; - zph->bind_instance_id = zps->bind_instance_id; - strcpy(zph->zone_name, zps->zone_name); - - zpf->unknown021 = 1; - zpf->unknown022 = 0; - zpf->unknown023 = 0; - zpf->unknown024 = 0; - - ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); - ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); - - delete[] buffer1; - delete[] buffer2; - delete[] (*p)->pBuffer; - - (*p)->pBuffer = new unsigned char[ss.str().size()]; - (*p)->size = ss.str().size(); - - memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); - dest->FastQueuePacket(&(*p)); -} - -ENCODE(OP_AdventureMerchantSell) { - ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); - SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - eq->unknown000 = 1; - OUT(npcid); - eq->slot = ServerToSoDSlot(emu->slot); - OUT(charges); - OUT(sell_price); - - FINISH_ENCODE(); -} - -ENCODE(OP_RaidUpdate) -{ - EQApplicationPacket *inapp = *p; - *p = nullptr; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; - - if(raid_gen->action == 0) // raid add has longer length than other raid updates +// DECODE methods + DECODE(OP_AdventureMerchantSell) { - RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); - structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + IN(npcid); + emu->slot = SoDToServerSlot(eq->slot); + IN(charges); + IN(sell_price); - add_member->raidGen.action = in_add_member->raidGen.action; - add_member->raidGen.parameter = in_add_member->raidGen.parameter; - strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); - strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); - add_member->_class = in_add_member->_class; - add_member->level= in_add_member->level; - add_member->isGroupLeader = in_add_member->isGroupLeader; - add_member->flags[0] = in_add_member->flags[0]; - add_member->flags[1] = in_add_member->flags[1]; - add_member->flags[2] = in_add_member->flags[2]; - add_member->flags[3] = in_add_member->flags[3]; - add_member->flags[4] = in_add_member->flags[4]; - dest->FastQueuePacket(&outapp); - } - else - { - RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); - strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); - raid_general->action = in_raid_general->action; - raid_general->parameter = in_raid_general->parameter; - dest->FastQueuePacket(&outapp); - } - delete[] __emu_buffer; -} - -ENCODE(OP_RaidJoin) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; - - general->action = 8; - general->parameter = 1; - strn0cpy(general->leader_name, raid_create->leader_name, 64); - strn0cpy(general->player_name, raid_create->leader_name, 64); - - dest->FastQueuePacket(&outapp_create); - delete[] __emu_buffer; -} - -ENCODE(OP_VetRewardsAvaliable) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - - uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for(unsigned int i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; - - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for(int x = 0; x < 8; ++x) - { - vr->items[x].item_id = ivr->items[x].item_id; - strcpy(vr->items[x].item_name, ivr->items[x].item_name); - vr->items[x].charges = ivr->items[x].charges; - } - - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); + FINISH_DIRECT_DECODE(); } - dest->FastQueuePacket(&outapp_create); - delete inapp; -} - -ENCODE(OP_WhoAllResponse) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *InBuffer = (char *)in->pBuffer; - - WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; - - int Count = wars->playercount; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); - - char *OutBuffer = (char *)outapp->pBuffer; - - memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); - - OutBuffer += sizeof(WhoAllReturnStruct); - InBuffer += sizeof(WhoAllReturnStruct); - - for(int i = 0; i < Count; ++i) + DECODE(OP_AltCurrencySell) { - uint32 x; + DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + IN(merchant_entity_id); + emu->slot_id = SoDToServerSlot(eq->slot_id); + IN(charges); + IN(cost); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); - - InBuffer += 4; - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); - - char Name[64]; - - - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); - - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - - for(int j = 0; j < 7; ++j) - { - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); - } - - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + FINISH_DIRECT_DECODE(); } - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - delete in; -} - -ENCODE(OP_GroupInvite) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); - memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow2) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupCancelInvite) -{ - ENCODE_LENGTH_EXACT(GroupCancel_Struct); - SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - OUT(toggle); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionEndsWarning) -{ - ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); - SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - OUT(minutes_remaining); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionInfo) -{ - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - OUT(max_players); - eq->unknown004 = 785316192; - eq->unknown008 = 435601; - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->leader_name, emu->leader_name); - FINISH_ENCODE(); -} - -ENCODE(OP_DzCompass) -{ - SETUP_VAR_ENCODE(ExpeditionCompass_Struct); - ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - OUT(count); - - for(uint32 i = 0; i < emu->count; ++i) + DECODE(OP_AltCurrencySellSelection) { - OUT(entries[i].x); - OUT(entries[i].y); - OUT(entries[i].z); + DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); + + IN(merchant_entity_id); + emu->slot_id = SoDToServerSlot(eq->slot_id); + + FINISH_DIRECT_DECODE(); } - FINISH_ENCODE(); -} - -ENCODE(OP_DzMemberList) -{ - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) + DECODE(OP_ApplyPoison) { - ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].status, sizeof(char)); + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = SoDToServerSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); } - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionList) -{ - SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) + DECODE(OP_AugmentInfo) { - ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); - ss.write((const char*)&null_term, sizeof(char)); + DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); + SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + IN(itemid); + IN(window); + + FINISH_DIRECT_DECODE(); } - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzLeaderStatus) -{ - SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->leader_name, strlen(emu->leader_name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//1 - ss.write((const char*)&client_id, sizeof(uint32)); - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzJoinExpeditionConfirm) -{ - ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); - SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->player_name, emu->player_name); - FINISH_ENCODE(); -} - -ENCODE(OP_TargetBuffs) -{ - SETUP_VAR_ENCODE(BuffIcon_Struct); - - uint32 sz = 7 + (13 * emu->count); - __packet->size = sz; - __packet->pBuffer = new unsigned char[sz]; - memset(__packet->pBuffer, 0, sz); - - uchar *ptr = __packet->pBuffer; - *((uint32*)ptr) = emu->entity_id; - ptr += sizeof(uint32); - - *((uint16*)ptr) = emu->count; - ptr += sizeof(uint16); - - for(uint16 i = 0; i < emu->count; ++i) + DECODE(OP_AugmentItem) { - *((uint32*)ptr) = emu->entries[i].buff_slot; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].spell_id; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].tics_remaining; - ptr += sizeof(uint32); - ptr += 1; - } - /*std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); - uint8 write_var8 = 1; - ss.write((const char*)&emu->entity_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint16)); - write_var8 = 0; - for(uint16 i = 0; i < emu->count; ++i) - { - ss.write((const char*)&emu->entries[i].buff_slot, sizeof(uint32)); - ss.write((const char*)&emu->entries[i].spell_id, sizeof(uint32)); - ss.write((const char*)&emu->entries[i].tics_remaining, sizeof(uint32)); - ss.write((const char*)&write_var8, sizeof(uint8)); + emu->container_slot = SoDToServerSlot(eq->container_slot); + emu->augment_slot = eq->augment_slot; + + FINISH_DIRECT_DECODE(); } - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - */ - - FINISH_ENCODE(); -} - -ENCODE(OP_GroupUpdate) -{ - //_log(NET__ERROR, "OP_GroupUpdate"); - EQApplicationPacket *in = *p; - - GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; - - //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); - if((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) + DECODE(OP_BazaarSearch) { - if((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) - { - //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + char *Buffer = (char *)__packet->pBuffer; - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); - dest->FastQueuePacket(&outapp); - - // Make an empty GLAA packet to clear out their useable GLAAs - // - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - dest->FastQueuePacket(&outapp); - - delete in; + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + if ((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) return; - } - //if(gjs->action == groupActLeave) - // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - delete in; - return; + SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); + MEMSET_IN(structs::NewBazaarInspect_Struct); + IN(Beginning.Action); + memcpy(emu->Name, eq->Name, sizeof(emu->Name)); + IN(SerialNumber); + FINISH_DIRECT_DECODE(); } - if(in->size == sizeof(GroupUpdate2_Struct)) + DECODE(OP_Buff) { - // Group Update2 - //_log(NET__ERROR, "Struct is GroupUpdate2"); + DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct); + SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); - unsigned char *__emu_buffer = in->pBuffer; - GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*) __emu_buffer; + IN(entityid); + IN(slot); + IN(level); + IN(effect); + IN(spellid); + IN(duration); + IN(slotid); + IN(bufffade); - //_log(NET__ERROR, "Yourname is %s", gu2->yourname); + FINISH_DIRECT_DECODE(); + } - int MemberCount = 1; + DECODE(OP_Bug) + { + DECODE_LENGTH_EXACT(structs::BugStruct); + SETUP_DIRECT_DECODE(BugStruct, structs::BugStruct); - int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; + strn0cpy(emu->chartype, eq->chartype, sizeof(emu->chartype)); + strn0cpy(emu->name, eq->name, sizeof(emu->name)); + strn0cpy(emu->ui, eq->ui, sizeof(emu->ui)); + IN(x); + IN(y); + IN(z); + IN(heading); + strn0cpy(emu->target_name, eq->target_name, sizeof(emu->target_name)); + strn0cpy(emu->bug, eq->bug, sizeof(emu->bug)); + strn0cpy(emu->system_info, eq->system_info, sizeof(emu->system_info)); - for(int i = 0; i < 5; ++i) + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + IN(slot); + IN(spell_id); + emu->inventoryslot = SoDToServerSlot(eq->inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + IN(class_); + IN(beardcolor); + IN(beard); + IN(hairstyle); + IN(gender); + IN(race); + + if (RuleB(World, EnableTutorialButton) && eq->tutorial) + emu->start_zone = RuleI(World, TutorialZoneID); + else + emu->start_zone = eq->start_zone; + + IN(haircolor); + IN(deity); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ClientUpdate) + { + // for some odd reason, there is an extra byte on the end of this on occasion.. + DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); + SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); + + IN(spawn_id); + IN(sequence); + IN(x_pos); + IN(y_pos); + IN(z_pos); + IN(heading); + IN(delta_x); + IN(delta_y); + IN(delta_z); + IN(delta_heading); + IN(animation); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Consider) + { + DECODE_LENGTH_EXACT(structs::Consider_Struct); + SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); + + IN(playerid); + IN(targetid); + IN(faction); + IN(level); + //emu->cur_hp = 1; + //emu->max_hp = 2; + //emu->pvpcon = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = SoDToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = SoDToServerSlot(eq->from_slot); + emu->to_slot = SoDToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FindPersonRequest) + { + DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); + SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); + + IN(npc_id); + IN(client_pos.x); + IN(client_pos.y); + IN(client_pos.z); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupCancelInvite) + { + DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); + SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + IN(toggle); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupDisband) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_Disband"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow2) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupInvite"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); + memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite2) + { + //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); + DECODE_FORWARD(OP_GroupInvite); + } + + DECODE(OP_InspectRequest) + { + DECODE_LENGTH_EXACT(structs::Inspect_Struct); + SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); + + IN(TargetID); + IN(PlayerID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + IN(link_hash); + IN(icon); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemVerifyRequest) + { + DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); + SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); + + emu->slot = SoDToServerSlot(eq->slot); + IN(target); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LoadSpellSet) + { + DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); + SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); + + for (uint32 i = 0; i < MAX_PP_MEMSPELL; ++i) + emu->spell[i] = eq->spell[i]; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = eq->slot_id - 1; + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); + + emu->from_slot = SoDToServerSlot(eq->from_slot); + emu->to_slot = SoDToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_RaidInvite) + { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ReadBook) + { + DECODE_LENGTH_EXACT(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(type); + emu->invslot = SoDToServerSlot(eq->invslot); + emu->window = (uint8)eq->window; + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Save) + { + DECODE_LENGTH_EXACT(structs::Save_Struct); + SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); + + memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + IN(filters[r]); + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerBuy) + { + DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); + SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + IN(npcid); + IN(playerid); + IN(itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = SoDToServerSlot(eq->itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TraderBuy) + { + DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); + SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); + MEMSET_IN(TraderBuy_Struct); + + IN(Action); + IN(Price); + IN(TraderID); + memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); + IN(ItemID); + IN(Quantity); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = SoDToServerSlot(eq->container_slot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = SoDToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + IN(material); + IN(unknown06); + IN(elite_material); + IN(color.color); + IN(wear_slot_id); + emu->hero_forge_model = 0; + emu->unknown18 = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + IN(guildid); + IN(type); + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + uint32 NextItemInstSerialNumber = 1; + uint32 MaxInstances = 2000000000; + + static inline int32 GetNextItemInstSerialNumber() + { + if (NextItemInstSerialNumber >= MaxInstances) + NextItemInstSerialNumber = 1; + else + NextItemInstSerialNumber++; + + return NextItemInstSerialNumber; + } + + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) + { + uint8 null_term = 0; + bool stackable = inst->IsStackable(); + uint32 merchant_slot = inst->GetMerchantSlot(); + uint32 charges = inst->GetCharges(); + if (!stackable && charges > 254) + charges = 0xFFFFFFFF; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + const Item_Struct *item = inst->GetItem(); + //_log(NET__ERROR, "Serialize called for: %s", item->Name); + SoD::structs::ItemSerializationHeader hdr; + hdr.stacksize = stackable ? charges : 1; + hdr.unknown004 = 0; + + int32 slot_id = ServerToSoDSlot(slot_id_in); + + hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; + hdr.price = inst->GetPrice(); + hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); + hdr.unknown020 = 0; + hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; + hdr.unknown028 = 0; + hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); + hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; + hdr.unknown044 = 0; + hdr.unknown048 = 0; + hdr.unknown052 = 0; + hdr.unknown056 = 0; + hdr.unknown060 = 0; + hdr.unknown061 = 0; + hdr.unknown062 = 0; + hdr.ItemClass = item->ItemClass; + + ss.write((const char*)&hdr, sizeof(SoD::structs::ItemSerializationHeader)); + + if (strlen(item->Name) > 0) { - //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); - if(gu2->membername[i][0] != '\0') - { - PacketLength += (22 + strlen(gu2->membername[i]) + 1); - ++MemberCount; + ss.write(item->Name, strlen(item->Name)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + if (strlen(item->Lore) > 0) + { + ss.write(item->Lore, strlen(item->Lore)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + if (strlen(item->IDFile) > 0) + { + ss.write(item->IDFile, strlen(item->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + SoD::structs::ItemBodyStruct ibs; + memset(&ibs, 0, sizeof(SoD::structs::ItemBodyStruct)); + + ibs.id = item->ID; + ibs.weight = item->Weight; + ibs.norent = item->NoRent; + ibs.nodrop = item->NoDrop; + ibs.attune = item->Attuneable; + ibs.size = item->Size; + ibs.slots = SwapBits21and22(item->Slots); + ibs.price = item->Price; + ibs.icon = item->Icon; + ibs.unknown1 = 1; + ibs.unknown2 = 1; + ibs.BenefitFlag = item->BenefitFlag; + ibs.tradeskills = item->Tradeskills; + ibs.CR = item->CR; + ibs.DR = item->DR; + ibs.PR = item->PR; + ibs.MR = item->MR; + ibs.FR = item->FR; + ibs.SVCorruption = item->SVCorruption; + ibs.AStr = item->AStr; + ibs.ASta = item->ASta; + ibs.AAgi = item->AAgi; + ibs.ADex = item->ADex; + ibs.ACha = item->ACha; + ibs.AInt = item->AInt; + ibs.AWis = item->AWis; + + ibs.HP = item->HP; + ibs.Mana = item->Mana; + ibs.Endur = item->Endur; + ibs.AC = item->AC; + ibs.regen = item->Regen; + ibs.mana_regen = item->ManaRegen; + ibs.end_regen = item->EnduranceRegen; + ibs.Classes = item->Classes; + ibs.Races = item->Races; + ibs.Deity = item->Deity; + ibs.SkillModValue = item->SkillModValue; + ibs.unknown6 = 0; + ibs.SkillModType = item->SkillModType; + ibs.BaneDmgRace = item->BaneDmgRace; + ibs.BaneDmgBody = item->BaneDmgBody; + ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; + ibs.BaneDmgAmt = item->BaneDmgAmt; + ibs.Magic = item->Magic; + ibs.CastTime_ = item->CastTime_; + ibs.ReqLevel = item->ReqLevel; + ibs.RecLevel = item->RecLevel; + ibs.RecSkill = item->RecSkill; + ibs.BardType = item->BardType; + ibs.BardValue = item->BardValue; + ibs.Light = item->Light; + ibs.Delay = item->Delay; + ibs.ElemDmgType = item->ElemDmgType; + ibs.ElemDmgAmt = item->ElemDmgAmt; + ibs.Range = item->Range; + ibs.Damage = item->Damage; + ibs.Color = item->Color; + ibs.ItemType = item->ItemType; + ibs.Material = item->Material; + ibs.unknown7 = 0; + ibs.EliteMaterial = item->EliteMaterial; + ibs.SellRate = item->SellRate; + + ibs.CombatEffects = item->CombatEffects; + ibs.Shielding = item->Shielding; + ibs.StunResist = item->StunResist; + ibs.StrikeThrough = item->StrikeThrough; + ibs.ExtraDmgSkill = item->ExtraDmgSkill; + ibs.ExtraDmgAmt = item->ExtraDmgAmt; + ibs.SpellShield = item->SpellShield; + ibs.Avoidance = item->Avoidance; + ibs.Accuracy = item->Accuracy; + ibs.FactionAmt1 = item->FactionAmt1; + ibs.FactionMod1 = item->FactionMod1; + ibs.FactionAmt2 = item->FactionAmt2; + ibs.FactionMod2 = item->FactionMod2; + ibs.FactionAmt3 = item->FactionAmt3; + ibs.FactionMod3 = item->FactionMod3; + ibs.FactionAmt4 = item->FactionAmt4; + ibs.FactionMod4 = item->FactionMod4; + + ss.write((const char*)&ibs, sizeof(SoD::structs::ItemBodyStruct)); + + //charm text + if (strlen(item->CharmFile) > 0) + { + ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + SoD::structs::ItemSecondaryBodyStruct isbs; + memset(&isbs, 0, sizeof(SoD::structs::ItemSecondaryBodyStruct)); + + isbs.augtype = item->AugType; + isbs.augrestrict = item->AugRestrict; + + for (int x = 0; x < 5; ++x) + { + isbs.augslots[x].type = item->AugSlotType[x]; + isbs.augslots[x].visible = item->AugSlotVisible[x]; + isbs.augslots[x].unknown = item->AugSlotUnk2[x]; + } + + isbs.ldonpoint_type = item->PointType; + isbs.ldontheme = item->LDoNTheme; + isbs.ldonprice = item->LDoNPrice; + isbs.ldonsellbackrate = item->LDoNSellBackRate; + isbs.ldonsold = item->LDoNSold; + + isbs.bagtype = item->BagType; + isbs.bagslots = item->BagSlots; + isbs.bagsize = item->BagSize; + isbs.wreduction = item->BagWR; + + isbs.book = item->Book; + isbs.booktype = item->BookType; + + ss.write((const char*)&isbs, sizeof(SoD::structs::ItemSecondaryBodyStruct)); + + if (strlen(item->Filename) > 0) + { + ss.write((const char*)item->Filename, strlen(item->Filename)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + SoD::structs::ItemTertiaryBodyStruct itbs; + memset(&itbs, 0, sizeof(SoD::structs::ItemTertiaryBodyStruct)); + + itbs.loregroup = item->LoreGroup; + itbs.artifact = item->ArtifactFlag; + itbs.summonedflag = item->SummonedFlag; + itbs.favor = item->Favor; + itbs.fvnodrop = item->FVNoDrop; + itbs.dotshield = item->DotShielding; + itbs.atk = item->Attack; + itbs.haste = item->Haste; + itbs.damage_shield = item->DamageShield; + itbs.guildfavor = item->GuildFavor; + itbs.augdistil = item->AugDistiller; + itbs.no_pet = item->NoPet; + + itbs.potion_belt_enabled = item->PotionBelt; + itbs.potion_belt_slots = item->PotionBeltSlots; + itbs.stacksize = stackable ? item->StackSize : 0; + itbs.no_transfer = item->NoTransfer; + itbs.expendablearrow = item->ExpendableArrow; + + ss.write((const char*)&itbs, sizeof(SoD::structs::ItemTertiaryBodyStruct)); + + // Effect Structures Broken down to allow variable length strings for effect names + int32 effect_unknown = 0; + + SoD::structs::ClickEffectStruct ices; + memset(&ices, 0, sizeof(SoD::structs::ClickEffectStruct)); + + ices.effect = item->Click.Effect; + ices.level2 = item->Click.Level2; + ices.type = item->Click.Type; + ices.level = item->Click.Level; + ices.max_charges = item->MaxCharges; + ices.cast_time = item->CastTime; + ices.recast = item->RecastDelay; + ices.recast_type = item->RecastType; + + ss.write((const char*)&ices, sizeof(SoD::structs::ClickEffectStruct)); + + if (strlen(item->ClickName) > 0) + { + ss.write((const char*)item->ClickName, strlen(item->ClickName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 + + SoD::structs::ProcEffectStruct ipes; + memset(&ipes, 0, sizeof(SoD::structs::ProcEffectStruct)); + + ipes.effect = item->Proc.Effect; + ipes.level2 = item->Proc.Level2; + ipes.type = item->Proc.Type; + ipes.level = item->Proc.Level; + ipes.procrate = item->ProcRate; + + ss.write((const char*)&ipes, sizeof(SoD::structs::ProcEffectStruct)); + + if (strlen(item->ProcName) > 0) + { + ss.write((const char*)item->ProcName, strlen(item->ProcName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 + + SoD::structs::WornEffectStruct iwes; + memset(&iwes, 0, sizeof(SoD::structs::WornEffectStruct)); + + iwes.effect = item->Worn.Effect; + iwes.level2 = item->Worn.Level2; + iwes.type = item->Worn.Type; + iwes.level = item->Worn.Level; + + ss.write((const char*)&iwes, sizeof(SoD::structs::WornEffectStruct)); + + if (strlen(item->WornName) > 0) + { + ss.write((const char*)item->WornName, strlen(item->WornName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + SoD::structs::WornEffectStruct ifes; + memset(&ifes, 0, sizeof(SoD::structs::WornEffectStruct)); + + ifes.effect = item->Focus.Effect; + ifes.level2 = item->Focus.Level2; + ifes.type = item->Focus.Type; + ifes.level = item->Focus.Level; + + ss.write((const char*)&ifes, sizeof(SoD::structs::WornEffectStruct)); + + if (strlen(item->FocusName) > 0) + { + ss.write((const char*)item->FocusName, strlen(item->FocusName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + SoD::structs::WornEffectStruct ises; + memset(&ises, 0, sizeof(SoD::structs::WornEffectStruct)); + + ises.effect = item->Scroll.Effect; + ises.level2 = item->Scroll.Level2; + ises.type = item->Scroll.Type; + ises.level = item->Scroll.Level; + + ss.write((const char*)&ises, sizeof(SoD::structs::WornEffectStruct)); + + if (strlen(item->ScrollName) > 0) + { + ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + // End of Effects + + SoD::structs::ItemQuaternaryBodyStruct iqbs; + memset(&iqbs, 0, sizeof(SoD::structs::ItemQuaternaryBodyStruct)); + + iqbs.scriptfileid = item->ScriptFileID; + iqbs.quest_item = item->QuestItemFlag; + iqbs.unknown15 = 0xffffffff; + + iqbs.Purity = item->Purity; + iqbs.BackstabDmg = item->BackstabDmg; + iqbs.DSMitigation = item->DSMitigation; + iqbs.HeroicStr = item->HeroicStr; + iqbs.HeroicInt = item->HeroicInt; + iqbs.HeroicWis = item->HeroicWis; + iqbs.HeroicAgi = item->HeroicAgi; + iqbs.HeroicDex = item->HeroicDex; + iqbs.HeroicSta = item->HeroicSta; + iqbs.HeroicCha = item->HeroicCha; + iqbs.HeroicMR = item->HeroicMR; + iqbs.HeroicFR = item->HeroicFR; + iqbs.HeroicCR = item->HeroicCR; + iqbs.HeroicDR = item->HeroicDR; + iqbs.HeroicPR = item->HeroicPR; + iqbs.HeroicSVCorrup = item->HeroicSVCorrup; + iqbs.HealAmt = item->HealAmt; + iqbs.SpellDmg = item->SpellDmg; + iqbs.clairvoyance = item->Clairvoyance; + + iqbs.subitem_count = 0; + + char *SubSerializations[10]; // + + uint32 SubLengths[10]; + + for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + + SubSerializations[x] = nullptr; + + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + + if (subitem) { + + int SubSlotNumber; + + iqbs.subitem_count++; + + if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? + //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); + else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) + //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); + else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) + //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); + else + SubSlotNumber = slot_id_in; // ??????? + + /* + // TEST CODE: + SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); + */ + + SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); } } - //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); + ss.write((const char*)&iqbs, sizeof(SoD::structs::ItemQuaternaryBodyStruct)); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); + for (int x = 0; x < 10; ++x) { - char *Buffer = (char *)outapp->pBuffer; + if (SubSerializations[x]) { - // Header - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); + ss.write((const char*)&x, sizeof(uint32)); - // Leader - // + ss.write(SubSerializations[x], SubLengths[x]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - - int MemberNumber = 1; - - for(int i = 0; i < 5; ++i) - { - if(gu2->membername[i][0] == '\0') - continue; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + safe_delete_array(SubSerializations[x]); + } } - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = gu2->NPCMarkerID; - memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); - - dest->FastQueuePacket(&outapp); - delete in; - - return; + char* item_serial = new char[ss.tellp()]; + memset(item_serial, 0, ss.tellp()); + memcpy(item_serial, ss.str().c_str(), ss.tellp()); + *length = ss.tellp(); + return item_serial; } - //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - ENCODE_LENGTH_EXACT(GroupJoin_Struct); - SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); - memcpy(eq->membername, emu->membername, sizeof(eq->membername)); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = emu->NPCMarkerID; - - memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); - //_hex(NET__ERROR, __packet->pBuffer, __packet->size); - FINISH_ENCODE(); - - dest->FastQueuePacket(&outapp); -} - -ENCODE(OP_AltCurrencySell) -{ - ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); - SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - - OUT(merchant_entity_id); - eq->slot_id = ServerToSoDSlot(emu->slot_id); - OUT(charges); - OUT(cost); - FINISH_ENCODE(); -} - -ENCODE(OP_WearChange) -{ - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(unknown06); - OUT(elite_material); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -DECODE(OP_BazaarSearch) -{ - char *Buffer = (char *)__packet->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) - return; - - SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); - MEMSET_IN(structs::NewBazaarInspect_Struct); - IN(Beginning.Action); - memcpy(emu->Name, eq->Name, sizeof(emu->Name)); - IN(SerialNumber); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); - - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AdventureMerchantSell) { - DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); - SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - IN(npcid); - emu->slot = SoDToServerSlot(eq->slot); - IN(charges); - IN(sell_price); - - FINISH_DIRECT_DECODE(); -} - - -DECODE(OP_ApplyPoison) { - DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); - SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - - emu->inventorySlot = SoDToServerSlot(eq->inventorySlot); - IN(success); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemVerifyRequest) { - DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); - SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); - - emu->slot = SoDToServerSlot(eq->slot); - IN(target); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Consume) { - DECODE_LENGTH_EXACT(structs::Consume_Struct); - SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); - - emu->slot = SoDToServerSlot(eq->slot); - IN(auto_consumed); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CastSpell) { - DECODE_LENGTH_EXACT(structs::CastSpell_Struct); - SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); - - IN(slot); - IN(spell_id); - emu->inventoryslot = SoDToServerSlot(eq->inventoryslot); - IN(target_id); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_DeleteItem) -{ - DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); - SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - emu->from_slot = SoDToServerSlot(eq->from_slot); - emu->to_slot = SoDToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_MoveItem) -{ - DECODE_LENGTH_EXACT(structs::MoveItem_Struct); - SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - - _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); - - emu->from_slot = SoDToServerSlot(eq->from_slot); - emu->to_slot = SoDToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); - } - IN(link_hash); - IN(icon); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 29; r++) { - IN(filters[r]); - } - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } -DECODE(OP_Consider) { - DECODE_LENGTH_EXACT(structs::Consider_Struct); - SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); - IN(playerid); - IN(targetid); - IN(faction); - IN(level); - //emu->cur_hp = 1; - //emu->max_hp = 2; - //emu->pvpcon = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerBuy) -{ - DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); - SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - - IN(npcid); - IN(playerid); - IN(itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ClientUpdate) { - // for some odd reason, there is an extra byte on the end of this on occasion.. - DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); - SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); - IN(spawn_id); - IN(sequence); - IN(x_pos); - IN(y_pos); - IN(z_pos); - IN(heading); - IN(delta_x); - IN(delta_y); - IN(delta_z); - IN(delta_heading); - IN(animation); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - IN(class_); - IN(beardcolor); - IN(beard); - IN(hairstyle); - IN(gender); - IN(race); - - if(RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - - IN(haircolor); - IN(deity); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - IN(guildid); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupInvite2) -{ - //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); - DECODE_FORWARD(OP_GroupInvite); -} - -DECODE(OP_GroupInvite) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupInvite"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); - memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow2) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupDisband) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_Disband"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupCancelInvite) -{ - DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); - SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - IN(toggle); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Buff) { - DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct); - SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); - IN(entityid); - IN(slot); - IN(level); - IN(effect); - IN(spellid); - IN(duration); - IN(slotid); - IN(bufffade); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerSell) { - DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); - SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - - IN(npcid); - emu->itemslot = SoDToServerSlot(eq->itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Save) { - DECODE_LENGTH_EXACT(structs::Save_Struct); - SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); - memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FindPersonRequest) { - DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); - SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); - IN(npc_id); - IN(client_pos.x); - IN(client_pos.y); - IN(client_pos.z); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WearChange) { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - IN(spawn_id); - IN(material); - IN(unknown06); - IN(elite_material); - IN(color.color); - IN(wear_slot_id); - emu->hero_forge_model = 0; - emu->unknown18 = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TraderBuy) -{ - DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); - SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); - MEMSET_IN(TraderBuy_Struct); - - IN(Action); - IN(Price); - IN(TraderID); - memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); - IN(ItemID); - IN(Quantity); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LootItem) { - DECODE_LENGTH_EXACT(structs::LootingItem_Struct); - SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); - IN(lootee); - IN(looter); - emu->slot_id = eq->slot_id - 1; - IN(auto_loot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TributeItem) { - DECODE_LENGTH_EXACT(structs::TributeItem_Struct); - SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); - - emu->slot = SoDToServerSlot(eq->slot); - IN(quantity); - IN(tribute_master_id); - IN(tribute_points); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_EXACT(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(type); - emu->invslot = SoDToServerSlot(eq->invslot); - emu->window = (uint8) eq->window; - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TradeSkillCombine) { - DECODE_LENGTH_EXACT(structs::NewCombine_Struct); - SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - - emu->container_slot = SoDToServerSlot(eq->container_slot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentItem) { - DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); - SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); - - emu->container_slot = SoDToServerSlot(eq->container_slot); - emu->augment_slot = eq->augment_slot; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentInfo) { - DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); - SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); - - IN(itemid); - IN(window); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LoadSpellSet) -{ - DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); - SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); - - for(uint32 i = 0; i < MAX_PP_MEMSPELL; ++i) - emu->spell[i] = eq->spell[i]; - - FINISH_DIRECT_DECODE(); -} - -uint32 NextItemInstSerialNumber = 1; -uint32 MaxInstances = 2000000000; - -static inline int32 GetNextItemInstSerialNumber() { - - if(NextItemInstSerialNumber >= MaxInstances) - NextItemInstSerialNumber = 1; - else - NextItemInstSerialNumber++; - - return NextItemInstSerialNumber; -} - - -char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { - uint8 null_term = 0; - bool stackable = inst->IsStackable(); - uint32 merchant_slot = inst->GetMerchantSlot(); - uint32 charges = inst->GetCharges(); - if (!stackable && charges > 254) - charges = 0xFFFFFFFF; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - const Item_Struct *item = inst->GetItem(); - //_log(NET__ERROR, "Serialize called for: %s", item->Name); - SoD::structs::ItemSerializationHeader hdr; - hdr.stacksize = stackable ? charges : 1; - hdr.unknown004 = 0; - - int32 slot_id = ServerToSoDSlot(slot_id_in); - - hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; - hdr.price = inst->GetPrice(); - hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); - hdr.unknown020 = 0; - hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; - hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); - hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); - hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; - hdr.unknown044 = 0; - hdr.unknown048 = 0; - hdr.unknown052 = 0; - hdr.unknown056 = 0; - hdr.unknown060 = 0; - hdr.unknown061 = 0; - hdr.unknown062 = 0; - hdr.ItemClass = item->ItemClass; - - ss.write((const char*)&hdr, sizeof(SoD::structs::ItemSerializationHeader)); - - if(strlen(item->Name) > 0) + static inline uint32 ServerToSoDSlot(uint32 ServerSlot) { - ss.write(item->Name, strlen(item->Name)); - ss.write((const char*)&null_term, sizeof(uint8)); + uint32 SoDSlot = 0; + + if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots + SoDSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) + SoDSlot = ServerSlot + 11; + else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) + SoDSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) + SoDSlot = ServerSlot + 1; + else if (ServerSlot == MainPowerSource) + SoDSlot = slots::MainPowerSource; + else + SoDSlot = ServerSlot; + return SoDSlot; } - else + + static inline uint32 ServerToSoDCorpseSlot(uint32 ServerCorpse) { - ss.write((const char*)&null_term, sizeof(uint8)); + //uint32 SoDCorpse; } - if(strlen(item->Lore) > 0) + static inline uint32 SoDToServerSlot(uint32 SoDSlot) { - ss.write(item->Lore, strlen(item->Lore)); - ss.write((const char*)&null_term, sizeof(uint8)); + uint32 ServerSlot = 0; + + if (SoDSlot >= slots::MainAmmo && SoDSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots + ServerSlot = SoDSlot - 1; + else if (SoDSlot >= consts::GENERAL_BAGS_BEGIN && SoDSlot <= consts::CURSOR_BAG_END) + ServerSlot = SoDSlot - 11; + else if (SoDSlot >= consts::BANK_BAGS_BEGIN && SoDSlot <= consts::BANK_BAGS_END) + ServerSlot = SoDSlot - 1; + else if (SoDSlot >= consts::SHARED_BANK_BAGS_BEGIN && SoDSlot <= consts::SHARED_BANK_BAGS_END) + ServerSlot = SoDSlot - 1; + else if (SoDSlot == slots::MainPowerSource) + ServerSlot = MainPowerSource; + else + ServerSlot = SoDSlot; + return ServerSlot; } - else + + static inline uint32 SoDToServerCorpseSlot(uint32 SoDCorpse) { - ss.write((const char*)&null_term, sizeof(uint8)); + //uint32 ServerCorpse; } - - if(strlen(item->IDFile) > 0) - { - ss.write(item->IDFile, strlen(item->IDFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoD::structs::ItemBodyStruct ibs; - memset(&ibs, 0, sizeof(SoD::structs::ItemBodyStruct)); - - uint32 adjusted_slots = item->Slots; - - // Conversions for Ammo and Power Source Slots - if(item->Slots & (1 << 21) & (1 << 22)) - { - // Do nothing - } - else - { - if(item->Slots & (1 << 21)) // Ammo Slot from Database - { - adjusted_slots -= (1 << 21); // Ammo Slot in Titanium - adjusted_slots += (1 << 22); // Ammo Slot in SoF - } - - if(item->Slots & (1 << 22)) // Power Source Slot from Database - { - adjusted_slots -= (1 << 22); // Non Existant Worn Slot in Titanium - adjusted_slots += (1 << 21); // Power Source Slot in SoF - } - } - - ibs.id = item->ID; - ibs.weight = item->Weight; - ibs.norent = item->NoRent; - ibs.nodrop = item->NoDrop; - ibs.attune = item->Attuneable; - ibs.size = item->Size; - ibs.slots = adjusted_slots; - ibs.price = item->Price; - ibs.icon = item->Icon; - ibs.unknown1 = 1; - ibs.unknown2 = 1; - ibs.BenefitFlag = item->BenefitFlag; - ibs.tradeskills = item->Tradeskills; - ibs.CR = item->CR; - ibs.DR = item->DR; - ibs.PR = item->PR; - ibs.MR = item->MR; - ibs.FR = item->FR; - ibs.SVCorruption = item->SVCorruption; - ibs.AStr = item->AStr; - ibs.ASta = item->ASta; - ibs.AAgi = item->AAgi; - ibs.ADex = item->ADex; - ibs.ACha = item->ACha; - ibs.AInt = item->AInt; - ibs.AWis = item->AWis; - - ibs.HP = item->HP; - ibs.Mana = item->Mana; - ibs.Endur = item->Endur; - ibs.AC = item->AC; - ibs.regen = item->Regen; - ibs.mana_regen = item->ManaRegen; - ibs.end_regen = item->EnduranceRegen; - ibs.Classes = item->Classes; - ibs.Races = item->Races; - ibs.Deity = item->Deity; - ibs.SkillModValue = item->SkillModValue; - ibs.unknown6 = 0; - ibs.SkillModType = item->SkillModType; - ibs.BaneDmgRace = item->BaneDmgRace; - ibs.BaneDmgBody = item->BaneDmgBody; - ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; - ibs.BaneDmgAmt = item->BaneDmgAmt; - ibs.Magic = item->Magic; - ibs.CastTime_ = item->CastTime_; - ibs.ReqLevel = item->ReqLevel; - ibs.RecLevel = item->RecLevel; - ibs.RecSkill = item->RecSkill; - ibs.BardType = item->BardType; - ibs.BardValue = item->BardValue; - ibs.Light = item->Light; - ibs.Delay = item->Delay; - ibs.ElemDmgType = item->ElemDmgType; - ibs.ElemDmgAmt = item->ElemDmgAmt; - ibs.Range = item->Range; - ibs.Damage = item->Damage; - ibs.Color = item->Color; - ibs.ItemType = item->ItemType; - ibs.Material = item->Material; - ibs.unknown7 = 0; - ibs.EliteMaterial = item->EliteMaterial; - ibs.SellRate = item->SellRate; - - ibs.CombatEffects = item->CombatEffects; - ibs.Shielding = item->Shielding; - ibs.StunResist = item->StunResist; - ibs.StrikeThrough = item->StrikeThrough; - ibs.ExtraDmgSkill = item->ExtraDmgSkill; - ibs.ExtraDmgAmt = item->ExtraDmgAmt; - ibs.SpellShield = item->SpellShield; - ibs.Avoidance = item->Avoidance; - ibs.Accuracy = item->Accuracy; - ibs.FactionAmt1 = item->FactionAmt1; - ibs.FactionMod1 = item->FactionMod1; - ibs.FactionAmt2 = item->FactionAmt2; - ibs.FactionMod2 = item->FactionMod2; - ibs.FactionAmt3 = item->FactionAmt3; - ibs.FactionMod3 = item->FactionMod3; - ibs.FactionAmt4 = item->FactionAmt4; - ibs.FactionMod4 = item->FactionMod4; - - ss.write((const char*)&ibs, sizeof(SoD::structs::ItemBodyStruct)); - - //charm text - if(strlen(item->CharmFile) > 0) - { - ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoD::structs::ItemSecondaryBodyStruct isbs; - memset(&isbs, 0, sizeof(SoD::structs::ItemSecondaryBodyStruct)); - - isbs.augtype = item->AugType; - isbs.augrestrict = item->AugRestrict; - - for(int x = 0; x < 5; ++x) - { - isbs.augslots[x].type = item->AugSlotType[x]; - isbs.augslots[x].visible = item->AugSlotVisible[x]; - isbs.augslots[x].unknown = item->AugSlotUnk2[x]; - } - - isbs.ldonpoint_type = item->PointType; - isbs.ldontheme = item->LDoNTheme; - isbs.ldonprice = item->LDoNPrice; - isbs.ldonsellbackrate = item->LDoNSellBackRate; - isbs.ldonsold = item->LDoNSold; - - isbs.bagtype = item->BagType; - isbs.bagslots = item->BagSlots; - isbs.bagsize = item->BagSize; - isbs.wreduction = item->BagWR; - - isbs.book = item->Book; - isbs.booktype = item->BookType; - - ss.write((const char*)&isbs, sizeof(SoD::structs::ItemSecondaryBodyStruct)); - - if(strlen(item->Filename) > 0) - { - ss.write((const char*)item->Filename, strlen(item->Filename)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoD::structs::ItemTertiaryBodyStruct itbs; - memset(&itbs, 0, sizeof(SoD::structs::ItemTertiaryBodyStruct)); - - itbs.loregroup = item->LoreGroup; - itbs.artifact = item->ArtifactFlag; - itbs.summonedflag = item->SummonedFlag; - itbs.favor = item->Favor; - itbs.fvnodrop = item->FVNoDrop; - itbs.dotshield = item->DotShielding; - itbs.atk = item->Attack; - itbs.haste = item->Haste; - itbs.damage_shield = item->DamageShield; - itbs.guildfavor = item->GuildFavor; - itbs.augdistil = item->AugDistiller; - itbs.no_pet = item->NoPet; - - itbs.potion_belt_enabled = item->PotionBelt; - itbs.potion_belt_slots = item->PotionBeltSlots; - itbs.stacksize = stackable ? item->StackSize : 0; - itbs.no_transfer = item->NoTransfer; - itbs.expendablearrow = item->ExpendableArrow; - - ss.write((const char*)&itbs, sizeof(SoD::structs::ItemTertiaryBodyStruct)); - - // Effect Structures Broken down to allow variable length strings for effect names - int32 effect_unknown = 0; - - SoD::structs::ClickEffectStruct ices; - memset(&ices, 0, sizeof(SoD::structs::ClickEffectStruct)); - - ices.effect = item->Click.Effect; - ices.level2 = item->Click.Level2; - ices.type = item->Click.Type; - ices.level = item->Click.Level; - ices.max_charges = item->MaxCharges; - ices.cast_time = item->CastTime; - ices.recast = item->RecastDelay; - ices.recast_type = item->RecastType; - - ss.write((const char*)&ices, sizeof(SoD::structs::ClickEffectStruct)); - - if(strlen(item->ClickName) > 0) - { - ss.write((const char*)item->ClickName, strlen(item->ClickName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 - - SoD::structs::ProcEffectStruct ipes; - memset(&ipes, 0, sizeof(SoD::structs::ProcEffectStruct)); - - ipes.effect = item->Proc.Effect; - ipes.level2 = item->Proc.Level2; - ipes.type = item->Proc.Type; - ipes.level = item->Proc.Level; - ipes.procrate = item->ProcRate; - - ss.write((const char*)&ipes, sizeof(SoD::structs::ProcEffectStruct)); - - if(strlen(item->ProcName) > 0) - { - ss.write((const char*)item->ProcName, strlen(item->ProcName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 - - SoD::structs::WornEffectStruct iwes; - memset(&iwes, 0, sizeof(SoD::structs::WornEffectStruct)); - - iwes.effect = item->Worn.Effect; - iwes.level2 = item->Worn.Level2; - iwes.type = item->Worn.Type; - iwes.level = item->Worn.Level; - - ss.write((const char*)&iwes, sizeof(SoD::structs::WornEffectStruct)); - - if(strlen(item->WornName) > 0) - { - ss.write((const char*)item->WornName, strlen(item->WornName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - SoD::structs::WornEffectStruct ifes; - memset(&ifes, 0, sizeof(SoD::structs::WornEffectStruct)); - - ifes.effect = item->Focus.Effect; - ifes.level2 = item->Focus.Level2; - ifes.type = item->Focus.Type; - ifes.level = item->Focus.Level; - - ss.write((const char*)&ifes, sizeof(SoD::structs::WornEffectStruct)); - - if(strlen(item->FocusName) > 0) - { - ss.write((const char*)item->FocusName, strlen(item->FocusName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - SoD::structs::WornEffectStruct ises; - memset(&ises, 0, sizeof(SoD::structs::WornEffectStruct)); - - ises.effect = item->Scroll.Effect; - ises.level2 = item->Scroll.Level2; - ises.type = item->Scroll.Type; - ises.level = item->Scroll.Level; - - ss.write((const char*)&ises, sizeof(SoD::structs::WornEffectStruct)); - - if(strlen(item->ScrollName) > 0) - { - ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - // End of Effects - - SoD::structs::ItemQuaternaryBodyStruct iqbs; - memset(&iqbs, 0, sizeof(SoD::structs::ItemQuaternaryBodyStruct)); - - iqbs.scriptfileid = item->ScriptFileID; - iqbs.quest_item = item->QuestItemFlag; - iqbs.unknown15 = 0xffffffff; - - iqbs.Purity = item->Purity; - iqbs.BackstabDmg = item->BackstabDmg; - iqbs.DSMitigation = item->DSMitigation; - iqbs.HeroicStr = item->HeroicStr; - iqbs.HeroicInt = item->HeroicInt; - iqbs.HeroicWis = item->HeroicWis; - iqbs.HeroicAgi = item->HeroicAgi; - iqbs.HeroicDex = item->HeroicDex; - iqbs.HeroicSta = item->HeroicSta; - iqbs.HeroicCha = item->HeroicCha; - iqbs.HeroicMR = item->HeroicMR; - iqbs.HeroicFR = item->HeroicFR; - iqbs.HeroicCR = item->HeroicCR; - iqbs.HeroicDR = item->HeroicDR; - iqbs.HeroicPR = item->HeroicPR; - iqbs.HeroicSVCorrup = item->HeroicSVCorrup; - iqbs.HealAmt = item->HealAmt; - iqbs.SpellDmg = item->SpellDmg; - iqbs.clairvoyance = item->Clairvoyance; - - iqbs.subitem_count = 0; - - char *SubSerializations[10]; // - - uint32 SubLengths[10]; - - for(int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { - - SubSerializations[x] = nullptr; - - const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); - - if(subitem) { - - int SubSlotNumber; - - iqbs.subitem_count++; - - if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? - //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); - SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); - else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) - //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); - else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) - //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); - else - SubSlotNumber = slot_id_in; // ??????? - - /* - // TEST CODE: - SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); - */ - - SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); - } - } - - ss.write((const char*)&iqbs, sizeof(SoD::structs::ItemQuaternaryBodyStruct)); - - for(int x = 0; x < 10; ++x) { - - if(SubSerializations[x]) { - - ss.write((const char*)&x, sizeof(uint32)); - - ss.write(SubSerializations[x], SubLengths[x]); - - safe_delete_array(SubSerializations[x]); - } - } - - char* item_serial = new char[ss.tellp()]; - memset(item_serial, 0, ss.tellp()); - memcpy(item_serial, ss.str().c_str(), ss.tellp()); - - *length = ss.tellp(); - return item_serial; } - -DECODE(OP_Bug) -{ - DECODE_LENGTH_EXACT(structs::BugStruct); - SETUP_DIRECT_DECODE(BugStruct, structs::BugStruct); - strn0cpy(emu->chartype, eq->chartype, sizeof(emu->chartype)); - strn0cpy(emu->name, eq->name, sizeof(emu->name)); - strn0cpy(emu->ui, eq->ui, sizeof(emu->ui)); - IN(x); - IN(y); - IN(z); - IN(heading); - strn0cpy(emu->target_name, eq->target_name, sizeof(emu->target_name)); - strn0cpy(emu->bug, eq->bug, sizeof(emu->bug)); - strn0cpy(emu->system_info, eq->system_info, sizeof(emu->system_info)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySellSelection) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); - IN(merchant_entity_id); - emu->slot_id = SoDToServerSlot(eq->slot_id); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySell) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - IN(merchant_entity_id); - emu->slot_id = SoDToServerSlot(eq->slot_id); - IN(charges); - IN(cost); - FINISH_DIRECT_DECODE(); -} - - -} //end namespace SoD +// end namespace SoD diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 4116d3251..5e00a2870 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1,4 +1,3 @@ - #include "../debug.h" #include "sof.h" #include "../opcodemgr.h" @@ -7,6 +6,7 @@ #include "../crc32.h" #include "../eq_packet_structs.h" +#include "../misc_functions.h" #include "../string_util.h" #include "../item.h" #include "sof_structs.h" @@ -15,903 +15,1873 @@ #include #include -namespace SoF { +namespace SoF +{ + static const char *name = "SoF"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "SoF"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); -char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline uint32 ServerToSoFSlot(uint32 ServerSlot); + static inline uint32 ServerToSoFCorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 SoFToServerSlot(uint32 SoFSlot); + static inline uint32 SoFToServerCorpseSlot(uint32 SoFCorpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "sof_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "sof_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClientSoF; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClientSoF; + } #include "ss_define.h" - -// Converts Server Slot IDs to SoF Slot IDs for use in Encodes -static inline uint32 ServerToSoFSlot(uint32 ServerSlot) { - uint32 SoFSlot = 0; - - if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots - SoFSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) - SoFSlot = ServerSlot + 11; - - else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) - SoFSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) - SoFSlot = ServerSlot + 1; - - else if (ServerSlot == MainPowerSource) - SoFSlot = slots::MainPowerSource; - - else - SoFSlot = ServerSlot; - - return SoFSlot; -} - -// Converts SoF Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 SoFToServerSlot(uint32 SoFSlot) { - uint32 ServerSlot = 0; - - if(SoFSlot >= slots::MainAmmo && SoFSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots - ServerSlot = SoFSlot - 1; - - else if(SoFSlot >= consts::GENERAL_BAGS_BEGIN && SoFSlot <= consts::CURSOR_BAG_END) - ServerSlot = SoFSlot - 11; - - else if(SoFSlot >= consts::BANK_BAGS_BEGIN && SoFSlot <= consts::BANK_BAGS_END) - ServerSlot = SoFSlot - 1; - - else if(SoFSlot >= consts::SHARED_BANK_BAGS_BEGIN && SoFSlot <= consts::SHARED_BANK_BAGS_END) - ServerSlot = SoFSlot - 1; - - else if(SoFSlot == slots::MainPowerSource) - ServerSlot = MainPowerSource; - - else - ServerSlot = SoFSlot; - - - return ServerSlot; -} - -/* -// Converts Server Corpse Slot IDs to SoF Corpse Slot IDs for use in Encodes -static inline uint32 ServerToSoFCorpseSlot(uint32 ServerCorpse) { - uint32 SoFCorpse; - // reserved -} -*/ -/* -// Converts SoF Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 SoFToServerCorpseSlot(uint32 SoFCorpse) { - uint32 ServerCorpse; - // reserved -} -*/ - - -ENCODE(OP_OpenNewTasksWindow) { - - AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; - AvailableTaskData1_Struct* __emu_AvailableTaskData1; - AvailableTaskData2_Struct* __emu_AvailableTaskData2; - AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; - - structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; - structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; - structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; - structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; - - // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. - // - in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); - - in->pBuffer = new unsigned char[in->size]; - - unsigned char *__eq_buffer = in->pBuffer; - - __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; - - char *__eq_ptr, *__emu_Ptr; - - // Copy Header - // - // - - __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; - __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; - __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; - - __emu_Ptr = (char *) __emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); - __eq_ptr = (char *) __eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); - - for(uint32 i=0; i<__emu_AvailableTaskHeader->TaskCount; i++) { - - __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; - __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; - - __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; - // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen - // in Live packets. Changing it to 0x3f000000 makes the title red. - __eq_AvailableTaskData1->unknown1 = 0x3f800000; - __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; - __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; - - __emu_Ptr += sizeof(AvailableTaskData1_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Title - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Description - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __eq_ptr[0] = 0; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; - __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; - - __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; - __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; - __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; - __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; - - __emu_Ptr += sizeof(AvailableTaskData2_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; - __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; - - __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; - __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; - __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; - __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; - - __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); - __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_VAR_ENCODE(CharacterSelect_Struct); - - - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; - - int char_count; - int namelen = 0; - for(char_count = 0; char_count < 10; char_count++) { - if(emu->name[char_count][0] == '\0') - break; - if(strcmp(emu->name[char_count], "") == 0) - break; - namelen += strlen(emu->name[char_count]); - } - - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; - - ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); - - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; - - eq->char_count = char_count; - eq->total_chars = 10; - - unsigned char *bufptr = (unsigned char *) eq->entries; - int r; - for(r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->level = emu->level[r]; - eq2->hairstyle = emu->hairstyle[r]; - eq2->gender = emu->gender[r]; - memcpy(eq2->name, emu->name[r], strlen(emu->name[r])+1); - } - //adjust for name. - bufptr += strlen(emu->name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->beard = emu->beard[r]; - eq2->haircolor = emu->haircolor[r]; - eq2->face = emu->face[r]; - int k; - for(k = 0; k < _MaterialCount; k++) { - eq2->equip[k].equip0 = emu->equip[r][k]; - eq2->equip[k].equip1 = 0; - eq2->equip[k].itemid = 0; - eq2->equip[k].color.color = emu->cs_colors[r][k].color; - } - eq2->primary = emu->primary[r]; - eq2->secondary = emu->secondary[r]; - eq2->tutorial = emu->tutorial[r]; // was u15 - eq2->u15 = 0xff; - eq2->deity = emu->deity[r]; - eq2->zone = emu->zone[r]; - eq2->u19 = 0xFF; - eq2->race = emu->race[r]; - eq2->gohome = emu->gohome[r]; - eq2->class_ = emu->class_[r]; - eq2->eyecolor1 = emu->eyecolor1[r]; - eq2->beardcolor = emu->beardcolor[r]; - eq2->eyecolor2 = emu->eyecolor2[r]; - eq2->drakkin_heritage = emu->drakkin_heritage[r]; - eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; - eq2->drakkin_details = emu->drakkin_details[r]; - } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); - } - - FINISH_ENCODE(); - -} - -ENCODE(OP_ZoneServerInfo) { - SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); - OUT_str(ip); - OUT(port); - FINISH_ENCODE(); - - //this is SUCH bullshit to be doing from down here. but the - // new client requires us to close immediately following this - // packet, so do it. - //dest->Close(); -} - -//hack hack hack -ENCODE(OP_SendZonepoints) { - ENCODE_LENGTH_ATLEAST(ZonePoints); - - SETUP_VAR_ENCODE(ZonePoints); - ALLOC_VAR_ENCODE(structs::ZonePoints, __packet->size); - - memcpy(eq, emu, __packet->size); - - FINISH_ENCODE(); -// unknown0xxx[24]; - //this is utter crap... the client is waiting for this - //certain 0 length opcode to come after the reqclientspawn - //stuff... so this is a dirty way to put it in there. - // this needs to be done better - - //EQApplicationPacket hack_test(OP_PetitionUnCheckout, 0); - //dest->QueuePacket(&hack_test); - -} - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for SoF - // clientver 1 is for all clients and 4 is for SoF - if (emu->clientver <= 4 ) +// ENCODE methods + ENCODE(OP_Action) { - OUT(id); - eq->unknown004 = 1; - //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->title_sid = emu->id - emu->current_level + 1; - //eq->desc_sid = emu->id - emu->current_level + 1; - eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->sof_next_skill); - eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->sof_next_skill); - eq->title_sid = emu->sof_next_skill; - eq->desc_sid = emu->sof_next_skill; - OUT(class_type); + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); + + OUT(target); + OUT(source); + OUT(level); + OUT(instrument_mod); + eq->sequence = emu->sequence; + OUT(type); + //OUT(damage); + OUT(spell); + eq->level2 = emu->level; + OUT(buff_unknown); // if this is 4, a buff icon is made + //eq->unknown0036 = -1; + //eq->unknown0040 = -1; + //eq->unknown0044 = -1; + + FINISH_ENCODE(); + } + + ENCODE(OP_AdventureMerchantSell) + { + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToSoFSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); + } + + ENCODE(OP_AltCurrencySell) + { + ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); + SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); + + OUT(merchant_entity_id); + eq->slot_id = ServerToSoFSlot(emu->slot_id); + OUT(charges); OUT(cost); - OUT(seq); - OUT(current_level); - OUT(prereq_skill); - OUT(prereq_minpoints); - eq->type = emu->sof_type; - OUT(spellid); - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - //eq->max_level = emu->sof_max_level; - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - eq->aa_expansion = emu->aa_expansion; - eq->special_category = emu->special_category; - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - -ENCODE(OP_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); - - uint32 r; - - eq->available_slots=0xffffffff; - memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); - memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); - -// OUT(checksum); - OUT(gender); - OUT(race); - OUT(class_); -// OUT(unknown00016); - OUT(level); - eq->level1 = emu->level; -// OUT(unknown00022[2]); - for(r = 0; r < 5; r++) { - OUT(binds[r].zoneId); - OUT(binds[r].x); - OUT(binds[r].y); - OUT(binds[r].z); - OUT(binds[r].heading); - } - OUT(deity); - OUT(intoxication); - OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); - OUT(abilitySlotRefresh); - OUT(points); // Relocation Test -// OUT(unknown0166[4]); - OUT(haircolor); - OUT(beardcolor); - OUT(eyecolor1); - OUT(eyecolor2); - OUT(hairstyle); - OUT(beard); -// OUT(unknown00178[10]); - for(r = 0; r < 9; r++) { - eq->equipment[r].equip0 = emu->item_material[r]; - eq->equipment[r].equip1 = 0; - eq->equipment[r].itemId = 0; - //eq->colors[r].color = emu->colors[r].color; - } - for(r = 0; r < 7; r++) { - OUT(item_tint[r].color); - } -// OUT(unknown00224[48]); - //NOTE: new client supports 300 AAs, our internal rep/PP - //only supports 240.. - for(r = 0; r < MAX_PP_AA_ARRAY; r++) { - OUT(aa_array[r].AA); - OUT(aa_array[r].value); - } -// OUT(unknown02220[4]); - OUT(mana); - OUT(cur_hp); - OUT(STR); - OUT(STA); - OUT(CHA); - OUT(AGI); - OUT(INT); - OUT(DEX); - OUT(WIS); - OUT(face); -// OUT(unknown02264[47]); - OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); -// OUT(unknown4184[128]); - OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); -// OUT(unknown04396[32]); - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - OUT(platinum_cursor); - OUT(gold_cursor); - OUT(silver_cursor); - OUT(copper_cursor); - - OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) - -// OUT(unknown04760[236]); - OUT(toxicity); - OUT(thirst_level); - OUT(hunger_level); - for(r = 0; r < structs::BUFF_COUNT; r++) { - OUT(buffs[r].slotid); - OUT(buffs[r].level); - OUT(buffs[r].bard_modifier); - OUT(buffs[r].effect); - OUT(buffs[r].spellid); - OUT(buffs[r].duration); - OUT(buffs[r].counters); - OUT(buffs[r].player_id); - } - for(r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { - OUT(disciplines.values[r]); - } - OUT_array(recastTimers, structs::MAX_RECAST_TYPES); -// OUT(unknown08124[360]); - OUT(endurance); - OUT(aapoints_spent); - OUT(aapoints); -// OUT(unknown06160[4]); - //NOTE: new client supports 20 bandoliers, our internal rep - //only supports 4.. - for(r = 0; r < 4; r++) { - OUT_str(bandoliers[r].name); - uint32 k; - for(k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { - OUT(bandoliers[r].items[k].item_id); - OUT(bandoliers[r].items[k].icon); - OUT_str(bandoliers[r].items[k].item_name); - } - } -// OUT(unknown07444[5120]); - for(r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { - OUT(potionbelt.items[r].item_id); - OUT(potionbelt.items[r].icon); - OUT_str(potionbelt.items[r].item_name); - } -// OUT(unknown12852[8]); -// OUT(unknown12864[76]); - OUT_str(name); - OUT_str(last_name); - OUT(guild_id); - OUT(birthday); - OUT(lastlogin); - OUT(timePlayedMin); - OUT(pvp); - OUT(anon); - OUT(gm); - OUT(guildrank); - OUT(guildbanker); -// OUT(unknown13054[12]); - OUT(exp); -// OUT(unknown13072[8]); - OUT(timeentitledonaccount); - OUT_array(languages, structs::MAX_PP_LANGUAGE); -// OUT(unknown13109[7]); - OUT(y); //reversed x and y - OUT(x); - OUT(z); - OUT(heading); -// OUT(unknown13132[4]); - OUT(platinum_bank); - OUT(gold_bank); - OUT(silver_bank); - OUT(copper_bank); - OUT(platinum_shared); -// OUT(unknown13156[84]); - //OUT(expansions); - eq->expansions = 16383; -// OUT(unknown13244[12]); - OUT(autosplit); -// OUT(unknown13260[16]); - OUT(zone_id); - OUT(zoneInstance); - for(r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { - OUT_str(groupMembers[r]); - } - strcpy(eq->groupLeader, emu->groupMembers[0]); -// OUT_str(groupLeader); -// OUT(unknown13728[660]); - OUT(entityid); - OUT(leadAAActive); -// OUT(unknown14392[4]); - OUT(ldon_points_guk); - OUT(ldon_points_mir); - OUT(ldon_points_mmc); - OUT(ldon_points_ruj); - OUT(ldon_points_tak); - OUT(ldon_points_available); -// OUT(unknown14420[132]); - OUT(tribute_time_remaining); - OUT(career_tribute_points); -// OUT(unknown7208); - OUT(tribute_points); -// OUT(unknown7216); - OUT(tribute_active); - for(r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { - OUT(tributes[r].tribute); - OUT(tributes[r].tier); - } -// OUT(unknown14616[8]); - OUT(group_leadership_exp); -// OUT(unknown14628); - OUT(raid_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_points); - OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); -// OUT(unknown14772[128]); - OUT(air_remaining); - OUT(PVPKills); - OUT(PVPDeaths); - OUT(PVPCurrentPoints); - OUT(PVPCareerPoints); - OUT(PVPBestKillStreak); - OUT(PVPWorstDeathStreak); - OUT(PVPCurrentKillStreak); -// OUT(unknown17892[4580]); - OUT(expAA); -// OUT(unknown19516[40]); - OUT(currentRadCrystals); - OUT(careerRadCrystals); - OUT(currentEbonCrystals); - OUT(careerEbonCrystals); - OUT(groupAutoconsent); - OUT(raidAutoconsent); - OUT(guildAutoconsent); -// OUT(unknown19575[5]); - eq->level3 = emu->level; - eq->showhelm = emu->showhelm; - OUT(RestTimer); -// OUT(unknown19584[4]); -// OUT(unknown19588); - - -const uint8 bytes[] = { -0xa3,0x02,0x00,0x00,0x95,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x00,0x00,0x00, -0x19,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, -0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1F,0x85,0xEB,0x3E,0x33,0x33,0x33,0x3F, -0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; - - memcpy(eq->unknown12864, bytes, sizeof(bytes)); - - - - //set the checksum... - CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct)-4); - - FINISH_ENCODE(); -} - -ENCODE(OP_NewZone) { - SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); - OUT_str(char_name); - OUT_str(zone_short_name); - OUT_str(zone_long_name); - OUT(ztype); - int r; - for(r = 0; r < 4; r++) { - OUT(fog_red[r]); - OUT(fog_green[r]); - OUT(fog_blue[r]); - OUT(fog_minclip[r]); - OUT(fog_maxclip[r]); - } - OUT(gravity); - OUT(time_type); - for(r = 0; r < 4; r++) { - OUT(rain_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(rain_duration[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_duration[r]); - } - for(r = 0; r < 32; r++) { - eq->unknown537[r] = 0xFF; //observed - } - OUT(sky); - OUT(zone_exp_multiplier); - OUT(safe_y); - OUT(safe_x); - OUT(safe_z); - OUT(max_z); - OUT(underworld); - OUT(minclip); - OUT(maxclip); - OUT_str(zone_short_name2); - OUT(zone_id); - OUT(zone_instance); - OUT(SuspendBuffs); - - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown796 = -1; - eq->unknown840 = 600; - eq->unknown876 = 50; - eq->unknown880 = 10; - eq->unknown884 = 1; - eq->unknown885 = 0; - eq->unknown886 = 1; - eq->unknown887 = 0; - eq->unknown888 = 0; - eq->unknown889 = 0; - eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off - eq->unknown891 = 0; - eq->unknown892 = 180; - eq->unknown896 = 180; - eq->unknown900 = 180; - eq->unknown904 = 2; - eq->unknown908 = 2; - - FINISH_ENCODE(); -} - -ENCODE(OP_Track) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + ENCODE(OP_ApplyPoison) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); - delete in; - return; + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToSoFSlot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); } - in->size = sizeof(structs::Track_Struct) * EntryCount; - in->pBuffer = new unsigned char[in->size]; - structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++eq, ++emu) + ENCODE(OP_BazaarSearch) { + if (((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) { + + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; + } + + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + + //determine and verify length + int entrycount = in->size / sizeof(BazaarSearchResults_Struct); + if (entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + + //make the EQ struct. + in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount; + in->pBuffer = new unsigned char[in->size]; + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer; + + //zero out the packet. We could avoid this memset by setting all fields (including unknowns) + //in the loop. + memset(in->pBuffer, 0, in->size); + + for (int i = 0; iBeginning.Action = emu->Beginning.Action; + eq->Beginning.Unknown001 = emu->Beginning.Unknown001; + eq->Beginning.Unknown002 = emu->Beginning.Unknown002; + eq->NumItems = emu->NumItems; + eq->SerialNumber = emu->SerialNumber; + eq->SellerID = emu->SellerID; + eq->Cost = emu->Cost; + eq->ItemStat = emu->ItemStat; + strcpy(eq->ItemName, emu->ItemName); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_BecomeTrader) + { + ENCODE_LENGTH_EXACT(BecomeTrader_Struct); + SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); + + OUT(ID); + OUT(Code); + + FINISH_ENCODE(); + } + + ENCODE(OP_Buff) + { + ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); + SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); + OUT(entityid); - OUT(padding002); - OUT(distance); + OUT(slot); + OUT(level); + OUT(effect); + //eq->unknown7 = 10; + OUT(spellid); + OUT(duration); + OUT(slotid); + OUT(bufffade); + + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_CancelTrade) + { + ENCODE_LENGTH_EXACT(CancelTrade_Struct); + SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); - dest->FastQueuePacket(&in, ack_req); -} + OUT(fromid); + OUT(action); -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; - - //determine and verify length - int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); - delete in; - return; + FINISH_ENCODE(); } - //make the EQ struct. - in->size = sizeof(structs::Spawn_Struct)*entrycount; - in->pBuffer = new unsigned char[in->size]; - structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; + ENCODE(OP_CharInventory) + { + //consume the packet + EQApplicationPacket *in = *p; - //zero out the packet. We could avoid this memset by setting all fields (including unknowns) - //in the loop. - memset(in->pBuffer, 0, in->size); + *p = nullptr; - //do the transform... - int r; - int k; - for(r = 0; r < entrycount; r++, eq++, emu++) { + if (in->size == 0) { + in->size = 4; + in->pBuffer = new uchar[in->size]; + *((uint32 *)in->pBuffer) = 0; - eq->showname = 1; //New Field - Toggles Name Display on or off - 0 = off, 1 = on - eq->linkdead = 0; //New Field - Toggles LD on or off after name - 0 = off, 1 = on - eq->statue = 0; //New Field - 1 freezes animation - eq->showhelm = emu->showhelm; - eq->deity = emu->deity; - eq->drakkin_heritage = emu->drakkin_heritage; - eq->gender = emu->gender; - for(k = 0; k < 9; k++) { - eq->equipment[k].equip0 = emu->equipment[k]; - eq->equipment[k].equip1 = 0; - eq->equipment[k].itemId = 0; - eq->colors[k].color = emu->colors[k].color; + dest->FastQueuePacket(&in, ack_req); + return; } - eq->StandState = emu->StandState; - eq->guildID = emu->guildID; - eq->spelleffect = 0; - eq->spelleffect2 = 0; - eq->spelleffect3 = 0; - eq->spelleffect4 = 0; - eq->spelleffect5 = 0; - eq->spelleffect6 = 0; - eq->class_ = emu->class_; - eq->flymode = emu->flymode; - eq->gm = emu->gm; - eq->helm = emu->helm; - eq->drakkin_tattoo = emu->drakkin_tattoo; - eq->beardcolor = emu->beardcolor; - eq->runspeed = emu->runspeed; - eq->light = emu->light; - eq->level = emu->level; - eq->lfg = emu->lfg; - eq->hairstyle = emu->hairstyle; - eq->haircolor = emu->haircolor; - eq->race = emu->race; - strcpy(eq->suffix, emu->suffix); - eq->findable = emu->findable; - if(emu->bodytype >= 66) + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + + int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); + + if (ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + + delete in; + return; + } + + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; + + in->pBuffer = new uchar[4]; + *(uint32 *)in->pBuffer = ItemCount; + in->size = 4; + + for (int r = 0; r < ItemCount; r++, eq++) { + + uint32 Length = 0; + + char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); + + if (Serialized) { + uchar *OldBuffer = in->pBuffer; + + in->pBuffer = new uchar[in->size + Length]; + memcpy(in->pBuffer, OldBuffer, in->size); + + safe_delete_array(OldBuffer); + + memcpy(in->pBuffer + in->size, Serialized, Length); + in->size += Length; + + safe_delete_array(Serialized); + + } + else { + _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } + } + + delete[] __emu_buffer; + + //_log(NET__ERROR, "Sending inventory to client"); + //_hex(NET__ERROR, in->pBuffer, in->size); + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ClientUpdate) + { + ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); + SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); + + OUT(spawn_id); + OUT(x_pos); + OUT(delta_x); + OUT(delta_y); + OUT(z_pos); + OUT(delta_heading); + OUT(y_pos); + OUT(delta_z); + OUT(animation); + OUT(heading); + + FINISH_ENCODE(); + } + + ENCODE(OP_Consider) + { + ENCODE_LENGTH_EXACT(Consider_Struct); + SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); + + OUT(playerid); + OUT(targetid); + OUT(faction); + OUT(level); + OUT(pvpcon); + + FINISH_ENCODE(); + } + + ENCODE(OP_Damage) + { + ENCODE_LENGTH_EXACT(CombatDamage_Struct); + SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + OUT(target); + OUT(source); + OUT(type); + OUT(spellid); + OUT(damage); + eq->sequence = emu->sequence; + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToSoFSlot(emu->from_slot); + eq->to_slot = ServerToSoFSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteSpawn) + { + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); + + OUT(spawn_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzCompass) + { + SETUP_VAR_ENCODE(ExpeditionCompass_Struct); + ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); + OUT(count); + + for (uint32 i = 0; i < emu->count; ++i) { - eq->bodytype = 11; //non-targetable - eq->showname = 0; //no visible name - eq->race = 127; //invisible man - eq->gender = 0; //invisible men are gender 0 + OUT(entries[i].x); + OUT(entries[i].y); + OUT(entries[i].z); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionEndsWarning) + { + ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); + SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); + + OUT(minutes_remaining); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionInfo) + { + ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + + OUT(max_players); + eq->enabled_max = 1; + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->leader_name, emu->leader_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionList) + { + SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (int i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); + ss.write((const char*)&null_term, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzJoinExpeditionConfirm) + { + ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); + SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); + + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->player_name, emu->player_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzLeaderStatus) + { + SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + + ss.write((const char*)&client_id, sizeof(uint32)); + //ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->leader_name, strlen(emu->leader_name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//1 + ss.write((const char*)&client_id, sizeof(uint32)); + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzMemberList) + { + SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].status, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpansionInfo) + { + ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + + OUT(Expansions); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroundSpawn) + { + ENCODE_LENGTH_EXACT(Object_Struct); + SETUP_DIRECT_ENCODE(Object_Struct, structs::Object_Struct); + + OUT(drop_id); + OUT(zone_id); + OUT(zone_instance); + OUT(heading); + OUT(x); + OUT(y); + OUT(z); + OUT_str(object_name); + OUT(object_type); + OUT(spawn_id); + + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown020 = 0; + eq->unknown024 = 0; + eq->size = 1; //This forces all objects to standard size for now + eq->unknown088 = 0; + memset(eq->unknown096, 0xFF, sizeof(eq->unknown096)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GuildMemberList) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count*sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header + emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data + ); + const char *emu_note = (emu_name + + emu->name_length + //skip name contents + emu->count //skip string terminators + ); + + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { + + //the order we set things here must match the struct + + //nice helper macro + /*#define SlideStructString(field, str) \ + strcpy(e->field, str.c_str()); \ + e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ +#define SlideStructString(field, str) \ + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) + + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + PutFieldN(rank); + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + PutFieldN(total_tribute); + PutFieldN(last_tribute); + e->unknown_one = htonl(1); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); + +#undef SlideStructString +#undef PutFieldN + + e++; + } + } + + delete[] __emu_buffer; + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + OUT(race); + OUT(unknown006[0]); + OUT(unknown006[1]); + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + OUT(drakkin_heritage); + OUT(drakkin_tattoo); + OUT(drakkin_details); + + FINISH_ENCODE(); + } + + ENCODE(OP_InspectRequest) + { + ENCODE_LENGTH_EXACT(Inspect_Struct); + SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); + + OUT(TargetID); + OUT(PlayerID); + + FINISH_ENCODE(); + } + + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } + + ENCODE(OP_ItemPacket) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); + + uint32 length; + char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); + + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 4; + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length); + + delete[] __emu_buffer; + safe_delete_array(serialized); + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ItemVerifyReply) + { + ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); + SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); + + eq->slot = ServerToSoFSlot(emu->slot); + OUT(spell); + OUT(target); + + FINISH_ENCODE(); + } + + ENCODE(OP_LeadershipExpUpdate) + { + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LogServer) + { + ENCODE_LENGTH_EXACT(LogServer_Struct); + SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); + + strcpy(eq->worldshortname, emu->worldshortname); + + OUT(enablevoicemacros); + OUT(enablemail); + OUT(enable_pvp); + OUT(enable_FV); + + // These next two need to be set like this for the Tutorial Button to work. + eq->unknown263[0] = 0; + eq->unknown263[2] = 1; + + FINISH_ENCODE(); + } + + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = emu->slot_id + 1; + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_ManaChange) + { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); + + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? + + FINISH_ENCODE(); + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToSoFSlot(emu->from_slot); + eq->to_slot = ServerToSoFSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_NewZone) + { + SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); + + OUT_str(char_name); + OUT_str(zone_short_name); + OUT_str(zone_long_name); + OUT(ztype); + int r; + for (r = 0; r < 4; r++) { + OUT(fog_red[r]); + OUT(fog_green[r]); + OUT(fog_blue[r]); + OUT(fog_minclip[r]); + OUT(fog_maxclip[r]); + } + OUT(gravity); + OUT(time_type); + for (r = 0; r < 4; r++) { + OUT(rain_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(rain_duration[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_duration[r]); + } + for (r = 0; r < 32; r++) { + eq->unknown537[r] = 0xFF; //observed + } + OUT(sky); + OUT(zone_exp_multiplier); + OUT(safe_y); + OUT(safe_x); + OUT(safe_z); + OUT(max_z); + OUT(underworld); + OUT(minclip); + OUT(maxclip); + OUT_str(zone_short_name2); + OUT(zone_id); + OUT(zone_instance); + OUT(SuspendBuffs); + + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown796 = -1; + eq->unknown840 = 600; + eq->unknown876 = 50; + eq->unknown880 = 10; + eq->unknown884 = 1; + eq->unknown885 = 0; + eq->unknown886 = 1; + eq->unknown887 = 0; + eq->unknown888 = 0; + eq->unknown889 = 0; + eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off + eq->unknown891 = 0; + eq->unknown892 = 180; + eq->unknown896 = 180; + eq->unknown900 = 180; + eq->unknown904 = 2; + eq->unknown908 = 2; + + FINISH_ENCODE(); + } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + OUT_str(Title); + OUT_str(Text); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + + eq->unknown4236 = 0x00000000; + eq->unknown4240 = 0xffffffff; + + FINISH_ENCODE(); + } + + ENCODE(OP_OpenNewTasksWindow) + { + AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; + AvailableTaskData1_Struct* __emu_AvailableTaskData1; + AvailableTaskData2_Struct* __emu_AvailableTaskData2; + AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; + + structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; + structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; + structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; + structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; + + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + + __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; + + // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. + // + in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); + + in->pBuffer = new unsigned char[in->size]; + + unsigned char *__eq_buffer = in->pBuffer; + + __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; + + char *__eq_ptr, *__emu_Ptr; + + // Copy Header + // + // + + __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; + __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; + __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; + + __emu_Ptr = (char *)__emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); + __eq_ptr = (char *)__eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); + + for (uint32 i = 0; i<__emu_AvailableTaskHeader->TaskCount; i++) { + + __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; + __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; + + __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; + // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen + // in Live packets. Changing it to 0x3f000000 makes the title red. + __eq_AvailableTaskData1->unknown1 = 0x3f800000; + __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; + __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; + + __emu_Ptr += sizeof(AvailableTaskData1_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Title + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Description + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __eq_ptr[0] = 0; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; + __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; + + __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; + __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; + __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; + __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; + + __emu_Ptr += sizeof(AvailableTaskData2_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; + __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; + + __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; + __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; + __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; + __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; + + __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); + __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_PetBuffWindow) + { + ENCODE_LENGTH_EXACT(PetBuff_Struct); + SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); + + OUT(petid); + OUT(buffcount); + + int EQBuffSlot = 0; + + for (uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) + { + if (emu->spellid[EmuBuffSlot]) + { + eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; + eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_PlayerProfile) + { + SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); + + uint32 r; + + eq->available_slots = 0xffffffff; + memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); + memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); + + // OUT(checksum); + OUT(gender); + OUT(race); + OUT(class_); + // OUT(unknown00016); + OUT(level); + eq->level1 = emu->level; + // OUT(unknown00022[2]); + for (r = 0; r < 5; r++) { + OUT(binds[r].zoneId); + OUT(binds[r].x); + OUT(binds[r].y); + OUT(binds[r].z); + OUT(binds[r].heading); + } + OUT(deity); + OUT(intoxication); + OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); + OUT(abilitySlotRefresh); + OUT(points); // Relocation Test + // OUT(unknown0166[4]); + OUT(haircolor); + OUT(beardcolor); + OUT(eyecolor1); + OUT(eyecolor2); + OUT(hairstyle); + OUT(beard); + // OUT(unknown00178[10]); + for (r = 0; r < 9; r++) { + eq->equipment[r].equip0 = emu->item_material[r]; + eq->equipment[r].equip1 = 0; + eq->equipment[r].itemId = 0; + //eq->colors[r].color = emu->colors[r].color; + } + for (r = 0; r < 7; r++) { + OUT(item_tint[r].color); + } + // OUT(unknown00224[48]); + //NOTE: new client supports 300 AAs, our internal rep/PP + //only supports 240.. + for (r = 0; r < MAX_PP_AA_ARRAY; r++) { + OUT(aa_array[r].AA); + OUT(aa_array[r].value); + } + // OUT(unknown02220[4]); + OUT(mana); + OUT(cur_hp); + OUT(STR); + OUT(STA); + OUT(CHA); + OUT(AGI); + OUT(INT); + OUT(DEX); + OUT(WIS); + OUT(face); + // OUT(unknown02264[47]); + OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); + // OUT(unknown4184[128]); + OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); + // OUT(unknown04396[32]); + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + OUT(platinum_cursor); + OUT(gold_cursor); + OUT(silver_cursor); + OUT(copper_cursor); + + OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) + + // OUT(unknown04760[236]); + OUT(toxicity); + OUT(thirst_level); + OUT(hunger_level); + for (r = 0; r < structs::BUFF_COUNT; r++) { + OUT(buffs[r].slotid); + OUT(buffs[r].level); + OUT(buffs[r].bard_modifier); + OUT(buffs[r].effect); + OUT(buffs[r].spellid); + OUT(buffs[r].duration); + OUT(buffs[r].counters); + OUT(buffs[r].player_id); + } + for (r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { + OUT(disciplines.values[r]); + } + OUT_array(recastTimers, structs::MAX_RECAST_TYPES); + // OUT(unknown08124[360]); + OUT(endurance); + OUT(aapoints_spent); + OUT(aapoints); + // OUT(unknown06160[4]); + //NOTE: new client supports 20 bandoliers, our internal rep + //only supports 4.. + for (r = 0; r < 4; r++) { + OUT_str(bandoliers[r].name); + uint32 k; + for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { + OUT(bandoliers[r].items[k].item_id); + OUT(bandoliers[r].items[k].icon); + OUT_str(bandoliers[r].items[k].item_name); + } + } + // OUT(unknown07444[5120]); + for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { + OUT(potionbelt.items[r].item_id); + OUT(potionbelt.items[r].icon); + OUT_str(potionbelt.items[r].item_name); + } + // OUT(unknown12852[8]); + // OUT(unknown12864[76]); + OUT_str(name); + OUT_str(last_name); + OUT(guild_id); + OUT(birthday); + OUT(lastlogin); + OUT(timePlayedMin); + OUT(pvp); + OUT(anon); + OUT(gm); + OUT(guildrank); + OUT(guildbanker); + // OUT(unknown13054[12]); + OUT(exp); + // OUT(unknown13072[8]); + OUT(timeentitledonaccount); + OUT_array(languages, structs::MAX_PP_LANGUAGE); + // OUT(unknown13109[7]); + OUT(y); //reversed x and y + OUT(x); + OUT(z); + OUT(heading); + // OUT(unknown13132[4]); + OUT(platinum_bank); + OUT(gold_bank); + OUT(silver_bank); + OUT(copper_bank); + OUT(platinum_shared); + // OUT(unknown13156[84]); + //OUT(expansions); + eq->expansions = 16383; + // OUT(unknown13244[12]); + OUT(autosplit); + // OUT(unknown13260[16]); + OUT(zone_id); + OUT(zoneInstance); + for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { + OUT_str(groupMembers[r]); + } + strcpy(eq->groupLeader, emu->groupMembers[0]); + // OUT_str(groupLeader); + // OUT(unknown13728[660]); + OUT(entityid); + OUT(leadAAActive); + // OUT(unknown14392[4]); + OUT(ldon_points_guk); + OUT(ldon_points_mir); + OUT(ldon_points_mmc); + OUT(ldon_points_ruj); + OUT(ldon_points_tak); + OUT(ldon_points_available); + // OUT(unknown14420[132]); + OUT(tribute_time_remaining); + OUT(career_tribute_points); + // OUT(unknown7208); + OUT(tribute_points); + // OUT(unknown7216); + OUT(tribute_active); + for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { + OUT(tributes[r].tribute); + OUT(tributes[r].tier); + } + // OUT(unknown14616[8]); + OUT(group_leadership_exp); + // OUT(unknown14628); + OUT(raid_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_points); + OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); + // OUT(unknown14772[128]); + OUT(air_remaining); + OUT(PVPKills); + OUT(PVPDeaths); + OUT(PVPCurrentPoints); + OUT(PVPCareerPoints); + OUT(PVPBestKillStreak); + OUT(PVPWorstDeathStreak); + OUT(PVPCurrentKillStreak); + // OUT(unknown17892[4580]); + OUT(expAA); + // OUT(unknown19516[40]); + OUT(currentRadCrystals); + OUT(careerRadCrystals); + OUT(currentEbonCrystals); + OUT(careerEbonCrystals); + OUT(groupAutoconsent); + OUT(raidAutoconsent); + OUT(guildAutoconsent); + // OUT(unknown19575[5]); + eq->level3 = emu->level; + eq->showhelm = emu->showhelm; + OUT(RestTimer); + // OUT(unknown19584[4]); + // OUT(unknown19588); + + const uint8 bytes[] = { + 0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + memcpy(eq->unknown12864, bytes, sizeof(bytes)); + + //set the checksum... + CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); + + FINISH_ENCODE(); + } + + ENCODE(OP_RaidJoin) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; + + general->action = 8; + general->parameter = 1; + strn0cpy(general->leader_name, raid_create->leader_name, 64); + strn0cpy(general->player_name, raid_create->leader_name, 64); + + dest->FastQueuePacket(&outapp_create); + delete[] __emu_buffer; + } + + ENCODE(OP_RaidUpdate) + { + EQApplicationPacket *inapp = *p; + *p = nullptr; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; + + if (raid_gen->action == 0) // raid add has longer length than other raid updates + { + RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); + structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + + add_member->raidGen.action = in_add_member->raidGen.action; + add_member->raidGen.parameter = in_add_member->raidGen.parameter; + strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); + strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); + add_member->_class = in_add_member->_class; + add_member->level = in_add_member->level; + add_member->isGroupLeader = in_add_member->isGroupLeader; + add_member->flags[0] = in_add_member->flags[0]; + add_member->flags[1] = in_add_member->flags[1]; + add_member->flags[2] = in_add_member->flags[2]; + add_member->flags[3] = in_add_member->flags[3]; + add_member->flags[4] = in_add_member->flags[4]; + dest->FastQueuePacket(&outapp); } else { - eq->bodytype = emu->bodytype; + RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); + strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); + raid_general->action = in_raid_general->action; + raid_general->parameter = in_raid_general->parameter; + dest->FastQueuePacket(&outapp); } - //eq->bodytype2 = 0; - eq->equip_chest2 = emu->equip_chest2; - eq->curHp = emu->curHp; - eq->invis = emu->invis; - strcpy(eq->lastName, emu->lastName); - eq->eyecolor1 = emu->eyecolor1; - strcpy(eq->title, emu->title); - eq->beard = emu->beard; - eq->targetable = 1; //New Field - Toggle Targetable on or off - 0 = off, 1 = on - eq->NPC = emu->NPC; - eq->targetable_with_hotkey = 1;//New Field - Toggle Targetable on or off - 0 = off, 1 = on - eq->x = emu->x; - eq->deltaX = emu->deltaX; - eq->deltaY = emu->deltaY; - eq->z = emu->z; - eq->deltaHeading = emu->deltaHeading; - eq->y = emu->y; - eq->deltaZ = emu->deltaZ; - eq->animation = emu->animation; - eq->heading = emu->heading; - eq->spawnId = emu->spawnId; - eq->nonvisible = 0; - strcpy(eq->name, emu->name); - eq->petOwnerId = emu->petOwnerId; - eq->pvp = 0; // 0 = non-pvp colored name, 1 = red pvp name - for(k = 0; k < 9; k++) { - eq->colors[k].color = emu->colors[k].color; - } - eq->anon = emu->anon; - eq->face = emu->face; - eq->drakkin_details = emu->drakkin_details; - eq->size = emu->size; - eq->walkspeed = emu->walkspeed; - /* - //Uncomment this section to use this hack test with NPC last names - //Hack Test for finding more fields in the Struct: - if (emu->lastName[0] == '*') // Test NPC! + + delete[] __emu_buffer; + } + + ENCODE(OP_ReadBook) + { + ENCODE_LENGTH_ATLEAST(BookText_Struct); + SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); + + if (emu->window == 0xFF) + eq->window = 0xFFFFFFFF; + else + eq->window = emu->window; + OUT(type); + eq->invslot = ServerToSoFSlot(emu->invslot); + strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for SoF + // clientver 1 is for all clients and 4 is for SoF + if (emu->clientver <= 4) { + OUT(id); + eq->unknown004 = 1; + //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->title_sid = emu->id - emu->current_level + 1; + //eq->desc_sid = emu->id - emu->current_level + 1; + eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->title_sid = emu->sof_next_skill; + eq->desc_sid = emu->sof_next_skill; + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + OUT(prereq_skill); + OUT(prereq_minpoints); + eq->type = emu->sof_type; + OUT(spellid); + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + //eq->max_level = emu->sof_max_level; + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + eq->aa_expansion = emu->aa_expansion; + eq->special_category = emu->special_category; + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_VAR_ENCODE(CharacterSelect_Struct); + + //EQApplicationPacket *packet = *p; + //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + + int char_count; + int namelen = 0; + for (char_count = 0; char_count < 10; char_count++) { + if (emu->name[char_count][0] == '\0') + break; + if (strcmp(emu->name[char_count], "") == 0) + break; + namelen += strlen(emu->name[char_count]); + } + + int total_length = sizeof(structs::CharacterSelect_Struct) + + char_count * sizeof(structs::CharacterSelectEntry_Struct) + + namelen; + + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + + //unsigned char *eq_buffer = new unsigned char[total_length]; + //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + + eq->char_count = char_count; + eq->total_chars = 10; + + unsigned char *bufptr = (unsigned char *)eq->entries; + int r; + for (r = 0; r < char_count; r++) { + { //pre-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->level = emu->level[r]; + eq2->hairstyle = emu->hairstyle[r]; + eq2->gender = emu->gender[r]; + memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); + } + //adjust for name. + bufptr += strlen(emu->name[r]); + { //post-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->beard = emu->beard[r]; + eq2->haircolor = emu->haircolor[r]; + eq2->face = emu->face[r]; + int k; + for (k = 0; k < _MaterialCount; k++) { + eq2->equip[k].equip0 = emu->equip[r][k]; + eq2->equip[k].equip1 = 0; + eq2->equip[k].itemid = 0; + eq2->equip[k].color.color = emu->cs_colors[r][k].color; + } + eq2->primary = emu->primary[r]; + eq2->secondary = emu->secondary[r]; + eq2->tutorial = emu->tutorial[r]; // was u15 + eq2->u15 = 0xff; + eq2->deity = emu->deity[r]; + eq2->zone = emu->zone[r]; + eq2->u19 = 0xFF; + eq2->race = emu->race[r]; + eq2->gohome = emu->gohome[r]; + eq2->class_ = emu->class_[r]; + eq2->eyecolor1 = emu->eyecolor1[r]; + eq2->beardcolor = emu->beardcolor[r]; + eq2->eyecolor2 = emu->eyecolor2[r]; + eq2->drakkin_heritage = emu->drakkin_heritage[r]; + eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; + eq2->drakkin_details = emu->drakkin_details[r]; + } + bufptr += sizeof(structs::CharacterSelectEntry_Struct); + } + + FINISH_ENCODE(); + } + + //hack hack hack + ENCODE(OP_SendZonepoints) + { + ENCODE_LENGTH_ATLEAST(ZonePoints); + + SETUP_VAR_ENCODE(ZonePoints); + ALLOC_VAR_ENCODE(structs::ZonePoints, __packet->size); + + memcpy(eq, emu, __packet->size); + + FINISH_ENCODE(); + // unknown0xxx[24]; + //this is utter crap... the client is waiting for this + //certain 0 length opcode to come after the reqclientspawn + //stuff... so this is a dirty way to put it in there. + // this needs to be done better + + //EQApplicationPacket hack_test(OP_PetitionUnCheckout, 0); + //dest->QueuePacket(&hack_test); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToSoFSlot(emu->itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_SomeItemPacketMaybe) + { + // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow + // and flying to the target. + // + + ENCODE_LENGTH_EXACT(Arrow_Struct); + SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); + + OUT(src_y); + OUT(src_x); + OUT(src_z); + OUT(velocity); + OUT(launch_angle); + OUT(tilt); + OUT(arc); + OUT(source_id); + OUT(target_id); + OUT(item_id); + + eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. + + OUT(item_type); + OUT(skill); + + strcpy(eq->model_name, emu->model_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_SpawnDoor) + { + SETUP_VAR_ENCODE(Door_Struct); + + int door_count = __packet->size / sizeof(Door_Struct); + int total_length = door_count * sizeof(structs::Door_Struct); + ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); + int r; + for (r = 0; r < door_count; r++) { + strcpy(eq[r].name, emu[r].name); + eq[r].xPos = emu[r].xPos; + eq[r].yPos = emu[r].yPos; + eq[r].zPos = emu[r].zPos; + eq[r].heading = emu[r].heading; + eq[r].incline = emu[r].incline; + eq[r].size = emu[r].size; + eq[r].doorId = emu[r].doorId; + eq[r].opentype = emu[r].opentype; + eq[r].state_at_spawn = emu[r].state_at_spawn; + eq[r].invert_state = emu[r].invert_state; + eq[r].door_param = emu[r].door_param; + eq[r].unknown0076 = 0; + eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0078 = 0; + eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0080 = 0; + eq[r].unknown0081 = 0; + eq[r].unknown0082 = 0; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_Stun) + { + ENCODE_LENGTH_EXACT(Stun_Struct); + SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); + + OUT(duration); + eq->unknown005 = 163; + eq->unknown006 = 67; + + FINISH_ENCODE(); + } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + in->size = sizeof(structs::Track_Struct) * EntryCount; + in->pBuffer = new unsigned char[in->size]; + structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++eq, ++emu) + { + OUT(entityid); + OUT(padding002); + OUT(distance); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Trader) + { + if ((*p)->size != sizeof(TraderBuy_Struct)) { + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; + } + + ENCODE_FORWARD(OP_TraderBuy); + } + + ENCODE(OP_TraderBuy) + { + ENCODE_LENGTH_EXACT(TraderBuy_Struct); + SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); + + OUT(Action); + OUT(Price); + OUT(TraderID); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(ItemID); + OUT(Quantity); + OUT(AlreadySold); + + FINISH_ENCODE(); + } + + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToSoFSlot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_VetRewardsAvaliable) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + + uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); + *p = nullptr; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); + uchar *old_data = __emu_buffer; + uchar *data = outapp_create->pBuffer; + for (uint32 i = 0; i < count; ++i) + { + structs::VeteranReward *vr = (structs::VeteranReward*)data; + InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + + vr->claim_count = ivr->claim_count; + vr->claim_id = ivr->claim_id; + vr->number_available = ivr->number_available; + for (int x = 0; x < 8; ++x) + { + vr->items[x].item_id = ivr->items[x].item_id; + strcpy(vr->items[x].item_name, ivr->items[x].item_name); + vr->items[x].charges = ivr->items[x].charges; + } + + old_data += sizeof(InternalVeteranReward); + data += sizeof(structs::VeteranReward); + } + + dest->FastQueuePacket(&outapp_create); + delete inapp; + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(unknown06); + OUT(elite_material); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZonePlayerToBind) + { + ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); + + ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; + structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; + unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; + structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; + + zph->x = zps->x; + zph->y = zps->y; + zph->z = zps->z; + zph->heading = zps->heading; + zph->bind_zone_id = zps->bind_zone_id; + zph->bind_instance_id = zps->bind_instance_id; + strcpy(zph->zone_name, zps->zone_name); + + zpf->unknown021 = 1; + zpf->unknown022 = 0; + zpf->unknown023 = 0; + zpf->unknown024 = 0; + + ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); + ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); + + delete[] buffer1; + delete[] buffer2; + delete[](*p)->pBuffer; + + (*p)->pBuffer = new unsigned char[ss.str().size()]; + (*p)->size = ss.str().size(); + + memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); + dest->FastQueuePacket(&(*p)); + } + + ENCODE(OP_ZoneServerInfo) + { + SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); + + OUT_str(ip); + OUT(port); + + FINISH_ENCODE(); + + //this is SUCH bullshit to be doing from down here. but the + // new client requires us to close immediately following this + // packet, so do it. + //dest->Close(); + } + + ENCODE(OP_ZoneSpawns) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; + + //determine and verify length + int entrycount = in->size / sizeof(Spawn_Struct); + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + delete in; + return; + } + + //make the EQ struct. + in->size = sizeof(structs::Spawn_Struct)*entrycount; + in->pBuffer = new unsigned char[in->size]; + structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; + + //zero out the packet. We could avoid this memset by setting all fields (including unknowns) + //in the loop. + memset(in->pBuffer, 0, in->size); + + //do the transform... + int r; + int k; + for (r = 0; r < entrycount; r++, eq++, emu++) { + + eq->showname = 1; //New Field - Toggles Name Display on or off - 0 = off, 1 = on + eq->linkdead = 0; //New Field - Toggles LD on or off after name - 0 = off, 1 = on + eq->statue = 0; //New Field - 1 freezes animation + eq->showhelm = emu->showhelm; + eq->deity = emu->deity; + eq->drakkin_heritage = emu->drakkin_heritage; + eq->gender = emu->gender; + for (k = 0; k < 9; k++) { + eq->equipment[k].equip0 = emu->equipment[k]; + eq->equipment[k].equip1 = 0; + eq->equipment[k].itemId = 0; + eq->colors[k].color = emu->colors[k].color; + } + eq->StandState = emu->StandState; + eq->guildID = emu->guildID; + eq->spelleffect = 0; + eq->spelleffect2 = 0; + eq->spelleffect3 = 0; + eq->spelleffect4 = 0; + eq->spelleffect5 = 0; + eq->spelleffect6 = 0; + eq->class_ = emu->class_; + eq->flymode = emu->flymode; + eq->gm = emu->gm; + eq->helm = emu->helm; + eq->drakkin_tattoo = emu->drakkin_tattoo; + eq->beardcolor = emu->beardcolor; + eq->runspeed = emu->runspeed; + eq->light = emu->light; + eq->level = emu->level; + eq->lfg = emu->lfg; + eq->hairstyle = emu->hairstyle; + eq->haircolor = emu->haircolor; + eq->race = emu->race; + strcpy(eq->suffix, emu->suffix); + eq->findable = emu->findable; + if (emu->bodytype >= 66) + { + eq->bodytype = 11; //non-targetable + eq->showname = 0; //no visible name + eq->race = 127; //invisible man + eq->gender = 0; //invisible men are gender 0 + } + else + { + eq->bodytype = emu->bodytype; + } + //eq->bodytype2 = 0; + eq->equip_chest2 = emu->equip_chest2; + eq->curHp = emu->curHp; + eq->invis = emu->invis; + strcpy(eq->lastName, emu->lastName); + eq->eyecolor1 = emu->eyecolor1; + strcpy(eq->title, emu->title); + eq->beard = emu->beard; + eq->targetable = 1; //New Field - Toggle Targetable on or off - 0 = off, 1 = on + eq->NPC = emu->NPC; + eq->targetable_with_hotkey = 1;//New Field - Toggle Targetable on or off - 0 = off, 1 = on + eq->x = emu->x; + eq->deltaX = emu->deltaX; + eq->deltaY = emu->deltaY; + eq->z = emu->z; + eq->deltaHeading = emu->deltaHeading; + eq->y = emu->y; + eq->deltaZ = emu->deltaZ; + eq->animation = emu->animation; + eq->heading = emu->heading; + eq->spawnId = emu->spawnId; + eq->nonvisible = 0; + strcpy(eq->name, emu->name); + eq->petOwnerId = emu->petOwnerId; + eq->pvp = 0; // 0 = non-pvp colored name, 1 = red pvp name + for (k = 0; k < 9; k++) { + eq->colors[k].color = emu->colors[k].color; + } + eq->anon = emu->anon; + eq->face = emu->face; + eq->drakkin_details = emu->drakkin_details; + eq->size = emu->size; + eq->walkspeed = emu->walkspeed; + /* + //Uncomment this section to use this hack test with NPC last names + //Hack Test for finding more fields in the Struct: + if (emu->lastName[0] == '*') // Test NPC! + { char code = emu->lastName[1]; size_t len = strlen(emu->lastName); char* sep = (char*)memchr(&emu->lastName[2], '=', len - 2); @@ -921,29 +1891,29 @@ ENCODE(OP_ZoneSpawns) { uint8 rnd = rand() & 0x0F; if (sep == nullptr) { - ofs = 0; - if ((emu->lastName[2] < '0') || (emu->lastName[2] > '9')) - { - val = rnd; - } - else - { - val = atoi(&emu->lastName[2]); - } + ofs = 0; + if ((emu->lastName[2] < '0') || (emu->lastName[2] > '9')) + { + val = rnd; } else { - sep[0] = nullptr; - ofs = atoi(&emu->lastName[2]); - sep[0] = '='; - if ((sep[1] < '0') || (sep[1] > '9')) - { - val = rnd; - } - else - { - val = atoi(&sep[1]); - } + val = atoi(&emu->lastName[2]); + } + } + else + { + sep[0] = nullptr; + ofs = atoi(&emu->lastName[2]); + sep[0] = '='; + if ((sep[1] < '0') || (sep[1] > '9')) + { + val = rnd; + } + else + { + val = atoi(&sep[1]); + } } char hex[] = "0123456789ABCDEF"; @@ -962,1906 +1932,1018 @@ ENCODE(OP_ZoneSpawns) { switch (code) { - case 'a': - eq->unknown0001[ofs % 4] = val; break; - case 'b': - eq->unknown0008 = val; break; - case 'c': - eq->unknown0011[ofs % 3] = val; break; - case 'd': - eq->unknown0018[ofs % 4] = val; break; - case 'e': - eq->unknown0023[ofs % 4] = val; break; - case 'f': - eq->unknown0136 = val; break; - case 'g': - eq->unknown0166[ofs % 8] = val; break; - case 'h': - eq->unknown0175[ofs % 192] = val; break; - case 'i': - eq->unknown0370[ofs % 3] = val; break; - case 'j': - eq->unknown0374[ofs % 128] = val; break; - case 'k': - eq->unknown0507[ofs % 4] = val; break; - case 'l': - eq->unknown0512[ofs % 16] = val; break; - case 'm': - eq->unknown0529[ofs % 4] = val; break; - case 'n': - eq->unknown0539[ofs % 41] = val; break; - case 'o': - eq->unknown0614[ofs % 11] = val; break; - case 'p': - eq->unknown0626[ofs % 28] = val; break; - case 'q': - eq->unknown0690 = val; break; - case 'r': - eq->unknown0726[ofs % 4] = val; break; - case 's': - eq->unknown0731[ofs % 11] = val; break; - case 't': - eq->unknown0767[ofs % 3] = val; break; - case 'u': - eq->unknown0883[ofs % 4] = val; break; - case 'v': - eq->unknown0895[ofs % 2] = val; break; - case 'X': - ((uint8*)eq)[ofs % 897] = val; break; - case 'Z': - eq->size = (float)val; break; // Test w/ size. + case 'a': + eq->unknown0001[ofs % 4] = val; break; + case 'b': + eq->unknown0008 = val; break; + case 'c': + eq->unknown0011[ofs % 3] = val; break; + case 'd': + eq->unknown0018[ofs % 4] = val; break; + case 'e': + eq->unknown0023[ofs % 4] = val; break; + case 'f': + eq->unknown0136 = val; break; + case 'g': + eq->unknown0166[ofs % 8] = val; break; + case 'h': + eq->unknown0175[ofs % 192] = val; break; + case 'i': + eq->unknown0370[ofs % 3] = val; break; + case 'j': + eq->unknown0374[ofs % 128] = val; break; + case 'k': + eq->unknown0507[ofs % 4] = val; break; + case 'l': + eq->unknown0512[ofs % 16] = val; break; + case 'm': + eq->unknown0529[ofs % 4] = val; break; + case 'n': + eq->unknown0539[ofs % 41] = val; break; + case 'o': + eq->unknown0614[ofs % 11] = val; break; + case 'p': + eq->unknown0626[ofs % 28] = val; break; + case 'q': + eq->unknown0690 = val; break; + case 'r': + eq->unknown0726[ofs % 4] = val; break; + case 's': + eq->unknown0731[ofs % 11] = val; break; + case 't': + eq->unknown0767[ofs % 3] = val; break; + case 'u': + eq->unknown0883[ofs % 4] = val; break; + case 'v': + eq->unknown0895[ofs % 2] = val; break; + case 'X': + ((uint8*)eq)[ofs % 897] = val; break; + case 'Z': + eq->size = (float)val; break; // Test w/ size. } - }*/ - } + }*/ + } + //kill off the emu structure and send the eq packet. + delete[] __emu_buffer; - //kill off the emu structure and send the eq packet. - delete[] __emu_buffer; - - //_log(NET__ERROR, "Sending zone spawns"); - //_hex(NET__ERROR, in->pBuffer, in->size); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+4; - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length); - - delete[] __emu_buffer; - safe_delete_array(serialized); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - - *p = nullptr; - - if(in->size == 0) { - - in->size = 4; - - in->pBuffer = new uchar[in->size]; - - *((uint32 *) in->pBuffer) = 0; + //_log(NET__ERROR, "Sending zone spawns"); + //_hex(NET__ERROR, in->pBuffer, in->size); dest->FastQueuePacket(&in, ack_req); - - return; } - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); - - if(ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); - - delete in; - - return; - } - - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - in->pBuffer = new uchar[4]; - - *(uint32 *)in->pBuffer = ItemCount; - - in->size = 4; - - for(int r = 0; r < ItemCount; r++, eq++) { - - uint32 Length = 0; - - char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); - - if(Serialized) { - - uchar *OldBuffer = in->pBuffer; - - in->pBuffer = new uchar[in->size + Length]; - - memcpy(in->pBuffer, OldBuffer, in->size); - - safe_delete_array(OldBuffer); - - memcpy(in->pBuffer + in->size, Serialized, Length); - - in->size += Length; - - safe_delete_array(Serialized); - - } - else { - _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); - } - } - - delete[] __emu_buffer; - - //_log(NET__ERROR, "Sending inventory to client"); - - //_hex(NET__ERROR, in->pBuffer, in->size); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; - - - - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. - - uint8 *buffer; - buffer = in->pBuffer; - - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; - - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); - - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header - emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data - ); - const char *emu_note = (emu_name + - emu->name_length + //skip name contents - emu->count //skip string terminators - ); - - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { - - //the order we set things here must match the struct - -//nice helper macro -/*#define SlideStructString(field, str) \ - strcpy(e->field, str.c_str()); \ - e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ -#define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) - - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - PutFieldN(rank); - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - PutFieldN(total_tribute); - PutFieldN(last_tribute); - e->unknown_one = htonl(1); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); - - -#undef SlideStructString -#undef PutFieldN - - e++; - } - } - - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SpawnDoor) { - SETUP_VAR_ENCODE(Door_Struct); - int door_count = __packet->size/sizeof(Door_Struct); - int total_length = door_count * sizeof(structs::Door_Struct); - ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); - int r; - for(r = 0; r < door_count; r++) { - strcpy(eq[r].name, emu[r].name); - eq[r].xPos = emu[r].xPos; - eq[r].yPos = emu[r].yPos; - eq[r].zPos = emu[r].zPos; - eq[r].heading = emu[r].heading; - eq[r].incline = emu[r].incline; - eq[r].size = emu[r].size; - eq[r].doorId = emu[r].doorId; - eq[r].opentype = emu[r].opentype; - eq[r].state_at_spawn = emu[r].state_at_spawn; - eq[r].invert_state = emu[r].invert_state; - eq[r].door_param = emu[r].door_param; - eq[r].unknown0076 = 0; - eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0078 = 0; - eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0080 = 0; - eq[r].unknown0081 = 0; - eq[r].unknown0082 = 0; - } - FINISH_ENCODE(); -} - -ENCODE(OP_GroundSpawn) { - ENCODE_LENGTH_EXACT(Object_Struct); - SETUP_DIRECT_ENCODE(Object_Struct, structs::Object_Struct); - OUT(drop_id); - OUT(zone_id); - OUT(zone_instance); - OUT(heading); - OUT(x); - OUT(y); - OUT(z); - OUT_str(object_name); - OUT(object_type); - OUT(spawn_id); - - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown020 = 0; - eq->unknown024 = 0; - eq->size = 1; //This forces all objects to standard size for now - eq->unknown088 = 0; - memset(eq->unknown096, 0xFF, sizeof(eq->unknown096)); - FINISH_ENCODE(); -} - -ENCODE(OP_ManaChange) { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? - FINISH_ENCODE(); -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - OUT(race); - OUT(unknown006[0]); - OUT(unknown006[1]); - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - OUT(drakkin_heritage); - OUT(drakkin_tattoo); - OUT(drakkin_details); - - FINISH_ENCODE(); -} - -ENCODE(OP_ClientUpdate) { - ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); - SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); - OUT(spawn_id); - OUT(x_pos); - OUT(delta_x); - OUT(delta_y); - OUT(z_pos); - OUT(delta_heading); - OUT(y_pos); - OUT(delta_z); - OUT(animation); - OUT(heading); - FINISH_ENCODE(); -} - -ENCODE(OP_ExpansionInfo) { - ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); - OUT(Expansions); - FINISH_ENCODE(); -} - -ENCODE(OP_LogServer) { - ENCODE_LENGTH_EXACT(LogServer_Struct); - SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); - strcpy(eq->worldshortname, emu->worldshortname); - - OUT(enablevoicemacros); - OUT(enablemail); - OUT(enable_pvp); - OUT(enable_FV); - - // These next two need to be set like this for the Tutorial Button to work. - eq->unknown263[0] = 0; - eq->unknown263[2] = 1; - - FINISH_ENCODE(); -} - -ENCODE(OP_Damage) { - ENCODE_LENGTH_EXACT(CombatDamage_Struct); - SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); - OUT(target); - OUT(source); - OUT(type); - OUT(spellid); - OUT(damage); - eq->sequence = emu->sequence; - FINISH_ENCODE(); -} - -ENCODE(OP_Consider) { - ENCODE_LENGTH_EXACT(Consider_Struct); - SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); - OUT(playerid); - OUT(targetid); - OUT(faction); - OUT(level); - OUT(pvpcon); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); - OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - eq->sequence = emu->sequence; - OUT(type); - //OUT(damage); - OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1; - FINISH_ENCODE(); -} - -ENCODE(OP_Buff) { - ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); - SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); - OUT(entityid); - OUT(slot); - OUT(level); - OUT(effect); - //eq->unknown7 = 10; - OUT(spellid); - OUT(duration); - OUT(slotid); - OUT(bufffade); - FINISH_ENCODE(); -} - -ENCODE(OP_CancelTrade) { - ENCODE_LENGTH_EXACT(CancelTrade_Struct); - SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); - OUT(fromid); - OUT(action); - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerSell) { - ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); - SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - OUT(npcid); - eq->itemslot = ServerToSoFSlot(emu->itemslot); - OUT(quantity); - OUT(price); - FINISH_ENCODE(); -} - -ENCODE(OP_ApplyPoison) { - ENCODE_LENGTH_EXACT(ApplyPoison_Struct); - SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - eq->inventorySlot = ServerToSoFSlot(emu->inventorySlot); - OUT(success); - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteItem) { - ENCODE_LENGTH_EXACT(DeleteItem_Struct); - SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - eq->from_slot = ServerToSoFSlot(emu->from_slot); - eq->to_slot = ServerToSoFSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } -ENCODE(OP_MoveItem) { - ENCODE_LENGTH_EXACT(MoveItem_Struct); - SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); - - eq->from_slot = ServerToSoFSlot(emu->from_slot); - eq->to_slot = ServerToSoFSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_ItemVerifyReply) { - ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); - SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); - - eq->slot = ServerToSoFSlot(emu->slot); - OUT(spell); - OUT(target); - - FINISH_ENCODE(); -} - -ENCODE(OP_BazaarSearch) { - - if(((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) { - - EQApplicationPacket *in = *p; - *p = nullptr; - dest->FastQueuePacket(&in, ack_req); - return; - } - - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; - - //determine and verify length - int entrycount = in->size / sizeof(BazaarSearchResults_Struct); - if(entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; - } - - //make the EQ struct. - in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount; - in->pBuffer = new unsigned char[in->size]; - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer; - - //zero out the packet. We could avoid this memset by setting all fields (including unknowns) - //in the loop. - memset(in->pBuffer, 0, in->size); - - for(int i=0; iBeginning.Action = emu->Beginning.Action; - eq->Beginning.Unknown001 = emu->Beginning.Unknown001; - eq->Beginning.Unknown002 = emu->Beginning.Unknown002; - eq->NumItems = emu->NumItems; - eq->SerialNumber = emu->SerialNumber; - eq->SellerID = emu->SellerID; - eq->Cost = emu->Cost; - eq->ItemStat = emu->ItemStat; - strcpy(eq->ItemName, emu->ItemName); - } - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); - - -} - -ENCODE(OP_Trader) { - - if((*p)->size != sizeof(TraderBuy_Struct)) { - EQApplicationPacket *in = *p; - *p = nullptr; - dest->FastQueuePacket(&in, ack_req); - return; - } - ENCODE_FORWARD(OP_TraderBuy); -} - -ENCODE(OP_TraderBuy) { - - ENCODE_LENGTH_EXACT(TraderBuy_Struct); - SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); - - OUT(Action); - OUT(Price); - OUT(TraderID); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(ItemID); - OUT(Quantity); - OUT(AlreadySold); - - FINISH_ENCODE(); -} - -ENCODE(OP_LootItem) { - - ENCODE_LENGTH_EXACT(LootingItem_Struct); - SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); - OUT(lootee); - OUT(looter); - eq->slot_id = emu->slot_id + 1; - OUT(auto_loot); - - FINISH_ENCODE(); -} - -ENCODE(OP_TributeItem) { - ENCODE_LENGTH_EXACT(TributeItem_Struct); - SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); - - eq->slot = ServerToSoFSlot(emu->slot); - OUT(quantity); - OUT(tribute_master_id); - OUT(tribute_points); - - FINISH_ENCODE(); -} - -ENCODE(OP_SomeItemPacketMaybe) { - // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow - // and flying to the target. - // - - ENCODE_LENGTH_EXACT(Arrow_Struct); - SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); - - OUT(src_y); - OUT(src_x); - OUT(src_z); - OUT(velocity); - OUT(launch_angle); - OUT(tilt); - OUT(arc); - OUT(source_id); - OUT(target_id); - OUT(item_id); - - eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. - - OUT(item_type); - OUT(skill); - - strcpy(eq->model_name, emu->model_name); - - FINISH_ENCODE(); -} - -ENCODE(OP_ReadBook) { - - ENCODE_LENGTH_ATLEAST(BookText_Struct); - SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); - - if(emu->window == 0xFF) - eq->window = 0xFFFFFFFF; - else - eq->window = emu->window; - OUT(type); - eq->invslot = ServerToSoFSlot(emu->invslot); - strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); - FINISH_ENCODE(); -} - -ENCODE(OP_Stun) { - - ENCODE_LENGTH_EXACT(Stun_Struct); - SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); - OUT(duration); - eq->unknown005 = 163; - eq->unknown006 = 67; - - FINISH_ENCODE(); -} - -ENCODE(OP_ZonePlayerToBind) -{ - ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); - ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; - structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; - unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; - structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; - - zph->x = zps->x; - zph->y = zps->y; - zph->z = zps->z; - zph->heading = zps->heading; - zph->bind_zone_id = zps->bind_zone_id; - zph->bind_instance_id = zps->bind_instance_id; - strcpy(zph->zone_name, zps->zone_name); - - zpf->unknown021 = 1; - zpf->unknown022 = 0; - zpf->unknown023 = 0; - zpf->unknown024 = 0; - - ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); - ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); - - delete[] buffer1; - delete[] buffer2; - delete[] (*p)->pBuffer; - - (*p)->pBuffer = new unsigned char[ss.str().size()]; - (*p)->size = ss.str().size(); - - memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); - dest->FastQueuePacket(&(*p)); -} - -ENCODE(OP_AdventureMerchantSell) { - ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); - SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - eq->unknown000 = 1; - OUT(npcid); - eq->slot = ServerToSoFSlot(emu->slot); - OUT(charges); - OUT(sell_price); - - FINISH_ENCODE(); -} - -ENCODE(OP_RaidUpdate) -{ - EQApplicationPacket *inapp = *p; - *p = nullptr; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; - - if(raid_gen->action == 0) // raid add has longer length than other raid updates +// DECODE methods + DECODE(OP_AdventureMerchantSell) { - RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); - structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + IN(npcid); + emu->slot = SoFToServerSlot(eq->slot); + IN(charges); + IN(sell_price); - add_member->raidGen.action = in_add_member->raidGen.action; - add_member->raidGen.parameter = in_add_member->raidGen.parameter; - strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); - strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); - add_member->_class = in_add_member->_class; - add_member->level= in_add_member->level; - add_member->isGroupLeader = in_add_member->isGroupLeader; - add_member->flags[0] = in_add_member->flags[0]; - add_member->flags[1] = in_add_member->flags[1]; - add_member->flags[2] = in_add_member->flags[2]; - add_member->flags[3] = in_add_member->flags[3]; - add_member->flags[4] = in_add_member->flags[4]; - dest->FastQueuePacket(&outapp); + FINISH_DIRECT_DECODE(); } - else - { - RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); - strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); - raid_general->action = in_raid_general->action; - raid_general->parameter = in_raid_general->parameter; - dest->FastQueuePacket(&outapp); + DECODE(OP_AltCurrencySell) + { + DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); + + IN(merchant_entity_id); + emu->slot_id = SoFToServerSlot(eq->slot_id); + IN(charges); + IN(cost); + + FINISH_DIRECT_DECODE(); } - delete[] __emu_buffer; -} -ENCODE(OP_RaidJoin) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; - - general->action = 8; - general->parameter = 1; - strn0cpy(general->leader_name, raid_create->leader_name, 64); - strn0cpy(general->player_name, raid_create->leader_name, 64); - - dest->FastQueuePacket(&outapp_create); - delete[] __emu_buffer; -} - -ENCODE(OP_VetRewardsAvaliable) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - - uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for(uint32 i = 0; i < count; ++i) + DECODE(OP_AltCurrencySellSelection) { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for(int x = 0; x < 8; ++x) + IN(merchant_entity_id); + emu->slot_id = SoFToServerSlot(eq->slot_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = SoFToServerSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentInfo) + { + DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); + SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + IN(itemid); + IN(window); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = SoFToServerSlot(eq->container_slot); + emu->augment_slot = eq->augment_slot; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Buff) + { + DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct); + SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); + + IN(entityid); + IN(slot); + IN(level); + IN(effect); + IN(spellid); + IN(duration); + IN(slotid); + IN(bufffade); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + IN(slot); + IN(spell_id); + emu->inventoryslot = SoFToServerSlot(eq->inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + IN(class_); + IN(beardcolor); + IN(beard); + IN(hairstyle); + IN(gender); + IN(race); + + if (RuleB(World, EnableTutorialButton) && eq->tutorial) + emu->start_zone = RuleI(World, TutorialZoneID); + else + emu->start_zone = eq->start_zone; + + IN(haircolor); + IN(deity); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ClientUpdate) + { + // for some odd reason, there is an extra byte on the end of this on occasion.. + DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); + SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); + + IN(spawn_id); + IN(sequence); + IN(x_pos); + IN(y_pos); + IN(z_pos); + IN(heading); + IN(delta_x); + IN(delta_y); + IN(delta_z); + IN(delta_heading); + IN(animation); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Consider) + { + DECODE_LENGTH_EXACT(structs::Consider_Struct); + SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); + + IN(playerid); + IN(targetid); + IN(faction); + IN(level); + //emu->cur_hp = 1; + //emu->max_hp = 2; + //emu->pvpcon = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = SoFToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = SoFToServerSlot(eq->from_slot); + emu->to_slot = SoFToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FindPersonRequest) + { + DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); + SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); + + IN(npc_id); + IN(client_pos.x); + IN(client_pos.y); + IN(client_pos.z); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow) + { + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow2) + { + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_InspectRequest) + { + DECODE_LENGTH_EXACT(structs::Inspect_Struct); + SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); + + IN(TargetID); + IN(PlayerID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + IN(link_hash); + IN(icon); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemVerifyRequest) + { + DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); + SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); + + emu->slot = SoFToServerSlot(eq->slot); + IN(target); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = eq->slot_id - 1; + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); + + emu->from_slot = SoFToServerSlot(eq->from_slot); + emu->to_slot = SoFToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_RaidInvite) + { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ReadBook) + { + DECODE_LENGTH_EXACT(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(type); + emu->invslot = SoFToServerSlot(eq->invslot); + emu->window = (uint8)eq->window; + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Save) + { + DECODE_LENGTH_EXACT(structs::Save_Struct); + SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); + + memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + IN(filters[r]); + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = SoFToServerSlot(eq->itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TraderBuy) + { + DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); + SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); + MEMSET_IN(TraderBuy_Struct); + + IN(Action); + IN(Price); + IN(TraderID); + memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); + IN(ItemID); + IN(Quantity); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = SoFToServerSlot(eq->container_slot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = SoFToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + IN(material); + IN(unknown06); + IN(elite_material); + IN(color.color); + IN(wear_slot_id); + emu->hero_forge_model = 0; + emu->unknown18 = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + IN(guildid); + IN(type); + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + uint32 NextItemInstSerialNumber = 1; + uint32 MaxInstances = 2000000000; + + static inline int32 GetNextItemInstSerialNumber() + { + if (NextItemInstSerialNumber >= MaxInstances) + NextItemInstSerialNumber = 1; + else + NextItemInstSerialNumber++; + + return NextItemInstSerialNumber; + } + + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) + { + uint8 null_term = 0; + bool stackable = inst->IsStackable(); + uint32 merchant_slot = inst->GetMerchantSlot(); + uint32 charges = inst->GetCharges(); + if (!stackable && charges > 254) + charges = 0xFFFFFFFF; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + const Item_Struct *item = inst->GetItem(); + //_log(NET__ERROR, "Serialize called for: %s", item->Name); + SoF::structs::ItemSerializationHeader hdr; + hdr.stacksize = stackable ? charges : 1; + hdr.unknown004 = 0; + + int32 slot_id = ServerToSoFSlot(slot_id_in); + + hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; + hdr.price = inst->GetPrice(); + hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); + hdr.unknown020 = 0; + hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; + hdr.unknown028 = 0; + hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); + hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; + hdr.unknown044 = 0; + hdr.unknown048 = 0; + hdr.unknown052 = 0; + hdr.unknown056 = 0; + hdr.unknown060 = 0; + hdr.unknown061 = 0; + hdr.ItemClass = item->ItemClass; + + ss.write((const char*)&hdr, sizeof(SoF::structs::ItemSerializationHeader)); + + if (strlen(item->Name) > 0) { - vr->items[x].item_id = ivr->items[x].item_id; - strcpy(vr->items[x].item_name, ivr->items[x].item_name); - vr->items[x].charges = ivr->items[x].charges; + ss.write(item->Name, strlen(item->Name)); + ss.write((const char*)&null_term, sizeof(uint8)); } - - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); - } - - dest->FastQueuePacket(&outapp_create); - delete inapp; -} - -ENCODE(OP_DeleteSpawn) { - SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - OUT(spawn_id); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionEndsWarning) -{ - ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); - SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - OUT(minutes_remaining); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionInfo) -{ - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - OUT(max_players); - eq->enabled_max = 1; - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->leader_name, emu->leader_name); - FINISH_ENCODE(); -} - -ENCODE(OP_DzCompass) -{ - SETUP_VAR_ENCODE(ExpeditionCompass_Struct); - ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - OUT(count); - - for(uint32 i = 0; i < emu->count; ++i) - { - OUT(entries[i].x); - OUT(entries[i].y); - OUT(entries[i].z); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_DzMemberList) -{ - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].status, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionList) -{ - SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(int i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); - ss.write((const char*)&null_term, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzLeaderStatus) -{ - SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - - ss.write((const char*)&client_id, sizeof(uint32)); - //ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->leader_name, strlen(emu->leader_name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//1 - ss.write((const char*)&client_id, sizeof(uint32)); - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzJoinExpeditionConfirm) -{ - ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); - SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->player_name, emu->player_name); - FINISH_ENCODE(); -} - -ENCODE(OP_BecomeTrader) -{ - ENCODE_LENGTH_EXACT(BecomeTrader_Struct); - SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); - OUT(ID); - OUT(Code); - FINISH_ENCODE(); -} - -ENCODE(OP_PetBuffWindow) -{ - ENCODE_LENGTH_EXACT(PetBuff_Struct); - SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); - - OUT(petid); - OUT(buffcount); - - int EQBuffSlot = 0; - - for(uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) - { - if(emu->spellid[EmuBuffSlot]) + else { - eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; - eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; + ss.write((const char*)&null_term, sizeof(uint8)); } - } - FINISH_ENCODE(); -} - -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - OUT_str(Title); - OUT_str(Text); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); - - eq->unknown4236 = 0x00000000; - eq->unknown4240 = 0xffffffff; - - FINISH_ENCODE(); -} - -ENCODE(OP_AltCurrencySell) -{ - ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); - SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - - OUT(merchant_entity_id); - eq->slot_id = ServerToSoFSlot(emu->slot_id); - OUT(charges); - OUT(cost); - FINISH_ENCODE(); -} - -ENCODE(OP_WearChange) -{ - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(unknown06); - OUT(elite_material); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -ENCODE(OP_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - FINISH_ENCODE(); -} - -DECODE(OP_InspectRequest) { - DECODE_LENGTH_EXACT(structs::Inspect_Struct); - SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); - IN(TargetID); - IN(PlayerID); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); - - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AdventureMerchantSell) { - DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); - SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - IN(npcid); - emu->slot = SoFToServerSlot(eq->slot); - IN(charges); - IN(sell_price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ApplyPoison) { - DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); - SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - - emu->inventorySlot = SoFToServerSlot(eq->inventorySlot); - IN(success); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemVerifyRequest) { - DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); - SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); - - emu->slot = SoFToServerSlot(eq->slot); - IN(target); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Consume) { - DECODE_LENGTH_EXACT(structs::Consume_Struct); - SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); - - emu->slot = SoFToServerSlot(eq->slot); - IN(auto_consumed); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CastSpell) { - DECODE_LENGTH_EXACT(structs::CastSpell_Struct); - SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); - - IN(slot); - IN(spell_id); - emu->inventoryslot = SoFToServerSlot(eq->inventoryslot); - IN(target_id); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_DeleteItem) -{ - DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); - SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - emu->from_slot = SoFToServerSlot(eq->from_slot); - emu->to_slot = SoFToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_MoveItem) -{ - DECODE_LENGTH_EXACT(structs::MoveItem_Struct); - SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - - _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); - - emu->from_slot = SoFToServerSlot(eq->from_slot); - emu->to_slot = SoFToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); - } - IN(link_hash); - IN(icon); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 29; r++) { - IN(filters[r]); - } - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } -DECODE(OP_Consider) { - DECODE_LENGTH_EXACT(structs::Consider_Struct); - SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); - IN(playerid); - IN(targetid); - IN(faction); - IN(level); - //emu->cur_hp = 1; - //emu->max_hp = 2; - //emu->pvpcon = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ClientUpdate) { - // for some odd reason, there is an extra byte on the end of this on occasion.. - DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); - SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); - IN(spawn_id); - IN(sequence); - IN(x_pos); - IN(y_pos); - IN(z_pos); - IN(heading); - IN(delta_x); - IN(delta_y); - IN(delta_z); - IN(delta_heading); - IN(animation); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - IN(class_); - IN(beardcolor); - IN(beard); - IN(hairstyle); - IN(gender); - IN(race); - - if(RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - - IN(haircolor); - IN(deity); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - IN(guildid); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow) { - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow2) { - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Buff) { - DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct); - SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); - IN(entityid); - IN(slot); - IN(level); - IN(effect); - IN(spellid); - IN(duration); - IN(slotid); - IN(bufffade); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerSell) { - DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); - SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - - IN(npcid); - emu->itemslot = SoFToServerSlot(eq->itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Save) { - DECODE_LENGTH_EXACT(structs::Save_Struct); - SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); - memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FindPersonRequest) { - DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); - SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); - IN(npc_id); - IN(client_pos.x); - IN(client_pos.y); - IN(client_pos.z); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WearChange) { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - IN(spawn_id); - IN(material); - IN(unknown06); - IN(elite_material); - IN(color.color); - IN(wear_slot_id); - emu->hero_forge_model = 0; - emu->unknown18 = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TraderBuy) { - DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); - SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); - MEMSET_IN(TraderBuy_Struct); - - IN(Action); - IN(Price); - IN(TraderID); - memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); - IN(ItemID); - IN(Quantity); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LootItem) { - DECODE_LENGTH_EXACT(structs::LootingItem_Struct); - SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); - IN(lootee); - IN(looter); - emu->slot_id = eq->slot_id - 1; - IN(auto_loot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TributeItem) { - DECODE_LENGTH_EXACT(structs::TributeItem_Struct); - SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); - - emu->slot = SoFToServerSlot(eq->slot); - IN(quantity); - IN(tribute_master_id); - IN(tribute_points); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_EXACT(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(type); - emu->invslot = SoFToServerSlot(eq->invslot); - emu->window = (uint8) eq->window; - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TradeSkillCombine) { - DECODE_LENGTH_EXACT(structs::NewCombine_Struct); - SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - - emu->container_slot = SoFToServerSlot(eq->container_slot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentItem) { - DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); - SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); - - emu->container_slot = SoFToServerSlot(eq->container_slot); - emu->augment_slot = eq->augment_slot; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentInfo) { - DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); - SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); - - IN(itemid); - IN(window); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - - -uint32 NextItemInstSerialNumber = 1; -uint32 MaxInstances = 2000000000; - -static inline int32 GetNextItemInstSerialNumber() { - - if(NextItemInstSerialNumber >= MaxInstances) - NextItemInstSerialNumber = 1; - else - NextItemInstSerialNumber++; - - return NextItemInstSerialNumber; -} - - -char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { - uint8 null_term = 0; - bool stackable = inst->IsStackable(); - uint32 merchant_slot = inst->GetMerchantSlot(); - uint32 charges = inst->GetCharges(); - if (!stackable && charges > 254) - charges = 0xFFFFFFFF; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - const Item_Struct *item = inst->GetItem(); - //_log(NET__ERROR, "Serialize called for: %s", item->Name); - SoF::structs::ItemSerializationHeader hdr; - hdr.stacksize = stackable ? charges : 1; - hdr.unknown004 = 0; - - int32 slot_id = ServerToSoFSlot(slot_id_in); - - hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; - hdr.price = inst->GetPrice(); - hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); - hdr.unknown020 = 0; - hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; - hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); - hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); - hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; - hdr.unknown044 = 0; - hdr.unknown048 = 0; - hdr.unknown052 = 0; - hdr.unknown056 = 0; - hdr.unknown060 = 0; - hdr.unknown061 = 0; - hdr.ItemClass = item->ItemClass; - - ss.write((const char*)&hdr, sizeof(SoF::structs::ItemSerializationHeader)); - - if(strlen(item->Name) > 0) - { - ss.write(item->Name, strlen(item->Name)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - if(strlen(item->Lore) > 0) - { - ss.write(item->Lore, strlen(item->Lore)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - if(strlen(item->IDFile) > 0) - { - ss.write(item->IDFile, strlen(item->IDFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoF::structs::ItemBodyStruct ibs; - memset(&ibs, 0, sizeof(SoF::structs::ItemBodyStruct)); - - uint32 adjusted_slots = item->Slots; - - // Conversions for Ammo and Power Source Slots - if(item->Slots & (1 << 21) & (1 << 22)) - { - // Do nothing - } - else - { - if(item->Slots & (1 << 21)) // Ammo Slot from Database + if (strlen(item->Lore) > 0) { - adjusted_slots -= (1 << 21); // Ammo Slot in Titanium - adjusted_slots += (1 << 22); // Ammo Slot in SoF + ss.write(item->Lore, strlen(item->Lore)); + ss.write((const char*)&null_term, sizeof(uint8)); } - - if(item->Slots & (1 << 22)) // Power Source Slot from Database + else { - adjusted_slots -= (1 << 22); // Non Existant Worn Slot in Titanium - adjusted_slots += (1 << 21); // Power Source Slot in SoF + ss.write((const char*)&null_term, sizeof(uint8)); } - } - ibs.id = item->ID; - ibs.weight = item->Weight; - ibs.norent = item->NoRent; - ibs.nodrop = item->NoDrop; - ibs.attune = item->Attuneable; - ibs.size = item->Size; - ibs.slots = adjusted_slots; - ibs.price = item->Price; - ibs.icon = item->Icon; - ibs.unknown1 = 1; - ibs.unknown2 = 1; - ibs.BenefitFlag = item->BenefitFlag; - ibs.tradeskills = item->Tradeskills; - ibs.CR = item->CR; - ibs.DR = item->DR; - ibs.PR = item->PR; - ibs.MR = item->MR; - ibs.FR = item->FR; - ibs.SVCorruption = item->SVCorruption; - ibs.AStr = item->AStr; - ibs.ASta = item->ASta; - ibs.AAgi = item->AAgi; - ibs.ADex = item->ADex; - ibs.ACha = item->ACha; - ibs.AInt = item->AInt; - ibs.AWis = item->AWis; - - ibs.HP = item->HP; - ibs.Mana = item->Mana; - ibs.Endur = item->Endur; - ibs.AC = item->AC; - ibs.regen = item->Regen; - ibs.mana_regen = item->ManaRegen; - ibs.end_regen = item->EnduranceRegen; - ibs.Classes = item->Classes; - ibs.Races = item->Races; - ibs.Deity = item->Deity; - ibs.SkillModValue = item->SkillModValue; - ibs.unknown6 = 0; - ibs.SkillModType = item->SkillModType; - ibs.BaneDmgRace = item->BaneDmgRace; - ibs.BaneDmgBody = item->BaneDmgBody; - ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; - ibs.BaneDmgAmt = item->BaneDmgAmt; - ibs.Magic = item->Magic; - ibs.CastTime_ = item->CastTime_; - ibs.ReqLevel = item->ReqLevel; - ibs.RecLevel = item->RecLevel; - ibs.RecSkill = item->RecSkill; - ibs.BardType = item->BardType; - ibs.BardValue = item->BardValue; - ibs.Light = item->Light; - ibs.Delay = item->Delay; - ibs.ElemDmgType = item->ElemDmgType; - ibs.ElemDmgAmt = item->ElemDmgAmt; - ibs.Range = item->Range; - ibs.Damage = item->Damage; - ibs.Color = item->Color; - ibs.ItemType = item->ItemType; - ibs.Material = item->Material; - ibs.unknown7 = 0; - ibs.EliteMaterial = item->EliteMaterial; - ibs.SellRate = item->SellRate; - - ibs.CombatEffects = item->CombatEffects; - ibs.Shielding = item->Shielding; - ibs.StunResist = item->StunResist; - ibs.StrikeThrough = item->StrikeThrough; - ibs.ExtraDmgSkill = item->ExtraDmgSkill; - ibs.ExtraDmgAmt = item->ExtraDmgAmt; - ibs.SpellShield = item->SpellShield; - ibs.Avoidance = item->Avoidance; - ibs.Accuracy = item->Accuracy; - ibs.FactionAmt1 = item->FactionAmt1; - ibs.FactionMod1 = item->FactionMod1; - ibs.FactionAmt2 = item->FactionAmt2; - ibs.FactionMod2 = item->FactionMod2; - ibs.FactionAmt3 = item->FactionAmt3; - ibs.FactionMod3 = item->FactionMod3; - ibs.FactionAmt4 = item->FactionAmt4; - ibs.FactionMod4 = item->FactionMod4; - - ss.write((const char*)&ibs, sizeof(SoF::structs::ItemBodyStruct)); - - //charm text - if(strlen(item->CharmFile) > 0) - { - ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoF::structs::ItemSecondaryBodyStruct isbs; - memset(&isbs, 0, sizeof(SoF::structs::ItemSecondaryBodyStruct)); - - isbs.augtype = item->AugType; - isbs.augrestrict = item->AugRestrict; - - for(int x = 0; x < 5; ++x) - { - isbs.augslots[x].type = item->AugSlotType[x]; - isbs.augslots[x].visible = item->AugSlotVisible[x]; - isbs.augslots[x].unknown = item->AugSlotUnk2[x]; - } - - isbs.ldonpoint_type = item->PointType; - isbs.ldontheme = item->LDoNTheme; - isbs.ldonprice = item->LDoNPrice; - isbs.ldonsellbackrate = item->LDoNSellBackRate; - isbs.ldonsold = item->LDoNSold; - - isbs.bagtype = item->BagType; - isbs.bagslots = item->BagSlots; - isbs.bagsize = item->BagSize; - isbs.wreduction = item->BagWR; - - isbs.book = item->Book; - isbs.booktype = item->BookType; - - ss.write((const char*)&isbs, sizeof(SoF::structs::ItemSecondaryBodyStruct)); - - if(strlen(item->Filename) > 0) - { - ss.write((const char*)item->Filename, strlen(item->Filename)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoF::structs::ItemTertiaryBodyStruct itbs; - memset(&itbs, 0, sizeof(SoF::structs::ItemTertiaryBodyStruct)); - - itbs.loregroup = item->LoreGroup; - itbs.artifact = item->ArtifactFlag; - itbs.summonedflag = item->SummonedFlag; - itbs.favor = item->Favor; - itbs.fvnodrop = item->FVNoDrop; - itbs.dotshield = item->DotShielding; - itbs.atk = item->Attack; - itbs.haste = item->Haste; - itbs.damage_shield = item->DamageShield; - itbs.guildfavor = item->GuildFavor; - itbs.augdistil = item->AugDistiller; - itbs.no_pet = item->NoPet; - - itbs.potion_belt_enabled = item->PotionBelt; - itbs.potion_belt_slots = item->PotionBeltSlots; - itbs.stacksize = stackable ? item->StackSize : 0; - itbs.no_transfer = item->NoTransfer; - itbs.expendablearrow = item->ExpendableArrow; - - ss.write((const char*)&itbs, sizeof(SoF::structs::ItemTertiaryBodyStruct)); - - // Effect Structures Broken down to allow variable length strings for effect names - int32 effect_unknown = 0; - - SoF::structs::ClickEffectStruct ices; - memset(&ices, 0, sizeof(SoF::structs::ClickEffectStruct)); - - ices.effect = item->Click.Effect; - ices.level2 = item->Click.Level2; - ices.type = item->Click.Type; - ices.level = item->Click.Level; - ices.max_charges = item->MaxCharges; - ices.cast_time = item->CastTime; - ices.recast = item->RecastDelay; - ices.recast_type = item->RecastType; - - ss.write((const char*)&ices, sizeof(SoF::structs::ClickEffectStruct)); - - if(strlen(item->ClickName) > 0) - { - ss.write((const char*)item->ClickName, strlen(item->ClickName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 - - SoF::structs::ProcEffectStruct ipes; - memset(&ipes, 0, sizeof(SoF::structs::ProcEffectStruct)); - - ipes.effect = item->Proc.Effect; - ipes.level2 = item->Proc.Level2; - ipes.type = item->Proc.Type; - ipes.level = item->Proc.Level; - ipes.procrate = item->ProcRate; - - ss.write((const char*)&ipes, sizeof(SoF::structs::ProcEffectStruct)); - - if(strlen(item->ProcName) > 0) - { - ss.write((const char*)item->ProcName, strlen(item->ProcName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 - - SoF::structs::WornEffectStruct iwes; - memset(&iwes, 0, sizeof(SoF::structs::WornEffectStruct)); - - iwes.effect = item->Worn.Effect; - iwes.level2 = item->Worn.Level2; - iwes.type = item->Worn.Type; - iwes.level = item->Worn.Level; - - ss.write((const char*)&iwes, sizeof(SoF::structs::WornEffectStruct)); - - if(strlen(item->WornName) > 0) - { - ss.write((const char*)item->WornName, strlen(item->WornName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - SoF::structs::WornEffectStruct ifes; - memset(&ifes, 0, sizeof(SoF::structs::WornEffectStruct)); - - ifes.effect = item->Focus.Effect; - ifes.level2 = item->Focus.Level2; - ifes.type = item->Focus.Type; - ifes.level = item->Focus.Level; - - ss.write((const char*)&ifes, sizeof(SoF::structs::WornEffectStruct)); - - if(strlen(item->FocusName) > 0) - { - ss.write((const char*)item->FocusName, strlen(item->FocusName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - SoF::structs::WornEffectStruct ises; - memset(&ises, 0, sizeof(SoF::structs::WornEffectStruct)); - - ises.effect = item->Scroll.Effect; - ises.level2 = item->Scroll.Level2; - ises.type = item->Scroll.Type; - ises.level = item->Scroll.Level; - - ss.write((const char*)&ises, sizeof(SoF::structs::WornEffectStruct)); - - if(strlen(item->ScrollName) > 0) - { - ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - // End of Effects - - SoF::structs::ItemQuaternaryBodyStruct iqbs; - memset(&iqbs, 0, sizeof(SoF::structs::ItemQuaternaryBodyStruct)); - - iqbs.scriptfileid = item->ScriptFileID; - iqbs.quest_item = item->QuestItemFlag; - iqbs.unknown15 = 0xffffffff; - - iqbs.Purity = item->Purity; - iqbs.BackstabDmg = item->BackstabDmg; - iqbs.DSMitigation = item->DSMitigation; - iqbs.HeroicStr = item->HeroicStr; - iqbs.HeroicInt = item->HeroicInt; - iqbs.HeroicWis = item->HeroicWis; - iqbs.HeroicAgi = item->HeroicAgi; - iqbs.HeroicDex = item->HeroicDex; - iqbs.HeroicSta = item->HeroicSta; - iqbs.HeroicCha = item->HeroicCha; - iqbs.HeroicMR = item->HeroicMR; - iqbs.HeroicFR = item->HeroicFR; - iqbs.HeroicCR = item->HeroicCR; - iqbs.HeroicDR = item->HeroicDR; - iqbs.HeroicPR = item->HeroicPR; - iqbs.HeroicSVCorrup = item->HeroicSVCorrup; - iqbs.HealAmt = item->HealAmt; - iqbs.SpellDmg = item->SpellDmg; - //iqbs.clairvoyance = item->Clairvoyance; - - iqbs.subitem_count = 0; - - char *SubSerializations[10]; // - - uint32 SubLengths[10]; - - for(int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { - - SubSerializations[x] = nullptr; - - const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); - - if(subitem) { - - int SubSlotNumber; - - iqbs.subitem_count++; - - if(slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? - //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); - SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); - else if(slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) - //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); - else if(slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) - //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); - else - SubSlotNumber = slot_id_in; // ??????? - - /* - // TEST CODE: - SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); - */ - - SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + if (strlen(item->IDFile) > 0) + { + ss.write(item->IDFile, strlen(item->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); } - } - - ss.write((const char*)&iqbs, sizeof(SoF::structs::ItemQuaternaryBodyStruct)); - - for(int x = 0; x < 10; ++x) { - - if(SubSerializations[x]) { - - ss.write((const char*)&x, sizeof(uint32)); - - ss.write(SubSerializations[x], SubLengths[x]); - - safe_delete_array(SubSerializations[x]); + else + { + ss.write((const char*)&null_term, sizeof(uint8)); } + + SoF::structs::ItemBodyStruct ibs; + memset(&ibs, 0, sizeof(SoF::structs::ItemBodyStruct)); + + ibs.id = item->ID; + ibs.weight = item->Weight; + ibs.norent = item->NoRent; + ibs.nodrop = item->NoDrop; + ibs.attune = item->Attuneable; + ibs.size = item->Size; + ibs.slots = SwapBits21and22(item->Slots); + ibs.price = item->Price; + ibs.icon = item->Icon; + ibs.unknown1 = 1; + ibs.unknown2 = 1; + ibs.BenefitFlag = item->BenefitFlag; + ibs.tradeskills = item->Tradeskills; + ibs.CR = item->CR; + ibs.DR = item->DR; + ibs.PR = item->PR; + ibs.MR = item->MR; + ibs.FR = item->FR; + ibs.SVCorruption = item->SVCorruption; + ibs.AStr = item->AStr; + ibs.ASta = item->ASta; + ibs.AAgi = item->AAgi; + ibs.ADex = item->ADex; + ibs.ACha = item->ACha; + ibs.AInt = item->AInt; + ibs.AWis = item->AWis; + + ibs.HP = item->HP; + ibs.Mana = item->Mana; + ibs.Endur = item->Endur; + ibs.AC = item->AC; + ibs.regen = item->Regen; + ibs.mana_regen = item->ManaRegen; + ibs.end_regen = item->EnduranceRegen; + ibs.Classes = item->Classes; + ibs.Races = item->Races; + ibs.Deity = item->Deity; + ibs.SkillModValue = item->SkillModValue; + ibs.unknown6 = 0; + ibs.SkillModType = item->SkillModType; + ibs.BaneDmgRace = item->BaneDmgRace; + ibs.BaneDmgBody = item->BaneDmgBody; + ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; + ibs.BaneDmgAmt = item->BaneDmgAmt; + ibs.Magic = item->Magic; + ibs.CastTime_ = item->CastTime_; + ibs.ReqLevel = item->ReqLevel; + ibs.RecLevel = item->RecLevel; + ibs.RecSkill = item->RecSkill; + ibs.BardType = item->BardType; + ibs.BardValue = item->BardValue; + ibs.Light = item->Light; + ibs.Delay = item->Delay; + ibs.ElemDmgType = item->ElemDmgType; + ibs.ElemDmgAmt = item->ElemDmgAmt; + ibs.Range = item->Range; + ibs.Damage = item->Damage; + ibs.Color = item->Color; + ibs.ItemType = item->ItemType; + ibs.Material = item->Material; + ibs.unknown7 = 0; + ibs.EliteMaterial = item->EliteMaterial; + ibs.SellRate = item->SellRate; + + ibs.CombatEffects = item->CombatEffects; + ibs.Shielding = item->Shielding; + ibs.StunResist = item->StunResist; + ibs.StrikeThrough = item->StrikeThrough; + ibs.ExtraDmgSkill = item->ExtraDmgSkill; + ibs.ExtraDmgAmt = item->ExtraDmgAmt; + ibs.SpellShield = item->SpellShield; + ibs.Avoidance = item->Avoidance; + ibs.Accuracy = item->Accuracy; + ibs.FactionAmt1 = item->FactionAmt1; + ibs.FactionMod1 = item->FactionMod1; + ibs.FactionAmt2 = item->FactionAmt2; + ibs.FactionMod2 = item->FactionMod2; + ibs.FactionAmt3 = item->FactionAmt3; + ibs.FactionMod3 = item->FactionMod3; + ibs.FactionAmt4 = item->FactionAmt4; + ibs.FactionMod4 = item->FactionMod4; + + ss.write((const char*)&ibs, sizeof(SoF::structs::ItemBodyStruct)); + + //charm text + if (strlen(item->CharmFile) > 0) + { + ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + SoF::structs::ItemSecondaryBodyStruct isbs; + memset(&isbs, 0, sizeof(SoF::structs::ItemSecondaryBodyStruct)); + + isbs.augtype = item->AugType; + isbs.augrestrict = item->AugRestrict; + + for (int x = 0; x < 5; ++x) + { + isbs.augslots[x].type = item->AugSlotType[x]; + isbs.augslots[x].visible = item->AugSlotVisible[x]; + isbs.augslots[x].unknown = item->AugSlotUnk2[x]; + } + + isbs.ldonpoint_type = item->PointType; + isbs.ldontheme = item->LDoNTheme; + isbs.ldonprice = item->LDoNPrice; + isbs.ldonsellbackrate = item->LDoNSellBackRate; + isbs.ldonsold = item->LDoNSold; + + isbs.bagtype = item->BagType; + isbs.bagslots = item->BagSlots; + isbs.bagsize = item->BagSize; + isbs.wreduction = item->BagWR; + + isbs.book = item->Book; + isbs.booktype = item->BookType; + + ss.write((const char*)&isbs, sizeof(SoF::structs::ItemSecondaryBodyStruct)); + + if (strlen(item->Filename) > 0) + { + ss.write((const char*)item->Filename, strlen(item->Filename)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + SoF::structs::ItemTertiaryBodyStruct itbs; + memset(&itbs, 0, sizeof(SoF::structs::ItemTertiaryBodyStruct)); + + itbs.loregroup = item->LoreGroup; + itbs.artifact = item->ArtifactFlag; + itbs.summonedflag = item->SummonedFlag; + itbs.favor = item->Favor; + itbs.fvnodrop = item->FVNoDrop; + itbs.dotshield = item->DotShielding; + itbs.atk = item->Attack; + itbs.haste = item->Haste; + itbs.damage_shield = item->DamageShield; + itbs.guildfavor = item->GuildFavor; + itbs.augdistil = item->AugDistiller; + itbs.no_pet = item->NoPet; + + itbs.potion_belt_enabled = item->PotionBelt; + itbs.potion_belt_slots = item->PotionBeltSlots; + itbs.stacksize = stackable ? item->StackSize : 0; + itbs.no_transfer = item->NoTransfer; + itbs.expendablearrow = item->ExpendableArrow; + + ss.write((const char*)&itbs, sizeof(SoF::structs::ItemTertiaryBodyStruct)); + + // Effect Structures Broken down to allow variable length strings for effect names + int32 effect_unknown = 0; + + SoF::structs::ClickEffectStruct ices; + memset(&ices, 0, sizeof(SoF::structs::ClickEffectStruct)); + + ices.effect = item->Click.Effect; + ices.level2 = item->Click.Level2; + ices.type = item->Click.Type; + ices.level = item->Click.Level; + ices.max_charges = item->MaxCharges; + ices.cast_time = item->CastTime; + ices.recast = item->RecastDelay; + ices.recast_type = item->RecastType; + + ss.write((const char*)&ices, sizeof(SoF::structs::ClickEffectStruct)); + + if (strlen(item->ClickName) > 0) + { + ss.write((const char*)item->ClickName, strlen(item->ClickName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 + + SoF::structs::ProcEffectStruct ipes; + memset(&ipes, 0, sizeof(SoF::structs::ProcEffectStruct)); + + ipes.effect = item->Proc.Effect; + ipes.level2 = item->Proc.Level2; + ipes.type = item->Proc.Type; + ipes.level = item->Proc.Level; + ipes.procrate = item->ProcRate; + + ss.write((const char*)&ipes, sizeof(SoF::structs::ProcEffectStruct)); + + if (strlen(item->ProcName) > 0) + { + ss.write((const char*)item->ProcName, strlen(item->ProcName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 + + SoF::structs::WornEffectStruct iwes; + memset(&iwes, 0, sizeof(SoF::structs::WornEffectStruct)); + + iwes.effect = item->Worn.Effect; + iwes.level2 = item->Worn.Level2; + iwes.type = item->Worn.Type; + iwes.level = item->Worn.Level; + + ss.write((const char*)&iwes, sizeof(SoF::structs::WornEffectStruct)); + + if (strlen(item->WornName) > 0) + { + ss.write((const char*)item->WornName, strlen(item->WornName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + SoF::structs::WornEffectStruct ifes; + memset(&ifes, 0, sizeof(SoF::structs::WornEffectStruct)); + + ifes.effect = item->Focus.Effect; + ifes.level2 = item->Focus.Level2; + ifes.type = item->Focus.Type; + ifes.level = item->Focus.Level; + + ss.write((const char*)&ifes, sizeof(SoF::structs::WornEffectStruct)); + + if (strlen(item->FocusName) > 0) + { + ss.write((const char*)item->FocusName, strlen(item->FocusName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + SoF::structs::WornEffectStruct ises; + memset(&ises, 0, sizeof(SoF::structs::WornEffectStruct)); + + ises.effect = item->Scroll.Effect; + ises.level2 = item->Scroll.Level2; + ises.type = item->Scroll.Type; + ises.level = item->Scroll.Level; + + ss.write((const char*)&ises, sizeof(SoF::structs::WornEffectStruct)); + + if (strlen(item->ScrollName) > 0) + { + ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + // End of Effects + + SoF::structs::ItemQuaternaryBodyStruct iqbs; + memset(&iqbs, 0, sizeof(SoF::structs::ItemQuaternaryBodyStruct)); + + iqbs.scriptfileid = item->ScriptFileID; + iqbs.quest_item = item->QuestItemFlag; + iqbs.unknown15 = 0xffffffff; + + iqbs.Purity = item->Purity; + iqbs.BackstabDmg = item->BackstabDmg; + iqbs.DSMitigation = item->DSMitigation; + iqbs.HeroicStr = item->HeroicStr; + iqbs.HeroicInt = item->HeroicInt; + iqbs.HeroicWis = item->HeroicWis; + iqbs.HeroicAgi = item->HeroicAgi; + iqbs.HeroicDex = item->HeroicDex; + iqbs.HeroicSta = item->HeroicSta; + iqbs.HeroicCha = item->HeroicCha; + iqbs.HeroicMR = item->HeroicMR; + iqbs.HeroicFR = item->HeroicFR; + iqbs.HeroicCR = item->HeroicCR; + iqbs.HeroicDR = item->HeroicDR; + iqbs.HeroicPR = item->HeroicPR; + iqbs.HeroicSVCorrup = item->HeroicSVCorrup; + iqbs.HealAmt = item->HealAmt; + iqbs.SpellDmg = item->SpellDmg; + //iqbs.clairvoyance = item->Clairvoyance; + + iqbs.subitem_count = 0; + + char *SubSerializations[10]; // + + uint32 SubLengths[10]; + + for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + + SubSerializations[x] = nullptr; + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + + if (subitem) { + + int SubSlotNumber; + + iqbs.subitem_count++; + + if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? + //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); + else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) + //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); + else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) + //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); + else + SubSlotNumber = slot_id_in; // ??????? + + /* + // TEST CODE: + SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); + */ + + SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + } + } + + ss.write((const char*)&iqbs, sizeof(SoF::structs::ItemQuaternaryBodyStruct)); + + for (int x = 0; x < 10; ++x) { + + if (SubSerializations[x]) { + ss.write((const char*)&x, sizeof(uint32)); + ss.write(SubSerializations[x], SubLengths[x]); + + safe_delete_array(SubSerializations[x]); + } + } + + char* item_serial = new char[ss.tellp()]; + memset(item_serial, 0, ss.tellp()); + memcpy(item_serial, ss.str().c_str(), ss.tellp()); + + *length = ss.tellp(); + return item_serial; } - char* item_serial = new char[ss.tellp()]; - memset(item_serial, 0, ss.tellp()); - memcpy(item_serial, ss.str().c_str(), ss.tellp()); + static inline uint32 ServerToSoFSlot(uint32 ServerSlot) + { + uint32 SoFSlot = 0; - *length = ss.tellp(); - return item_serial; + if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots + SoFSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) + SoFSlot = ServerSlot + 11; + else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) + SoFSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) + SoFSlot = ServerSlot + 1; + else if (ServerSlot == MainPowerSource) + SoFSlot = slots::MainPowerSource; + else + SoFSlot = ServerSlot; + + return SoFSlot; + } + + static inline uint32 ServerToSoFCorpseSlot(uint32 ServerCorpse) + { + //uint32 SoFCorpse; + } + + static inline uint32 SoFToServerSlot(uint32 SoFSlot) + { + uint32 ServerSlot = 0; + + if (SoFSlot >= slots::MainAmmo && SoFSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots + ServerSlot = SoFSlot - 1; + else if (SoFSlot >= consts::GENERAL_BAGS_BEGIN && SoFSlot <= consts::CURSOR_BAG_END) + ServerSlot = SoFSlot - 11; + else if (SoFSlot >= consts::BANK_BAGS_BEGIN && SoFSlot <= consts::BANK_BAGS_END) + ServerSlot = SoFSlot - 1; + else if (SoFSlot >= consts::SHARED_BANK_BAGS_BEGIN && SoFSlot <= consts::SHARED_BANK_BAGS_END) + ServerSlot = SoFSlot - 1; + else if (SoFSlot == slots::MainPowerSource) + ServerSlot = MainPowerSource; + else + ServerSlot = SoFSlot; + + return ServerSlot; + } + + static inline uint32 SoFToServerCorpseSlot(uint32 SoFCorpse) + { + //uint32 ServerCorpse; + } } - -DECODE(OP_AltCurrencySellSelection) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); - IN(merchant_entity_id); - emu->slot_id = SoFToServerSlot(eq->slot_id); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySell) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - IN(merchant_entity_id); - emu->slot_id = SoFToServerSlot(eq->slot_id); - IN(charges); - IN(cost); - FINISH_DIRECT_DECODE(); -} - - -} //end namespace SoF - - - - +// end namespace SoF diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 77d87cb7d..eaa7b3b10 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1,4 +1,3 @@ - #include "../debug.h" #include "titanium.h" #include "../opcodemgr.h" @@ -13,1475 +12,1515 @@ #include "titanium_structs.h" #include -namespace Titanium { +namespace Titanium +{ + static const char *name = "Titanium"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "Titanium"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); -char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline uint32 ServerToTitaniumSlot(uint32 ServerSlot); + static inline uint32 ServerToTitaniumCorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 TitaniumToServerSlot(uint32 TitaniumSlot); + static inline uint32 TitaniumToServerCorpseSlot(uint32 TitaniumCorpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "titanium_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "titanium_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClientTitanium; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClientTitanium; + } #include "ss_define.h" +// ENCODE methods + EAT_ENCODE(OP_GuildMemberLevelUpdate); // added ; -/* -// Converts Server Slot IDs to Titanium Slot IDs for use in Encodes -static inline uint32 ServerToTitaniumSlot(uint32 ServerSlot) { - uint32 TitaniumSlot; - // reserved -} -*/ -/* -// Converts Titanium Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 TitaniumToServerSlot(uint32 TitaniumSlot) { - uint32 ServerSlot; - // reserved -} -*/ -/* -// Converts Server Corpse Slot IDs to Titanium Corpse Slot IDs for use in Encodes -static inline uint32 ServerToTitaniumCorpseSlot(uint32 ServerCorpse) { - uint32 TitaniumCorpse; - // reserved -} -*/ -/* -// Converts Titanium Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 TitaniumToServerCorpseSlot(uint32 TitaniumCorpse) { - uint32 ServerCorpse; - // reserved -} -*/ - - -EAT_ENCODE(OP_ZoneServerReady) -EAT_ENCODE(OP_GuildMemberLevelUpdate) - -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct); - int r; - for(r = 0; r < 10; r++) { - OUT(zone[r]); - OUT(eyecolor1[r]); - OUT(eyecolor2[r]); - OUT(hairstyle[r]); - OUT(primary[r]); - if(emu->race[r] > 473) - eq->race[r] = 1; - else - eq->race[r] = emu->race[r]; - OUT(class_[r]); - OUT_str(name[r]); - OUT(gender[r]); - OUT(level[r]); - OUT(secondary[r]); - OUT(face[r]); - OUT(beard[r]); - int k; - for(k = 0; k < 9; k++) { - OUT(equip[r][k]); - OUT(cs_colors[r][k].color); - } - OUT(haircolor[r]); - OUT(gohome[r]); - OUT(tutorial[r]); - OUT(deity[r]); - OUT(beardcolor[r]); - eq->unknown820[r] = 0xFF; - eq->unknown902[r] = 0xFF; - } - FINISH_ENCODE(); -} - - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for Titanium - // clientver 1 is for all clients and 3 is for Titanium - if (emu->clientver <= 3 ) + EAT_ENCODE(OP_ZoneServerReady); // added ; + + ENCODE(OP_Action) { - OUT(id); - eq->unknown004 = 1; - eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); - eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); - eq->title_sid = emu->id - emu->current_level + 1; - eq->desc_sid = emu->id - emu->current_level + 1; - OUT(class_type); - OUT(cost); - OUT(seq); - OUT(current_level); - OUT(prereq_skill); - OUT(prereq_minpoints); + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); + + OUT(target); + OUT(source); + OUT(level); + OUT(instrument_mod); + OUT(sequence); OUT(type); - OUT(spellid); - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - OUT(unknown80[0]); - OUT(unknown80[1]); - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } + //OUT(damage); + OUT(spell); + OUT(buff_unknown); // if this is 4, a buff icon is made + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - -ENCODE(OP_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); - - uint32 r; - - eq->available_slots=0xffffffff; - memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); - memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); - -// OUT(checksum); - OUT(gender); - OUT(race); - OUT(class_); -// OUT(unknown00016); - OUT(level); - eq->level1 = emu->level; -// OUT(unknown00022[2]); - for(r = 0; r < 5; r++) { - OUT(binds[r].zoneId); - OUT(binds[r].x); - OUT(binds[r].y); - OUT(binds[r].z); - OUT(binds[r].heading); - } - OUT(deity); - OUT(intoxication); - OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); - OUT(abilitySlotRefresh); - OUT(haircolor); - OUT(beardcolor); - OUT(eyecolor1); - OUT(eyecolor2); - OUT(hairstyle); - OUT(beard); -// OUT(unknown00178[10]); - for(r = 0; r < 9; r++) { - OUT(item_material[r]); - OUT(item_tint[r].color); - } -// OUT(unknown00224[48]); - for(r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { - OUT(aa_array[r].AA); - OUT(aa_array[r].value); - } -// OUT(unknown02220[4]); - OUT(points); - OUT(mana); - OUT(cur_hp); - OUT(STR); - OUT(STA); - OUT(CHA); - OUT(DEX); - OUT(INT); - OUT(AGI); - OUT(WIS); - OUT(face); -// OUT(unknown02264[47]); - OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); -// OUT(unknown4184[448]); - OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); -// OUT(unknown04396[32]); - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - OUT(platinum_cursor); - OUT(gold_cursor); - OUT(silver_cursor); - OUT(copper_cursor); - - OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) - -// OUT(unknown04760[236]); - OUT(toxicity); - OUT(thirst_level); - OUT(hunger_level); - for(r = 0; r < structs::BUFF_COUNT; r++) { - OUT(buffs[r].slotid); - OUT(buffs[r].level); - OUT(buffs[r].bard_modifier); - OUT(buffs[r].effect); - OUT(buffs[r].spellid); - OUT(buffs[r].duration); - OUT(buffs[r].counters); - OUT(buffs[r].player_id); - } - for(r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { - OUT(disciplines.values[r]); - } -// OUT(unknown05008[360]); -// OUT_array(recastTimers, structs::MAX_RECAST_TYPES); - OUT(endurance); - OUT(aapoints_spent); - OUT(aapoints); -// OUT(unknown06160[4]); - for(r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) { - OUT_str(bandoliers[r].name); - uint32 k; - for(k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { - OUT(bandoliers[r].items[k].item_id); - OUT(bandoliers[r].items[k].icon); - OUT_str(bandoliers[r].items[k].item_name); - } - } -// OUT(unknown07444[5120]); - for(r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) { - OUT(potionbelt.items[r].item_id); - OUT(potionbelt.items[r].icon); - OUT_str(potionbelt.items[r].item_name); - } -// OUT(unknown12852[8]); -// OUT(unknown12864[76]); - OUT_str(name); - OUT_str(last_name); - OUT(guild_id); - OUT(birthday); - OUT(lastlogin); - OUT(timePlayedMin); - OUT(pvp); - OUT(anon); - OUT(gm); - OUT(guildrank); - OUT(guildbanker); -// OUT(unknown13054[8]); - OUT(exp); -// OUT(unknown13072[12]); - OUT(timeentitledonaccount); - OUT_array(languages, structs::MAX_PP_LANGUAGE); -// OUT(unknown13109[7]); - OUT(x); - OUT(y); - OUT(z); - OUT(heading); -// OUT(unknown13132[4]); - OUT(platinum_bank); - OUT(gold_bank); - OUT(silver_bank); - OUT(copper_bank); - OUT(platinum_shared); -// OUT(unknown13156[84]); - OUT(expansions); -// OUT(unknown13244[12]); - OUT(autosplit); -// OUT(unknown13260[16]); - OUT(zone_id); - OUT(zoneInstance); - for(r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { - OUT_str(groupMembers[r]); - } - strcpy(eq->groupLeader, emu->groupMembers[0]); -// OUT_str(groupLeader); -// OUT(unknown13728[660]); - OUT(entityid); - OUT(leadAAActive); -// OUT(unknown14392[4]); - OUT(ldon_points_guk); - OUT(ldon_points_mir); - OUT(ldon_points_mmc); - OUT(ldon_points_ruj); - OUT(ldon_points_tak); - OUT(ldon_points_available); -// OUT(unknown14420[132]); - OUT(tribute_time_remaining); - OUT(career_tribute_points); -// OUT(unknown7208); - OUT(tribute_points); -// OUT(unknown7216); - OUT(tribute_active); - for(r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { - OUT(tributes[r].tribute); - OUT(tributes[r].tier); - } -// OUT(unknown14616[8]); - OUT(group_leadership_exp); - OUT(raid_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_points); - OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); -// OUT(unknown14772[128]); - OUT(air_remaining); - OUT(PVPKills); - OUT(PVPDeaths); - OUT(PVPCurrentPoints); - OUT(PVPCareerPoints); - OUT(PVPBestKillStreak); - OUT(PVPWorstDeathStreak); - OUT(PVPCurrentKillStreak); -// OUT(unknown14932[4580]); - OUT(expAA); -// OUT(unknown19516[40]); - OUT(currentRadCrystals); - OUT(careerRadCrystals); - OUT(currentEbonCrystals); - OUT(careerEbonCrystals); - OUT(groupAutoconsent); - OUT(raidAutoconsent); - OUT(guildAutoconsent); -// OUT(unknown19575[5]); - eq->level3 = emu->level; - eq->showhelm = emu->showhelm; -// OUT(unknown19584[4]); -// OUT(unknown19588); - - -const uint8 bytes[] = { -0x78,0x03,0x00,0x00,0x1A,0x04,0x00,0x00,0x1A,0x04,0x00,0x00,0x19,0x00,0x00,0x00, -0x19,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, -0x0F,0x00,0x00,0x00,0x1F,0x85,0xEB,0x3E,0x33,0x33,0x33,0x3F,0x09,0x00,0x00,0x00, -0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x14 -}; - memcpy(eq->unknown12864, bytes, sizeof(bytes)); - - - - //set the checksum... - CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct)-4); - - FINISH_ENCODE(); -} - -ENCODE(OP_Track) -{ - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + ENCODE(OP_BazaarSearch) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); - delete in; - return; - } + if (((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) { - in->size = sizeof(structs::Track_Struct) * EntryCount; - in->pBuffer = new unsigned char[in->size]; - structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++eq, ++emu) - { - OUT(entityid); - OUT(padding002); - OUT(distance); - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; - - //determine and verify length - int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); - delete in; - return; - } - - //make the EQ struct. - in->size = sizeof(structs::Spawn_Struct)*entrycount; - in->pBuffer = new unsigned char[in->size]; - structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; - - //zero out the packet. We could avoid this memset by setting all fields (including unknowns) - //in the loop. - memset(in->pBuffer, 0, in->size); - - //do the transform... - int r; - int k; - for(r = 0; r < entrycount; r++, eq++, emu++) { -// eq->unknown0000 = emu->unknown0000; - eq->gm = emu->gm; -// eq->unknown0003 = emu->unknown0003; - eq->aaitle = emu->aaitle; -// eq->unknown0004 = emu->unknown0004; - eq->anon = emu->anon; - eq->face = emu->face; - strcpy(eq->name, emu->name); - eq->deity = emu->deity; -// eq->unknown0073 = emu->unknown0073; - eq->size = emu->size; -// eq->unknown0079 = emu->unknown0079; - eq->NPC = emu->NPC; - eq->invis = emu->invis; - eq->haircolor = emu->haircolor; - eq->curHp = emu->curHp; - eq->max_hp = emu->max_hp; - eq->findable = emu->findable; -// eq->unknown0089[5] = emu->unknown0089[5]; - eq->deltaHeading = emu->deltaHeading; - eq->x = emu->x; -// eq->padding0054 = emu->padding0054; - eq->y = emu->y; - eq->animation = emu->animation; -// eq->padding0058 = emu->padding0058; - eq->z = emu->z; - eq->deltaY = emu->deltaY; - eq->deltaX = emu->deltaX; - eq->heading = emu->heading; -// eq->padding0066 = emu->padding0066; - eq->deltaZ = emu->deltaZ; -// eq->padding0070 = emu->padding0070; - eq->eyecolor1 = emu->eyecolor1; -// eq->unknown0115[24] = emu->unknown0115[24]; - eq->showhelm = emu->showhelm; -// eq->unknown0140[4] = emu->unknown0140[4]; - eq->is_npc = emu->is_npc; - eq->hairstyle = emu->hairstyle; - - //if(emu->gender == 1){ - // eq->hairstyle = eq->hairstyle == 0xFF ? 0 : eq->hairstyle; - //} - - eq->beardcolor = emu->beardcolor; -// eq->unknown0147[4] = emu->unknown0147[4]; - eq->level = emu->level; -// eq->unknown0259[4] = emu->unknown0259[4]; - eq->beard = emu->beard; - strcpy(eq->suffix, emu->suffix); - eq->petOwnerId = emu->petOwnerId; - eq->guildrank = emu->guildrank; -// eq->unknown0194[3] = emu->unknown0194[3]; - for(k = 0; k < 9; k++) { - eq->equipment[k] = emu->equipment[k]; - eq->colors[k].color = emu->colors[k].color; - } - for(k = 0; k < 8; k++) { - eq->set_to_0xFF[k] = 0xFF; + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; } - eq->runspeed = emu->runspeed; - eq->afk = emu->afk; - eq->guildID = emu->guildID; - strcpy(eq->title, emu->title); -// eq->unknown0274 = emu->unknown0274; - eq->helm = emu->helm; - if(emu->race > 473) - eq->race = 1; - else - eq->race = emu->race; -// eq->unknown0288 = emu->unknown0288; - strcpy(eq->lastName, emu->lastName); - eq->walkspeed = emu->walkspeed; -// eq->unknown0328 = emu->unknown0328; - eq->is_pet = emu->is_pet; - eq->light = emu->light; - eq->class_ = emu->class_; - eq->eyecolor2 = emu->eyecolor2; -// eq->unknown0333 = emu->unknown0333; - eq->flymode = emu->flymode; - eq->gender = emu->gender; - eq->bodytype = emu->bodytype; -// eq->unknown0336[3] = emu->unknown0336[3]; - eq->equip_chest2 = emu->equip_chest2; - eq->spawnId = emu->spawnId; -// eq->unknown0344[4] = emu->unknown0344[4]; - eq->lfg = emu->lfg; - - /* - if (emu->face == 99) {eq->face = 0;} - if (emu->eyecolor1 == 99) {eq->eyecolor1 = 0;} - if (emu->eyecolor2 == 99) {eq->eyecolor2 = 0;} - if (emu->hairstyle == 99) {eq->hairstyle = 0;} - if (emu->haircolor == 99) {eq->haircolor = 0;} - if (emu->beard == 99) {eq->beard = 0;} - if (emu->beardcolor == 99) {eq->beardcolor = 0;} - */ - - } - - - //kill off the emu structure and send the eq packet. - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+5; // ItemPacketType + Serialization + \0 - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length+1); - - delete[] __emu_buffer; - safe_delete_array(serialized); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int itemcount = in->size / sizeof(InternalSerializedItem_Struct); - if(itemcount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); - delete in; - return; - } - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - //do the transform... - int r; - std::string serial_string; - for(r = 0; r < itemcount; r++, eq++) { - uint32 length; - char *serialized=SerializeItem((const ItemInst*)eq->inst,eq->slot_id,&length,0); - if (serialized) { - serial_string.append(serialized,length+1); - safe_delete_array(serialized); - } else { - _log(NET__STRUCTS, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); - } - - } - - in->size = serial_string.length(); - in->pBuffer = new unsigned char[in->size]; - memcpy(in->pBuffer,serial_string.c_str(),serial_string.length()); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_BazaarSearch) { - - if(((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) { - + //consume the packet EQApplicationPacket *in = *p; *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + + //determine and verify length + int entrycount = in->size / sizeof(BazaarSearchResults_Struct); + if (entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + + //make the EQ struct. + in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount; + in->pBuffer = new unsigned char[in->size]; + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer; + + //zero out the packet. We could avoid this memset by setting all fields (including unknowns) in the loop. + memset(in->pBuffer, 0, in->size); + + for (int i = 0; i < entrycount; i++, eq++, emu++) { + eq->Beginning.Action = emu->Beginning.Action; + eq->Beginning.Unknown001 = emu->Beginning.Unknown001; + eq->Beginning.Unknown002 = emu->Beginning.Unknown002; + eq->NumItems = emu->NumItems; + eq->SerialNumber = emu->SerialNumber; + eq->SellerID = emu->SellerID; + eq->Cost = emu->Cost; + eq->ItemStat = emu->ItemStat; + strcpy(eq->ItemName, emu->ItemName); + } + + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); - return; } - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; + ENCODE(OP_BecomeTrader) + { + ENCODE_LENGTH_EXACT(BecomeTrader_Struct); + SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; + OUT(ID); + OUT(Code); - //determine and verify length - int entrycount = in->size / sizeof(BazaarSearchResults_Struct); - if(entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; + FINISH_ENCODE(); } - //make the EQ struct. - in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount; - in->pBuffer = new unsigned char[in->size]; - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer; - - //zero out the packet. We could avoid this memset by setting all fields (including unknowns) - //in the loop. - memset(in->pBuffer, 0, in->size); - - for(int i=0; iBeginning.Action = emu->Beginning.Action; - eq->Beginning.Unknown001 = emu->Beginning.Unknown001; - eq->Beginning.Unknown002 = emu->Beginning.Unknown002; - eq->NumItems = emu->NumItems; - eq->SerialNumber = emu->SerialNumber; - eq->SellerID = emu->SellerID; - eq->Cost = emu->Cost; - eq->ItemStat = emu->ItemStat; - strcpy(eq->ItemName, emu->ItemName); - } - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); - - -} - -ENCODE(OP_Trader) { - - if((*p)->size != sizeof(TraderBuy_Struct)) { + ENCODE(OP_CharInventory) + { + //consume the packet EQApplicationPacket *in = *p; *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + + int itemcount = in->size / sizeof(InternalSerializedItem_Struct); + if (itemcount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + delete in; + return; + } + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; + + //do the transform... + int r; + std::string serial_string; + for (r = 0; r < itemcount; r++, eq++) { + uint32 length; + char *serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &length, 0); + if (serialized) { + serial_string.append(serialized, length + 1); + safe_delete_array(serialized); + } + else { + _log(NET__STRUCTS, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } + + } + + in->size = serial_string.length(); + in->pBuffer = new unsigned char[in->size]; + memcpy(in->pBuffer, serial_string.c_str(), serial_string.length()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); - return; } - ENCODE_FORWARD(OP_TraderBuy); -} -ENCODE(OP_TraderBuy) { + ENCODE(OP_DeleteSpawn) + { + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - ENCODE_LENGTH_EXACT(TraderBuy_Struct); - SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); + OUT(spawn_id); - OUT(Action); - OUT(Price); - OUT(TraderID); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(ItemID); - OUT(Quantity); - OUT(AlreadySold); + FINISH_ENCODE(); + } - FINISH_ENCODE(); -} + ENCODE(OP_DzCompass) + { + SETUP_VAR_ENCODE(ExpeditionCompass_Struct); + ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); + + OUT(count); -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; + for (uint32 i = 0; i < emu->count; ++i) + { + OUT(entries[i].x); + OUT(entries[i].y); + OUT(entries[i].z); + } - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; + FINISH_ENCODE(); + } + ENCODE(OP_DzExpeditionEndsWarning) + { + ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); + SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); + OUT(minutes_remaining); - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. + FINISH_ENCODE(); + } - uint8 *buffer; - buffer = in->pBuffer; + ENCODE(OP_DzExpeditionInfo) + { + ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; + OUT(max_players); + eq->enabled_max = 1; + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->leader_name, emu->leader_name); - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); + FINISH_ENCODE(); + } - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header + ENCODE(OP_DzExpeditionList) + { + SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); + ss.write((const char*)&null_term, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzJoinExpeditionConfirm) + { + ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); + SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); + + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->player_name, emu->player_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzLeaderStatus) + { + SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + + ss.write((const char*)&client_id, sizeof(uint32)); + //ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->leader_name, strlen(emu->leader_name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//1 + ss.write((const char*)&client_id, sizeof(uint32)); + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzMemberList) + { + SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].status, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_GuildMemberList) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count*sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data ); - const char *emu_note = (emu_name + + const char *emu_note = (emu_name + emu->name_length + //skip name contents emu->count //skip string terminators ); - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { - //the order we set things here must match the struct + //the order we set things here must match the struct -//nice helper macro -/*#define SlideStructString(field, str) \ - strcpy(e->field, str.c_str()); \ - e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ + //nice helper macro + /*#define SlideStructString(field, str) \ + strcpy(e->field, str.c_str()); \ + e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ #define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) - - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - PutFieldN(rank); - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - PutFieldN(total_tribute); - PutFieldN(last_tribute); - e->unknown_one = htonl(1); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + PutFieldN(rank); + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + PutFieldN(total_tribute); + PutFieldN(last_tribute); + e->unknown_one = htonl(1); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); #undef SlideStructString #undef PutFieldN - e++; + e++; + } } - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ReadBook) { - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - BookText_Struct *emu_BookText_Struct = (BookText_Struct *)__emu_buffer; - - in->size = sizeof(structs::BookText_Struct) + strlen(emu_BookText_Struct->booktext); - - in->pBuffer = new unsigned char[in->size]; - - structs::BookText_Struct *eq_BookText_Struct = (structs::BookText_Struct*)in->pBuffer; - - eq_BookText_Struct->window = emu_BookText_Struct->window; - eq_BookText_Struct->type = emu_BookText_Struct->type; - strcpy(eq_BookText_Struct->booktext, emu_BookText_Struct->booktext); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - if(emu->race > 473){ - eq->race = 1; - } - else { - OUT(race); - } - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - /* - //Test code for identifying the structure - uint8 ofs; - uint8 val; - ofs = emu->texture; - val = emu->face; - ((uint8*)eq)[ofs % 168] = val; - */ - FINISH_ENCODE(); -} - -ENCODE(OP_VetRewardsAvaliable) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - - uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for(uint32 i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; - - vr->claim_id = ivr->claim_id; - vr->item.item_id = ivr->items[0].item_id; - strcpy(vr->item.item_name, ivr->items[0].item_name); - - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); - } - - dest->FastQueuePacket(&outapp_create); - delete inapp; -} - -ENCODE(OP_InspectAnswer) { - ENCODE_LENGTH_EXACT(InspectResponse_Struct); - SETUP_DIRECT_ENCODE(InspectResponse_Struct, structs::InspectResponse_Struct); - - OUT(TargetID); - OUT(playerid); - - int r; - for (r = 0; r <= 20; r++) { - strn0cpy(eq->itemnames[r], emu->itemnames[r], sizeof(eq->itemnames[r])); - } - - // move arrow item down to last element in titanium array - strn0cpy(eq->itemnames[21], emu->itemnames[22], sizeof(eq->itemnames[21])); - - int k; - for (k = 0; k <= 20; k++) { - OUT(itemicons[k]); - } - - // move arrow icon down to last element in titanium array - eq->itemicons[21] = emu->itemicons[22]; - - strn0cpy(eq->text, emu->text, sizeof(eq->text)); - - FINISH_ENCODE(); -} - -ENCODE(OP_RespondAA) { - ENCODE_LENGTH_EXACT(AATable_Struct); - SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); - - unsigned int r; - for(r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { - OUT(aa_list[r].aa_skill); - OUT(aa_list[r].aa_value); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteSpawn) { - SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - OUT(spawn_id); - FINISH_ENCODE(); -} - -ENCODE(OP_WearChange) { - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionEndsWarning) -{ - ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); - SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - OUT(minutes_remaining); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionInfo) -{ - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - OUT(max_players); - eq->enabled_max = 1; - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->leader_name, emu->leader_name); - FINISH_ENCODE(); -} - -ENCODE(OP_DzCompass) -{ - SETUP_VAR_ENCODE(ExpeditionCompass_Struct); - ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - OUT(count); - - for(uint32 i = 0; i < emu->count; ++i) - { - OUT(entries[i].x); - OUT(entries[i].y); - OUT(entries[i].z); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_DzMemberList) -{ - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].status, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionList) -{ - SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); - ss.write((const char*)&null_term, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzLeaderStatus) -{ - SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - - ss.write((const char*)&client_id, sizeof(uint32)); - //ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->leader_name, strlen(emu->leader_name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//1 - ss.write((const char*)&client_id, sizeof(uint32)); - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzJoinExpeditionConfirm) -{ - ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); - SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->player_name, emu->player_name); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); - OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - OUT(sequence); - OUT(type); - //OUT(damage); - OUT(spell); - OUT(buff_unknown); // if this is 4, a buff icon is made - FINISH_ENCODE(); -} - -ENCODE(OP_BecomeTrader) -{ - ENCODE_LENGTH_EXACT(BecomeTrader_Struct); - SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); - OUT(ID); - OUT(Code); - FINISH_ENCODE(); -} - -ENCODE(OP_PetBuffWindow) -{ - ENCODE_LENGTH_EXACT(PetBuff_Struct); - SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); - - OUT(petid); - OUT(buffcount); - - int EQBuffSlot = 0; - - for(uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) - { - if(emu->spellid[EmuBuffSlot]) - { - eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; - eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; - } - } - - FINISH_ENCODE(); -} - -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - OUT_str(Title); - OUT_str(Text); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); - - eq->unknown4236 = 0x00000000; - eq->unknown4240 = 0xffffffff; - - FINISH_ENCODE(); -} - -ENCODE(OP_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - FINISH_ENCODE(); -} - -DECODE(OP_InspectRequest) { - DECODE_LENGTH_EXACT(structs::Inspect_Struct); - SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); - IN(TargetID); - IN(PlayerID); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_InspectAnswer) { - DECODE_LENGTH_EXACT(structs::InspectResponse_Struct); - SETUP_DIRECT_DECODE(InspectResponse_Struct, structs::InspectResponse_Struct); - - IN(TargetID); - IN(playerid); - - int r; - for (r = 0; r <= 20; r++) { - strn0cpy(emu->itemnames[r], eq->itemnames[r], sizeof(emu->itemnames[r])); - } - - // move arrow item up to last element in server array - strn0cpy(emu->itemnames[21], "", sizeof(emu->itemnames[21])); - strn0cpy(emu->itemnames[22], eq->itemnames[21], sizeof(emu->itemnames[22])); - - int k; - for (k = 0; k <= 20; k++) { - IN(itemicons[k]); - } - - // move arrow icon up to last element in server array - emu->itemicons[21] = 0xFFFFFFFF; - emu->itemicons[22] = eq->itemicons[21]; - - strn0cpy(emu->text, eq->text, sizeof(emu->text)); - - FINISH_DIRECT_DECODE(); -} - -ENCODE(OP_LFGuild) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - uint32 Command = in->ReadUInt32(); - - if(Command != 0) - { + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); - return; } - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, sizeof(structs::LFGuild_PlayerToggle_Struct)); + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - memcpy(outapp->pBuffer, in->pBuffer, sizeof(structs::LFGuild_PlayerToggle_Struct)); + OUT(spawnid); + OUT_str(charname); - dest->FastQueuePacket(&outapp, ack_req); + if (emu->race > 473) + eq->race = 1; + else + OUT(race); - delete in; -} + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + /* + //Test code for identifying the structure + uint8 ofs; + uint8 val; + ofs = emu->texture; + val = emu->face; + ((uint8*)eq)[ofs % 168] = val; + */ -DECODE(OP_WearChange) { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - IN(spawn_id); - IN(material); - IN(color.color); - IN(wear_slot_id); - emu->unknown06 = 0; - emu->elite_material = 0; - emu->hero_forge_model = 0; - emu->unknown18 = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TraderBuy) { - DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); - SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); - MEMSET_IN(TraderBuy_Struct); - - IN(Action); - IN(Price); - IN(TraderID); - memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); - IN(ItemID); - IN(Quantity); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); + FINISH_ENCODE(); } - IN(link_hash); - FINISH_DIRECT_DECODE(); -} + ENCODE(OP_InspectAnswer) + { + ENCODE_LENGTH_EXACT(InspectResponse_Struct); + SETUP_DIRECT_ENCODE(InspectResponse_Struct, structs::InspectResponse_Struct); -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 29; r++) { - IN(filters[r]); - } - FINISH_DIRECT_DECODE(); -} + OUT(TargetID); + OUT(playerid); - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - IN(class_); - IN(beardcolor); - IN(beard); - IN(haircolor); - IN(gender); - IN(race); - IN(start_zone); - IN(hairstyle); - IN(deity); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - emu->type = 3; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(window); - IN(type); - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LFGuild) -{ - uint32 Command = __packet->ReadUInt32(); - - if(Command != 0) - return; - - SETUP_DIRECT_DECODE(LFGuild_PlayerToggle_Struct, structs::LFGuild_PlayerToggle_Struct); - memcpy(emu, eq, sizeof(structs::LFGuild_PlayerToggle_Struct)); - memset(emu->Unknown612, 0, sizeof(emu->Unknown612)); - - FINISH_DIRECT_DECODE(); -} - -char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth) { - char *serialization = nullptr; - char *instance = nullptr; - const char *protection=(const char *)"\\\\\\\\\\"; - char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; - bool stackable=inst->IsStackable(); - uint32 merchant_slot=inst->GetMerchantSlot(); - int16 charges=inst->GetCharges(); - const Item_Struct *item=inst->GetItem(); - int i; - uint32 sub_length; - - MakeAnyLenString(&instance, - "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", - stackable ? charges : 0, - 0, - (merchant_slot==0) ? slot_id : merchant_slot, - inst->GetPrice(), - (merchant_slot==0) ? 1 : inst->GetMerchantCount(), - 0, - //merchant_slot, //instance ID, bullshit for now - (merchant_slot==0) ? inst->GetSerialNumber() : merchant_slot, - 0, - (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? 1 : 0) : charges), - inst->IsInstNoDrop() ? 1 : 0, - 0 - ); - - for(i=0;i<10;i++) { - ItemInst *sub=inst->GetItem(i); - if (sub) { - sub_items[i]=SerializeItem(sub,0,&sub_length,depth+1); + int r; + for (r = 0; r <= 20; r++) { + strn0cpy(eq->itemnames[r], emu->itemnames[r], sizeof(eq->itemnames[r])); } + + // move arrow item down to last element in titanium array + strn0cpy(eq->itemnames[21], emu->itemnames[22], sizeof(eq->itemnames[21])); + + int k; + for (k = 0; k <= 20; k++) { + OUT(itemicons[k]); + } + + // move arrow icon down to last element in titanium array + eq->itemicons[21] = emu->itemicons[22]; + strn0cpy(eq->text, emu->text, sizeof(eq->text)); + + FINISH_ENCODE(); } + ENCODE(OP_InspectRequest) + { + ENCODE_LENGTH_EXACT(Inspect_Struct); + SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - *length=MakeAnyLenString(&serialization, - "%.*s%s" // For leading quotes (and protection) if a subitem; - "%s" // Instance data - "%.*s\"" // Quotes (and protection, if needed) around static data - "%i" // item->ItemClass so we can do |%s instead of %s| + OUT(TargetID); + OUT(PlayerID); + + FINISH_ENCODE(); + } + + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } + + ENCODE(OP_ItemPacket) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); + + uint32 length; + char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); + + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 5; // ItemPacketType + Serialization + \0 + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length + 1); + + delete[] __emu_buffer; + safe_delete_array(serialized); + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_LeadershipExpUpdate) + { + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LFGuild) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + uint32 Command = in->ReadUInt32(); + + if (Command != 0) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, sizeof(structs::LFGuild_PlayerToggle_Struct)); + + memcpy(outapp->pBuffer, in->pBuffer, sizeof(structs::LFGuild_PlayerToggle_Struct)); + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + OUT_str(Title); + OUT_str(Text); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + + eq->unknown4236 = 0x00000000; + eq->unknown4240 = 0xffffffff; + + FINISH_ENCODE(); + } + + ENCODE(OP_PetBuffWindow) + { + ENCODE_LENGTH_EXACT(PetBuff_Struct); + SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); + + OUT(petid); + OUT(buffcount); + + int EQBuffSlot = 0; + + for (uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) + { + if (emu->spellid[EmuBuffSlot]) + { + eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; + eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_PlayerProfile) + { + SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); + + uint32 r; + + eq->available_slots = 0xffffffff; + memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); + memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); + + // OUT(checksum); + OUT(gender); + OUT(race); + OUT(class_); + // OUT(unknown00016); + OUT(level); + eq->level1 = emu->level; + // OUT(unknown00022[2]); + for (r = 0; r < 5; r++) { + OUT(binds[r].zoneId); + OUT(binds[r].x); + OUT(binds[r].y); + OUT(binds[r].z); + OUT(binds[r].heading); + } + OUT(deity); + OUT(intoxication); + OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); + OUT(abilitySlotRefresh); + OUT(haircolor); + OUT(beardcolor); + OUT(eyecolor1); + OUT(eyecolor2); + OUT(hairstyle); + OUT(beard); + // OUT(unknown00178[10]); + for (r = 0; r < 9; r++) { + OUT(item_material[r]); + OUT(item_tint[r].color); + } + // OUT(unknown00224[48]); + for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { + OUT(aa_array[r].AA); + OUT(aa_array[r].value); + } + // OUT(unknown02220[4]); + OUT(points); + OUT(mana); + OUT(cur_hp); + OUT(STR); + OUT(STA); + OUT(CHA); + OUT(DEX); + OUT(INT); + OUT(AGI); + OUT(WIS); + OUT(face); + // OUT(unknown02264[47]); + OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); + // OUT(unknown4184[448]); + OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); + // OUT(unknown04396[32]); + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + OUT(platinum_cursor); + OUT(gold_cursor); + OUT(silver_cursor); + OUT(copper_cursor); + + OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) + + // OUT(unknown04760[236]); + OUT(toxicity); + OUT(thirst_level); + OUT(hunger_level); + for (r = 0; r < structs::BUFF_COUNT; r++) { + OUT(buffs[r].slotid); + OUT(buffs[r].level); + OUT(buffs[r].bard_modifier); + OUT(buffs[r].effect); + OUT(buffs[r].spellid); + OUT(buffs[r].duration); + OUT(buffs[r].counters); + OUT(buffs[r].player_id); + } + for (r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { + OUT(disciplines.values[r]); + } + // OUT(unknown05008[360]); + // OUT_array(recastTimers, structs::MAX_RECAST_TYPES); + OUT(endurance); + OUT(aapoints_spent); + OUT(aapoints); + // OUT(unknown06160[4]); + for (r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) { + OUT_str(bandoliers[r].name); + uint32 k; + for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { + OUT(bandoliers[r].items[k].item_id); + OUT(bandoliers[r].items[k].icon); + OUT_str(bandoliers[r].items[k].item_name); + } + } + // OUT(unknown07444[5120]); + for (r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) { + OUT(potionbelt.items[r].item_id); + OUT(potionbelt.items[r].icon); + OUT_str(potionbelt.items[r].item_name); + } + // OUT(unknown12852[8]); + // OUT(unknown12864[76]); + OUT_str(name); + OUT_str(last_name); + OUT(guild_id); + OUT(birthday); + OUT(lastlogin); + OUT(timePlayedMin); + OUT(pvp); + OUT(anon); + OUT(gm); + OUT(guildrank); + OUT(guildbanker); + // OUT(unknown13054[8]); + OUT(exp); + // OUT(unknown13072[12]); + OUT(timeentitledonaccount); + OUT_array(languages, structs::MAX_PP_LANGUAGE); + // OUT(unknown13109[7]); + OUT(x); + OUT(y); + OUT(z); + OUT(heading); + // OUT(unknown13132[4]); + OUT(platinum_bank); + OUT(gold_bank); + OUT(silver_bank); + OUT(copper_bank); + OUT(platinum_shared); + // OUT(unknown13156[84]); + OUT(expansions); + // OUT(unknown13244[12]); + OUT(autosplit); + // OUT(unknown13260[16]); + OUT(zone_id); + OUT(zoneInstance); + for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { + OUT_str(groupMembers[r]); + } + strcpy(eq->groupLeader, emu->groupMembers[0]); + // OUT_str(groupLeader); + // OUT(unknown13728[660]); + OUT(entityid); + OUT(leadAAActive); + // OUT(unknown14392[4]); + OUT(ldon_points_guk); + OUT(ldon_points_mir); + OUT(ldon_points_mmc); + OUT(ldon_points_ruj); + OUT(ldon_points_tak); + OUT(ldon_points_available); + // OUT(unknown14420[132]); + OUT(tribute_time_remaining); + OUT(career_tribute_points); + // OUT(unknown7208); + OUT(tribute_points); + // OUT(unknown7216); + OUT(tribute_active); + for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { + OUT(tributes[r].tribute); + OUT(tributes[r].tier); + } + // OUT(unknown14616[8]); + OUT(group_leadership_exp); + OUT(raid_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_points); + OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); + // OUT(unknown14772[128]); + OUT(air_remaining); + OUT(PVPKills); + OUT(PVPDeaths); + OUT(PVPCurrentPoints); + OUT(PVPCareerPoints); + OUT(PVPBestKillStreak); + OUT(PVPWorstDeathStreak); + OUT(PVPCurrentKillStreak); + // OUT(unknown14932[4580]); + OUT(expAA); + // OUT(unknown19516[40]); + OUT(currentRadCrystals); + OUT(careerRadCrystals); + OUT(currentEbonCrystals); + OUT(careerEbonCrystals); + OUT(groupAutoconsent); + OUT(raidAutoconsent); + OUT(guildAutoconsent); + // OUT(unknown19575[5]); + eq->level3 = emu->level; + eq->showhelm = emu->showhelm; + // OUT(unknown19584[4]); + // OUT(unknown19588); + + + const uint8 bytes[] = { + 0x78, 0x03, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x09, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14 + }; + memcpy(eq->unknown12864, bytes, sizeof(bytes)); + + //set the checksum... + CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); + + FINISH_ENCODE(); + } + + ENCODE(OP_ReadBook) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + + BookText_Struct *emu_BookText_Struct = (BookText_Struct *)__emu_buffer; + + in->size = sizeof(structs::BookText_Struct) + strlen(emu_BookText_Struct->booktext); + in->pBuffer = new unsigned char[in->size]; + + structs::BookText_Struct *eq_BookText_Struct = (structs::BookText_Struct*)in->pBuffer; + + eq_BookText_Struct->window = emu_BookText_Struct->window; + eq_BookText_Struct->type = emu_BookText_Struct->type; + strcpy(eq_BookText_Struct->booktext, emu_BookText_Struct->booktext); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_RespondAA) + { + ENCODE_LENGTH_EXACT(AATable_Struct); + SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); + + unsigned int r; + for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { + OUT(aa_list[r].aa_skill); + OUT(aa_list[r].aa_value); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for Titanium + // clientver 1 is for all clients and 3 is for Titanium + if (emu->clientver <= 3) + { + OUT(id); + eq->unknown004 = 1; + eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->id - emu->current_level + 1); + eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->id - emu->current_level + 1); + eq->title_sid = emu->id - emu->current_level + 1; + eq->desc_sid = emu->id - emu->current_level + 1; + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + OUT(prereq_skill); + OUT(prereq_minpoints); + OUT(type); + OUT(spellid); + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + OUT(unknown80[0]); + OUT(unknown80[1]); + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct); + + int r; + for (r = 0; r < 10; r++) { + OUT(zone[r]); + OUT(eyecolor1[r]); + OUT(eyecolor2[r]); + OUT(hairstyle[r]); + OUT(primary[r]); + if (emu->race[r] > 473) + eq->race[r] = 1; + else + eq->race[r] = emu->race[r]; + OUT(class_[r]); + OUT_str(name[r]); + OUT(gender[r]); + OUT(level[r]); + OUT(secondary[r]); + OUT(face[r]); + OUT(beard[r]); + int k; + for (k = 0; k < 9; k++) { + OUT(equip[r][k]); + OUT(cs_colors[r][k].color); + } + OUT(haircolor[r]); + OUT(gohome[r]); + OUT(tutorial[r]); + OUT(deity[r]); + OUT(beardcolor[r]); + eq->unknown820[r] = 0xFF; + eq->unknown902[r] = 0xFF; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + in->size = sizeof(structs::Track_Struct) * EntryCount; + in->pBuffer = new unsigned char[in->size]; + structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++eq, ++emu) + { + OUT(entityid); + OUT(padding002); + OUT(distance); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Trader) + { + if ((*p)->size != sizeof(TraderBuy_Struct)) { + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; + } + + ENCODE_FORWARD(OP_TraderBuy); + } + + ENCODE(OP_TraderBuy) + { + ENCODE_LENGTH_EXACT(TraderBuy_Struct); + SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); + + OUT(Action); + OUT(Price); + OUT(TraderID); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(ItemID); + OUT(Quantity); + OUT(AlreadySold); + + FINISH_ENCODE(); + } + + ENCODE(OP_VetRewardsAvaliable) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + + uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); + *p = nullptr; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); + uchar *old_data = __emu_buffer; + uchar *data = outapp_create->pBuffer; + for (uint32 i = 0; i < count; ++i) + { + structs::VeteranReward *vr = (structs::VeteranReward*)data; + InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + + vr->claim_id = ivr->claim_id; + vr->item.item_id = ivr->items[0].item_id; + strcpy(vr->item.item_name, ivr->items[0].item_name); + + old_data += sizeof(InternalVeteranReward); + data += sizeof(structs::VeteranReward); + } + + dest->FastQueuePacket(&outapp_create); + delete inapp; + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZoneSpawns) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; + + //determine and verify length + int entrycount = in->size / sizeof(Spawn_Struct); + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + delete in; + return; + } + + //make the EQ struct. + in->size = sizeof(structs::Spawn_Struct)*entrycount; + in->pBuffer = new unsigned char[in->size]; + structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; + + //zero out the packet. We could avoid this memset by setting all fields (including unknowns) + //in the loop. + memset(in->pBuffer, 0, in->size); + + //do the transform... + int r; + int k; + for (r = 0; r < entrycount; r++, eq++, emu++) { + // eq->unknown0000 = emu->unknown0000; + eq->gm = emu->gm; + // eq->unknown0003 = emu->unknown0003; + eq->aaitle = emu->aaitle; + // eq->unknown0004 = emu->unknown0004; + eq->anon = emu->anon; + eq->face = emu->face; + strcpy(eq->name, emu->name); + eq->deity = emu->deity; + // eq->unknown0073 = emu->unknown0073; + eq->size = emu->size; + // eq->unknown0079 = emu->unknown0079; + eq->NPC = emu->NPC; + eq->invis = emu->invis; + eq->haircolor = emu->haircolor; + eq->curHp = emu->curHp; + eq->max_hp = emu->max_hp; + eq->findable = emu->findable; + // eq->unknown0089[5] = emu->unknown0089[5]; + eq->deltaHeading = emu->deltaHeading; + eq->x = emu->x; + // eq->padding0054 = emu->padding0054; + eq->y = emu->y; + eq->animation = emu->animation; + // eq->padding0058 = emu->padding0058; + eq->z = emu->z; + eq->deltaY = emu->deltaY; + eq->deltaX = emu->deltaX; + eq->heading = emu->heading; + // eq->padding0066 = emu->padding0066; + eq->deltaZ = emu->deltaZ; + // eq->padding0070 = emu->padding0070; + eq->eyecolor1 = emu->eyecolor1; + // eq->unknown0115[24] = emu->unknown0115[24]; + eq->showhelm = emu->showhelm; + // eq->unknown0140[4] = emu->unknown0140[4]; + eq->is_npc = emu->is_npc; + eq->hairstyle = emu->hairstyle; + + //if(emu->gender == 1){ + // eq->hairstyle = eq->hairstyle == 0xFF ? 0 : eq->hairstyle; + //} + + eq->beardcolor = emu->beardcolor; + // eq->unknown0147[4] = emu->unknown0147[4]; + eq->level = emu->level; + // eq->unknown0259[4] = emu->unknown0259[4]; + eq->beard = emu->beard; + strcpy(eq->suffix, emu->suffix); + eq->petOwnerId = emu->petOwnerId; + eq->guildrank = emu->guildrank; + // eq->unknown0194[3] = emu->unknown0194[3]; + for (k = 0; k < 9; k++) { + eq->equipment[k] = emu->equipment[k]; + eq->colors[k].color = emu->colors[k].color; + } + for (k = 0; k < 8; k++) { + eq->set_to_0xFF[k] = 0xFF; + } + + eq->runspeed = emu->runspeed; + eq->afk = emu->afk; + eq->guildID = emu->guildID; + strcpy(eq->title, emu->title); + // eq->unknown0274 = emu->unknown0274; + eq->helm = emu->helm; + if (emu->race > 473) + eq->race = 1; + else + eq->race = emu->race; + // eq->unknown0288 = emu->unknown0288; + strcpy(eq->lastName, emu->lastName); + eq->walkspeed = emu->walkspeed; + // eq->unknown0328 = emu->unknown0328; + eq->is_pet = emu->is_pet; + eq->light = emu->light; + eq->class_ = emu->class_; + eq->eyecolor2 = emu->eyecolor2; + // eq->unknown0333 = emu->unknown0333; + eq->flymode = emu->flymode; + eq->gender = emu->gender; + eq->bodytype = emu->bodytype; + // eq->unknown0336[3] = emu->unknown0336[3]; + eq->equip_chest2 = emu->equip_chest2; + eq->spawnId = emu->spawnId; + // eq->unknown0344[4] = emu->unknown0344[4]; + eq->lfg = emu->lfg; + + /* + if (emu->face == 99) {eq->face = 0;} + if (emu->eyecolor1 == 99) {eq->eyecolor1 = 0;} + if (emu->eyecolor2 == 99) {eq->eyecolor2 = 0;} + if (emu->hairstyle == 99) {eq->hairstyle = 0;} + if (emu->haircolor == 99) {eq->haircolor = 0;} + if (emu->beard == 99) {eq->beard = 0;} + if (emu->beardcolor == 99) {eq->beardcolor = 0;} + */ + + } + + //kill off the emu structure and send the eq packet. + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + +// DECODE methods + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + IN(class_); + IN(beardcolor); + IN(beard); + IN(haircolor); + IN(gender); + IN(race); + IN(start_zone); + IN(hairstyle); + IN(deity); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_InspectAnswer) + { + DECODE_LENGTH_EXACT(structs::InspectResponse_Struct); + SETUP_DIRECT_DECODE(InspectResponse_Struct, structs::InspectResponse_Struct); + + IN(TargetID); + IN(playerid); + + int r; + for (r = 0; r <= 20; r++) { + strn0cpy(emu->itemnames[r], eq->itemnames[r], sizeof(emu->itemnames[r])); + } + + // move arrow item up to last element in server array + strn0cpy(emu->itemnames[21], "", sizeof(emu->itemnames[21])); + strn0cpy(emu->itemnames[22], eq->itemnames[21], sizeof(emu->itemnames[22])); + + int k; + for (k = 0; k <= 20; k++) { + IN(itemicons[k]); + } + + // move arrow icon up to last element in server array + emu->itemicons[21] = 0xFFFFFFFF; + emu->itemicons[22] = eq->itemicons[21]; + + strn0cpy(emu->text, eq->text, sizeof(emu->text)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_InspectRequest) + { + DECODE_LENGTH_EXACT(structs::Inspect_Struct); + SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); + + IN(TargetID); + IN(PlayerID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + IN(link_hash); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LFGuild) + { + uint32 Command = __packet->ReadUInt32(); + + if (Command != 0) + return; + + SETUP_DIRECT_DECODE(LFGuild_PlayerToggle_Struct, structs::LFGuild_PlayerToggle_Struct); + memcpy(emu, eq, sizeof(structs::LFGuild_PlayerToggle_Struct)); + memset(emu->Unknown612, 0, sizeof(emu->Unknown612)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ReadBook) + { + DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(window); + IN(type); + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + IN(filters[r]); + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TraderBuy) + { + DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); + SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); + MEMSET_IN(TraderBuy_Struct); + + IN(Action); + IN(Price); + IN(TraderID); + memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); + IN(ItemID); + IN(Quantity); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + IN(material); + IN(color.color); + IN(wear_slot_id); + emu->unknown06 = 0; + emu->elite_material = 0; + emu->hero_forge_model = 0; + emu->unknown18 = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + emu->type = 3; + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth) + { + char *serialization = nullptr; + char *instance = nullptr; + const char *protection = (const char *)"\\\\\\\\\\"; + char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + bool stackable = inst->IsStackable(); + uint32 merchant_slot = inst->GetMerchantSlot(); + int16 charges = inst->GetCharges(); + const Item_Struct *item = inst->GetItem(); + int i; + uint32 sub_length; + + MakeAnyLenString(&instance, + "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", + stackable ? charges : 0, + 0, + (merchant_slot == 0) ? slot_id : merchant_slot, + inst->GetPrice(), + (merchant_slot == 0) ? 1 : inst->GetMerchantCount(), + 0, + //merchant_slot, //instance ID, bullshit for now + (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot, + 0, + (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? 1 : 0) : charges), + inst->IsInstNoDrop() ? 1 : 0, + 0 + ); + + for (i = 0; i<10; i++) { + ItemInst *sub = inst->GetItem(i); + if (sub) { + sub_items[i] = SerializeItem(sub, 0, &sub_length, depth + 1); + } + } + + *length = MakeAnyLenString(&serialization, + "%.*s%s" // For leading quotes (and protection) if a subitem; + "%s" // Instance data + "%.*s\"" // Quotes (and protection, if needed) around static data + "%i" // item->ItemClass so we can do |%s instead of %s| #define I(field) "|%i" #define C(field) "|%s" #define S(field) "|%s" #define F(field) "|%f" #include "titanium_itemfields.h" - "%.*s\"" // Quotes (and protection, if needed) around static data - "|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" // Sub items - "%.*s%s" // For trailing quotes (and protection) if a subitem; - ,depth ? depth-1 : 0,protection,(depth) ? "\"" : "" - ,instance - ,depth,protection - ,item->ItemClass + "%.*s\"" // Quotes (and protection, if needed) around static data + "|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" // Sub items + "%.*s%s" // For trailing quotes (and protection) if a subitem; + , depth ? depth - 1 : 0, protection, (depth) ? "\"" : "" + , instance + , depth, protection + , item->ItemClass #define I(field) ,item->field #define C(field) ,field #define S(field) ,item->field #define F(field) ,item->field #include "titanium_itemfields.h" - ,depth,protection - ,sub_items[0] ? sub_items[0] : "" - ,sub_items[1] ? sub_items[1] : "" - ,sub_items[2] ? sub_items[2] : "" - ,sub_items[3] ? sub_items[3] : "" - ,sub_items[4] ? sub_items[4] : "" - ,sub_items[5] ? sub_items[5] : "" - ,sub_items[6] ? sub_items[6] : "" - ,sub_items[7] ? sub_items[7] : "" - ,sub_items[8] ? sub_items[8] : "" - ,sub_items[9] ? sub_items[9] : "" - ,(depth) ? depth-1 : 0,protection,(depth) ? "\"" : "" - ); + , depth, protection + , sub_items[0] ? sub_items[0] : "" + , sub_items[1] ? sub_items[1] : "" + , sub_items[2] ? sub_items[2] : "" + , sub_items[3] ? sub_items[3] : "" + , sub_items[4] ? sub_items[4] : "" + , sub_items[5] ? sub_items[5] : "" + , sub_items[6] ? sub_items[6] : "" + , sub_items[7] ? sub_items[7] : "" + , sub_items[8] ? sub_items[8] : "" + , sub_items[9] ? sub_items[9] : "" + , (depth) ? depth - 1 : 0, protection, (depth) ? "\"" : "" + ); - for(i=0;i<10;i++) { - if (sub_items[i]) - safe_delete_array(sub_items[i]); + for (i = 0; i<10; i++) { + if (sub_items[i]) + safe_delete_array(sub_items[i]); + } + + safe_delete_array(instance); + return serialization; } - safe_delete_array(instance); - return serialization; + static inline uint32 ServerToTitaniumSlot(uint32 ServerSlot) + { + //uint32 TitaniumSlot; + } + + static inline uint32 ServerToTitaniumCorpseSlot(uint32 ServerCorpse) + { + //uint32 TitaniumCorpse; + } + + static inline uint32 TitaniumToServerSlot(uint32 TitaniumSlot) + { + //uint32 ServerSlot; + } + + static inline uint32 TitaniumToServerCorpseSlot(uint32 TitaniumCorpse) + { + //uint32 ServerCorpse; + } } - -} //end namespace Titanium - - - - +// end namespace Titanium diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index 524a4d23b..a1d16f077 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -1,4 +1,3 @@ - #include "../debug.h" #include "underfoot.h" #include "../opcodemgr.h" @@ -18,1001 +17,2548 @@ namespace Underfoot { + static const char *name = "Underfoot"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "Underfoot"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); -char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline uint32 ServerToUnderfootSlot(uint32 ServerSlot); + static inline uint32 ServerToUnderFootCorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 UnderfootToServerSlot(uint32 UnderfootSlot); + static inline uint32 UnderfootToServerCorpseSlot(uint32 UnderfootCorpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "underfoot_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "underfoot_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClientUnderfoot; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClientUnderfoot; + } #include "ss_define.h" +// ENCODE methods + ENCODE(OP_Action) + { + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::ActionAlt_Struct); -// Converts Server Slot IDs to Underfoot Slot IDs for use in Encodes -static inline uint32 ServerToUnderfootSlot(uint32 ServerSlot) { - uint32 UnderfootSlot = 0; + OUT(target); + OUT(source); + OUT(level); + eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; + eq->knockback_angle = emu->sequence; + OUT(type); + OUT(spell); + eq->level2 = eq->level; + eq->effect_flag = emu->buff_unknown; + eq->unknown37 = 0x01; + eq->unknown44 = 0xFFFFFFFF; + eq->unknown48 = 0xFFFFFFFF; + eq->unknown52 = 0xFFFFFFFF; - if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots - UnderfootSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) - UnderfootSlot = ServerSlot + 11; - - else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) - UnderfootSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) - UnderfootSlot = ServerSlot + 1; - - else if (ServerSlot == MainPowerSource) - UnderfootSlot = slots::MainPowerSource; - - else - UnderfootSlot = ServerSlot; + /*OUT(target); + OUT(source); + OUT(level); + OUT(instrument_mod); + eq->sequence = emu->sequence; + OUT(type); + //OUT(damage); + OUT(spell); + eq->level2 = emu->level; + OUT(buff_unknown); // if this is 4, a buff icon is made + //eq->unknown0036 = -1; + //eq->unknown0040 = -1; + //eq->unknown0044 = -1;*/ - return UnderfootSlot; -} - -// Converts Underfoot Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 UnderfootToServerSlot(uint32 UnderfootSlot) { - uint32 ServerSlot = 0; - - if(UnderfootSlot >= slots::MainAmmo && UnderfootSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots - ServerSlot = UnderfootSlot - 1; - - else if(UnderfootSlot >= consts::GENERAL_BAGS_BEGIN && UnderfootSlot <= consts::CURSOR_BAG_END) - ServerSlot = UnderfootSlot - 11; - - else if(UnderfootSlot >= consts::BANK_BAGS_BEGIN && UnderfootSlot <= consts::BANK_BAGS_END) - ServerSlot = UnderfootSlot - 1; - - else if(UnderfootSlot >= consts::SHARED_BANK_BAGS_BEGIN && UnderfootSlot <= consts::SHARED_BANK_BAGS_END) - ServerSlot = UnderfootSlot - 1; - - else if(UnderfootSlot == slots::MainPowerSource) - ServerSlot = MainPowerSource; - - else - ServerSlot = UnderfootSlot; - - return ServerSlot; -} - -/* -// Converts Server Corpse Slot IDs to Underfoot Corpse Slot IDs for use in Encodes -static inline uint32 ServerToUnderFootCorpseSlot(uint32 ServerCorpse) { - uint32 UnderfootCorpse; - // reserved -} -*/ -/* -// Converts Underfoot Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 UnderfootToServerCorpseSlot(uint32 UnderfootCorpse) { - uint32 ServerCorpse; - // reserved -} -*/ - - -ENCODE(OP_OpenNewTasksWindow) { - - AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; - AvailableTaskData1_Struct* __emu_AvailableTaskData1; - AvailableTaskData2_Struct* __emu_AvailableTaskData2; - AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; - - structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; - structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; - structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; - structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; - - // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. - // - in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); - - in->pBuffer = new unsigned char[in->size]; - - unsigned char *__eq_buffer = in->pBuffer; - - __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; - - char *__eq_ptr, *__emu_Ptr; - - // Copy Header - // - // - - __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; - __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; - __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; - - __emu_Ptr = (char *) __emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); - __eq_ptr = (char *) __eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); - - for(uint32 i=0; i<__emu_AvailableTaskHeader->TaskCount; i++) { - - __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; - __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; - - __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; - // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen - // in Underfoot packets. Changing it to 0x3f000000 makes the title red. - __eq_AvailableTaskData1->unknown1 = 0x3f800000; - __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; - __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; - - __emu_Ptr += sizeof(AvailableTaskData1_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Title - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Description - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __eq_ptr[0] = 0; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; - __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; - - __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; - __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; - __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; - __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; - - __emu_Ptr += sizeof(AvailableTaskData2_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; - __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; - - __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; - __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; - __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; - __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; - - __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); - __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_AdventureMerchantSell) + { + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - dest->FastQueuePacket(&in, ack_req); -} + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToUnderfootSlot(emu->slot); + OUT(charges); + OUT(sell_price); + FINISH_ENCODE(); + } -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_VAR_ENCODE(CharacterSelect_Struct); + ENCODE(OP_AltCurrency) + { + EQApplicationPacket *in = *p; + *p = nullptr; + unsigned char *emu_buffer = in->pBuffer; + uint32 opcode = *((uint32*)emu_buffer); - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + if (opcode == 8) { + AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer; - int char_count; - int namelen = 0; - for(char_count = 0; char_count < 10; char_count++) { - if(emu->name[char_count][0] == '\0') - break; - if(strcmp(emu->name[char_count], "") == 0) - break; - namelen += strlen(emu->name[char_count]); - } + EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(structs::AltCurrencyPopulate_Struct) + + sizeof(structs::AltCurrencyPopulateEntry_Struct) * populate->count); + structs::AltCurrencyPopulate_Struct *out_populate = (structs::AltCurrencyPopulate_Struct*)outapp->pBuffer; - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; - - ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); - - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; - - eq->char_count = char_count; - eq->total_chars = 10; - - unsigned char *bufptr = (unsigned char *) eq->entries; - int r; - for(r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->level = emu->level[r]; - eq2->hairstyle = emu->hairstyle[r]; - eq2->gender = emu->gender[r]; - memcpy(eq2->name, emu->name[r], strlen(emu->name[r])+1); - } - //adjust for name. - bufptr += strlen(emu->name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->beard = emu->beard[r]; - eq2->haircolor = emu->haircolor[r]; - eq2->face = emu->face[r]; - int k; - for(k = 0; k < _MaterialCount; k++) { - eq2->equip[k].equip0 = emu->equip[r][k]; - eq2->equip[k].equip1 = 0; - eq2->equip[k].itemid = 0; - eq2->equip[k].color.color = emu->cs_colors[r][k].color; + out_populate->opcode = populate->opcode; + out_populate->count = populate->count; + for (uint32 i = 0; i < populate->count; ++i) { + out_populate->entries[i].currency_number = populate->entries[i].currency_number; + out_populate->entries[i].currency_number2 = populate->entries[i].currency_number2; + out_populate->entries[i].item_id = populate->entries[i].item_id; + out_populate->entries[i].item_icon = populate->entries[i].item_icon; + out_populate->entries[i].stack_size = populate->entries[i].stack_size; + out_populate->entries[i].unknown00 = populate->entries[i].unknown00; } - eq2->primary = emu->primary[r]; - eq2->secondary = emu->secondary[r]; - eq2->tutorial = emu->tutorial[r]; // was u15 - eq2->u15 = 0xff; - eq2->deity = emu->deity[r]; - eq2->zone = emu->zone[r]; - eq2->u19 = 0xFF; - eq2->race = emu->race[r]; - eq2->gohome = emu->gohome[r]; - eq2->class_ = emu->class_[r]; - eq2->eyecolor1 = emu->eyecolor1[r]; - eq2->beardcolor = emu->beardcolor[r]; - eq2->eyecolor2 = emu->eyecolor2[r]; - eq2->drakkin_heritage = emu->drakkin_heritage[r]; - eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; - eq2->drakkin_details = emu->drakkin_details[r]; + + dest->FastQueuePacket(&outapp, ack_req); } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); + else { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyUpdate_Struct)); + memcpy(outapp->pBuffer, emu_buffer, sizeof(AltCurrencyUpdate_Struct)); + dest->FastQueuePacket(&outapp, ack_req); + } + + //dest->FastQueuePacket(&outapp, ack_req); + delete in; } - FINISH_ENCODE(); - -} - -ENCODE(OP_ZoneServerInfo) { - SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); - OUT_str(ip); - OUT(port); - FINISH_ENCODE(); -} - -ENCODE(OP_SendZonepoints) { - SETUP_VAR_ENCODE(ZonePoints); - ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); - - eq->count = emu->count; - for(uint32 i = 0; i < emu->count; ++i) + ENCODE(OP_AltCurrencySell) { - eq->zpe[i].iterator = emu->zpe[i].iterator; - eq->zpe[i].x = emu->zpe[i].x; - eq->zpe[i].y = emu->zpe[i].y; - eq->zpe[i].z = emu->zpe[i].z; - eq->zpe[i].heading = emu->zpe[i].heading; - eq->zpe[i].zoneid = emu->zpe[i].zoneid; - eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; - } + ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); + SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - FINISH_ENCODE(); -} - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for SoF - // clientver 1 is for all clients and 6 is for Underfoot - if (emu->clientver <= 6 ) - { - OUT(id); - eq->unknown004 = 1; - //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->title_sid = emu->id - emu->current_level + 1; - //eq->desc_sid = emu->id - emu->current_level + 1; - eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->sof_next_skill); - eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->sof_next_skill); - eq->title_sid = emu->sof_next_skill; - eq->desc_sid = emu->sof_next_skill; - OUT(class_type); + OUT(merchant_entity_id); + eq->slot_id = ServerToUnderfootSlot(emu->slot_id); + OUT(charges); OUT(cost); - OUT(seq); - OUT(current_level); - OUT(prereq_skill); - OUT(prereq_minpoints); - eq->type = emu->sof_type; + + FINISH_ENCODE(); + } + + ENCODE(OP_ApplyPoison) + { + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToUnderfootSlot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); + } + + ENCODE(OP_Barter) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + + uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + + if (SubAction != Barter_BuyerAppearance) + { + dest->FastQueuePacket(&in, ack_req); + + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + + in->size = 80; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + char Name[64]; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); + uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); + uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + VARSTRUCT_DECODE_STRING(Name, Buffer); + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + OutBuffer = (char *)in->pBuffer + 72; + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_BazaarSearch) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if (SubAction != BazaarSearchResults) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); + + if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + + in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); + in->pBuffer = new unsigned char[in->size]; + memset(in->pBuffer, 0, in->size); + + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++emu, ++eq) + { + OUT(Beginning.Action); + OUT(SellerID); + memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); + OUT(NumItems); + OUT(ItemID); + OUT(SerialNumber); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(Cost); + OUT(ItemStat); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Buff) + { + ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); + SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Underfoot); + + OUT(entityid); + OUT(slot); + OUT(level); + OUT(effect); + //eq->unknown7 = 10; OUT(spellid); - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - //eq->max_level = emu->sof_max_level; - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - eq->aa_expansion = emu->aa_expansion; - eq->special_category = emu->special_category; - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } + OUT(duration); + OUT(slotid); + OUT(bufffade); // Live (October 2011) sends a 2 rather than 0 when a buff is created, but it doesn't seem to matter. + eq->unknown008 = 1.0f; + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - -ENCODE(OP_RespondAA) { - SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); - - eq->aa_spent = emu->aa_spent; - eq->aa_assigned = emu->aa_spent; - eq->aa_spent3 = emu->aa_spent; - eq->unknown012 = 0; - eq->unknown016 = 0; - eq->unknown020 = 0; - - for(uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) + ENCODE(OP_BuffCreate) { - eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; - eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; - eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; - } + SETUP_VAR_ENCODE(BuffIcon_Struct); - FINISH_ENCODE(); -} + uint32 sz = 12 + (17 * emu->count); + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); -ENCODE(OP_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); + uchar *ptr = __packet->pBuffer; + *((uint32*)ptr) = emu->entity_id; + ptr += sizeof(uint32); + ptr += sizeof(uint32); + *((uint8*)ptr) = 1; + ptr += sizeof(uchar); + *((uint16*)ptr) = emu->count; + ptr += sizeof(uint16); - uint32 r; - - eq->available_slots=0xffffffff; - memset(eq->unknown07284, 0xff, sizeof(eq->unknown07284)); - -// OUT(checksum); - OUT(gender); - OUT(race); - OUT(class_); -// OUT(unknown00016); - OUT(level); - eq->level1 = emu->level; -// OUT(unknown00022[2]); - for(r = 0; r < 5; r++) { - OUT(binds[r].zoneId); - OUT(binds[r].x); - OUT(binds[r].y); - OUT(binds[r].z); - OUT(binds[r].heading); - } - OUT(deity); - OUT(intoxication); - OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); - OUT(abilitySlotRefresh); - OUT(points); // Relocation Test -// OUT(unknown0166[4]); - OUT(haircolor); - OUT(beardcolor); - OUT(eyecolor1); - OUT(eyecolor2); - OUT(hairstyle); - OUT(beard); -// OUT(unknown00178[10]); - for(r = 0; r < 9; r++) { - eq->equipment[r].equip0 = emu->item_material[r]; - eq->equipment[r].equip1 = 0; - eq->equipment[r].itemId = 0; - //eq->colors[r].color = emu->colors[r].color; - } - for(r = 0; r < 7; r++) { - OUT(item_tint[r].color); - } -// OUT(unknown00224[48]); - //NOTE: new client supports 300 AAs, our internal rep/PP - //only supports 240.. - for(r = 0; r < MAX_PP_AA_ARRAY; r++) { - OUT(aa_array[r].AA); - OUT(aa_array[r].value); - } -// OUT(unknown02220[4]); - OUT(mana); - OUT(cur_hp); - OUT(STR); - OUT(STA); - OUT(CHA); - OUT(AGI); - OUT(INT); - OUT(DEX); - OUT(WIS); - OUT(face); -// OUT(unknown02264[47]); - memset(eq->spell_book, 0xFF, sizeof(uint32)* structs::MAX_PP_SPELLBOOK); - OUT_array(spell_book, 480U); -// OUT(unknown4184[128]); - OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); -// OUT(unknown04396[32]); - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - OUT(platinum_cursor); - OUT(gold_cursor); - OUT(silver_cursor); - OUT(copper_cursor); - - OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) - -// OUT(unknown04760[236]); - OUT(toxicity); - OUT(thirst_level); - OUT(hunger_level); - //PS this needs to be figured out more; but it was 'good enough' - for(r = 0; r < structs::BUFF_COUNT; r++) - { - if(emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0) + for (uint16 i = 0; i < emu->count; ++i) { - eq->buffs[r].unknown004 = 0x3f800000; - eq->buffs[r].slotid = 2; - eq->buffs[r].player_id = 0x000717fd; + uint16 buffslot = emu->entries[i].buff_slot; + if (emu->entries[i].buff_slot >= 25 && emu->entries[i].buff_slot < 37) + { + buffslot += 5; + } + else if (emu->entries[i].buff_slot >= 37) + { + buffslot += 14; + } + + *((uint32*)ptr) = buffslot; + ptr += sizeof(uint32); + *((uint32*)ptr) = emu->entries[i].spell_id; + ptr += sizeof(uint32); + *((uint32*)ptr) = emu->entries[i].tics_remaining; + ptr += sizeof(uint32); + ptr += sizeof(uint32); + ptr += 1; } - else + + FINISH_ENCODE(); + /* + uint32 write_var32 = 60; + uint8 write_var8 = 1; + ss.write((const char*)&emu->entity_id, sizeof(uint32)); + ss.write((const char*)&write_var32, sizeof(uint32)); + ss.write((const char*)&write_var8, sizeof(uint8)); + ss.write((const char*)&emu->count, sizeof(uint16)); + write_var32 = 0; + write_var8 = 0; + for(uint16 i = 0; i < emu->count; ++i) { - eq->buffs[r].slotid = 0; - } - //OUT(buffs[r].slotid); - OUT(buffs[r].level); - //OUT(buffs[r].bard_modifier); - //OUT(buffs[r].effect); - OUT(buffs[r].spellid); - OUT(buffs[r].duration); - OUT(buffs[r].counters); - //OUT(buffs[r].player_id); - } - for(r = 0; r < MAX_PP_DISCIPLINES; r++) { - OUT(disciplines.values[r]); - } - OUT_array(recastTimers, structs::MAX_RECAST_TYPES); -// OUT(unknown08124[360]); - OUT(endurance); - OUT(aapoints_spent); - OUT(aapoints); -// OUT(unknown06160[4]); - //NOTE: new client supports 20 bandoliers, our internal rep - //only supports 4.. - for(r = 0; r < 4; r++) { - OUT_str(bandoliers[r].name); - uint32 k; - for(k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { - OUT(bandoliers[r].items[k].item_id); - OUT(bandoliers[r].items[k].icon); - OUT_str(bandoliers[r].items[k].item_name); - } - } -// OUT(unknown07444[5120]); - for(r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { - OUT(potionbelt.items[r].item_id); - OUT(potionbelt.items[r].icon); - OUT_str(potionbelt.items[r].item_name); - } -// OUT(unknown12852[8]); -// OUT(unknown12864[76]); - OUT_str(name); - OUT_str(last_name); - OUT(guild_id); - OUT(birthday); - OUT(lastlogin); - OUT(timePlayedMin); - OUT(pvp); - OUT(anon); - OUT(gm); - OUT(guildrank); - OUT(guildbanker); -// OUT(unknown13054[12]); - OUT(exp); -// OUT(unknown13072[8]); - OUT(timeentitledonaccount); - OUT_array(languages, structs::MAX_PP_LANGUAGE); -// OUT(unknown13109[7]); - OUT(y); //reversed x and y - OUT(x); - OUT(z); - OUT(heading); -// OUT(unknown13132[4]); - OUT(platinum_bank); - OUT(gold_bank); - OUT(silver_bank); - OUT(copper_bank); - OUT(platinum_shared); -// OUT(unknown13156[84]); - //OUT(expansions); - eq->expansions = 0xffff; -// OUT(unknown13244[12]); - OUT(autosplit); -// OUT(unknown13260[16]); - OUT(zone_id); - OUT(zoneInstance); - for(r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { - OUT_str(groupMembers[r]); - } - strcpy(eq->groupLeader, emu->groupMembers[0]); -// OUT_str(groupLeader); -// OUT(unknown13728[660]); - OUT(entityid); - OUT(leadAAActive); -// OUT(unknown14392[4]); - OUT(ldon_points_guk); - OUT(ldon_points_mir); - OUT(ldon_points_mmc); - OUT(ldon_points_ruj); - OUT(ldon_points_tak); - OUT(ldon_points_available); -// OUT(unknown14420[132]); - OUT(tribute_time_remaining); - OUT(career_tribute_points); -// OUT(unknown7208); - OUT(tribute_points); -// OUT(unknown7216); - OUT(tribute_active); - for(r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { - OUT(tributes[r].tribute); - OUT(tributes[r].tier); - } -// OUT(unknown14616[8]); - OUT(group_leadership_exp); -// OUT(unknown14628); - OUT(raid_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_points); - OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); -// OUT(unknown14772[128]); - OUT(air_remaining); - OUT(PVPKills); - OUT(PVPDeaths); - OUT(PVPCurrentPoints); - OUT(PVPCareerPoints); - OUT(PVPBestKillStreak); - OUT(PVPWorstDeathStreak); - OUT(PVPCurrentKillStreak); -// OUT(unknown17892[4580]); - OUT(expAA); -// OUT(unknown19516[40]); - OUT(currentRadCrystals); - OUT(careerRadCrystals); - OUT(currentEbonCrystals); - OUT(careerEbonCrystals); - OUT(groupAutoconsent); - OUT(raidAutoconsent); - OUT(guildAutoconsent); -// OUT(unknown19575[5]); - eq->level3 = emu->level; - eq->showhelm = emu->showhelm; - OUT(RestTimer); -// OUT(unknown19584[4]); -// OUT(unknown19588); - - -const uint8 bytes[] = { -0xa3,0x02,0x00,0x00,0x95,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x00,0x00,0x00, -0x19,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, -0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1F,0x85,0xEB,0x3E,0x33,0x33,0x33,0x3F, -0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; - - memcpy(eq->unknown18020, bytes, sizeof(bytes)); - - //set the checksum... - CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct)-4); - - FINISH_ENCODE(); -} - -ENCODE(OP_NewZone) { - SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); - OUT_str(char_name); - OUT_str(zone_short_name); - OUT_str(zone_long_name); - OUT(ztype); - int r; - for(r = 0; r < 4; r++) { - OUT(fog_red[r]); - OUT(fog_green[r]); - OUT(fog_blue[r]); - OUT(fog_minclip[r]); - OUT(fog_maxclip[r]); - } - OUT(gravity); - OUT(time_type); - for(r = 0; r < 4; r++) { - OUT(rain_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(rain_duration[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_duration[r]); - } - for(r = 0; r < 32; r++) { - eq->unknown537[r] = 0xFF; //observed - } - OUT(sky); - OUT(zone_exp_multiplier); - OUT(safe_y); - OUT(safe_x); - OUT(safe_z); - OUT(max_z); - OUT(underworld); - OUT(minclip); - OUT(maxclip); - OUT_str(zone_short_name2); - OUT(zone_id); - OUT(zone_instance); - OUT(SuspendBuffs); - - eq->FogDensity = emu->fog_density; - - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown800 = -1; - eq->unknown844 = 600; - eq->unknown880 = 50; - eq->unknown884 = 10; - eq->unknown888 = 1; - eq->unknown889 = 0; - eq->unknown890 = 1; - eq->unknown891 = 0; - eq->unknown892 = 0; - eq->unknown893 = 0; - eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off - eq->unknown895 = 0; - eq->unknown896 = 180; - eq->unknown900 = 180; - eq->unknown904 = 180; - eq->unknown908 = 2; - eq->unknown912 = 2; - - FINISH_ENCODE(); -} - - -ENCODE(OP_Track) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) - { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); - delete in; - return; - } - - int PacketSize = 2; - - for(int i = 0; i < EntryCount; ++i, ++emu) - PacketSize += (12 + strlen(emu->name)); - - emu = (Track_Struct *) __emu_buffer; - - in->size = PacketSize; - in->pBuffer = new unsigned char[in->size]; - - char *Buffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); - - for(int i = 0; i < EntryCount; ++i, ++emu) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); - VARSTRUCT_ENCODE_STRING(Buffer, emu->name); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_PetBuffWindow) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - PetBuff_Struct *emu = (PetBuff_Struct *) __emu_buffer; - - int PacketSize = 12 + (emu->buffcount * 17); - - in->size = PacketSize; - - in->pBuffer = new unsigned char[in->size]; - - char *Buffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount); - - for(unsigned int i = 0; i < BUFF_COUNT; ++i) - { - if(emu->spellid[i]) + if(emu->entries[i].buff_slot >= 25 && emu->entries[i].buff_slot < 37) { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]); + emu->entries[i].buff_slot += 5; + } + else if(emu->entries[i].buff_slot >= 37) + { + emu->entries[i].buff_slot += 14; + } + ss.write((const char*)&emu->entries[i].buff_slot, sizeof(uint32)); + ss.write((const char*)&emu->entries[i].spell_id, sizeof(uint32)); + ss.write((const char*)&emu->entries[i].tics_remaining, sizeof(uint32)); + ss.write((const char*)&write_var32, sizeof(uint32)); + ss.write((const char*)&write_var8, sizeof(uint8)); + } + ss.write((const char*)&write_var8, sizeof(uint8)); + */ + } + + ENCODE(OP_CancelTrade) + { + ENCODE_LENGTH_EXACT(CancelTrade_Struct); + SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); + + OUT(fromid); + OUT(action); + + FINISH_ENCODE(); + } + + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_CharInventory) + { + //consume the packet + EQApplicationPacket *in = *p; + + *p = nullptr; + + if (in->size == 0) { + + in->size = 4; + in->pBuffer = new uchar[in->size]; + *((uint32 *)in->pBuffer) = 0; + + dest->FastQueuePacket(&in, ack_req); + return; + } + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + + int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); + + if (ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + + delete in; + return; + } + + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; + + in->pBuffer = new uchar[4]; + *(uint32 *)in->pBuffer = ItemCount; + in->size = 4; + + for (int r = 0; r < ItemCount; r++, eq++) { + + uint32 Length = 0; + char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); + + if (Serialized) { + + uchar *OldBuffer = in->pBuffer; + in->pBuffer = new uchar[in->size + Length]; + memcpy(in->pBuffer, OldBuffer, in->size); + + safe_delete_array(OldBuffer); + + memcpy(in->pBuffer + in->size, Serialized, Length); + in->size += Length; + + safe_delete_array(Serialized); + } + else { + _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } + } + + delete[] __emu_buffer; + + //_log(NET__ERROR, "Sending inventory to client"); + //_hex(NET__ERROR, in->pBuffer, in->size); + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ClientUpdate) + { + ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); + SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); + + OUT(spawn_id); + OUT(x_pos); + OUT(delta_x); + OUT(delta_y); + OUT(z_pos); + OUT(delta_heading); + OUT(y_pos); + OUT(delta_z); + OUT(animation); + OUT(heading); + + FINISH_ENCODE(); + } + + ENCODE(OP_Consider) + { + ENCODE_LENGTH_EXACT(Consider_Struct); + SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); + + OUT(playerid); + OUT(targetid); + OUT(faction); + OUT(level); + OUT(pvpcon); + + FINISH_ENCODE(); + } + + ENCODE(OP_Damage) + { + ENCODE_LENGTH_EXACT(CombatDamage_Struct); + SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + OUT(target); + OUT(source); + OUT(type); + OUT(spellid); + OUT(damage); + eq->sequence = emu->sequence; + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToUnderfootSlot(emu->from_slot); + eq->to_slot = ServerToUnderfootSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_DisciplineUpdate) + { + ENCODE_LENGTH_EXACT(Disciplines_Struct); + SETUP_DIRECT_ENCODE(Disciplines_Struct, structs::Disciplines_Struct); + + memcpy(&eq->values, &emu->values, sizeof(Disciplines_Struct)); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzCompass) + { + SETUP_VAR_ENCODE(ExpeditionCompass_Struct); + ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); + OUT(count); + + for (uint32 i = 0; i < emu->count; ++i) + { + OUT(entries[i].x); + OUT(entries[i].y); + OUT(entries[i].z); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionEndsWarning) + { + ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); + SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); + + OUT(minutes_remaining); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionInfo) + { + ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + + OUT(max_players); + eq->unknown004 = 785316192; + eq->unknown008 = 435601; + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->leader_name, emu->leader_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionList) + { + SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); + ss.write((const char*)&null_term, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzJoinExpeditionConfirm) + { + ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); + SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); + + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->player_name, emu->player_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzLeaderStatus) + { + SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->leader_name, strlen(emu->leader_name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//1 + ss.write((const char*)&client_id, sizeof(uint32)); + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzMemberList) + { + SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].status, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpansionInfo) + { + ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + + OUT(Expansions); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroundSpawn) + { + // We are not encoding the spawn_id field here, or a size but it doesn't appear to matter. + // + EQApplicationPacket *in = *p; + *p = nullptr; + + Object_Struct *emu = (Object_Struct *)in->pBuffer; + unsigned char *__emu_buffer = in->pBuffer; + in->size = strlen(emu->object_name) + 58; + in->pBuffer = new unsigned char[in->size]; + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->object_name); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_id); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_instance); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0x00006762 + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observer 0x7fffbb64 + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->heading); + // This next field is actually a float. There is a groundspawn in freeportwest (sack of money sitting on some barrels) which requires this + // field to be set to (float)255.0 to appear at all, and also the size field below to be 5, to be the correct size. I think SoD has the same + // issue. + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0 + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // This appears to be the size field. + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->object_type); // Unknown, observed 0x00000014 + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); // Unknown, observed 0xffffffff + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0x00000014 + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown, observed 0x00 + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_GroupCancelInvite) + { + ENCODE_LENGTH_EXACT(GroupCancel_Struct); + SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + OUT(toggle); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow2) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupInvite) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); + memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupUpdate) + { + //_log(NET__ERROR, "OP_GroupUpdate"); + EQApplicationPacket *in = *p; + GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; + + //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); + if ((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) + { + if ((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) + { + //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); + dest->FastQueuePacket(&outapp); + + // Make an empty GLAA packet to clear out their useable GLAAs + // + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + dest->FastQueuePacket(&outapp); + + delete in; + return; + } + //if(gjs->action == groupActLeave) + // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + delete in; + return; + } + + if (in->size == sizeof(GroupUpdate2_Struct)) + { + // Group Update2 + //_log(NET__ERROR, "Struct is GroupUpdate2"); + + unsigned char *__emu_buffer = in->pBuffer; + GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*)__emu_buffer; + + //_log(NET__ERROR, "Yourname is %s", gu2->yourname); + + int MemberCount = 1; + int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; + + for (int i = 0; i < 5; ++i) + { + //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); + if (gu2->membername[i][0] != '\0') + { + PacketLength += (22 + strlen(gu2->membername[i]) + 1); + ++MemberCount; + } + } + + //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); + + char *Buffer = (char *)outapp->pBuffer; + + // Header + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); + + // Leader + // + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff. + VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + + int MemberNumber = 1; + + for (int i = 0; i < 5; ++i) + { + if (gu2->membername[i][0] == '\0') + continue; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = gu2->NPCMarkerID; + memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); + + dest->FastQueuePacket(&outapp); + delete in; + return; } + //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + ENCODE_LENGTH_EXACT(GroupJoin_Struct); + SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); + + memcpy(eq->membername, emu->membername, sizeof(eq->membername)); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = emu->NPCMarkerID; + + memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); + //_hex(NET__ERROR, __packet->pBuffer, __packet->size); + + FINISH_ENCODE(); + + dest->FastQueuePacket(&outapp); } - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_Barter) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - - if(SubAction != Barter_BuyerAppearance) + ENCODE(OP_GuildMemberList) { - dest->FastQueuePacket(&in, ack_req); - - return; - } - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = 80; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - char Name[64]; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); - uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); - uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - VARSTRUCT_DECODE_STRING(Name, Buffer); - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - OutBuffer = (char *)in->pBuffer + 72; - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - -} - -ENCODE(OP_BazaarSearch) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if(SubAction != BazaarSearchResults) - { - dest->FastQueuePacket(&in, ack_req); - - return; - } - - unsigned char *__emu_buffer = in->pBuffer; - - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); - - if(EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) - { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; - } - in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); - - in->pBuffer = new unsigned char[in->size]; - - memset(in->pBuffer, 0, in->size); - - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++emu, ++eq) - { - OUT(Beginning.Action); - OUT(SellerID); - memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); - OUT(NumItems); - OUT(ItemID); - OUT(SerialNumber); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(Cost); - OUT(ItemStat); - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) { //consume the packet EQApplicationPacket *in = *p; *p = nullptr; //store away the emu struct unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count*sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header + emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data + ); + const char *emu_note = (emu_name + + emu->name_length + //skip name contents + emu->count //skip string terminators + ); + + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { + + //the order we set things here must match the struct + + //nice helper macro + /*#define SlideStructString(field, str) \ + strcpy(e->field, str.c_str()); \ + e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ +#define SlideStructString(field, str) \ + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) + + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + PutFieldN(rank); + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + PutFieldN(total_tribute); + PutFieldN(last_tribute); + e->unknown_one = htonl(1); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); + +#undef SlideStructString +#undef PutFieldN + + e++; + } + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_GuildsList) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + uint32 NumberOfGuilds = in->size / 64; + uint32 PacketSize = 68; // 64 x 0x00 + a uint32 that I am guessing is the highest guild ID in use. + + unsigned char *__emu_buffer = in->pBuffer; + char *InBuffer = (char *)__emu_buffer; + uint32 HighestGuildID = 0; + + for (unsigned int i = 0; i < NumberOfGuilds; ++i) + { + if (InBuffer[0]) + { + PacketSize += (5 + strlen(InBuffer)); + HighestGuildID = i - 1; + } + InBuffer += 64; + } + + PacketSize++; // Appears to be an extra 0x00 at the very end. + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + InBuffer = (char *)__emu_buffer; + char *OutBuffer = (char *)in->pBuffer; + + // Init the first 64 bytes to zero, as per live. + // + memset(OutBuffer, 0, 64); + OutBuffer += 64; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, HighestGuildID); + + for (unsigned int i = 0; i < NumberOfGuilds; ++i) + { + if (InBuffer[0]) + { + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, i - 1); + VARSTRUCT_ENCODE_STRING(OutBuffer, InBuffer); + } + InBuffer += 64; + } + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0x00); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + OUT(race); + OUT(unknown006[0]); + OUT(unknown006[1]); + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + OUT(drakkin_heritage); + OUT(drakkin_tattoo); + OUT(drakkin_details); + + FINISH_ENCODE(); + } + + ENCODE(OP_InspectRequest) + { + ENCODE_LENGTH_EXACT(Inspect_Struct); + SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); + + OUT(TargetID); + OUT(PlayerID); + + FINISH_ENCODE(); + } + + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } + + ENCODE(OP_ItemPacket) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); + + uint32 length; + char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); + + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 4; + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length); + + delete[] __emu_buffer; + safe_delete_array(serialized); + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ItemVerifyReply) + { + ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); + SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); + + eq->slot = ServerToUnderfootSlot(emu->slot); + OUT(spell); + OUT(target); + + FINISH_ENCODE(); + } + + ENCODE(OP_LeadershipExpUpdate) + { + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LogServer) + { + ENCODE_LENGTH_EXACT(LogServer_Struct); + SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); + + strcpy(eq->worldshortname, emu->worldshortname); + + OUT(enablevoicemacros); + OUT(enablemail); + OUT(enable_pvp); + OUT(enable_FV); + + eq->unknown016 = 1; + eq->unknown020[0] = 1; + + // These next two need to be set like this for the Tutorial Button to work. + eq->unknown263[0] = 0; + eq->unknown263[2] = 1; + eq->unknown263[4] = 1; + eq->unknown263[5] = 1; + eq->unknown263[6] = 1; + eq->unknown263[9] = 8; + eq->unknown263[19] = 0x80; + eq->unknown263[20] = 0x3f; + eq->unknown263[23] = 0x80; + eq->unknown263[24] = 0x3f; + eq->unknown263[33] = 1; + + FINISH_ENCODE(); + } + + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = emu->slot_id + 1; + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_ManaChange) + { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); + + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? + + FINISH_ENCODE(); + } + + ENCODE(OP_MercenaryDataResponse) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + + int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; + PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); + + for (r = 0; r < emu->MercTypeCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); + } + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk01); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MerchantSlot); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk02); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk03); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk04); + for (k = 0; k < emu->Mercs[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); + } + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + // This packet does not appear to exist in UF, but leaving it here just in case + ENCODE(OP_MercenaryDataUpdate) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + + EQApplicationPacket *outapp; + + uint32 PacketSize = 0; + + // There are 2 different sized versions of this packet depending if a merc is hired or not + if (emu->MercStatus >= 0) + { + PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; + } + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk01); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MerchantSlot); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk02); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].MercUnk03); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk04); + for (k = 0; k < emu->MercData[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); + } + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk05); + } + } + else + { + PacketSize += sizeof(structs::NoMercenaryHired_Struct); + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToUnderfootSlot(emu->from_slot); + eq->to_slot = ServerToUnderfootSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_NewZone) + { + SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); + + OUT_str(char_name); + OUT_str(zone_short_name); + OUT_str(zone_long_name); + OUT(ztype); + int r; + for (r = 0; r < 4; r++) { + OUT(fog_red[r]); + OUT(fog_green[r]); + OUT(fog_blue[r]); + OUT(fog_minclip[r]); + OUT(fog_maxclip[r]); + } + OUT(gravity); + OUT(time_type); + for (r = 0; r < 4; r++) { + OUT(rain_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(rain_duration[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_duration[r]); + } + for (r = 0; r < 32; r++) { + eq->unknown537[r] = 0xFF; //observed + } + OUT(sky); + OUT(zone_exp_multiplier); + OUT(safe_y); + OUT(safe_x); + OUT(safe_z); + OUT(max_z); + OUT(underworld); + OUT(minclip); + OUT(maxclip); + OUT_str(zone_short_name2); + OUT(zone_id); + OUT(zone_instance); + OUT(SuspendBuffs); + + eq->FogDensity = emu->fog_density; + + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown800 = -1; + eq->unknown844 = 600; + eq->unknown880 = 50; + eq->unknown884 = 10; + eq->unknown888 = 1; + eq->unknown889 = 0; + eq->unknown890 = 1; + eq->unknown891 = 0; + eq->unknown892 = 0; + eq->unknown893 = 0; + eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off + eq->unknown895 = 0; + eq->unknown896 = 180; + eq->unknown900 = 180; + eq->unknown904 = 180; + eq->unknown908 = 2; + eq->unknown912 = 2; + + FINISH_ENCODE(); + } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + memcpy(eq->Title, emu->Title, sizeof(eq->Title)); + memcpy(eq->Text, emu->Text, sizeof(eq->Text)); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + OUT(NegativeID); + // These two field names are used if Buttons == 1. + OUT_str(ButtonName0); + OUT_str(ButtonName1); + + FINISH_ENCODE(); + } + + ENCODE(OP_OpenNewTasksWindow) + { + AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; + AvailableTaskData1_Struct* __emu_AvailableTaskData1; + AvailableTaskData2_Struct* __emu_AvailableTaskData2; + AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; + + structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; + structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; + structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; + structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; + + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + + __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; + + // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. + // + in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); + in->pBuffer = new unsigned char[in->size]; + + unsigned char *__eq_buffer = in->pBuffer; + + __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; + + char *__eq_ptr, *__emu_Ptr; + + // Copy Header + // + // + + __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; + __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; + __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; + + __emu_Ptr = (char *)__emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); + __eq_ptr = (char *)__eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); + + for (uint32 i = 0; i<__emu_AvailableTaskHeader->TaskCount; i++) { + + __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; + __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; + + __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; + // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen + // in Underfoot packets. Changing it to 0x3f000000 makes the title red. + __eq_AvailableTaskData1->unknown1 = 0x3f800000; + __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; + __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; + + __emu_Ptr += sizeof(AvailableTaskData1_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Title + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Description + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __eq_ptr[0] = 0; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; + __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; + + __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; + __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; + __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; + __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; + + __emu_Ptr += sizeof(AvailableTaskData2_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; + __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; + + __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; + __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; + __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; + __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; + + __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); + __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_PetBuffWindow) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + + PetBuff_Struct *emu = (PetBuff_Struct *)__emu_buffer; + + int PacketSize = 12 + (emu->buffcount * 17); + + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + + char *Buffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount); + + for (unsigned int i = 0; i < BUFF_COUNT; ++i) + { + if (emu->spellid[i]) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff. + } + } + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_PlayerProfile) + { + SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); + + uint32 r; + + eq->available_slots = 0xffffffff; + memset(eq->unknown07284, 0xff, sizeof(eq->unknown07284)); + + // OUT(checksum); + OUT(gender); + OUT(race); + OUT(class_); + // OUT(unknown00016); + OUT(level); + eq->level1 = emu->level; + // OUT(unknown00022[2]); + for (r = 0; r < 5; r++) { + OUT(binds[r].zoneId); + OUT(binds[r].x); + OUT(binds[r].y); + OUT(binds[r].z); + OUT(binds[r].heading); + } + OUT(deity); + OUT(intoxication); + OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); + OUT(abilitySlotRefresh); + OUT(points); // Relocation Test + // OUT(unknown0166[4]); + OUT(haircolor); + OUT(beardcolor); + OUT(eyecolor1); + OUT(eyecolor2); + OUT(hairstyle); + OUT(beard); + // OUT(unknown00178[10]); + for (r = 0; r < 9; r++) { + eq->equipment[r].equip0 = emu->item_material[r]; + eq->equipment[r].equip1 = 0; + eq->equipment[r].itemId = 0; + //eq->colors[r].color = emu->colors[r].color; + } + for (r = 0; r < 7; r++) { + OUT(item_tint[r].color); + } + // OUT(unknown00224[48]); + //NOTE: new client supports 300 AAs, our internal rep/PP + //only supports 240.. + for (r = 0; r < MAX_PP_AA_ARRAY; r++) { + OUT(aa_array[r].AA); + OUT(aa_array[r].value); + } + // OUT(unknown02220[4]); + OUT(mana); + OUT(cur_hp); + OUT(STR); + OUT(STA); + OUT(CHA); + OUT(AGI); + OUT(INT); + OUT(DEX); + OUT(WIS); + OUT(face); + // OUT(unknown02264[47]); + memset(eq->spell_book, 0xFF, sizeof(uint32)* structs::MAX_PP_SPELLBOOK); + OUT_array(spell_book, 480U); + // OUT(unknown4184[128]); + OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); + // OUT(unknown04396[32]); + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + OUT(platinum_cursor); + OUT(gold_cursor); + OUT(silver_cursor); + OUT(copper_cursor); + + OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) + + // OUT(unknown04760[236]); + OUT(toxicity); + OUT(thirst_level); + OUT(hunger_level); + //PS this needs to be figured out more; but it was 'good enough' + for (r = 0; r < structs::BUFF_COUNT; r++) + { + if (emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0) + { + eq->buffs[r].unknown004 = 0x3f800000; + eq->buffs[r].slotid = 2; + eq->buffs[r].player_id = 0x000717fd; + } + else + { + eq->buffs[r].slotid = 0; + } + //OUT(buffs[r].slotid); + OUT(buffs[r].level); + //OUT(buffs[r].bard_modifier); + //OUT(buffs[r].effect); + OUT(buffs[r].spellid); + OUT(buffs[r].duration); + OUT(buffs[r].counters); + //OUT(buffs[r].player_id); + } + for (r = 0; r < MAX_PP_DISCIPLINES; r++) { + OUT(disciplines.values[r]); + } + OUT_array(recastTimers, structs::MAX_RECAST_TYPES); + // OUT(unknown08124[360]); + OUT(endurance); + OUT(aapoints_spent); + OUT(aapoints); + // OUT(unknown06160[4]); + //NOTE: new client supports 20 bandoliers, our internal rep + //only supports 4.. + for (r = 0; r < 4; r++) { + OUT_str(bandoliers[r].name); + uint32 k; + for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { + OUT(bandoliers[r].items[k].item_id); + OUT(bandoliers[r].items[k].icon); + OUT_str(bandoliers[r].items[k].item_name); + } + } + // OUT(unknown07444[5120]); + for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { + OUT(potionbelt.items[r].item_id); + OUT(potionbelt.items[r].icon); + OUT_str(potionbelt.items[r].item_name); + } + // OUT(unknown12852[8]); + // OUT(unknown12864[76]); + OUT_str(name); + OUT_str(last_name); + OUT(guild_id); + OUT(birthday); + OUT(lastlogin); + OUT(timePlayedMin); + OUT(pvp); + OUT(anon); + OUT(gm); + OUT(guildrank); + OUT(guildbanker); + // OUT(unknown13054[12]); + OUT(exp); + // OUT(unknown13072[8]); + OUT(timeentitledonaccount); + OUT_array(languages, structs::MAX_PP_LANGUAGE); + // OUT(unknown13109[7]); + OUT(y); //reversed x and y + OUT(x); + OUT(z); + OUT(heading); + // OUT(unknown13132[4]); + OUT(platinum_bank); + OUT(gold_bank); + OUT(silver_bank); + OUT(copper_bank); + OUT(platinum_shared); + // OUT(unknown13156[84]); + //OUT(expansions); + eq->expansions = 0xffff; + // OUT(unknown13244[12]); + OUT(autosplit); + // OUT(unknown13260[16]); + OUT(zone_id); + OUT(zoneInstance); + for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { + OUT_str(groupMembers[r]); + } + strcpy(eq->groupLeader, emu->groupMembers[0]); + // OUT_str(groupLeader); + // OUT(unknown13728[660]); + OUT(entityid); + OUT(leadAAActive); + // OUT(unknown14392[4]); + OUT(ldon_points_guk); + OUT(ldon_points_mir); + OUT(ldon_points_mmc); + OUT(ldon_points_ruj); + OUT(ldon_points_tak); + OUT(ldon_points_available); + // OUT(unknown14420[132]); + OUT(tribute_time_remaining); + OUT(career_tribute_points); + // OUT(unknown7208); + OUT(tribute_points); + // OUT(unknown7216); + OUT(tribute_active); + for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { + OUT(tributes[r].tribute); + OUT(tributes[r].tier); + } + // OUT(unknown14616[8]); + OUT(group_leadership_exp); + // OUT(unknown14628); + OUT(raid_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_points); + OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); + // OUT(unknown14772[128]); + OUT(air_remaining); + OUT(PVPKills); + OUT(PVPDeaths); + OUT(PVPCurrentPoints); + OUT(PVPCareerPoints); + OUT(PVPBestKillStreak); + OUT(PVPWorstDeathStreak); + OUT(PVPCurrentKillStreak); + // OUT(unknown17892[4580]); + OUT(expAA); + // OUT(unknown19516[40]); + OUT(currentRadCrystals); + OUT(careerRadCrystals); + OUT(currentEbonCrystals); + OUT(careerEbonCrystals); + OUT(groupAutoconsent); + OUT(raidAutoconsent); + OUT(guildAutoconsent); + // OUT(unknown19575[5]); + eq->level3 = emu->level; + eq->showhelm = emu->showhelm; + OUT(RestTimer); + // OUT(unknown19584[4]); + // OUT(unknown19588); + + const uint8 bytes[] = { + 0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + memcpy(eq->unknown18020, bytes, sizeof(bytes)); + + //set the checksum... + CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); + + FINISH_ENCODE(); + } + + ENCODE(OP_RaidJoin) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; + + general->action = 8; + general->parameter = 1; + strn0cpy(general->leader_name, raid_create->leader_name, 64); + strn0cpy(general->player_name, raid_create->leader_name, 64); + + dest->FastQueuePacket(&outapp_create); + delete[] __emu_buffer; + } + + ENCODE(OP_RaidUpdate) + { + EQApplicationPacket *inapp = *p; + *p = nullptr; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; + + if (raid_gen->action == 0) // raid add has longer length than other raid updates + { + RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); + structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + + add_member->raidGen.action = in_add_member->raidGen.action; + add_member->raidGen.parameter = in_add_member->raidGen.parameter; + strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); + strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); + add_member->_class = in_add_member->_class; + add_member->level = in_add_member->level; + add_member->isGroupLeader = in_add_member->isGroupLeader; + add_member->flags[0] = in_add_member->flags[0]; + add_member->flags[1] = in_add_member->flags[1]; + add_member->flags[2] = in_add_member->flags[2]; + add_member->flags[3] = in_add_member->flags[3]; + add_member->flags[4] = in_add_member->flags[4]; + dest->FastQueuePacket(&outapp); + } + else + { + RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); + strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); + raid_general->action = in_raid_general->action; + raid_general->parameter = in_raid_general->parameter; + dest->FastQueuePacket(&outapp); + } + + delete[] __emu_buffer; + } + + ENCODE(OP_ReadBook) + { + ENCODE_LENGTH_ATLEAST(BookText_Struct); + SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); + + if (emu->window == 0xFF) + eq->window = 0xFFFFFFFF; + else + eq->window = emu->window; + OUT(type); + eq->invslot = ServerToUnderfootSlot(emu->invslot); + strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); + + FINISH_ENCODE(); + } + + ENCODE(OP_RespondAA) + { + SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); + + eq->aa_spent = emu->aa_spent; + eq->aa_assigned = emu->aa_spent; + eq->aa_spent3 = emu->aa_spent; + eq->unknown012 = 0; + eq->unknown016 = 0; + eq->unknown020 = 0; + + for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) + { + eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; + eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; + eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for SoF + // clientver 1 is for all clients and 6 is for Underfoot + if (emu->clientver <= 6) + { + OUT(id); + eq->unknown004 = 1; + //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->title_sid = emu->id - emu->current_level + 1; + //eq->desc_sid = emu->id - emu->current_level + 1; + eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->title_sid = emu->sof_next_skill; + eq->desc_sid = emu->sof_next_skill; + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + OUT(prereq_skill); + OUT(prereq_minpoints); + eq->type = emu->sof_type; + OUT(spellid); + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + //eq->max_level = emu->sof_max_level; + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + eq->aa_expansion = emu->aa_expansion; + eq->special_category = emu->special_category; + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_VAR_ENCODE(CharacterSelect_Struct); + + //EQApplicationPacket *packet = *p; + //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + + int char_count; + int namelen = 0; + for (char_count = 0; char_count < 10; char_count++) { + if (emu->name[char_count][0] == '\0') + break; + if (strcmp(emu->name[char_count], "") == 0) + break; + namelen += strlen(emu->name[char_count]); + } + + int total_length = sizeof(structs::CharacterSelect_Struct) + + char_count * sizeof(structs::CharacterSelectEntry_Struct) + + namelen; + + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + + //unsigned char *eq_buffer = new unsigned char[total_length]; + //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + + eq->char_count = char_count; + eq->total_chars = 10; + + unsigned char *bufptr = (unsigned char *)eq->entries; + int r; + for (r = 0; r < char_count; r++) { + { //pre-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->level = emu->level[r]; + eq2->hairstyle = emu->hairstyle[r]; + eq2->gender = emu->gender[r]; + memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); + } + //adjust for name. + bufptr += strlen(emu->name[r]); + { //post-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->beard = emu->beard[r]; + eq2->haircolor = emu->haircolor[r]; + eq2->face = emu->face[r]; + int k; + for (k = 0; k < _MaterialCount; k++) { + eq2->equip[k].equip0 = emu->equip[r][k]; + eq2->equip[k].equip1 = 0; + eq2->equip[k].itemid = 0; + eq2->equip[k].color.color = emu->cs_colors[r][k].color; + } + eq2->primary = emu->primary[r]; + eq2->secondary = emu->secondary[r]; + eq2->tutorial = emu->tutorial[r]; // was u15 + eq2->u15 = 0xff; + eq2->deity = emu->deity[r]; + eq2->zone = emu->zone[r]; + eq2->u19 = 0xFF; + eq2->race = emu->race[r]; + eq2->gohome = emu->gohome[r]; + eq2->class_ = emu->class_[r]; + eq2->eyecolor1 = emu->eyecolor1[r]; + eq2->beardcolor = emu->beardcolor[r]; + eq2->eyecolor2 = emu->eyecolor2[r]; + eq2->drakkin_heritage = emu->drakkin_heritage[r]; + eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; + eq2->drakkin_details = emu->drakkin_details[r]; + } + + bufptr += sizeof(structs::CharacterSelectEntry_Struct); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendZonepoints) + { + SETUP_VAR_ENCODE(ZonePoints); + ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); + + eq->count = emu->count; + for (uint32 i = 0; i < emu->count; ++i) + { + eq->zpe[i].iterator = emu->zpe[i].iterator; + eq->zpe[i].x = emu->zpe[i].x; + eq->zpe[i].y = emu->zpe[i].y; + eq->zpe[i].z = emu->zpe[i].z; + eq->zpe[i].heading = emu->zpe[i].heading; + eq->zpe[i].zoneid = emu->zpe[i].zoneid; + eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerBuy) + { + ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); + SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + OUT(npcid); + OUT(playerid); + OUT(itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToUnderfootSlot(emu->itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_SomeItemPacketMaybe) + { + // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow + // and flying to the target. + // + + ENCODE_LENGTH_EXACT(Arrow_Struct); + SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); + + OUT(src_y); + OUT(src_x); + OUT(src_z); + OUT(velocity); + OUT(launch_angle); + OUT(tilt); + OUT(arc); + OUT(source_id); + OUT(target_id); + OUT(item_id); + + eq->unknown070 = 135; // This needs to be set to something, else we get a 1HS animation instead of ranged. + + OUT(item_type); + OUT(skill); + + strcpy(eq->model_name, emu->model_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_SpawnAppearance) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *emu_buffer = in->pBuffer; + + SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer; + + if (sas->type != AT_Size) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); + ChangeSize_Struct *css = (ChangeSize_Struct *)outapp->pBuffer; + + css->EntityID = sas->spawn_id; + css->Size = (float)sas->parameter; + css->Unknown08 = 0; + css->Unknown12 = 1.0f; + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_SpawnDoor) + { + SETUP_VAR_ENCODE(Door_Struct); + int door_count = __packet->size / sizeof(Door_Struct); + int total_length = door_count * sizeof(structs::Door_Struct); + ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); + + int r; + for (r = 0; r < door_count; r++) { + strcpy(eq[r].name, emu[r].name); + eq[r].xPos = emu[r].xPos; + eq[r].yPos = emu[r].yPos; + eq[r].zPos = emu[r].zPos; + eq[r].heading = emu[r].heading; + eq[r].incline = emu[r].incline; + eq[r].size = emu[r].size; + eq[r].doorId = emu[r].doorId; + eq[r].opentype = emu[r].opentype; + eq[r].state_at_spawn = emu[r].state_at_spawn; + eq[r].invert_state = emu[r].invert_state; + eq[r].door_param = emu[r].door_param; + eq[r].unknown0076 = 0; + eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0078 = 0; + eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0080 = 0; + eq[r].unknown0081 = 0; + eq[r].unknown0082 = 0; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_Stun) + { + ENCODE_LENGTH_EXACT(Stun_Struct); + SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); + + OUT(duration); + eq->unknown005 = 163; + eq->unknown006 = 67; + + FINISH_ENCODE(); + } + + ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + int PacketSize = 2; + + for (int i = 0; i < EntryCount; ++i, ++emu) + PacketSize += (12 + strlen(emu->name)); + + emu = (Track_Struct *)__emu_buffer; + + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + + char *Buffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); + + for (int i = 0; i < EntryCount; ++i, ++emu) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); + VARSTRUCT_ENCODE_STRING(Buffer, emu->name); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Trader) + { + if ((*p)->size != sizeof(TraderBuy_Struct)) { + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; + } + + ENCODE_FORWARD(OP_TraderBuy); + } + + ENCODE(OP_TraderBuy) + { + ENCODE_LENGTH_EXACT(TraderBuy_Struct); + SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); + + OUT(Action); + OUT(Price); + OUT(TraderID); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(ItemID); + OUT(Quantity); + OUT(AlreadySold); + + FINISH_ENCODE(); + } + + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToUnderfootSlot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_VetRewardsAvaliable) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + + uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); + *p = nullptr; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); + uchar *old_data = __emu_buffer; + uchar *data = outapp_create->pBuffer; + for (unsigned int i = 0; i < count; ++i) + { + structs::VeteranReward *vr = (structs::VeteranReward*)data; + InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + + vr->claim_count = ivr->claim_count; + vr->claim_id = ivr->claim_id; + vr->number_available = ivr->number_available; + for (int x = 0; x < 8; ++x) + { + vr->items[x].item_id = ivr->items[x].item_id; + strcpy(vr->items[x].item_name, ivr->items[x].item_name); + vr->items[x].charges = ivr->items[x].charges; + } + + old_data += sizeof(InternalVeteranReward); + data += sizeof(structs::VeteranReward); + } + + dest->FastQueuePacket(&outapp_create); + delete inapp; + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(unknown06); + OUT(elite_material); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_WhoAllResponse) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *InBuffer = (char *)in->pBuffer; + + WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; + + int Count = wars->playercount; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); + + char *OutBuffer = (char *)outapp->pBuffer; + + memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); + + OutBuffer += sizeof(WhoAllReturnStruct); + InBuffer += sizeof(WhoAllReturnStruct); + + for (int i = 0; i < Count; ++i) + { + uint32 x; + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + InBuffer += 4; + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); + + char Name[64]; + + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + for (int j = 0; j < 7; ++j) + { + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + delete in; + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZonePlayerToBind) + { + ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); + + ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; + structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; + unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; + structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; + + zph->x = zps->x; + zph->y = zps->y; + zph->z = zps->z; + zph->heading = zps->heading; + zph->bind_zone_id = zps->bind_zone_id; + zph->bind_instance_id = zps->bind_instance_id; + strcpy(zph->zone_name, zps->zone_name); + + zpf->unknown021 = 1; + zpf->unknown022 = 0; + zpf->unknown023 = 0; + zpf->unknown024 = 0; + + ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); + ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); + + delete[] buffer1; + delete[] buffer2; + delete[](*p)->pBuffer; + + (*p)->pBuffer = new unsigned char[ss.str().size()]; + (*p)->size = ss.str().size(); + + memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); + dest->FastQueuePacket(&(*p)); + } + + ENCODE(OP_ZoneServerInfo) + { + SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); + + OUT_str(ip); + OUT(port); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneSpawns) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } - //_log(NET__STRUCTS, "Spawn name is [%s]", emu->name); - emu = (Spawn_Struct *) __emu_buffer; + emu = (Spawn_Struct *)__emu_buffer; //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); - char *Buffer = (char *) in->pBuffer; - + char *Buffer = (char *)in->pBuffer; int r; int k; - for(r = 0; r < entrycount; r++, emu++) { + for (r = 0; r < entrycount; r++, emu++) { int PacketSize = sizeof(structs::Spawn_Struct); PacketSize += strlen(emu->name); PacketSize += strlen(emu->lastName); - if(strlen(emu->title)) + if (strlen(emu->title)) PacketSize += strlen(emu->title) + 1; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) PacketSize += strlen(emu->suffix) + 1; - if(emu->DestructibleObject) + if (emu->DestructibleObject) { PacketSize = PacketSize - 4; // No bodytype PacketSize += 53; // Fixed portion @@ -1022,7 +2568,7 @@ ENCODE(OP_ZoneSpawns) { } bool ShowName = 1; - if(emu->bodytype >= 66) + if (emu->bodytype >= 66) { emu->race = 127; emu->bodytype = 11; @@ -1031,30 +2577,30 @@ ENCODE(OP_ZoneSpawns) { } float SpawnSize = emu->size; - if(!((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) + if (!((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) { PacketSize -= (sizeof(structs::EquipStruct) * 9); - if(emu->size == 0) + if (emu->size == 0) { emu->size = 6; SpawnSize = 6; } } - if(SpawnSize == 0) + if (SpawnSize == 0) { SpawnSize = 3; } EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); - Buffer = (char *) outapp->pBuffer; + Buffer = (char *)outapp->pBuffer; VARSTRUCT_ENCODE_STRING(Buffer, emu->name); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(float, Buffer, 10); // was int and 0x41200000 } @@ -1085,10 +2631,10 @@ ENCODE(OP_ZoneSpawns) { Bitfields->showname = ShowName; - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x1d600000); - Buffer = Buffer -4; + Buffer = Buffer - 4; } Bitfields->ispet = emu->is_pet; @@ -1097,18 +2643,18 @@ ENCODE(OP_ZoneSpawns) { uint8 OtherData = 0; - if(strlen(emu->title)) + if (strlen(emu->title)) OtherData = OtherData | 0x04; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) OtherData = OtherData | 0x08; - if(emu->DestructibleObject) + if (emu->DestructibleObject) OtherData = OtherData | 0xd1; // Live has 0xe1 for OtherData VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); } @@ -1118,7 +2664,7 @@ ENCODE(OP_ZoneSpawns) { } VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleModel); VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleName2); @@ -1150,15 +2696,15 @@ ENCODE(OP_ZoneSpawns) { /* if(emu->bodytype >=66) { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname } else { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname }*/ - if(!emu->DestructibleObject) + if (!emu->DestructibleObject) { // Setting this next field to zero will cause a crash. Looking at ShowEQ, if it is zero, the bodytype field is not // present. Will sort that out later. @@ -1182,7 +2728,7 @@ ENCODE(OP_ZoneSpawns) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_details); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // ShowEQ calls this 'Holding' VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->deity); - if(emu->NPC) + if (emu->NPC) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); @@ -1213,7 +2759,6 @@ ENCODE(OP_ZoneSpawns) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown18 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown19 - structs::Spawn_Struct_Position *Position = (structs::Spawn_Struct_Position*)Buffer; Position->deltaX = emu->deltaX; @@ -1228,9 +2773,9 @@ ENCODE(OP_ZoneSpawns) { Buffer += sizeof(structs::Spawn_Struct_Position); - if((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu ->race == 130) || (emu->race == 330) || (emu->race == 522)) + if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { - for(k = 0; k < 9; ++k) + for (k = 0; k < 9; ++k) { { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); @@ -1252,12 +2797,11 @@ ENCODE(OP_ZoneSpawns) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); } - - if((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) + if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; - for(k = 0; k < 9; k++) { + for (k = 0; k < 9; k++) { Equipment[k].equip0 = emu->equipment[k]; Equipment[k].equip1 = 0; Equipment[k].itemId = 0; @@ -1265,12 +2809,12 @@ ENCODE(OP_ZoneSpawns) { Buffer += (sizeof(structs::EquipStruct) * 9); } - if(strlen(emu->title)) + if (strlen(emu->title)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->title); } - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); } @@ -1280,2083 +2824,503 @@ ENCODE(OP_ZoneSpawns) { Buffer += 28; // Unknown; dest->FastQueuePacket(&outapp, ack_req); - } - - delete in; -} - -ENCODE(OP_MercenaryDataResponse) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; - PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); - - for(r = 0; r < emu->MercTypeCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); - } - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk01); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MerchantSlot); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk02); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk03); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk04); - for(k = 0; k < emu->Mercs[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); } - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -// This packet does not appear to exist in UF, but leaving it here just in case -ENCODE(OP_MercenaryDataUpdate) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - EQApplicationPacket *outapp; - - uint32 PacketSize = 0; - - // There are 2 different sized versions of this packet depending if a merc is hired or not - if (emu->MercStatus >= 0) - { - PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; - } - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk01); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MerchantSlot); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk02); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].MercUnk03); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk04); - for(k = 0; k < emu->MercData[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); - } - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk05); - } - } - else - { - PacketSize += sizeof(structs::NoMercenaryHired_Struct); - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+4; - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length); - - delete[] __emu_buffer; - safe_delete_array(serialized); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - - *p = nullptr; - - if(in->size == 0) { - - in->size = 4; - - in->pBuffer = new uchar[in->size]; - - *((uint32 *) in->pBuffer) = 0; - - dest->FastQueuePacket(&in, ack_req); - - return; - } - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); - - if(ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); delete in; - - return; } - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - in->pBuffer = new uchar[4]; - - *(uint32 *)in->pBuffer = ItemCount; - - in->size = 4; - - for(int r = 0; r < ItemCount; r++, eq++) { - - uint32 Length = 0; - - char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); - - if(Serialized) { - - uchar *OldBuffer = in->pBuffer; - - in->pBuffer = new uchar[in->size + Length]; - - memcpy(in->pBuffer, OldBuffer, in->size); - - safe_delete_array(OldBuffer); - - memcpy(in->pBuffer + in->size, Serialized, Length); - - in->size += Length; - - safe_delete_array(Serialized); - - } - else { - _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); - } - } - - delete[] __emu_buffer; - - //_log(NET__ERROR, "Sending inventory to client"); - - //_hex(NET__ERROR, in->pBuffer, in->size); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; - - - - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. - - uint8 *buffer; - buffer = in->pBuffer; - - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; - - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); - - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header - emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data - ); - const char *emu_note = (emu_name + - emu->name_length + //skip name contents - emu->count //skip string terminators - ); - - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { - - //the order we set things here must match the struct - -//nice helper macro -/*#define SlideStructString(field, str) \ - strcpy(e->field, str.c_str()); \ - e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ -#define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) - - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - PutFieldN(rank); - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - PutFieldN(total_tribute); - PutFieldN(last_tribute); - e->unknown_one = htonl(1); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); - - -#undef SlideStructString -#undef PutFieldN - - e++; - } - } - - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SpawnDoor) { - SETUP_VAR_ENCODE(Door_Struct); - int door_count = __packet->size/sizeof(Door_Struct); - int total_length = door_count * sizeof(structs::Door_Struct); - ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); - int r; - for(r = 0; r < door_count; r++) { - strcpy(eq[r].name, emu[r].name); - eq[r].xPos = emu[r].xPos; - eq[r].yPos = emu[r].yPos; - eq[r].zPos = emu[r].zPos; - eq[r].heading = emu[r].heading; - eq[r].incline = emu[r].incline; - eq[r].size = emu[r].size; - eq[r].doorId = emu[r].doorId; - eq[r].opentype = emu[r].opentype; - eq[r].state_at_spawn = emu[r].state_at_spawn; - eq[r].invert_state = emu[r].invert_state; - eq[r].door_param = emu[r].door_param; - eq[r].unknown0076 = 0; - eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0078 = 0; - eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0080 = 0; - eq[r].unknown0081 = 0; - eq[r].unknown0082 = 0; - } - FINISH_ENCODE(); -} - -ENCODE(OP_GroundSpawn) -{ - - // We are not encoding the spawn_id field here, or a size but it doesn't appear to matter. - // - EQApplicationPacket *in = *p; - *p = nullptr; - - Object_Struct *emu = (Object_Struct *) in->pBuffer; - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = strlen(emu->object_name) + 58; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->object_name); - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_id); - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_instance); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0x00006762 - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observer 0x7fffbb64 - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->heading); - // This next field is actually a float. There is a groundspawn in freeportwest (sack of money sitting on some barrels) which requires this - // field to be set to (float)255.0 to appear at all, and also the size field below to be 5, to be the correct size. I think SoD has the same - // issue. - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0 - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // This appears to be the size field. - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->object_type); // Unknown, observed 0x00000014 - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); // Unknown, observed 0xffffffff - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0x00000014 - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown, observed 0x00 - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ManaChange) { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? - FINISH_ENCODE(); -} - -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - memcpy(eq->Title, emu->Title, sizeof(eq->Title)); - memcpy(eq->Text, emu->Text, sizeof(eq->Text)); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); - OUT(NegativeID); - // These two field names are used if Buttons == 1. - OUT_str(ButtonName0); - OUT_str(ButtonName1); - FINISH_ENCODE(); -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - OUT(race); - OUT(unknown006[0]); - OUT(unknown006[1]); - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - OUT(drakkin_heritage); - OUT(drakkin_tattoo); - OUT(drakkin_details); - - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerBuy) -{ - ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); - SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - OUT(npcid); - OUT(playerid); - OUT(itemslot); - OUT(quantity); - OUT(price); - - FINISH_ENCODE(); -} - -ENCODE(OP_ClientUpdate) { - ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); - SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); - OUT(spawn_id); - OUT(x_pos); - OUT(delta_x); - OUT(delta_y); - OUT(z_pos); - OUT(delta_heading); - OUT(y_pos); - OUT(delta_z); - OUT(animation); - OUT(heading); - FINISH_ENCODE(); -} - -ENCODE(OP_ExpansionInfo) { - ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); - OUT(Expansions); - FINISH_ENCODE(); -} - -ENCODE(OP_LogServer) { - ENCODE_LENGTH_EXACT(LogServer_Struct); - SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); - strcpy(eq->worldshortname, emu->worldshortname); - - OUT(enablevoicemacros); - OUT(enablemail); - OUT(enable_pvp); - OUT(enable_FV); - - eq->unknown016 = 1; - eq->unknown020[0] = 1; - - // These next two need to be set like this for the Tutorial Button to work. - eq->unknown263[0] = 0; - eq->unknown263[2] = 1; - eq->unknown263[4] = 1; - eq->unknown263[5] = 1; - eq->unknown263[6] = 1; - eq->unknown263[9] = 8; - eq->unknown263[19] = 0x80; - eq->unknown263[20] = 0x3f; - eq->unknown263[23] = 0x80; - eq->unknown263[24] = 0x3f; - eq->unknown263[33] = 1; - - FINISH_ENCODE(); -} - -ENCODE(OP_Damage) { - ENCODE_LENGTH_EXACT(CombatDamage_Struct); - SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); - OUT(target); - OUT(source); - OUT(type); - OUT(spellid); - OUT(damage); - eq->sequence = emu->sequence; - FINISH_ENCODE(); -} - -ENCODE(OP_Consider) { - ENCODE_LENGTH_EXACT(Consider_Struct); - SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); - OUT(playerid); - OUT(targetid); - OUT(faction); - OUT(level); - OUT(pvpcon); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::ActionAlt_Struct); - OUT(target); - OUT(source); - OUT(level); - eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; - eq->knockback_angle = emu->sequence; - OUT(type); - OUT(spell); - eq->level2 = eq->level; - eq->effect_flag = emu->buff_unknown; - eq->unknown37 = 0x01; - eq->unknown44 = 0xFFFFFFFF; - eq->unknown48 = 0xFFFFFFFF; - eq->unknown52 = 0xFFFFFFFF; - - /*OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - eq->sequence = emu->sequence; - OUT(type); - //OUT(damage); - OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1;*/ - FINISH_ENCODE(); -} - -ENCODE(OP_Buff) { - ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); - SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Underfoot); - OUT(entityid); - OUT(slot); - OUT(level); - OUT(effect); - //eq->unknown7 = 10; - OUT(spellid); - OUT(duration); - OUT(slotid); - OUT(bufffade); // Live (October 2011) sends a 2 rather than 0 when a buff is created, but it doesn't seem to matter. - eq->unknown008 = 1.0f; - FINISH_ENCODE(); -} - -ENCODE(OP_CancelTrade) { - ENCODE_LENGTH_EXACT(CancelTrade_Struct); - SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); - OUT(fromid); - OUT(action); - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerSell) { - ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); - SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - OUT(npcid); - eq->itemslot = ServerToUnderfootSlot(emu->itemslot); - OUT(quantity); - OUT(price); - FINISH_ENCODE(); -} - -ENCODE(OP_ApplyPoison) { - ENCODE_LENGTH_EXACT(ApplyPoison_Struct); - SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - eq->inventorySlot = ServerToUnderfootSlot(emu->inventorySlot); - OUT(success); - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteItem) { - ENCODE_LENGTH_EXACT(DeleteItem_Struct); - SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - eq->from_slot = ServerToUnderfootSlot(emu->from_slot); - eq->to_slot = ServerToUnderfootSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } -ENCODE(OP_MoveItem) { - ENCODE_LENGTH_EXACT(MoveItem_Struct); - SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); - - eq->from_slot = ServerToUnderfootSlot(emu->from_slot); - eq->to_slot = ServerToUnderfootSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_ItemVerifyReply) { - ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); - SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); - - eq->slot = ServerToUnderfootSlot(emu->slot); - OUT(spell); - OUT(target); - - FINISH_ENCODE(); -} - -ENCODE(OP_Trader) { - - if((*p)->size != sizeof(TraderBuy_Struct)) { - EQApplicationPacket *in = *p; - *p = nullptr; - dest->FastQueuePacket(&in, ack_req); - return; - } - ENCODE_FORWARD(OP_TraderBuy); -} - -ENCODE(OP_TraderBuy) { - - ENCODE_LENGTH_EXACT(TraderBuy_Struct); - SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); - - OUT(Action); - OUT(Price); - OUT(TraderID); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(ItemID); - OUT(Quantity); - OUT(AlreadySold); - - FINISH_ENCODE(); -} - -ENCODE(OP_LootItem) { - - ENCODE_LENGTH_EXACT(LootingItem_Struct); - SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); - OUT(lootee); - OUT(looter); - eq->slot_id = emu->slot_id + 1; - OUT(auto_loot); - - FINISH_ENCODE(); -} - -ENCODE(OP_TributeItem) { - ENCODE_LENGTH_EXACT(TributeItem_Struct); - SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); - - eq->slot = ServerToUnderfootSlot(emu->slot); - OUT(quantity); - OUT(tribute_master_id); - OUT(tribute_points); - - FINISH_ENCODE(); -} - -ENCODE(OP_SomeItemPacketMaybe) { - // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow - // and flying to the target. - // - - ENCODE_LENGTH_EXACT(Arrow_Struct); - SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); - - OUT(src_y); - OUT(src_x); - OUT(src_z); - OUT(velocity); - OUT(launch_angle); - OUT(tilt); - OUT(arc); - OUT(source_id); - OUT(target_id); - OUT(item_id); - - eq->unknown070 = 135; // This needs to be set to something, else we get a 1HS animation instead of ranged. - - OUT(item_type); - OUT(skill); - - strcpy(eq->model_name, emu->model_name); - - FINISH_ENCODE(); -} - -ENCODE(OP_ReadBook) { - - ENCODE_LENGTH_ATLEAST(BookText_Struct); - SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); - - if(emu->window == 0xFF) - eq->window = 0xFFFFFFFF; - else - eq->window = emu->window; - OUT(type); - eq->invslot = ServerToUnderfootSlot(emu->invslot); - strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); - FINISH_ENCODE(); -} - -ENCODE(OP_Stun) { - - ENCODE_LENGTH_EXACT(Stun_Struct); - SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); - OUT(duration); - eq->unknown005 = 163; - eq->unknown006 = 67; - - FINISH_ENCODE(); -} - -ENCODE(OP_ZonePlayerToBind) -{ - ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); - ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; - structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; - unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; - structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; - - zph->x = zps->x; - zph->y = zps->y; - zph->z = zps->z; - zph->heading = zps->heading; - zph->bind_zone_id = zps->bind_zone_id; - zph->bind_instance_id = zps->bind_instance_id; - strcpy(zph->zone_name, zps->zone_name); - - zpf->unknown021 = 1; - zpf->unknown022 = 0; - zpf->unknown023 = 0; - zpf->unknown024 = 0; - - ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); - ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); - - delete[] buffer1; - delete[] buffer2; - delete[] (*p)->pBuffer; - - (*p)->pBuffer = new unsigned char[ss.str().size()]; - (*p)->size = ss.str().size(); - - memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); - dest->FastQueuePacket(&(*p)); -} - -ENCODE(OP_AdventureMerchantSell) { - ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); - SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - eq->unknown000 = 1; - OUT(npcid); - eq->slot = ServerToUnderfootSlot(emu->slot); - OUT(charges); - OUT(sell_price); - - FINISH_ENCODE(); -} - -ENCODE(OP_RaidUpdate) -{ - EQApplicationPacket *inapp = *p; - *p = nullptr; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; - - if(raid_gen->action == 0) // raid add has longer length than other raid updates +// DECODE methods + DECODE(OP_AdventureMerchantSell) { - RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); - structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + IN(npcid); + emu->slot = UnderfootToServerSlot(eq->slot); + IN(charges); + IN(sell_price); - add_member->raidGen.action = in_add_member->raidGen.action; - add_member->raidGen.parameter = in_add_member->raidGen.parameter; - strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); - strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); - add_member->_class = in_add_member->_class; - add_member->level= in_add_member->level; - add_member->isGroupLeader = in_add_member->isGroupLeader; - add_member->flags[0] = in_add_member->flags[0]; - add_member->flags[1] = in_add_member->flags[1]; - add_member->flags[2] = in_add_member->flags[2]; - add_member->flags[3] = in_add_member->flags[3]; - add_member->flags[4] = in_add_member->flags[4]; - dest->FastQueuePacket(&outapp); - } - else - { - RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); - strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); - raid_general->action = in_raid_general->action; - raid_general->parameter = in_raid_general->parameter; - dest->FastQueuePacket(&outapp); - } - delete[] __emu_buffer; -} - -ENCODE(OP_RaidJoin) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; - - general->action = 8; - general->parameter = 1; - strn0cpy(general->leader_name, raid_create->leader_name, 64); - strn0cpy(general->player_name, raid_create->leader_name, 64); - - dest->FastQueuePacket(&outapp_create); - delete[] __emu_buffer; -} - -ENCODE(OP_VetRewardsAvaliable) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - - uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for(unsigned int i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; - - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for(int x = 0; x < 8; ++x) - { - vr->items[x].item_id = ivr->items[x].item_id; - strcpy(vr->items[x].item_name, ivr->items[x].item_name); - vr->items[x].charges = ivr->items[x].charges; - } - - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); + FINISH_DIRECT_DECODE(); } - dest->FastQueuePacket(&outapp_create); - delete inapp; -} - -ENCODE(OP_WhoAllResponse) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *InBuffer = (char *)in->pBuffer; - - WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; - - int Count = wars->playercount; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); - - char *OutBuffer = (char *)outapp->pBuffer; - - memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); - - OutBuffer += sizeof(WhoAllReturnStruct); - InBuffer += sizeof(WhoAllReturnStruct); - - for(int i = 0; i < Count; ++i) + DECODE(OP_AltCurrencySell) { - uint32 x; + DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + IN(merchant_entity_id); + emu->slot_id = UnderfootToServerSlot(eq->slot_id); + IN(charges); + IN(cost); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AltCurrencySellSelection) + { + DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); + + IN(merchant_entity_id); + emu->slot_id = UnderfootToServerSlot(eq->slot_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = UnderfootToServerSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentInfo) + { + DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); + SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + IN(itemid); + IN(window); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = UnderfootToServerSlot(eq->container_slot); + emu->augment_slot = eq->augment_slot; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_BazaarSearch) + { + char *Buffer = (char *)__packet->pBuffer; + + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if ((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) + return; + + SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); + MEMSET_IN(structs::NewBazaarInspect_Struct); + IN(Beginning.Action); + memcpy(emu->Name, eq->Name, sizeof(emu->Name)); + IN(SerialNumber); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Buff) + { + DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct_Underfoot); + SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Underfoot); + + IN(entityid); + IN(slot); + IN(level); + IN(effect); + IN(spellid); + IN(duration); + IN(slotid); + IN(bufffade); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_BuffRemoveRequest) + { + // This is to cater for the fact that short buff box buffs start at 30 as opposed to 25 in prior clients. + // + DECODE_LENGTH_EXACT(structs::BuffRemoveRequest_Struct); + SETUP_DIRECT_DECODE(BuffRemoveRequest_Struct, structs::BuffRemoveRequest_Struct); + + emu->SlotID = (eq->SlotID < 30) ? eq->SlotID : (eq->SlotID - 5); + + IN(EntityID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + if (eq->slot == 13) + emu->slot = 10; + else + IN(slot); + + IN(spell_id); + emu->inventoryslot = UnderfootToServerSlot(eq->inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ChannelMessage) + { + unsigned char *__eq_buffer = __packet->pBuffer; + + char *InBuffer = (char *)__eq_buffer; + + char Sender[64]; + char Target[64]; + + VARSTRUCT_DECODE_STRING(Sender, InBuffer); + VARSTRUCT_DECODE_STRING(Target, InBuffer); InBuffer += 4; - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); - char Name[64]; + uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + InBuffer += 5; - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + __packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + strn0cpy(emu->targetname, Target, sizeof(emu->targetname)); + strn0cpy(emu->sender, Target, sizeof(emu->sender)); + emu->language = Language; + emu->chan_num = Channel; + emu->skill_in_language = Skill; + strcpy(emu->message, InBuffer); - for(int j = 0; j < 7; ++j) - { - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); - } - - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + delete[] __eq_buffer; } - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - delete in; -} - -ENCODE(OP_GroupInvite) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); - memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow2) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupCancelInvite) -{ - ENCODE_LENGTH_EXACT(GroupCancel_Struct); - SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - OUT(toggle); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupUpdate) -{ - //_log(NET__ERROR, "OP_GroupUpdate"); - EQApplicationPacket *in = *p; - - GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; - - //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); - if((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) + DECODE(OP_CharacterCreate) { - if((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) - { - //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); - dest->FastQueuePacket(&outapp); - - // Make an empty GLAA packet to clear out their useable GLAAs - // - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - dest->FastQueuePacket(&outapp); - - delete in; - - return; - } - //if(gjs->action == groupActLeave) - // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - delete in; - return; - - - } - - if(in->size == sizeof(GroupUpdate2_Struct)) - { - // Group Update2 - //_log(NET__ERROR, "Struct is GroupUpdate2"); - - unsigned char *__emu_buffer = in->pBuffer; - GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*) __emu_buffer; - - //_log(NET__ERROR, "Yourname is %s", gu2->yourname); - - int MemberCount = 1; - - int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; - - for(int i = 0; i < 5; ++i) - { - //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); - if(gu2->membername[i][0] != '\0') - { - PacketLength += (22 + strlen(gu2->membername[i]) + 1); - ++MemberCount; - } - } - - //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); - - char *Buffer = (char *)outapp->pBuffer; - - // Header - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); - - // Leader - // - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - - int MemberNumber = 1; - - for(int i = 0; i < 5; ++i) - { - if(gu2->membername[i][0] == '\0') - continue; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - } - - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = gu2->NPCMarkerID; - memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); - - dest->FastQueuePacket(&outapp); - delete in; - - return; - - } - //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - ENCODE_LENGTH_EXACT(GroupJoin_Struct); - SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); - - memcpy(eq->membername, emu->membername, sizeof(eq->membername)); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = emu->NPCMarkerID; - - memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); - //_hex(NET__ERROR, __packet->pBuffer, __packet->size); - FINISH_ENCODE(); - - dest->FastQueuePacket(&outapp); -} - -ENCODE(OP_ChannelMessage) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - ChannelMessage_Struct *emu = (ChannelMessage_Struct *) in->pBuffer; - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildsList) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - uint32 NumberOfGuilds = in->size / 64; - - uint32 PacketSize = 68; // 64 x 0x00 + a uint32 that I am guessing is the highest guild ID in use. - - unsigned char *__emu_buffer = in->pBuffer; - - char *InBuffer = (char *)__emu_buffer; - - uint32 HighestGuildID = 0; - - for(unsigned int i = 0; i < NumberOfGuilds; ++i) - { - if(InBuffer[0]) - { - PacketSize += (5 + strlen(InBuffer)); - HighestGuildID = i - 1; - } - InBuffer += 64; - } - - PacketSize++; // Appears to be an extra 0x00 at the very end. - - in->size = PacketSize; - - in->pBuffer = new unsigned char[in->size]; - - InBuffer = (char *)__emu_buffer; - - char *OutBuffer = (char *)in->pBuffer; - - // Init the first 64 bytes to zero, as per live. - // - memset(OutBuffer, 0, 64); - - OutBuffer += 64; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, HighestGuildID); - - for(unsigned int i = 0; i < NumberOfGuilds; ++i) - { - if(InBuffer[0]) - { - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, i - 1); - VARSTRUCT_ENCODE_STRING(OutBuffer, InBuffer); - } - InBuffer += 64; - } - - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0x00); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_DzExpeditionEndsWarning) -{ - ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); - SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - OUT(minutes_remaining); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionInfo) -{ - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - OUT(max_players); - eq->unknown004 = 785316192; - eq->unknown008 = 435601; - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->leader_name, emu->leader_name); - FINISH_ENCODE(); -} - -ENCODE(OP_DzCompass) -{ - SETUP_VAR_ENCODE(ExpeditionCompass_Struct); - ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - OUT(count); - - for(uint32 i = 0; i < emu->count; ++i) - { - OUT(entries[i].x); - OUT(entries[i].y); - OUT(entries[i].z); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_DzMemberList) -{ - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].status, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionList) -{ - SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); - ss.write((const char*)&null_term, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzLeaderStatus) -{ - SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->leader_name, strlen(emu->leader_name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//1 - ss.write((const char*)&client_id, sizeof(uint32)); - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzJoinExpeditionConfirm) -{ - ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); - SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->player_name, emu->player_name); - FINISH_ENCODE(); -} - -ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); } -ENCODE(OP_BuffCreate) -{ - SETUP_VAR_ENCODE(BuffIcon_Struct); - - uint32 sz = 12 + (17 * emu->count); - __packet->size = sz; - __packet->pBuffer = new unsigned char[sz]; - memset(__packet->pBuffer, 0, sz); - - uchar *ptr = __packet->pBuffer; - *((uint32*)ptr) = emu->entity_id; - ptr += sizeof(uint32); - ptr += sizeof(uint32); - *((uint8*)ptr) = 1; - ptr += sizeof(uchar); - *((uint16*)ptr) = emu->count; - ptr += sizeof(uint16); - - for(uint16 i = 0; i < emu->count; ++i) - { - uint16 buffslot = emu->entries[i].buff_slot; - if(emu->entries[i].buff_slot >= 25 && emu->entries[i].buff_slot < 37) - { - buffslot += 5; - } - else if(emu->entries[i].buff_slot >= 37) - { - buffslot += 14; - } - - *((uint32*)ptr) = buffslot; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].spell_id; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].tics_remaining; - ptr += sizeof(uint32); - ptr += sizeof(uint32); - ptr += 1; - } - FINISH_ENCODE(); - /* - uint32 write_var32 = 60; - uint8 write_var8 = 1; - ss.write((const char*)&emu->entity_id, sizeof(uint32)); - ss.write((const char*)&write_var32, sizeof(uint32)); - ss.write((const char*)&write_var8, sizeof(uint8)); - ss.write((const char*)&emu->count, sizeof(uint16)); - write_var32 = 0; - write_var8 = 0; - for(uint16 i = 0; i < emu->count; ++i) - { - if(emu->entries[i].buff_slot >= 25 && emu->entries[i].buff_slot < 37) - { - emu->entries[i].buff_slot += 5; - } - else if(emu->entries[i].buff_slot >= 37) - { - emu->entries[i].buff_slot += 14; - } - ss.write((const char*)&emu->entries[i].buff_slot, sizeof(uint32)); - ss.write((const char*)&emu->entries[i].spell_id, sizeof(uint32)); - ss.write((const char*)&emu->entries[i].tics_remaining, sizeof(uint32)); - ss.write((const char*)&write_var32, sizeof(uint32)); - ss.write((const char*)&write_var8, sizeof(uint8)); - } - ss.write((const char*)&write_var8, sizeof(uint8)); - */ -} - -ENCODE(OP_WearChange) -{ - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(unknown06); - OUT(elite_material); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -ENCODE(OP_SpawnAppearance) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *emu_buffer = in->pBuffer; - - SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer; - - if(sas->type != AT_Size) - { - dest->FastQueuePacket(&in, ack_req); - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); - - ChangeSize_Struct *css = (ChangeSize_Struct *)outapp->pBuffer; - - css->EntityID = sas->spawn_id; - css->Size = (float)sas->parameter; - css->Unknown08 = 0; - css->Unknown12 = 1.0f; - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_DisciplineUpdate) -{ - ENCODE_LENGTH_EXACT(Disciplines_Struct); - SETUP_DIRECT_ENCODE(Disciplines_Struct, structs::Disciplines_Struct); - - memcpy(&eq->values, &emu->values, sizeof(Disciplines_Struct)); - - FINISH_ENCODE(); -} - -ENCODE(OP_AltCurrencySell) -{ - ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); - SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - - OUT(merchant_entity_id); - eq->slot_id = ServerToUnderfootSlot(emu->slot_id); - OUT(charges); - OUT(cost); - FINISH_ENCODE(); -} - -ENCODE(OP_AltCurrency) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *emu_buffer = in->pBuffer; - uint32 opcode = *((uint32*)emu_buffer); - - if(opcode == 8) { - AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(structs::AltCurrencyPopulate_Struct) - + sizeof(structs::AltCurrencyPopulateEntry_Struct) * populate->count); - structs::AltCurrencyPopulate_Struct *out_populate = (structs::AltCurrencyPopulate_Struct*)outapp->pBuffer; - - out_populate->opcode = populate->opcode; - out_populate->count = populate->count; - for(uint32 i = 0; i < populate->count; ++i) { - out_populate->entries[i].currency_number = populate->entries[i].currency_number; - out_populate->entries[i].currency_number2 = populate->entries[i].currency_number2; - out_populate->entries[i].item_id = populate->entries[i].item_id; - out_populate->entries[i].item_icon = populate->entries[i].item_icon; - out_populate->entries[i].stack_size = populate->entries[i].stack_size; - out_populate->entries[i].unknown00 = populate->entries[i].unknown00; - } - - dest->FastQueuePacket(&outapp, ack_req); - } else { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyUpdate_Struct)); - memcpy(outapp->pBuffer, emu_buffer, sizeof(AltCurrencyUpdate_Struct)); - dest->FastQueuePacket(&outapp, ack_req); - } - - //dest->FastQueuePacket(&outapp, ack_req); - delete in; -} - -ENCODE(OP_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - FINISH_ENCODE(); -} - -DECODE(OP_InspectRequest) { - DECODE_LENGTH_EXACT(structs::Inspect_Struct); - SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); - IN(TargetID); - IN(PlayerID); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_BazaarSearch) -{ - char *Buffer = (char *)__packet->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) - return; - - SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); - MEMSET_IN(structs::NewBazaarInspect_Struct); - IN(Beginning.Action); - memcpy(emu->Name, eq->Name, sizeof(emu->Name)); - IN(SerialNumber); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); - - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AdventureMerchantSell) { - DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); - SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - IN(npcid); - emu->slot = UnderfootToServerSlot(eq->slot); - IN(charges); - IN(sell_price); - - FINISH_DIRECT_DECODE(); -} - - -DECODE(OP_ApplyPoison) { - DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); - SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - - emu->inventorySlot = UnderfootToServerSlot(eq->inventorySlot); - IN(success); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemVerifyRequest) { - DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); - SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); - - emu->slot = UnderfootToServerSlot(eq->slot); - IN(target); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Consume) { - DECODE_LENGTH_EXACT(structs::Consume_Struct); - SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); - - emu->slot = UnderfootToServerSlot(eq->slot); - IN(auto_consumed); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CastSpell) { - DECODE_LENGTH_EXACT(structs::CastSpell_Struct); - SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); - - if(eq->slot == 13) - { - emu->slot = 10; - } - else - { - IN(slot); - } - IN(spell_id); - emu->inventoryslot = UnderfootToServerSlot(eq->inventoryslot); - IN(target_id); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_DeleteItem) -{ - DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); - SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - emu->from_slot = UnderfootToServerSlot(eq->from_slot); - emu->to_slot = UnderfootToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_MoveItem) -{ - DECODE_LENGTH_EXACT(structs::MoveItem_Struct); - SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - - _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); - - emu->from_slot = UnderfootToServerSlot(eq->from_slot); - emu->to_slot = UnderfootToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); - } - IN(link_hash); - IN(icon); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 29; r++) { - IN(filters[r]); - } - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } -DECODE(OP_Consider) { - DECODE_LENGTH_EXACT(structs::Consider_Struct); - SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); - IN(playerid); - IN(targetid); - IN(faction); - IN(level); - //emu->cur_hp = 1; - //emu->max_hp = 2; - //emu->pvpcon = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerBuy) -{ - DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); - SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - - IN(npcid); - IN(playerid); - IN(itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ClientUpdate) { - // for some odd reason, there is an extra byte on the end of this on occasion.. - DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); - SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); - IN(spawn_id); - IN(sequence); - IN(x_pos); - IN(y_pos); - IN(z_pos); - IN(heading); - IN(delta_x); - IN(delta_y); - IN(delta_z); - IN(delta_heading); - IN(animation); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - IN(class_); - IN(beardcolor); - IN(beard); - IN(hairstyle); - IN(gender); - IN(race); - - if(RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - - IN(haircolor); - IN(deity); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - IN(guildid); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupInvite2) -{ - //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); - DECODE_FORWARD(OP_GroupInvite); -} - -DECODE(OP_GroupInvite) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupInvite"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); - memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow2) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupDisband) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_Disband"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupCancelInvite) -{ - DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); - SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - IN(toggle); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Buff) { - DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct_Underfoot); - SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Underfoot); - IN(entityid); - IN(slot); - IN(level); - IN(effect); - IN(spellid); - IN(duration); - IN(slotid); - IN(bufffade); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerSell) { - DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); - SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - - IN(npcid); - emu->itemslot = UnderfootToServerSlot(eq->itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Save) { - DECODE_LENGTH_EXACT(structs::Save_Struct); - SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); - memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FindPersonRequest) { - DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); - SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); - IN(npc_id); - IN(client_pos.x); - IN(client_pos.y); - IN(client_pos.z); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WearChange) { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - IN(spawn_id); - IN(material); - IN(unknown06); - IN(elite_material); - IN(color.color); - IN(wear_slot_id); - emu->hero_forge_model = 0; - emu->unknown18 = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TraderBuy) -{ - DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); - SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); - MEMSET_IN(TraderBuy_Struct); - - IN(Action); - IN(Price); - IN(TraderID); - memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); - IN(ItemID); - IN(Quantity); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LootItem) { - DECODE_LENGTH_EXACT(structs::LootingItem_Struct); - SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); - IN(lootee); - IN(looter); - emu->slot_id = eq->slot_id - 1; - IN(auto_loot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TributeItem) { - DECODE_LENGTH_EXACT(structs::TributeItem_Struct); - SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); - - emu->slot = UnderfootToServerSlot(eq->slot); - IN(quantity); - IN(tribute_master_id); - IN(tribute_points); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_EXACT(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(type); - emu->invslot = UnderfootToServerSlot(eq->invslot); - emu->window = (uint8) eq->window; - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TradeSkillCombine) { - DECODE_LENGTH_EXACT(structs::NewCombine_Struct); - SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - - emu->container_slot = UnderfootToServerSlot(eq->container_slot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentItem) { - DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); - SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); - - emu->container_slot = UnderfootToServerSlot(eq->container_slot); - emu->augment_slot = eq->augment_slot; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentInfo) { - DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); - SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); - - IN(itemid); - IN(window); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LoadSpellSet) -{ - DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); - SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); - - for(unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) - if(eq->spell[i]==0) - emu->spell[i] = 0xFFFFFFFF; + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + IN(class_); + IN(beardcolor); + IN(beard); + IN(hairstyle); + IN(gender); + IN(race); + + if (RuleB(World, EnableTutorialButton) && eq->tutorial) + emu->start_zone = RuleI(World, TutorialZoneID); else - emu->spell[i] = eq->spell[i]; + emu->start_zone = eq->start_zone; - FINISH_DIRECT_DECODE(); -} + IN(haircolor); + IN(deity); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); -DECODE(OP_Damage) { - DECODE_LENGTH_EXACT(structs::CombatDamage_Struct); - SETUP_DIRECT_DECODE(CombatDamage_Struct, structs::CombatDamage_Struct); - IN(target); - IN(source); - IN(type); - IN(spellid); - IN(damage); - emu->sequence = eq->sequence; - FINISH_DIRECT_DECODE(); -} + FINISH_DIRECT_DECODE(); + } -DECODE(OP_EnvDamage) { - DECODE_LENGTH_EXACT(structs::EnvDamage2_Struct); - SETUP_DIRECT_DECODE(EnvDamage2_Struct, structs::EnvDamage2_Struct); - IN(id); - IN(damage); - IN(dmgtype); - emu->constant = 0xFFFF; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_PetCommands) -{ - DECODE_LENGTH_EXACT(structs::PetCommand_Struct); - SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); - - switch(eq->command) + DECODE(OP_ClientUpdate) { + // for some odd reason, there is an extra byte on the end of this on occasion.. + DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); + SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); + + IN(spawn_id); + IN(sequence); + IN(x_pos); + IN(y_pos); + IN(z_pos); + IN(heading); + IN(delta_x); + IN(delta_y); + IN(delta_z); + IN(delta_heading); + IN(animation); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Consider) + { + DECODE_LENGTH_EXACT(structs::Consider_Struct); + SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); + + IN(playerid); + IN(targetid); + IN(faction); + IN(level); + //emu->cur_hp = 1; + //emu->max_hp = 2; + //emu->pvpcon = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = UnderfootToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Damage) + { + DECODE_LENGTH_EXACT(structs::CombatDamage_Struct); + SETUP_DIRECT_DECODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + IN(target); + IN(source); + IN(type); + IN(spellid); + IN(damage); + emu->sequence = eq->sequence; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = UnderfootToServerSlot(eq->from_slot); + emu->to_slot = UnderfootToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_EnvDamage) + { + DECODE_LENGTH_EXACT(structs::EnvDamage2_Struct); + SETUP_DIRECT_DECODE(EnvDamage2_Struct, structs::EnvDamage2_Struct); + + IN(id); + IN(damage); + IN(dmgtype); + emu->constant = 0xFFFF; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FindPersonRequest) + { + DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); + SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); + + IN(npc_id); + IN(client_pos.x); + IN(client_pos.y); + IN(client_pos.z); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupCancelInvite) + { + DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); + SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + IN(toggle); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupDisband) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_Disband"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow2) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupInvite"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); + memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite2) + { + //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); + DECODE_FORWARD(OP_GroupInvite); + } + + DECODE(OP_InspectRequest) + { + DECODE_LENGTH_EXACT(structs::Inspect_Struct); + SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); + + IN(TargetID); + IN(PlayerID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + IN(link_hash); + IN(icon); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemVerifyRequest) + { + DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); + SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); + + emu->slot = UnderfootToServerSlot(eq->slot); + IN(target); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LoadSpellSet) + { + DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); + SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); + + for (unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) + if (eq->spell[i] == 0) + emu->spell[i] = 0xFFFFFFFF; + else + emu->spell[i] = eq->spell[i]; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = eq->slot_id - 1; + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); + + emu->from_slot = UnderfootToServerSlot(eq->from_slot); + emu->to_slot = UnderfootToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_PetCommands) + { + DECODE_LENGTH_EXACT(structs::PetCommand_Struct); + SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); + + switch (eq->command) + { case 0x00: emu->command = 0x04; // Health break; @@ -3404,574 +3368,683 @@ DECODE(OP_PetCommands) break; default: emu->command = eq->command; - } - OUT(unknown); + } + OUT(unknown); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ChannelMessage) -{ - unsigned char *__eq_buffer = __packet->pBuffer; - - char *InBuffer = (char *)__eq_buffer; - - char Sender[64]; - char Target[64]; - - VARSTRUCT_DECODE_STRING(Sender, InBuffer); - VARSTRUCT_DECODE_STRING(Target, InBuffer); - - InBuffer += 4; - - uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - - InBuffer += 5; - - uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - - __packet->size = sizeof(ChannelMessage_Struct) + strlen(InBuffer) + 1; - __packet->pBuffer = new unsigned char[__packet->size]; - ChannelMessage_Struct *emu = (ChannelMessage_Struct *) __packet->pBuffer; - - strn0cpy(emu->targetname, Target, sizeof(emu->targetname)); - strn0cpy(emu->sender, Target, sizeof(emu->sender)); - emu->language = Language; - emu->chan_num = Channel; - emu->skill_in_language = Skill; - strcpy(emu->message, InBuffer); - - delete [] __eq_buffer; -} - -DECODE(OP_BuffRemoveRequest) -{ - // This is to cater for the fact that short buff box buffs start at 30 as opposed to 25 in prior clients. - // - DECODE_LENGTH_EXACT(structs::BuffRemoveRequest_Struct); - SETUP_DIRECT_DECODE(BuffRemoveRequest_Struct, structs::BuffRemoveRequest_Struct); - - emu->SlotID = (eq->SlotID < 30 ) ? eq->SlotID : (eq->SlotID - 5); - - IN(EntityID); - - FINISH_DIRECT_DECODE(); -} - -uint32 NextItemInstSerialNumber = 1; -uint32 MaxInstances = 2000000000; - -static inline int32 GetNextItemInstSerialNumber() { - - if(NextItemInstSerialNumber >= MaxInstances) - NextItemInstSerialNumber = 1; - else - NextItemInstSerialNumber++; - - return NextItemInstSerialNumber; -} - - -char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { - uint8 null_term = 0; - bool stackable = inst->IsStackable(); - uint32 merchant_slot = inst->GetMerchantSlot(); - uint32 charges = inst->GetCharges(); - if (!stackable && charges > 254) - charges = 0xFFFFFFFF; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - const Item_Struct *item = inst->GetItem(); - //_log(NET__ERROR, "Serialize called for: %s", item->Name); - Underfoot::structs::ItemSerializationHeader hdr; - hdr.stacksize = stackable ? charges : 1; - hdr.unknown004 = 0; - - int32 slot_id = ServerToUnderfootSlot(slot_id_in); - - hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; - hdr.price = inst->GetPrice(); - hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); - hdr.unknown020 = 0; - hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; - hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); - hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); - hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; - hdr.unknown044 = 0; - hdr.unknown048 = 0; - hdr.unknown052 = 0; - hdr.unknown056 = 0; - hdr.unknown060 = 0; - hdr.unknown061 = 0; - hdr.unknown062 = 0; - hdr.ItemClass = item->ItemClass; - - ss.write((const char*)&hdr, sizeof(Underfoot::structs::ItemSerializationHeader)); - - if(strlen(item->Name) > 0) - { - ss.write(item->Name, strlen(item->Name)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + FINISH_DIRECT_DECODE(); } - if(strlen(item->Lore) > 0) + DECODE(OP_RaidInvite) { - ss.write(item->Lore, strlen(item->Lore)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + + FINISH_DIRECT_DECODE(); } - if(strlen(item->IDFile) > 0) + DECODE(OP_ReadBook) { - ss.write(item->IDFile, strlen(item->IDFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + DECODE_LENGTH_EXACT(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(type); + emu->invslot = UnderfootToServerSlot(eq->invslot); + emu->window = (uint8)eq->window; + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); } - Underfoot::structs::ItemBodyStruct ibs; - memset(&ibs, 0, sizeof(Underfoot::structs::ItemBodyStruct)); - - uint32 adjusted_slots = item->Slots; - - // Conversions for Ammo and Power Source Slots - if(item->Slots & (1 << 21) & (1 << 22)) + DECODE(OP_Save) { - // Do nothing + DECODE_LENGTH_EXACT(structs::Save_Struct); + SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); + + memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); + + FINISH_DIRECT_DECODE(); } - else + + DECODE(OP_SetServerFilter) { - if(item->Slots & (1 << 21)) // Ammo Slot from Database - { - adjusted_slots -= (1 << 21); // Ammo Slot in Titanium - adjusted_slots += (1 << 22); // Ammo Slot in SoF + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + IN(filters[r]); } - if(item->Slots & (1 << 22)) // Power Source Slot from Database + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerBuy) + { + DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); + SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + IN(npcid); + IN(playerid); + IN(itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = UnderfootToServerSlot(eq->itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TraderBuy) + { + DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); + SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); + MEMSET_IN(TraderBuy_Struct); + + IN(Action); + IN(Price); + IN(TraderID); + memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); + IN(ItemID); + IN(Quantity); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = UnderfootToServerSlot(eq->container_slot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = UnderfootToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + IN(material); + IN(unknown06); + IN(elite_material); + IN(color.color); + IN(wear_slot_id); + emu->hero_forge_model = 0; + emu->unknown18 = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + IN(guildid); + IN(type); + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + uint32 NextItemInstSerialNumber = 1; + uint32 MaxInstances = 2000000000; + + static inline int32 GetNextItemInstSerialNumber() + { + if (NextItemInstSerialNumber >= MaxInstances) + NextItemInstSerialNumber = 1; + else + NextItemInstSerialNumber++; + + return NextItemInstSerialNumber; + } + + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) + { + uint8 null_term = 0; + bool stackable = inst->IsStackable(); + uint32 merchant_slot = inst->GetMerchantSlot(); + uint32 charges = inst->GetCharges(); + if (!stackable && charges > 254) + charges = 0xFFFFFFFF; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + const Item_Struct *item = inst->GetItem(); + //_log(NET__ERROR, "Serialize called for: %s", item->Name); + Underfoot::structs::ItemSerializationHeader hdr; + hdr.stacksize = stackable ? charges : 1; + hdr.unknown004 = 0; + + int32 slot_id = ServerToUnderfootSlot(slot_id_in); + + hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; + hdr.price = inst->GetPrice(); + hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); + hdr.unknown020 = 0; + hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; + hdr.unknown028 = 0; + hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); + hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; + hdr.unknown044 = 0; + hdr.unknown048 = 0; + hdr.unknown052 = 0; + hdr.unknown056 = 0; + hdr.unknown060 = 0; + hdr.unknown061 = 0; + hdr.unknown062 = 0; + hdr.ItemClass = item->ItemClass; + + ss.write((const char*)&hdr, sizeof(Underfoot::structs::ItemSerializationHeader)); + + if (strlen(item->Name) > 0) { - adjusted_slots -= (1 << 22); // Non Existant Worn Slot in Titanium - adjusted_slots += (1 << 21); // Power Source Slot in SoF + ss.write(item->Name, strlen(item->Name)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); } - } - ibs.id = item->ID; - ibs.weight = item->Weight; - ibs.norent = item->NoRent; - ibs.nodrop = item->NoDrop; - ibs.attune = item->Attuneable; - ibs.size = item->Size; - ibs.slots = adjusted_slots; - ibs.price = item->Price; - ibs.icon = item->Icon; - ibs.unknown1 = 1; - ibs.unknown2 = 1; - ibs.BenefitFlag = item->BenefitFlag; - ibs.tradeskills = item->Tradeskills; - ibs.CR = item->CR; - ibs.DR = item->DR; - ibs.PR = item->PR; - ibs.MR = item->MR; - ibs.FR = item->FR; - ibs.SVCorruption = item->SVCorruption; - ibs.AStr = item->AStr; - ibs.ASta = item->ASta; - ibs.AAgi = item->AAgi; - ibs.ADex = item->ADex; - ibs.ACha = item->ACha; - ibs.AInt = item->AInt; - ibs.AWis = item->AWis; + if (strlen(item->Lore) > 0) + { + ss.write(item->Lore, strlen(item->Lore)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ibs.HP = item->HP; - ibs.Mana = item->Mana; - ibs.Endur = item->Endur; - ibs.AC = item->AC; - ibs.regen = item->Regen; - ibs.mana_regen = item->ManaRegen; - ibs.end_regen = item->EnduranceRegen; - ibs.Classes = item->Classes; - ibs.Races = item->Races; - ibs.Deity = item->Deity; - ibs.SkillModValue = item->SkillModValue; - ibs.unknown5 = 0; - ibs.SkillModType = item->SkillModType; - ibs.BaneDmgRace = item->BaneDmgRace; - ibs.BaneDmgBody = item->BaneDmgBody; - ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; - ibs.BaneDmgAmt = item->BaneDmgAmt; - ibs.Magic = item->Magic; - ibs.CastTime_ = item->CastTime_; - ibs.ReqLevel = item->ReqLevel; - ibs.RecLevel = item->RecLevel; - ibs.RecSkill = item->RecSkill; - ibs.BardType = item->BardType; - ibs.BardValue = item->BardValue; - ibs.Light = item->Light; - ibs.Delay = item->Delay; - ibs.ElemDmgType = item->ElemDmgType; - ibs.ElemDmgAmt = item->ElemDmgAmt; - ibs.Range = item->Range; - ibs.Damage = item->Damage; - ibs.Color = item->Color; - ibs.ItemType = item->ItemType; - ibs.Material = item->Material; - ibs.unknown7 = 0; - ibs.EliteMaterial = item->EliteMaterial; - ibs.SellRate = item->SellRate; + if (strlen(item->IDFile) > 0) + { + ss.write(item->IDFile, strlen(item->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ibs.CombatEffects = item->CombatEffects; - ibs.Shielding = item->Shielding; - ibs.StunResist = item->StunResist; - ibs.StrikeThrough = item->StrikeThrough; - ibs.ExtraDmgSkill = item->ExtraDmgSkill; - ibs.ExtraDmgAmt = item->ExtraDmgAmt; - ibs.SpellShield = item->SpellShield; - ibs.Avoidance = item->Avoidance; - ibs.Accuracy = item->Accuracy; - ibs.FactionAmt1 = item->FactionAmt1; - ibs.FactionMod1 = item->FactionMod1; - ibs.FactionAmt2 = item->FactionAmt2; - ibs.FactionMod2 = item->FactionMod2; - ibs.FactionAmt3 = item->FactionAmt3; - ibs.FactionMod3 = item->FactionMod3; - ibs.FactionAmt4 = item->FactionAmt4; - ibs.FactionMod4 = item->FactionMod4; + Underfoot::structs::ItemBodyStruct ibs; + memset(&ibs, 0, sizeof(Underfoot::structs::ItemBodyStruct)); - ss.write((const char*)&ibs, sizeof(Underfoot::structs::ItemBodyStruct)); + ibs.id = item->ID; + ibs.weight = item->Weight; + ibs.norent = item->NoRent; + ibs.nodrop = item->NoDrop; + ibs.attune = item->Attuneable; + ibs.size = item->Size; + ibs.slots = SwapBits21and22(item->Slots); + ibs.price = item->Price; + ibs.icon = item->Icon; + ibs.unknown1 = 1; + ibs.unknown2 = 1; + ibs.BenefitFlag = item->BenefitFlag; + ibs.tradeskills = item->Tradeskills; + ibs.CR = item->CR; + ibs.DR = item->DR; + ibs.PR = item->PR; + ibs.MR = item->MR; + ibs.FR = item->FR; + ibs.SVCorruption = item->SVCorruption; + ibs.AStr = item->AStr; + ibs.ASta = item->ASta; + ibs.AAgi = item->AAgi; + ibs.ADex = item->ADex; + ibs.ACha = item->ACha; + ibs.AInt = item->AInt; + ibs.AWis = item->AWis; - //charm text - if(strlen(item->CharmFile) > 0) - { - ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + ibs.HP = item->HP; + ibs.Mana = item->Mana; + ibs.Endur = item->Endur; + ibs.AC = item->AC; + ibs.regen = item->Regen; + ibs.mana_regen = item->ManaRegen; + ibs.end_regen = item->EnduranceRegen; + ibs.Classes = item->Classes; + ibs.Races = item->Races; + ibs.Deity = item->Deity; + ibs.SkillModValue = item->SkillModValue; + ibs.unknown5 = 0; + ibs.SkillModType = item->SkillModType; + ibs.BaneDmgRace = item->BaneDmgRace; + ibs.BaneDmgBody = item->BaneDmgBody; + ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; + ibs.BaneDmgAmt = item->BaneDmgAmt; + ibs.Magic = item->Magic; + ibs.CastTime_ = item->CastTime_; + ibs.ReqLevel = item->ReqLevel; + ibs.RecLevel = item->RecLevel; + ibs.RecSkill = item->RecSkill; + ibs.BardType = item->BardType; + ibs.BardValue = item->BardValue; + ibs.Light = item->Light; + ibs.Delay = item->Delay; + ibs.ElemDmgType = item->ElemDmgType; + ibs.ElemDmgAmt = item->ElemDmgAmt; + ibs.Range = item->Range; + ibs.Damage = item->Damage; + ibs.Color = item->Color; + ibs.ItemType = item->ItemType; + ibs.Material = item->Material; + ibs.unknown7 = 0; + ibs.EliteMaterial = item->EliteMaterial; + ibs.SellRate = item->SellRate; - Underfoot::structs::ItemSecondaryBodyStruct isbs; - memset(&isbs, 0, sizeof(Underfoot::structs::ItemSecondaryBodyStruct)); + ibs.CombatEffects = item->CombatEffects; + ibs.Shielding = item->Shielding; + ibs.StunResist = item->StunResist; + ibs.StrikeThrough = item->StrikeThrough; + ibs.ExtraDmgSkill = item->ExtraDmgSkill; + ibs.ExtraDmgAmt = item->ExtraDmgAmt; + ibs.SpellShield = item->SpellShield; + ibs.Avoidance = item->Avoidance; + ibs.Accuracy = item->Accuracy; + ibs.FactionAmt1 = item->FactionAmt1; + ibs.FactionMod1 = item->FactionMod1; + ibs.FactionAmt2 = item->FactionAmt2; + ibs.FactionMod2 = item->FactionMod2; + ibs.FactionAmt3 = item->FactionAmt3; + ibs.FactionMod3 = item->FactionMod3; + ibs.FactionAmt4 = item->FactionAmt4; + ibs.FactionMod4 = item->FactionMod4; - isbs.augtype = item->AugType; - isbs.augrestrict = item->AugRestrict; + ss.write((const char*)&ibs, sizeof(Underfoot::structs::ItemBodyStruct)); - for(int x = 0; x < 5; ++x) - { - isbs.augslots[x].type = item->AugSlotType[x]; - isbs.augslots[x].visible = item->AugSlotVisible[x]; - isbs.augslots[x].unknown = item->AugSlotUnk2[x]; - } + //charm text + if (strlen(item->CharmFile) > 0) + { + ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - isbs.ldonpoint_type = item->PointType; - isbs.ldontheme = item->LDoNTheme; - isbs.ldonprice = item->LDoNPrice; - isbs.ldonsellbackrate = item->LDoNSellBackRate; - isbs.ldonsold = item->LDoNSold; + Underfoot::structs::ItemSecondaryBodyStruct isbs; + memset(&isbs, 0, sizeof(Underfoot::structs::ItemSecondaryBodyStruct)); - isbs.bagtype = item->BagType; - isbs.bagslots = item->BagSlots; - isbs.bagsize = item->BagSize; - isbs.wreduction = item->BagWR; + isbs.augtype = item->AugType; + isbs.augrestrict = item->AugRestrict; - isbs.book = item->Book; - isbs.booktype = item->BookType; + for (int x = 0; x < 5; ++x) + { + isbs.augslots[x].type = item->AugSlotType[x]; + isbs.augslots[x].visible = item->AugSlotVisible[x]; + isbs.augslots[x].unknown = item->AugSlotUnk2[x]; + } - ss.write((const char*)&isbs, sizeof(Underfoot::structs::ItemSecondaryBodyStruct)); + isbs.ldonpoint_type = item->PointType; + isbs.ldontheme = item->LDoNTheme; + isbs.ldonprice = item->LDoNPrice; + isbs.ldonsellbackrate = item->LDoNSellBackRate; + isbs.ldonsold = item->LDoNSold; - if(strlen(item->Filename) > 0) - { - ss.write((const char*)item->Filename, strlen(item->Filename)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + isbs.bagtype = item->BagType; + isbs.bagslots = item->BagSlots; + isbs.bagsize = item->BagSize; + isbs.wreduction = item->BagWR; - Underfoot::structs::ItemTertiaryBodyStruct itbs; - memset(&itbs, 0, sizeof(Underfoot::structs::ItemTertiaryBodyStruct)); + isbs.book = item->Book; + isbs.booktype = item->BookType; - itbs.loregroup = item->LoreGroup; - itbs.artifact = item->ArtifactFlag; - itbs.summonedflag = item->SummonedFlag; - itbs.favor = item->Favor; - itbs.fvnodrop = item->FVNoDrop; - itbs.dotshield = item->DotShielding; - itbs.atk = item->Attack; - itbs.haste = item->Haste; - itbs.damage_shield = item->DamageShield; - itbs.guildfavor = item->GuildFavor; - itbs.augdistil = item->AugDistiller; - itbs.no_pet = item->NoPet; + ss.write((const char*)&isbs, sizeof(Underfoot::structs::ItemSecondaryBodyStruct)); - itbs.potion_belt_enabled = item->PotionBelt; - itbs.potion_belt_slots = item->PotionBeltSlots; - itbs.stacksize = stackable ? item->StackSize : 0; - itbs.no_transfer = item->NoTransfer; - itbs.expendablearrow = item->ExpendableArrow; + if (strlen(item->Filename) > 0) + { + ss.write((const char*)item->Filename, strlen(item->Filename)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ss.write((const char*)&itbs, sizeof(Underfoot::structs::ItemTertiaryBodyStruct)); + Underfoot::structs::ItemTertiaryBodyStruct itbs; + memset(&itbs, 0, sizeof(Underfoot::structs::ItemTertiaryBodyStruct)); - // Effect Structures Broken down to allow variable length strings for effect names - int32 effect_unknown = 0; + itbs.loregroup = item->LoreGroup; + itbs.artifact = item->ArtifactFlag; + itbs.summonedflag = item->SummonedFlag; + itbs.favor = item->Favor; + itbs.fvnodrop = item->FVNoDrop; + itbs.dotshield = item->DotShielding; + itbs.atk = item->Attack; + itbs.haste = item->Haste; + itbs.damage_shield = item->DamageShield; + itbs.guildfavor = item->GuildFavor; + itbs.augdistil = item->AugDistiller; + itbs.no_pet = item->NoPet; - Underfoot::structs::ClickEffectStruct ices; - memset(&ices, 0, sizeof(Underfoot::structs::ClickEffectStruct)); + itbs.potion_belt_enabled = item->PotionBelt; + itbs.potion_belt_slots = item->PotionBeltSlots; + itbs.stacksize = stackable ? item->StackSize : 0; + itbs.no_transfer = item->NoTransfer; + itbs.expendablearrow = item->ExpendableArrow; - ices.effect = item->Click.Effect; - ices.level2 = item->Click.Level2; - ices.type = item->Click.Type; - ices.level = item->Click.Level; - ices.max_charges = item->MaxCharges; - ices.cast_time = item->CastTime; - ices.recast = item->RecastDelay; - ices.recast_type = item->RecastType; + ss.write((const char*)&itbs, sizeof(Underfoot::structs::ItemTertiaryBodyStruct)); - ss.write((const char*)&ices, sizeof(Underfoot::structs::ClickEffectStruct)); + // Effect Structures Broken down to allow variable length strings for effect names + int32 effect_unknown = 0; - if(strlen(item->ClickName) > 0) - { - ss.write((const char*)item->ClickName, strlen(item->ClickName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + Underfoot::structs::ClickEffectStruct ices; + memset(&ices, 0, sizeof(Underfoot::structs::ClickEffectStruct)); - ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 + ices.effect = item->Click.Effect; + ices.level2 = item->Click.Level2; + ices.type = item->Click.Type; + ices.level = item->Click.Level; + ices.max_charges = item->MaxCharges; + ices.cast_time = item->CastTime; + ices.recast = item->RecastDelay; + ices.recast_type = item->RecastType; - Underfoot::structs::ProcEffectStruct ipes; - memset(&ipes, 0, sizeof(Underfoot::structs::ProcEffectStruct)); + ss.write((const char*)&ices, sizeof(Underfoot::structs::ClickEffectStruct)); - ipes.effect = item->Proc.Effect; - ipes.level2 = item->Proc.Level2; - ipes.type = item->Proc.Type; - ipes.level = item->Proc.Level; - ipes.procrate = item->ProcRate; + if (strlen(item->ClickName) > 0) + { + ss.write((const char*)item->ClickName, strlen(item->ClickName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ss.write((const char*)&ipes, sizeof(Underfoot::structs::ProcEffectStruct)); + ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 - if(strlen(item->ProcName) > 0) - { - ss.write((const char*)item->ProcName, strlen(item->ProcName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + Underfoot::structs::ProcEffectStruct ipes; + memset(&ipes, 0, sizeof(Underfoot::structs::ProcEffectStruct)); - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 + ipes.effect = item->Proc.Effect; + ipes.level2 = item->Proc.Level2; + ipes.type = item->Proc.Type; + ipes.level = item->Proc.Level; + ipes.procrate = item->ProcRate; - Underfoot::structs::WornEffectStruct iwes; - memset(&iwes, 0, sizeof(Underfoot::structs::WornEffectStruct)); + ss.write((const char*)&ipes, sizeof(Underfoot::structs::ProcEffectStruct)); - iwes.effect = item->Worn.Effect; - iwes.level2 = item->Worn.Level2; - iwes.type = item->Worn.Type; - iwes.level = item->Worn.Level; + if (strlen(item->ProcName) > 0) + { + ss.write((const char*)item->ProcName, strlen(item->ProcName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ss.write((const char*)&iwes, sizeof(Underfoot::structs::WornEffectStruct)); + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 - if(strlen(item->WornName) > 0) - { - ss.write((const char*)item->WornName, strlen(item->WornName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + Underfoot::structs::WornEffectStruct iwes; + memset(&iwes, 0, sizeof(Underfoot::structs::WornEffectStruct)); - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + iwes.effect = item->Worn.Effect; + iwes.level2 = item->Worn.Level2; + iwes.type = item->Worn.Type; + iwes.level = item->Worn.Level; - Underfoot::structs::WornEffectStruct ifes; - memset(&ifes, 0, sizeof(Underfoot::structs::WornEffectStruct)); + ss.write((const char*)&iwes, sizeof(Underfoot::structs::WornEffectStruct)); - ifes.effect = item->Focus.Effect; - ifes.level2 = item->Focus.Level2; - ifes.type = item->Focus.Type; - ifes.level = item->Focus.Level; + if (strlen(item->WornName) > 0) + { + ss.write((const char*)item->WornName, strlen(item->WornName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ss.write((const char*)&ifes, sizeof(Underfoot::structs::WornEffectStruct)); + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - if(strlen(item->FocusName) > 0) - { - ss.write((const char*)item->FocusName, strlen(item->FocusName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + Underfoot::structs::WornEffectStruct ifes; + memset(&ifes, 0, sizeof(Underfoot::structs::WornEffectStruct)); - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + ifes.effect = item->Focus.Effect; + ifes.level2 = item->Focus.Level2; + ifes.type = item->Focus.Type; + ifes.level = item->Focus.Level; - Underfoot::structs::WornEffectStruct ises; - memset(&ises, 0, sizeof(Underfoot::structs::WornEffectStruct)); + ss.write((const char*)&ifes, sizeof(Underfoot::structs::WornEffectStruct)); - ises.effect = item->Scroll.Effect; - ises.level2 = item->Scroll.Level2; - ises.type = item->Scroll.Type; - ises.level = item->Scroll.Level; + if (strlen(item->FocusName) > 0) + { + ss.write((const char*)item->FocusName, strlen(item->FocusName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ss.write((const char*)&ises, sizeof(Underfoot::structs::WornEffectStruct)); + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - if(strlen(item->ScrollName) > 0) - { - ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + Underfoot::structs::WornEffectStruct ises; + memset(&ises, 0, sizeof(Underfoot::structs::WornEffectStruct)); - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + ises.effect = item->Scroll.Effect; + ises.level2 = item->Scroll.Level2; + ises.type = item->Scroll.Type; + ises.level = item->Scroll.Level; - // Bard Effect? - Underfoot::structs::WornEffectStruct ibes; - memset(&ibes, 0, sizeof(Underfoot::structs::WornEffectStruct)); + ss.write((const char*)&ises, sizeof(Underfoot::structs::WornEffectStruct)); - ibes.effect = item->Bard.Effect; - ibes.level2 = item->Bard.Level2; - ibes.type = item->Bard.Type; - ibes.level = item->Bard.Level; - //ibes.unknown6 = 0xffffffff; + if (strlen(item->ScrollName) > 0) + { + ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ss.write((const char*)&ibes, sizeof(Underfoot::structs::WornEffectStruct)); + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - /* - if(strlen(item->BardName) > 0) - { + // Bard Effect? + Underfoot::structs::WornEffectStruct ibes; + memset(&ibes, 0, sizeof(Underfoot::structs::WornEffectStruct)); + + ibes.effect = item->Bard.Effect; + ibes.level2 = item->Bard.Level2; + ibes.type = item->Bard.Type; + ibes.level = item->Bard.Level; + //ibes.unknown6 = 0xffffffff; + + ss.write((const char*)&ibes, sizeof(Underfoot::structs::WornEffectStruct)); + + /* + if(strlen(item->BardName) > 0) + { ss.write((const char*)item->BardName, strlen(item->BardName)); ss.write((const char*)&null_term, sizeof(uint8)); - } - else */ + } + else */ ss.write((const char*)&null_term, sizeof(uint8)); - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - // End of Effects + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + // End of Effects - Underfoot::structs::ItemQuaternaryBodyStruct iqbs; - memset(&iqbs, 0, sizeof(Underfoot::structs::ItemQuaternaryBodyStruct)); + Underfoot::structs::ItemQuaternaryBodyStruct iqbs; + memset(&iqbs, 0, sizeof(Underfoot::structs::ItemQuaternaryBodyStruct)); - iqbs.scriptfileid = item->ScriptFileID; - iqbs.quest_item = item->QuestItemFlag; - iqbs.unknown15 = 0; + iqbs.scriptfileid = item->ScriptFileID; + iqbs.quest_item = item->QuestItemFlag; + iqbs.unknown15 = 0; - iqbs.Purity = item->Purity; - iqbs.BackstabDmg = item->BackstabDmg; - iqbs.DSMitigation = item->DSMitigation; - iqbs.HeroicStr = item->HeroicStr; - iqbs.HeroicInt = item->HeroicInt; - iqbs.HeroicWis = item->HeroicWis; - iqbs.HeroicAgi = item->HeroicAgi; - iqbs.HeroicDex = item->HeroicDex; - iqbs.HeroicSta = item->HeroicSta; - iqbs.HeroicCha = item->HeroicCha; - iqbs.HeroicMR = item->HeroicMR; - iqbs.HeroicFR = item->HeroicFR; - iqbs.HeroicCR = item->HeroicCR; - iqbs.HeroicDR = item->HeroicDR; - iqbs.HeroicPR = item->HeroicPR; - iqbs.HeroicSVCorrup = item->HeroicSVCorrup; - iqbs.HealAmt = item->HealAmt; - iqbs.SpellDmg = item->SpellDmg; - iqbs.clairvoyance = item->Clairvoyance; + iqbs.Purity = item->Purity; + iqbs.BackstabDmg = item->BackstabDmg; + iqbs.DSMitigation = item->DSMitigation; + iqbs.HeroicStr = item->HeroicStr; + iqbs.HeroicInt = item->HeroicInt; + iqbs.HeroicWis = item->HeroicWis; + iqbs.HeroicAgi = item->HeroicAgi; + iqbs.HeroicDex = item->HeroicDex; + iqbs.HeroicSta = item->HeroicSta; + iqbs.HeroicCha = item->HeroicCha; + iqbs.HeroicMR = item->HeroicMR; + iqbs.HeroicFR = item->HeroicFR; + iqbs.HeroicCR = item->HeroicCR; + iqbs.HeroicDR = item->HeroicDR; + iqbs.HeroicPR = item->HeroicPR; + iqbs.HeroicSVCorrup = item->HeroicSVCorrup; + iqbs.HealAmt = item->HealAmt; + iqbs.SpellDmg = item->SpellDmg; + iqbs.clairvoyance = item->Clairvoyance; - iqbs.subitem_count = 0; + iqbs.subitem_count = 0; - char *SubSerializations[10]; // + char *SubSerializations[10]; // - uint32 SubLengths[10]; + uint32 SubLengths[10]; - for(int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { - SubSerializations[x] = nullptr; + SubSerializations[x] = nullptr; - const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); - if(subitem) { + if (subitem) { - int SubSlotNumber; + int SubSlotNumber; - iqbs.subitem_count++; + iqbs.subitem_count++; - if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? - //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); - SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); - else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) - //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); - else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) - //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); - else - SubSlotNumber = slot_id_in; // ??????? + if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? + //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); + else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) + //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); + else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) + //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); + else + SubSlotNumber = slot_id_in; // ??????? - /* - // TEST CODE: - SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); - */ + /* + // TEST CODE: + SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); + */ - SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + } } + + ss.write((const char*)&iqbs, sizeof(Underfoot::structs::ItemQuaternaryBodyStruct)); + + for (int x = 0; x < 10; ++x) { + + if (SubSerializations[x]) { + + ss.write((const char*)&x, sizeof(uint32)); + + ss.write(SubSerializations[x], SubLengths[x]); + + safe_delete_array(SubSerializations[x]); + } + } + + char* item_serial = new char[ss.tellp()]; + memset(item_serial, 0, ss.tellp()); + memcpy(item_serial, ss.str().c_str(), ss.tellp()); + + *length = ss.tellp(); + return item_serial; } - ss.write((const char*)&iqbs, sizeof(Underfoot::structs::ItemQuaternaryBodyStruct)); + static inline uint32 ServerToUnderfootSlot(uint32 ServerSlot) + { + uint32 UnderfootSlot = 0; - for(int x = 0; x < 10; ++x) { + if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots + UnderfootSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) + UnderfootSlot = ServerSlot + 11; + else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) + UnderfootSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) + UnderfootSlot = ServerSlot + 1; + else if (ServerSlot == MainPowerSource) + UnderfootSlot = slots::MainPowerSource; + else + UnderfootSlot = ServerSlot; - if(SubSerializations[x]) { - - ss.write((const char*)&x, sizeof(uint32)); - - ss.write(SubSerializations[x], SubLengths[x]); - - safe_delete_array(SubSerializations[x]); - } + return UnderfootSlot; } - char* item_serial = new char[ss.tellp()]; - memset(item_serial, 0, ss.tellp()); - memcpy(item_serial, ss.str().c_str(), ss.tellp()); + static inline uint32 ServerToUnderFootCorpseSlot(uint32 ServerCorpse) + { + //uint32 UnderfootCorpse; + } - *length = ss.tellp(); - return item_serial; + static inline uint32 UnderfootToServerSlot(uint32 UnderfootSlot) + { + uint32 ServerSlot = 0; + + if (UnderfootSlot >= slots::MainAmmo && UnderfootSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots + ServerSlot = UnderfootSlot - 1; + else if (UnderfootSlot >= consts::GENERAL_BAGS_BEGIN && UnderfootSlot <= consts::CURSOR_BAG_END) + ServerSlot = UnderfootSlot - 11; + else if (UnderfootSlot >= consts::BANK_BAGS_BEGIN && UnderfootSlot <= consts::BANK_BAGS_END) + ServerSlot = UnderfootSlot - 1; + else if (UnderfootSlot >= consts::SHARED_BANK_BAGS_BEGIN && UnderfootSlot <= consts::SHARED_BANK_BAGS_END) + ServerSlot = UnderfootSlot - 1; + else if (UnderfootSlot == slots::MainPowerSource) + ServerSlot = MainPowerSource; + else + ServerSlot = UnderfootSlot; + + return ServerSlot; + } + + static inline uint32 UnderfootToServerCorpseSlot(uint32 UnderfootCorpse) + { + //uint32 ServerCorpse; + } } - -DECODE(OP_AltCurrencySellSelection) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); - IN(merchant_entity_id); - emu->slot_id = UnderfootToServerSlot(eq->slot_id); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySell) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - IN(merchant_entity_id); - emu->slot_id = UnderfootToServerSlot(eq->slot_id); - IN(charges); - IN(cost); - FINISH_DIRECT_DECODE(); -} - -} //end namespace Underfoot +// end namespace Underfoot From f232bf1ccd281abcf7c24fe61987fcdb987adf24 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 27 Sep 2014 09:21:37 -0400 Subject: [PATCH 170/368] Fix for commit failure. --- zone/npc.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zone/npc.h b/zone/npc.h index 228ce7cf3..f1e4c790c 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -136,10 +136,6 @@ public: int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr); int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr); - inline void SetSpellFocusDMG(int32 NewSpellFocusDMG) {SpellFocusDMG = NewSpellFocusDMG;} - inline void SetSpellFocusHeal(int32 NewSpellFocusHeal) {SpellFocusHeal = NewSpellFocusHeal;} - int32 SpellFocusDMG; - int32 SpellFocusHeal; virtual void SetTarget(Mob* mob); virtual uint16 GetSkill(SkillUseTypes skill_num) const { if (skill_num <= HIGHEST_SKILL) { return skills[skill_num]; } return 0; } From 73a23e9f9d81229a2065c8192799015f594a1311 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sat, 27 Sep 2014 13:00:07 -0400 Subject: [PATCH 171/368] Changes so that blind effects like those in Flash of Light work. --- zone/fearpath.cpp | 8 +++++++- zone/mob.cpp | 1 + zone/mob.h | 1 + zone/mob_ai.cpp | 4 ++-- zone/spell_effects.cpp | 7 +++++-- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 636422b3f..592fa2090 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -130,7 +130,7 @@ void Mob::ProcessFlee() { } float Mob::GetFearSpeed() { - if(flee_mode) { + if(flee_mode || is_blind) { //we know ratio < FLEE_HP_RATIO float speed = GetBaseRunspeed(); float ratio = GetHPRatio(); @@ -141,6 +141,12 @@ float Mob::GetFearSpeed() { speed = speed * ratio * multiplier / 100; + // A blinded mob should be pretty slow when running amuck. + if (is_blind) + { + speed = speed/3.0; + } + //NPC will eventually stop. Snares speeds this up. if(speed < 0.09) speed = 0.0001f; diff --git a/zone/mob.cpp b/zone/mob.cpp index 77ff4938a..96153fede 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -114,6 +114,7 @@ Mob::Mob(const char* in_name, fear_walkto_y = -999999; fear_walkto_z = -999999; curfp = false; + is_blind = false; AI_Init(); SetMoving(false); diff --git a/zone/mob.h b/zone/mob.h index 4e72f0264..dcf9ef24d 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1191,6 +1191,7 @@ protected: float fear_walkto_y; float fear_walkto_z; bool curfp; + bool is_blind; // Pathing // diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 125e1d85f..d1dc18d5d 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1039,7 +1039,7 @@ void Mob::AI_Process() { // Begin: Additions for Wiz Fear Code // if(RuleB(Combat, EnableFearPathing)){ - if(curfp) { + if(curfp || (is_blind && !CombatRange(hate_list.GetClosest(this)))) { if(IsRooted()) { //make sure everybody knows were not moving, for appearance sake if(IsMoving()) @@ -1087,7 +1087,7 @@ void Mob::AI_Process() { if (engaged) { - if (IsRooted()) + if (IsRooted() || is_blind) SetTarget(hate_list.GetClosest(this)); else { diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a81377f5d..5bddd780a 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1267,8 +1267,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #endif if (spells[spell_id].base[i] == 1) BuffFadeByEffect(SE_Blind); - // handled by client - // TODO: blind flag? + is_blind = true; break; } @@ -3976,6 +3975,10 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) break; } + case SE_Blind: + is_blind = false; + break; + case SE_Fear: { if(RuleB(Combat, EnableFearPathing)){ From 8567fd928abf72ef2fe3508ee7ee407a20059ceb Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 27 Sep 2014 16:12:10 -0400 Subject: [PATCH 172/368] Perl Mob function GetSpellStat(spellid, identifier, slot) indentifier is the spell_new field slot is used for effectids,base,base2, max ect where 1 = first slot. Will add a wiki page --- zone/mob.cpp | 116 ++++++++++++++++++++++++++++++++++++++++++++++ zone/mob.h | 1 + zone/perl_mob.cpp | 34 ++++++++++++++ 3 files changed, 151 insertions(+) diff --git a/zone/mob.cpp b/zone/mob.cpp index 06e125aab..8a4253c19 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4993,3 +4993,119 @@ float Mob::HeadingAngleToMob(Mob *other) return (90.0 - angle + 270.0) * 511.5 * 0.0027777778; } +int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) +{ + if (!IsValidSpell(spell_id)) + return 0; + + if (!identifier) + return 0; + + int32 stat = 0; + + if (slot > 0) + slot = slot - 1; + + std::string id = identifier; + for(int i = 0; i < id.length(); ++i) + { + id[i] = tolower(id[i]); + } + + if (slot < 16){ + if (id == "classes") {stat = spells[spell_id].classes[slot]; } + else if (id == "dieties") {stat = spells[spell_id].deities[slot];} + } + + if (slot < 12){ + if (id == "base") {stat = spells[spell_id].base[slot];} + else if (id == "base2") {stat = spells[spell_id].base2[slot];} + else if (id == "max") {stat = spells[spell_id].max[slot];} + else if (id == "formula") {spells[spell_id].formula[slot];} + else if (id == "effectid") {spells[spell_id].effectid[slot];} + } + + if (slot < 4){ + if (id == "components") { spells[spell_id].components[slot];} + else if (id == "component_counts") {spells[spell_id].component_counts[slot];} + else if (id == "NoexpendReagent") {spells[spell_id].NoexpendReagent[slot];} + } + + if (id == "range") {stat = spells[spell_id].range; } + else if (id == "aoerange") {stat = spells[spell_id].aoerange;} + else if (id == "pushback") {stat = spells[spell_id].pushback;} + else if (id == "pushup") {stat = spells[spell_id].pushup;} + else if (id == "cast_time") {stat = spells[spell_id].cast_time;} + else if (id == "recovery_time") {stat = spells[spell_id].recovery_time;} + else if (id == "recast_time") {stat = spells[spell_id].recast_time;} + else if (id == "buffdurationformula") {stat = spells[spell_id].buffdurationformula;} + else if (id == "buffduration") {stat = spells[spell_id].buffduration;} + else if (id == "AEDuration") {stat = spells[spell_id].AEDuration;} + else if (id == "mana") {stat = spells[spell_id].mana;} + //else if (id == "LightType") {stat = spells[spell_id].LightType;} - Not implemented + else if (id == "goodEffect") {stat = spells[spell_id].goodEffect;} + else if (id == "Activated") {stat = spells[spell_id].Activated;} + else if (id == "resisttype") {stat = spells[spell_id].resisttype;} + else if (id == "targettype") {stat = spells[spell_id].targettype;} + else if (id == "basedeiff") {stat = spells[spell_id].basediff;} + else if (id == "skill") {stat = spells[spell_id].skill;} + else if (id == "zonetype") {stat = spells[spell_id].zonetype;} + else if (id == "EnvironmentType") {stat = spells[spell_id].EnvironmentType;} + else if (id == "TimeOfDay") {stat = spells[spell_id].TimeOfDay;} + else if (id == "CastingAnim") {stat = spells[spell_id].CastingAnim;} + else if (id == "SpellAffectIndex") {stat = spells[spell_id].SpellAffectIndex; } + else if (id == "disallow_sit") {stat = spells[spell_id].disallow_sit; } + //else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented + else if (id == "uninterruptable") {stat = spells[spell_id].uninterruptable; } + else if (id == "ResistDiff") {stat = spells[spell_id].ResistDiff; } + else if (id == "dot_stacking_exemp") {stat = spells[spell_id].dot_stacking_exempt; } + else if (id == "RecourseLink") {stat = spells[spell_id].RecourseLink; } + else if (id == "no_partial_resist") {stat = spells[spell_id].no_partial_resist; } + else if (id == "short_buff_box") {stat = spells[spell_id].short_buff_box; } + else if (id == "descnum") {stat = spells[spell_id].descnum; } + else if (id == "effectdescnum") {stat = spells[spell_id].effectdescnum; } + else if (id == "npc_no_los") {stat = spells[spell_id].npc_no_los; } + else if (id == "reflectable") {stat = spells[spell_id].reflectable; } + else if (id == "bonushate") {stat = spells[spell_id].bonushate; } + else if (id == "EndurCost") {stat = spells[spell_id].EndurCost; } + else if (id == "EndurTimerIndex") {stat = spells[spell_id].EndurTimerIndex; } + else if (id == "IsDisciplineBuf") {stat = spells[spell_id].IsDisciplineBuff; } + else if (id == "HateAdded") {stat = spells[spell_id].HateAdded; } + else if (id == "EndurUpkeep") {stat = spells[spell_id].EndurUpkeep; } + else if (id == "numhitstype") {stat = spells[spell_id].numhitstype; } + else if (id == "numhits") {stat = spells[spell_id].numhits; } + else if (id == "pvpresistbase") {stat = spells[spell_id].pvpresistbase; } + else if (id == "pvpresistcalc") {stat = spells[spell_id].pvpresistcalc; } + else if (id == "pvpresistcap") {stat = spells[spell_id].pvpresistcap; } + else if (id == "spell_category") {stat = spells[spell_id].spell_category; } + else if (id == "can_mgb") {stat = spells[spell_id].can_mgb; } + else if (id == "dispel_flag") {stat = spells[spell_id].dispel_flag; } + else if (id == "MinResist") {stat = spells[spell_id].MinResist; } + else if (id == "MaxResist") {stat = spells[spell_id].MaxResist; } + else if (id == "viral_targets") {stat = spells[spell_id].viral_targets; } + else if (id == "viral_timer") {stat = spells[spell_id].viral_timer; } + else if (id == "NimbusEffect") {stat = spells[spell_id].NimbusEffect; } + else if (id == "directional_start") {stat = spells[spell_id].directional_start; } + else if (id == "directional_end") {stat = spells[spell_id].directional_end; } + else if (id == "not_extendable") {stat = spells[spell_id].not_extendable; } + else if (id == "suspendable") {stat = spells[spell_id].suspendable; } + else if (id == "viral_range") {stat = spells[spell_id].viral_range; } + else if (id == "spellgroup") {stat = spells[spell_id].spellgroup; } + else if (id == "rank") {stat = spells[spell_id].rank; } + else if (id == "powerful_flag") {stat = spells[spell_id].powerful_flag; } + else if (id == "CastRestriction") {stat = spells[spell_id].CastRestriction; } + else if (id == "AllowRest") {stat = spells[spell_id].AllowRest; } + else if (id == "InCombat") {stat = spells[spell_id].InCombat; } + else if (id == "OutofCombat") {stat = spells[spell_id].OutofCombat; } + else if (id == "aemaxtargets") {stat = spells[spell_id].aemaxtargets; } + else if (id == "maxtargets") {stat = spells[spell_id].maxtargets; } + else if (id == "persistdeath") {stat = spells[spell_id].persistdeath; } + else if (id == "min_dist") {stat = spells[spell_id].min_dist; } + else if (id == "min_dist_mod") {stat = spells[spell_id].min_dist_mod; } + else if (id == "max_dist") {stat = spells[spell_id].max_dist; } + else if (id == "min_range") {stat = spells[spell_id].min_range; } + else if (id == "DamageShieldType") {stat = spells[spell_id].DamageShieldType; } + + return stat; +} + diff --git a/zone/mob.h b/zone/mob.h index cf6a032e5..2617e98e2 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -620,6 +620,7 @@ public: void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr); inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; }; inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; + int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0); void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 53a5aade8..762246519 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -8137,6 +8137,39 @@ XS(XS_Mob_GetFlurryChance) XSRETURN(1); } +XS(XS_Mob_GetSpellStat); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSpellStat) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::GetSpellStat(THIS, itemid, stat, slot)"); + { + Mob * THIS; + int32 RETVAL; + uint32 spellid = (uint32)SvUV(ST(1)); + Const_char * stat = (Const_char *)SvPV_nolen(ST(2)); + uint8 slot = (uint8)SvUV(ST(3)); + dXSTARG; + + if (sv_derived_from(ST(0), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Mob"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + if (items > 4) { slot = 0; } + + + RETVAL = THIS->GetSpellStat(spellid, stat, slot); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + + #ifdef __cplusplus extern "C" #endif @@ -8436,6 +8469,7 @@ XS(boot_Mob) newXSproto(strcpy(buf, "IsMeleeDisabled"), XS_Mob_IsMeleeDisabled, file, "$$"); newXSproto(strcpy(buf, "SetFlurryChance"), XS_Mob_SetFlurryChance, file, "$$"); newXSproto(strcpy(buf, "GetFlurryChance"), XS_Mob_GetFlurryChance, file, "$"); + newXSproto(strcpy(buf, "GetSpellStat"), XS_Mob_GetSpellStat, file, "$$$$"); XSRETURN_YES; } From 7831162235916fd9b76119ca3376375d2303c77d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 27 Sep 2014 16:39:37 -0400 Subject: [PATCH 173/368] changelog --- changelog.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/changelog.txt b/changelog.txt index 156244941..c0706ac74 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/27/2014 == +Kayen: Implemented perl function $mob->GetSpellStat(spell_id, identifier, slot); +Note: identifier is the stat field in spells_new, slot is used for certain effects like effectid, base,base2, max ect. +Example $mob->GetSpellStat(121, "range"); //Returns spell range +Example $mob->GetSpellStat(121, "effectid", 1); //Returns the the value of effectid1 +This will allow you to pull almost all the data for any spell in quest files. + == 09/24/2014 == Uleat: Re-ordered server opcodes and handlers to give them some predictability of location (I need this for the inventory re-enumeration.) demonstar55: Added helper function bool EQEmu::IsTradeskill(uint32 skill) From 05ed577e23f6109e81e5c3ae98768609dc30fad7 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sat, 27 Sep 2014 17:17:12 -0400 Subject: [PATCH 174/368] Fixed issue where blind flag was set for beneficial spells like cure.wq --- zone/spell_effects.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 5bddd780a..24b083414 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1267,7 +1267,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #endif if (spells[spell_id].base[i] == 1) BuffFadeByEffect(SE_Blind); - is_blind = true; + + // For spells like flash-of-light + if (IsDetrimentalSpell(spell_id)) + is_blind = true; break; } From d2acd0505cf218f74dbac0f7b55b89d31cfb542d Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sat, 27 Sep 2014 17:39:30 -0400 Subject: [PATCH 175/368] is_blind works better as an else to the line that handles cure --- zone/spell_effects.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 24b083414..79c5121db 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1267,9 +1267,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #endif if (spells[spell_id].base[i] == 1) BuffFadeByEffect(SE_Blind); - - // For spells like flash-of-light - if (IsDetrimentalSpell(spell_id)) + else is_blind = true; break; } From 9f6409260607f846f928d9c9f8982ee067ad4f43 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 27 Sep 2014 14:44:48 -0700 Subject: [PATCH 176/368] Merge of kayen's PR as well as some fixes i saw --- zone/lua_npc.cpp | 14 ++++++++++- zone/lua_npc.h | 2 ++ zone/lua_spell.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++ zone/lua_spell.h | 10 ++++++++ zone/perl_npc.cpp | 4 ++-- 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index dabc1c46f..869efb019 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -407,8 +407,18 @@ void Lua_NPC::SetSpellFocusHeal(int focus) { self->SetSpellFocusHeal(focus); } -float Lua_NPC::GetSlowMitigation() { +int Lua_NPC::GetSpellFocusDMG() { Lua_Safe_Call_Int(); + return self->GetSpellFocusDMG(); +} + +int Lua_NPC::GetSpellFocusHeal() { + Lua_Safe_Call_Int(); + return self->GetSpellFocusHeal(); +} + +float Lua_NPC::GetSlowMitigation() { + Lua_Safe_Call_Real(); return self->GetSlowMitigation(); } @@ -535,6 +545,8 @@ luabind::scope lua_register_npc() { .def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell) .def("SetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusDMG) .def("SetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusHeal) + .def("GetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::GetSpellFocusDMG) + .def("GetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::GetSpellFocusHeal) .def("GetSlowMitigation", (int(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation) .def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed) .def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating) diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 7db7f7e4b..2b201f323 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -107,6 +107,8 @@ public: void RemoveAISpell(int spell_id); void SetSpellFocusDMG(int focus); void SetSpellFocusHeal(int focus); + int GetSpellFocusDMG(); + int GetSpellFocusHeal(); float GetSlowMitigation(); float GetAttackSpeed(); int GetAccuracyRating(); diff --git a/zone/lua_spell.cpp b/zone/lua_spell.cpp index 597982770..e99b9ba84 100644 --- a/zone/lua_spell.cpp +++ b/zone/lua_spell.cpp @@ -419,6 +419,56 @@ bool Lua_Spell::GetAllowRest() { return self->AllowRest; } +bool Lua_Spell::GetInCombat() { + Lua_Safe_Call_Bool(); + return self->InCombat; +} + +bool Lua_Spell::GetOutOfCombat() { + Lua_Safe_Call_Bool(); + return self->OutofCombat; +} + +int Lua_Spell::GetAEMaxTargets() { + Lua_Safe_Call_Int(); + return self->aemaxtargets; +} + +int Lua_Spell::GetMaxTargets() { + Lua_Safe_Call_Int(); + return self->maxtargets; +} + +bool Lua_Spell::GetPersistDeath() { + Lua_Safe_Call_Bool(); + return self->persistdeath; +} + +float Lua_Spell::GetMinDist() { + Lua_Safe_Call_Real(); + return self->min_dist; +} + +float Lua_Spell::GetMinDistMod() { + Lua_Safe_Call_Real(); + return self->min_dist_mod; +} + +float Lua_Spell::GetMaxDist() { + Lua_Safe_Call_Real(); + return self->max_dist; +} + +float Lua_Spell::GetMaxDistMod() { + Lua_Safe_Call_Real(); + return self->max_dist_mod; +} + +float Lua_Spell::GetMinRange() { + Lua_Safe_Call_Real(); + return self->min_range; +} + int Lua_Spell::GetDamageShieldType() { Lua_Safe_Call_Int(); return self->DamageShieldType; @@ -501,6 +551,16 @@ luabind::scope lua_register_spell() { .def("PowerfulFlag", &Lua_Spell::GetPowerfulFlag) .def("CastRestriction", &Lua_Spell::GetCastRestriction) .def("AllowRest", &Lua_Spell::GetAllowRest) + .def("InCombat", &Lua_Spell::GetInCombat) + .def("OutOfCombat", &Lua_Spell::GetOutOfCombat) + .def("AEMaxTargets", &Lua_Spell::GetAEMaxTargets) + .def("MaxTargets", &Lua_Spell::GetMaxTargets) + .def("PersistDeath", &Lua_Spell::GetPersistDeath) + .def("MinDist", &Lua_Spell::GetMinDist) + .def("MinDistMod", &Lua_Spell::GetMinDistMod) + .def("MaxDist", &Lua_Spell::GetMaxDist) + .def("MaxDistMod", &Lua_Spell::GetMaxDistMod) + .def("MinRange", &Lua_Spell::GetMinRange) .def("DamageShieldType", &Lua_Spell::GetDamageShieldType); } diff --git a/zone/lua_spell.h b/zone/lua_spell.h index 9a8435cc5..bfa5a19ed 100644 --- a/zone/lua_spell.h +++ b/zone/lua_spell.h @@ -96,6 +96,16 @@ public: int GetPowerfulFlag(); int GetCastRestriction(); bool GetAllowRest(); + bool GetInCombat(); + bool GetOutOfCombat(); + int GetAEMaxTargets(); + int GetMaxTargets(); + bool GetPersistDeath(); + float GetMinDist(); + float GetMinDistMod(); + float GetMaxDist(); + float GetMaxDistMod(); + float GetMinRange(); int GetDamageShieldType(); }; diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 0ea8a5340..c1f8d8828 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -2340,8 +2340,8 @@ XS(boot_NPC) newXSproto(strcpy(buf, "SetSpellFocusHeal"), XS_NPC_SetSpellFocusHeal, file, "$$"); newXSproto(strcpy(buf, "GetSpellFocusDMG"), XS_NPC_GetSpellFocusDMG, file, "$"); newXSproto(strcpy(buf, "GetSpellFocusHeal"), XS_NPC_GetSpellFocusHeal, file, "$"); - newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetAttackSpeed, file, "$"); - newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetSlowMitigation, file, "$"); + newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetSlowMitigation, file, "$"); + newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetAttackSpeed, file, "$"); newXSproto(strcpy(buf, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$"); newXSproto(strcpy(buf, "GetSpawnKillCount"), XS_NPC_GetSpawnKillCount, file, "$"); newXSproto(strcpy(buf, "GetScore"), XS_NPC_GetScore, file, "$"); From e5822a0c4a12dd1912158fab4f5817740cf8deed Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 27 Sep 2014 21:37:14 -0400 Subject: [PATCH 177/368] Fix client SetAttackTimer issue Moved the call to the end of Client::CalcBonuses() since it depends on Client::CalcHaste() which requires a bunch of others to be called before it. --- changelog.txt | 1 + zone/bonuses.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index c0706ac74..ac69f9d00 100644 --- a/changelog.txt +++ b/changelog.txt @@ -6,6 +6,7 @@ Note: identifier is the stat field in spells_new, slot is used for certain effec Example $mob->GetSpellStat(121, "range"); //Returns spell range Example $mob->GetSpellStat(121, "effectid", 1); //Returns the the value of effectid1 This will allow you to pull almost all the data for any spell in quest files. +demonstar55: Move the client's SetAttackTimer to the end of Client::CalcBonuses to keep the haste in sync == 09/24/2014 == Uleat: Re-ordered server opcodes and handlers to give them some predictability of location (I need this for the inventory re-enumeration.) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index f1105ce73..d14624f2d 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -104,6 +104,8 @@ void Client::CalcBonuses() CalcMaxMana(); CalcMaxEndurance(); + SetAttackTimer(); + rooted = FindType(SE_Root); XPRate = 100 + spellbonuses.XPRateMod; @@ -174,8 +176,6 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { if(newbon->EnduranceRegen > CalcEnduranceRegenCap()) newbon->EnduranceRegen = CalcEnduranceRegenCap(); - - SetAttackTimer(); } void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug, bool isTribute) { From 3be7d45d36294f2ee0104225b36cec5ff8dde84d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 27 Sep 2014 23:14:11 -0400 Subject: [PATCH 178/368] Revamp attack delays / hastes / slows based on dev quotes See changelog --- changelog.txt | 4 ++ zone/attack.cpp | 64 +++++++++++++++-------- zone/bonuses.cpp | 5 ++ zone/bot.cpp | 109 +++++++++++---------------------------- zone/client_mods.cpp | 55 ++++++++++++++------ zone/merc.cpp | 10 +--- zone/mob.cpp | 37 ++++++++----- zone/mob.h | 1 - zone/special_attacks.cpp | 27 +++------- 9 files changed, 152 insertions(+), 160 deletions(-) diff --git a/changelog.txt b/changelog.txt index ac69f9d00..5b8d0cf9b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -7,6 +7,10 @@ Example $mob->GetSpellStat(121, "range"); //Returns spell range Example $mob->GetSpellStat(121, "effectid", 1); //Returns the the value of effectid1 This will allow you to pull almost all the data for any spell in quest files. demonstar55: Move the client's SetAttackTimer to the end of Client::CalcBonuses to keep the haste in sync +demonstar55: Correct haste/slow "stacking" rules +demonstar55: Correct SE_AttackSpeed4 to respect unslowable +demonstar55: Make the haste be between 1-225 like the client (<100 = slow, >100 = haste) to ... +demonstar55: Correct Hundred Hands effect and use formula provided by devs == 09/24/2014 == Uleat: Re-ordered server opcodes and handlers to give them some predictability of location (I need this for the inventory re-enumeration.) diff --git a/zone/attack.cpp b/zone/attack.cpp index d52d2a6ba..076eedf89 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4807,6 +4807,27 @@ void Mob::CommonBreakInvisible() improved_hidden = false; } +/* Dev quotes: + * Old formula + * Final delay = (Original Delay / (haste mod *.01f)) + ((Hundred Hands / 100) * Original Delay) + * New formula + * Final delay = (Original Delay / (haste mod *.01f)) + ((Hundred Hands / 1000) * (Original Delay / (haste mod *.01f)) + * Base Delay 20 25 30 37 + * Haste 2.25 2.25 2.25 2.25 + * HHE (old) -17 -17 -17 -17 + * Final Delay 5.488888889 6.861111111 8.233333333 10.15444444 + * + * Base Delay 20 25 30 37 + * Haste 2.25 2.25 2.25 2.25 + * HHE (new) -383 -383 -383 -383 + * Final Delay 5.484444444 6.855555556 8.226666667 10.14622222 + * + * Difference -0.004444444 -0.005555556 -0.006666667 -0.008222222 + * + * These times are in 10th of a second + * New formula currently unsupported + */ + void Mob::SetAttackTimer() { attack_timer.SetAtTrigger(4000, true); @@ -4814,7 +4835,7 @@ void Mob::SetAttackTimer() void Client::SetAttackTimer() { - float PermaHaste = GetPermaHaste(); + float haste_mod = GetHaste() * 0.01f; //default value for attack timer in case they have //an invalid weapon equipped: @@ -4879,28 +4900,27 @@ void Client::SetAttackTimer() } } - int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); + int hhe = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); int speed = 0; + int delay = 36; + float quiver_haste = 0.0f; //if we have no weapon.. if (ItemToUse == nullptr) { //above checks ensure ranged weapons do not fall into here // Work out if we're a monk - if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) - speed = static_cast((GetMonkHandToHandDelay() * (100 + DelayMod) / 100) * PermaHaste); - else - speed = static_cast((36 * (100 + DelayMod) / 100) * PermaHaste); + if (GetClass() == MONK || GetClass() == BEASTLORD) + delay = GetMonkHandToHandDelay(); } else { //we have a weapon, use its delay - // Convert weapon delay to timer resolution (milliseconds) - //delay * 100 - speed = static_cast((ItemToUse->Delay * (100 + DelayMod) / 100) * PermaHaste); - if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) { - float quiver_haste = GetQuiverHaste(); - if (quiver_haste > 0) - speed *= quiver_haste; - } + delay = ItemToUse->Delay; + if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) + quiver_haste = GetQuiverHaste(); } + speed = static_cast(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100); + // this is probably wrong + if (quiver_haste > 0) + speed *= quiver_haste; TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); if (i == MainPrimary) @@ -4910,13 +4930,20 @@ void Client::SetAttackTimer() void NPC::SetAttackTimer() { - float PermaHaste = GetPermaHaste(); + float haste_mod = GetHaste() * 0.01f; //default value for attack timer in case they have //an invalid weapon equipped: attack_timer.SetAtTrigger(4000, true); Timer *TimerToUse = nullptr; + int hhe = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); + + // Technically NPCs should do some logic for weapons, but the effect is minimal + // What they do is take the lower of their set delay and the weapon's + // ex. Mob's delay set to 20, weapon set to 19, delay 19 + // Mob's delay set to 20, weapon set to 21, delay 20 + int speed = static_cast(((attack_delay / haste_mod) + ((hhe / 100.0f) * attack_delay)) * 100); for (int i = MainRange; i <= MainSecondary; i++) { //pick a timer @@ -4938,13 +4965,6 @@ void NPC::SetAttackTimer() } } - int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); - - // Technically NPCs should do some logic for weapons, but the effect is minimal - // What they do is take the lower of their set delay and the weapon's - // ex. Mob's delay set to 20, weapon set to 19, delay 19 - // Mob's delay set to 20, weapon set to 21, delay 20 - int speed = static_cast((attack_delay * (100 + DelayMod) / 100) * PermaHaste); TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); } } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index d14624f2d..d3d4f03f4 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1546,6 +1546,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_AttackSpeed4: { + // These don't generate the IMMUNE_ATKSPEED message and the icon shows up + // but have no effect on the mobs attack speed + if (GetSpecialAbility(UNSLOWABLE)) + break; + if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow) effect_value = effect_value * -1; diff --git a/zone/bot.cpp b/zone/bot.cpp index d001a0b88..2d91be28e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -8349,11 +8349,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if(!ca_time) return; - float HasteModifier = 0; - if (GetHaste()) - HasteModifier = 10000 / (100 + GetHaste()); - else - HasteModifier = 100; + float HasteModifier = GetHaste() * 0.01f; int32 dmg = 0; uint16 skill_to_use = -1; @@ -8585,7 +8581,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { TryBackstab(target,reuse); } - classattack_timer.Start(reuse*HasteModifier/100); + classattack_timer.Start(reuse / HasteModifier); } bool Bot::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { @@ -8978,11 +8974,7 @@ int32 Bot::CalcMaxMana() { } void Bot::SetAttackTimer() { - float PermaHaste; - if (GetHaste()) - PermaHaste = 1 / (1 + (float)GetHaste()/100); - else - PermaHaste = 1.0f; + float haste_mod = GetHaste() * 0.01f; //default value for attack timer in case they have //an invalid weapon equipped: @@ -8991,117 +8983,74 @@ void Bot::SetAttackTimer() { Timer* TimerToUse = nullptr; const Item_Struct* PrimaryWeapon = nullptr; - for (int i=MainRange; i<=MainSecondary; i++) { - + for (int i = MainRange; i <= MainSecondary; i++) { //pick a timer if (i == MainPrimary) TimerToUse = &attack_timer; else if (i == MainRange) TimerToUse = &ranged_timer; - else if(i == MainSecondary) + else if (i == MainSecondary) TimerToUse = &attack_dw_timer; else //invalid slot (hands will always hit this) continue; const Item_Struct* ItemToUse = nullptr; ItemInst* ci = GetBotItem(i); - if(ci) + if (ci) ItemToUse = ci->GetItem(); //special offhand stuff - if(i == MainSecondary) { + if (i == MainSecondary) { //if we have a 2H weapon in our main hand, no dual - if(PrimaryWeapon != nullptr) { - if( PrimaryWeapon->ItemClass == ItemClassCommon - && (PrimaryWeapon->ItemType == ItemType2HSlash - || PrimaryWeapon->ItemType == ItemType2HBlunt - || PrimaryWeapon->ItemType == ItemType2HPiercing)) { - attack_dw_timer.Disable(); - continue; + if (PrimaryWeapon != nullptr) { + if (PrimaryWeapon->ItemClass == ItemClassCommon + && (PrimaryWeapon->ItemType == ItemType2HSlash + || PrimaryWeapon->ItemType == ItemType2HBlunt + || PrimaryWeapon->ItemType == ItemType2HPiercing)) { + attack_dw_timer.Disable(); + continue; } } //clients must have the skill to use it... - if(!GetSkill(SkillDualWield)) { + if (!GetSkill(SkillDualWield)) { attack_dw_timer.Disable(); continue; } } //see if we have a valid weapon - if(ItemToUse != nullptr) { + if (ItemToUse != nullptr) { //check type and damage/delay - if(ItemToUse->ItemClass != ItemClassCommon - || ItemToUse->Damage == 0 - || ItemToUse->Delay == 0) { + if (ItemToUse->ItemClass != ItemClassCommon + || ItemToUse->Damage == 0 + || ItemToUse->Delay == 0) { //no weapon - ItemToUse = nullptr; + ItemToUse = nullptr; } // Check to see if skill is valid - else if((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) { + else if ((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) { //no weapon ItemToUse = nullptr; } } - int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands; - if (DelayMod < -99) - DelayMod = -99; + int hhe = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); + int speed = 0; + int delay = 36; //if we have no weapon.. if (ItemToUse == nullptr) { //above checks ensure ranged weapons do not fall into here // Work out if we're a monk - if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) { - //we are a monk, use special delay - int speed = (int)( (GetMonkHandToHandDelay()*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste); - // 1200 seemed too much, with delay 10 weapons available - if(speed < RuleI(Combat, MinHastedDelay)) //lower bound - speed = RuleI(Combat, MinHastedDelay); - TimerToUse->SetAtTrigger(speed, true); // Hand to hand, delay based on level or epic - } else { - //not a monk... using fist, regular delay - int speed = (int)((36 *(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste); - //if(speed < RuleI(Combat, MinHastedDelay) && IsClient()) //lower bound - // speed = RuleI(Combat, MinHastedDelay); - TimerToUse->SetAtTrigger(speed, true); // Hand to hand, non-monk 2/36 - } + if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) + delay = GetMonkHandToHandDelay(); } else { //we have a weapon, use its delay - // Convert weapon delay to timer resolution (milliseconds) - //delay * 100 - int speed = (int)((ItemToUse->Delay*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste); - if(speed < RuleI(Combat, MinHastedDelay)) - speed = RuleI(Combat, MinHastedDelay); - - if(ItemToUse && (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing)) - { - /*if(IsClient()) - { - float max_quiver = 0; - for(int r = SLOT_PERSONAL_BEGIN; r <= SLOT_PERSONAL_END; r++) - { - const ItemInst *pi = CastToClient()->GetInv().GetItem(r); - if(!pi) - continue; - if(pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == bagTypeQuiver) - { - float temp_wr = (pi->GetItem()->BagWR / 3); - if(temp_wr > max_quiver) - { - max_quiver = temp_wr; - } - } - } - if(max_quiver > 0) - { - float quiver_haste = 1 / (1 + max_quiver / 100); - speed *= quiver_haste; - } - }*/ - } - TimerToUse->SetAtTrigger(speed, true); + delay = ItemToUse->Delay; } + speed = static_cast(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100); + TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); if(i == MainPrimary) PrimaryWeapon = ItemToUse; diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index ba8eae47e..2fb7ad9ca 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -1341,12 +1341,45 @@ int16 Client::CalcCHA() { return(CHA); } -int Client::CalcHaste() { - int h = spellbonuses.haste + spellbonuses.hastetype2; +int Client::CalcHaste() +{ + /* Tests: (based on results in newer char window) + * 68 v1 + 46 item + 25 over + 35 inhib = 204% + * 46 item + 5 v2 + 25 over + 35 inhib = 65% + * 68 v1 + 46 item + 5 v2 + 25 over + 35 inhib = 209% + * 75% slow + 35 inhib = 25% + * 35 inhib = 65% + * 75% slow = 25% + * Conclusions: + * the bigger effect in slow v. inhib wins + * slow negates all other hastes + * inhib will only negate all other hastes if you don't have v1 (ex. VQ) + */ + // slow beats all! Besides a better inhibit + if (spellbonuses.haste < 0) { + if (-spellbonuses.haste <= spellbonuses.inhibitmelee) + Haste = 100 - spellbonuses.inhibitmelee; + else + Haste = 100 + spellbonuses.haste; + return Haste; + } + + // No haste and inhibit, kills all other hastes + if (spellbonuses.haste == 0 && spellbonuses.inhibitmelee) { + Haste = 100 - spellbonuses.inhibitmelee; + return Haste; + } + + int h = 0; int cap = 0; - int overhaste = 0; int level = GetLevel(); + // we know we have a haste spell and not slowed, no extra inhibit melee checks needed + if (spellbonuses.haste) + h += spellbonuses.haste - spellbonuses.inhibitmelee; + if (spellbonuses.hastetype2 && level > 49) // type 2 is capped at 10% and only available to 50+ + h += spellbonuses.hastetype2 > 10 ? 10 : spellbonuses.hastetype2; + // 26+ no cap, 1-25 10 if (level > 25) // 26+ h += itembonuses.haste; @@ -1368,24 +1401,16 @@ int Client::CalcHaste() { // 51+ 25 (despite there being higher spells...), 1-50 10 if (level > 50) // 51+ - overhaste = spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3; + h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3; else // 1-50 - overhaste = spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; + h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; - h += overhaste; h += ExtraHaste; //GM granted haste. h = mod_client_haste(h); - if (spellbonuses.inhibitmelee) { - if (h >= 0) - h -= spellbonuses.inhibitmelee; - else - h -= ((100 + h) * spellbonuses.inhibitmelee / 100); - } - - Haste = h; - return(Haste); + Haste = 100 + h; + return Haste; } //The AA multipliers are set to be 5, but were 2 on WR diff --git a/zone/merc.cpp b/zone/merc.cpp index 2316c6af0..6f500b12e 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -4619,13 +4619,7 @@ void Merc::DoClassAttacks(Mob *target) { if(!ca_time) return; - float HasteModifier = 0; - if(GetHaste() > 0) - HasteModifier = 10000 / (100 + GetHaste()); - else if(GetHaste() < 0) - HasteModifier = (100 - GetHaste()); - else - HasteModifier = 100; + float HasteModifier = GetHaste() * 0.01f; int level = GetLevel(); int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will @@ -4689,7 +4683,7 @@ void Merc::DoClassAttacks(Mob *target) { } } - classattack_timer.Start(reuse*HasteModifier/100); + classattack_timer.Start(reuse / HasteModifier); } bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) diff --git a/zone/mob.cpp b/zone/mob.cpp index 8a4253c19..2441ca0bc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2735,12 +2735,29 @@ uint32 Mob::GetZoneID() const { return(zone->GetZoneID()); } -int Mob::GetHaste() { - int h = spellbonuses.haste + spellbonuses.hastetype2; +int Mob::GetHaste() +{ + // See notes in Client::CalcHaste + // Need to check if the effect of inhibit melee differs for NPCs + if (spellbonuses.haste < 0) { + if (-spellbonuses.haste <= spellbonuses.inhibitmelee) + return 100 - spellbonuses.inhibitmelee; + else + return 100 + spellbonuses.haste; + } + + if (spellbonuses.haste == 0 && spellbonuses.inhibitmelee) + return 100 - spellbonuses.inhibitmelee; + + int h = 0; int cap = 0; - int overhaste = 0; int level = GetLevel(); + if (spellbonuses.haste) + h += spellbonuses.haste - spellbonuses.inhibitmelee; + if (spellbonuses.hastetype2 && level > 49) + h += spellbonuses.hastetype2 > 10 ? 10 : spellbonuses.hastetype2; + // 26+ no cap, 1-25 10 if (level > 25) // 26+ h += itembonuses.haste; @@ -2760,21 +2777,13 @@ int Mob::GetHaste() { // 51+ 25 (despite there being higher spells...), 1-50 10 if (level > 50) // 51+ - overhaste = spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3; + h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3; else // 1-50 - overhaste = spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; + h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; - h += overhaste; h += ExtraHaste; //GM granted haste. - if (spellbonuses.inhibitmelee) { - if (h >= 0) - h -= spellbonuses.inhibitmelee; - else - h -= ((100 + h) * spellbonuses.inhibitmelee / 100); - } - - return(h); + return 100 + h; } void Mob::SetTarget(Mob* mob) { diff --git a/zone/mob.h b/zone/mob.h index 2617e98e2..b55dddd72 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -684,7 +684,6 @@ public: inline bool GetInvul(void) { return invulnerable; } inline void SetExtraHaste(int Haste) { ExtraHaste = Haste; } virtual int GetHaste(); - inline float GetPermaHaste() { return GetHaste() ? 100.0f / (1.0f + static_cast(GetHaste()) / 100.0f) : 100.0f; } uint8 GetWeaponDamageBonus(const Item_Struct* Weapon); uint16 GetDamageTable(SkillUseTypes skillinuse); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 904dc41b3..901456cd2 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1412,11 +1412,7 @@ void NPC::DoClassAttacks(Mob *target) { if(!ca_time) return; - float HasteModifier = 0; - if (GetHaste()) - HasteModifier = 10000 / (100 + GetHaste()); - else - HasteModifier = 100; + float HasteModifier = GetHaste() * 0.01f; int level = GetLevel(); int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will @@ -1568,7 +1564,7 @@ void NPC::DoClassAttacks(Mob *target) { } } - classattack_timer.Start(reuse*HasteModifier/100); + classattack_timer.Start(reuse / HasteModifier); } void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) @@ -1592,20 +1588,13 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } int ReuseTime = 0; - int ClientHaste = GetHaste(); - int HasteMod = 0; + float HasteMod = GetHaste() * 0.01f; - if(ClientHaste >= 0){ - HasteMod = (10000/(100+ClientHaste)); //+100% haste = 2x as many attacks - } - else{ - HasteMod = (100-ClientHaste); //-100% haste = 1/2 as many attacks - } int32 dmg = 0; uint16 skill_to_use = -1; - if (skill == -1){ + if (skill == -1){ switch(GetClass()){ case WARRIOR: case RANGER: @@ -1677,8 +1666,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } } - ReuseTime = BashReuseTime-1; - ReuseTime = (ReuseTime*HasteMod)/100; + ReuseTime = (BashReuseTime - 1) / HasteMod; DoSpecialAttackDamage(ca_target, SkillBash, dmg, 1,-1,ReuseTime); @@ -1705,8 +1693,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) if (min_dmg > max_dmg) max_dmg = min_dmg; - ReuseTime = FrenzyReuseTime-1; - ReuseTime = (ReuseTime*HasteMod)/100; + ReuseTime = (FrenzyReuseTime - 1) / HasteMod; //Live parses show around 55% Triple 35% Double 10% Single, you will always get first hit. while(AtkRounds > 0) { @@ -1781,7 +1768,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) TryBackstab(ca_target,ReuseTime); } - ReuseTime = (ReuseTime*HasteMod)/100; + ReuseTime = ReuseTime / HasteMod; if(ReuseTime > 0 && !IsRiposte){ p_timers.Start(pTimerCombatAbility, ReuseTime); } From 66c171b61bc8319e2e6f9aced76274cca5e9fef4 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 28 Sep 2014 13:27:38 -0400 Subject: [PATCH 179/368] Add support for post June 18, 2014 Hundred Hands effects Set Spells:Jun182014HundredHandsRevamp to true if your spell file is newer --- changelog.txt | 4 ++++ common/ruletypes.h | 1 + .../2014_09_28_NewHundredHandsEffect.sql | 1 + zone/attack.cpp | 16 +++++++++++----- zone/bot.cpp | 7 +++++-- 5 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 utils/sql/git/optional/2014_09_28_NewHundredHandsEffect.sql diff --git a/changelog.txt b/changelog.txt index 5b8d0cf9b..4657226a2 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/28/2014 == +demonstar55: Add support for post June 18, 2014 Hundred Hands Effect spells (they changed the formula and stuff) +set Spells:Jun182014HundredHandsRevamp to true if you're using a spell file from June 18, 2014+ + == 09/27/2014 == Kayen: Implemented perl function $mob->GetSpellStat(spell_id, identifier, slot); Note: identifier is the stat field in spells_new, slot is used for certain effects like effectid, base,base2, max ect. diff --git a/common/ruletypes.h b/common/ruletypes.h index 058d5ce6f..c5004ffd1 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -322,6 +322,7 @@ RULE_INT ( Spells, AI_IdleNoSpellMinRecast, 500) // AI spell recast time(MS) che RULE_INT ( Spells, AI_IdleNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) RULE_INT ( Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others. RULE_BOOL ( Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false) +RULE_BOOL ( Spells, Jun182014HundredHandsRevamp, false) // this should be true for if you import a spell file newer than June 18, 2014 RULE_CATEGORY_END() diff --git a/utils/sql/git/optional/2014_09_28_NewHundredHandsEffect.sql b/utils/sql/git/optional/2014_09_28_NewHundredHandsEffect.sql new file mode 100644 index 000000000..2c5acb205 --- /dev/null +++ b/utils/sql/git/optional/2014_09_28_NewHundredHandsEffect.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:Jun182014HundredHandsRevamp', 'false', 'Set this to true if your spell file is from after June 18, 2014.'); diff --git a/zone/attack.cpp b/zone/attack.cpp index 076eedf89..feb10369f 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4825,7 +4825,6 @@ void Mob::CommonBreakInvisible() * Difference -0.004444444 -0.005555556 -0.006666667 -0.008222222 * * These times are in 10th of a second - * New formula currently unsupported */ void Mob::SetAttackTimer() @@ -4900,7 +4899,7 @@ void Client::SetAttackTimer() } } - int hhe = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); + int hhe = itembonuses.HundredHands + spellbonuses.HundredHands; int speed = 0; int delay = 36; float quiver_haste = 0.0f; @@ -4917,7 +4916,10 @@ void Client::SetAttackTimer() if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) quiver_haste = GetQuiverHaste(); } - speed = static_cast(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100); + if (RuleB(Spells, Jun182014HundredHandsRevamp)) + speed = static_cast(((delay / haste_mod) + ((hhe / 1000.0f) * (delay / haste_mod))) * 100); + else + speed = static_cast(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100); // this is probably wrong if (quiver_haste > 0) speed *= quiver_haste; @@ -4937,13 +4939,17 @@ void NPC::SetAttackTimer() attack_timer.SetAtTrigger(4000, true); Timer *TimerToUse = nullptr; - int hhe = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); + int hhe = itembonuses.HundredHands + spellbonuses.HundredHands; // Technically NPCs should do some logic for weapons, but the effect is minimal // What they do is take the lower of their set delay and the weapon's // ex. Mob's delay set to 20, weapon set to 19, delay 19 // Mob's delay set to 20, weapon set to 21, delay 20 - int speed = static_cast(((attack_delay / haste_mod) + ((hhe / 100.0f) * attack_delay)) * 100); + int speed = 0; + if (RuleB(Spells, Jun182014HundredHandsRevamp)) + speed = static_cast(((attack_delay / haste_mod) + ((hhe / 1000.0f) * (attack_delay / haste_mod))) * 100); + else + speed = static_cast(((attack_delay / haste_mod) + ((hhe / 100.0f) * attack_delay)) * 100); for (int i = MainRange; i <= MainSecondary; i++) { //pick a timer diff --git a/zone/bot.cpp b/zone/bot.cpp index 2d91be28e..4102a2af6 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9035,7 +9035,7 @@ void Bot::SetAttackTimer() { } } - int hhe = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); + int hhe = itembonuses.HundredHands + spellbonuses.HundredHands; int speed = 0; int delay = 36; @@ -9049,7 +9049,10 @@ void Bot::SetAttackTimer() { //we have a weapon, use its delay delay = ItemToUse->Delay; } - speed = static_cast(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100); + if (RuleB(Spells, Jun182014HundredHandsRevamp)) + speed = static_cast(((delay / haste_mod) + ((hhe / 1000.0f) * (delay / haste_mod))) * 100); + else + speed = static_cast(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100); TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); if(i == MainPrimary) From 28e49801b76b9ee5f7380ce6de54fdcf3e22d1f8 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 29 Sep 2014 03:08:30 -0400 Subject: [PATCH 180/368] Fix issue with newly inserted saylinks not working correctly Sadly still silly --- zone/questmgr.cpp | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index ec8fbd584..90f0d370d 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2672,45 +2672,47 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam // Query for an existing phrase and id in the saylink table std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string); auto results = database.QueryDatabase(query); - if(results.Success()) - { - if (results.RowCount() >= 1) + if (results.Success()) { + if (results.RowCount() >= 1) { for (auto row = results.begin();row != results.end(); ++row) sayid = atoi(row[0]); - else // Add a new saylink entry to the database and query it again for the new sayid number - { - query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string); - results = database.QueryDatabase(query); - - if(!results.Success()) - LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str()); - else if (results.RowCount() >= 1) - for(auto row = results.begin(); row != results.end(); ++row) - sayid = atoi(row[0]); - + } else { // Add a new saylink entry to the database and query it again for the new sayid number + std::string insert_query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string); + results = database.QueryDatabase(insert_query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str()); + } else { + results = database.QueryDatabase(query); + if (results.Success()) { + if (results.RowCount() >= 1) + for(auto row = results.begin(); row != results.end(); ++row) + sayid = atoi(row[0]); + } else { + LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str()); + } + } } } safe_delete_array(escaped_string); - if(silent) + if (silent) sayid = sayid + 750000; else sayid = sayid + 500000; - //Create the say link as an item link hash - char linktext[250]; + //Create the say link as an item link hash + char linktext[250]; - if(initiator) - { + if (initiator) { if (initiator->GetClientVersion() >= EQClientRoF) sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12); else if (initiator->GetClientVersion() >= EQClientSoF) sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"00000000000000000000000000000000000000000000",LinkName,0x12); else sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"000000000000000000000000000000000000000",LinkName,0x12); - } - else // If no initiator, create an RoF saylink, since older clients handle RoF ones better than RoF handles older ones. + } else { // If no initiator, create an RoF saylink, since older clients handle RoF ones better than RoF handles older ones. sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12); + } strcpy(Phrase,linktext); return Phrase; From 8035c6c5586afc48c09d4b0e6eb5f825cd804f85 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 29 Sep 2014 16:16:19 -0400 Subject: [PATCH 181/368] Correct "cure blindness" catch --- zone/spell_effects.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 9373309c1..42a1edb65 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1267,7 +1267,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Blind: %+i", effect_value); #endif - if (spells[spell_id].base[i] == 1) + // this should catch the cures + if (BeneficialSpell(spell_id) && spells[spell_id].buffduration == 0) BuffFadeByEffect(SE_Blind); else is_blind = true; From 5ffb6bdee7840ec0f6506b90afa14ddb6825c857 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 29 Sep 2014 16:32:48 -0400 Subject: [PATCH 182/368] Reworked blind running around This should be more in line with how we do current fearpath stuff and with live. --- zone/bonuses.cpp | 12 +++++++-- zone/common.h | 1 + zone/fearpath.cpp | 55 ++++++++++++++---------------------------- zone/mob.cpp | 1 - zone/mob.h | 2 +- zone/mob_ai.cpp | 8 +++--- zone/spell_effects.cpp | 7 +++--- 7 files changed, 39 insertions(+), 47 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index d3d4f03f4..89af2a244 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2472,7 +2472,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } break; } - + case SE_ManaAbsorbPercentDamage: { if (newbon->ManaAbsorbPercentDamage[0] < effect_value){ @@ -2493,7 +2493,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_ShieldBlock: newbon->ShieldBlock += effect_value; break; - + case SE_ShieldEquipHateMod: newbon->ShieldEquipHateMod += effect_value; break; @@ -2507,6 +2507,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->BlockBehind += effect_value; break; + case SE_Blind: + newbon->IsBlind = true; + break; + case SE_Fear: newbon->IsFeared = true; break; @@ -4086,6 +4090,10 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) itembonuses.BlockBehind = effect_value; break; + case SE_Blind: + spellbonuses.IsBlind = false; + break; + case SE_Fear: spellbonuses.IsFeared = false; break; diff --git a/zone/common.h b/zone/common.h index 0dbb68e0b..fb56b3f01 100644 --- a/zone/common.h +++ b/zone/common.h @@ -294,6 +294,7 @@ struct StatBonuses { int16 ResistFearChance; //i bool Fearless; //i bool IsFeared; //i + bool IsBlind; //i int16 StunResist; //i int16 MeleeSkillCheck; //i uint8 MeleeSkillCheckSkill; diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 592fa2090..47fc53a96 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -31,12 +31,10 @@ #define snprintf _snprintf #endif - extern Zone* zone; #define FEAR_PATHING_DEBUG - //this is called whenever we are damaged to process possible fleeing void Mob::CheckFlee() { //if were allready fleeing, dont need to check more... @@ -55,7 +53,7 @@ void Mob::CheckFlee() { float ratio = GetHPRatio(); float fleeratio = GetSpecialAbility(FLEE_PERCENT); fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio); - + if(ratio >= fleeratio) return; @@ -101,12 +99,13 @@ void Mob::CheckFlee() { } } - -void Mob::ProcessFlee() { +void Mob::ProcessFlee() +{ //Stop fleeing if effect is applied after they start to run. //When ImmuneToFlee effect fades it will turn fear back on and check if it can still flee. - if(flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) && !spellbonuses.IsFeared){ + if (flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) && + !spellbonuses.IsFeared && !spellbonuses.IsBlind) { curfp = false; return; } @@ -114,46 +113,42 @@ void Mob::ProcessFlee() { //see if we are still dying, if so, do nothing float fleeratio = GetSpecialAbility(FLEE_PERCENT); fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio); - if(GetHPRatio() < fleeratio) + if (GetHPRatio() < fleeratio) return; //we are not dying anymore... see what we do next flee_mode = false; - //see if we are legitimately feared now - if(!spellbonuses.IsFeared) { - //not feared... were done... + //see if we are legitimately feared or blind now + if (!spellbonuses.IsFeared && !spellbonuses.IsBlind) { + //not feared or blind... were done... curfp = false; return; } } -float Mob::GetFearSpeed() { - if(flee_mode || is_blind) { +float Mob::GetFearSpeed() +{ + if (flee_mode) { //we know ratio < FLEE_HP_RATIO float speed = GetBaseRunspeed(); float ratio = GetHPRatio(); float multiplier = RuleR(Combat, FleeMultiplier); - if(GetSnaredAmount() > 40) + if (GetSnaredAmount() > 40) multiplier = multiplier / 6.0f; speed = speed * ratio * multiplier / 100; - // A blinded mob should be pretty slow when running amuck. - if (is_blind) - { - speed = speed/3.0; - } - //NPC will eventually stop. Snares speeds this up. - if(speed < 0.09) + if (speed < 0.09) speed = 0.0001f; - - return(speed); + + return speed; } - return(GetRunspeed()); + // fear and blind use their normal run speed + return GetRunspeed(); } void Mob::CalculateNewFearpoint() @@ -215,17 +210,3 @@ void Mob::CalculateNewFearpoint() } } - - - - - - - - - - - - - - diff --git a/zone/mob.cpp b/zone/mob.cpp index 7eb967428..2441ca0bc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -114,7 +114,6 @@ Mob::Mob(const char* in_name, fear_walkto_y = -999999; fear_walkto_z = -999999; curfp = false; - is_blind = false; AI_Init(); SetMoving(false); diff --git a/zone/mob.h b/zone/mob.h index 66c2a8711..352a610eb 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -768,6 +768,7 @@ public: inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); } void ProcessFlee(); void CheckFlee(); + inline bool IsBlind() { return spellbonuses.IsBlind; } inline bool CheckAggro(Mob* other) {return hate_list.IsOnHateList(other);} float CalculateHeadingToTarget(float in_x, float in_y); @@ -1190,7 +1191,6 @@ protected: float fear_walkto_y; float fear_walkto_z; bool curfp; - bool is_blind; // Pathing // diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index d1dc18d5d..5f8ea2c11 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1039,8 +1039,8 @@ void Mob::AI_Process() { // Begin: Additions for Wiz Fear Code // if(RuleB(Combat, EnableFearPathing)){ - if(curfp || (is_blind && !CombatRange(hate_list.GetClosest(this)))) { - if(IsRooted()) { + if(curfp) { + if(IsRooted() || (IsBlind() && CombatRange(hate_list.GetClosest(this)))) { //make sure everybody knows were not moving, for appearance sake if(IsMoving()) { @@ -1087,7 +1087,9 @@ void Mob::AI_Process() { if (engaged) { - if (IsRooted() || is_blind) + // we are prevented from getting here if we are blind and don't have a target in range + // from above, so no extra blind checks needed + if (IsRooted() || IsBlind()) SetTarget(hate_list.GetClosest(this)); else { diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 42a1edb65..f5509ece2 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1270,8 +1270,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) // this should catch the cures if (BeneficialSpell(spell_id) && spells[spell_id].buffduration == 0) BuffFadeByEffect(SE_Blind); - else - is_blind = true; + else if (!IsClient()) + CalculateNewFearpoint(); break; } @@ -3997,7 +3997,8 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) } case SE_Blind: - is_blind = false; + if (curfp && !FindType(SE_Fear)) + curfp = false; break; case SE_Fear: From f5fa07c8dbfe83ec5383e1397fe7990aa62fce51 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 29 Sep 2014 16:38:18 -0400 Subject: [PATCH 183/368] Update zone.cpp --- zone/zone.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 36cb3504a..446110a4a 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -433,8 +433,8 @@ void Zone::LoadTempMerchantData(){ npcid = ml.npcid; } ml.slot = atoul(row[1]); - ml.item = atoul(row[2]); - ml.charges = atoul(row[3]); + ml.charges = atoul(row[2]); + ml.item = atoul(row[3]); ml.origslot = ml.slot; cur->second.push_back(ml); } From f117aafc45eb504ad2225a60b92a4bbf3eba2f65 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 30 Sep 2014 13:41:09 -0400 Subject: [PATCH 184/368] Added some updownsign to some newer formulas --- zone/spell_effects.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f5509ece2..add06714b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3205,55 +3205,55 @@ snare has both of them negative, yet their range should work the same: case 124: // check sign result = ubase; if (caster_level > 50) - result += caster_level - 50; + result += updownsign * (caster_level - 50); break; case 125: // check sign result = ubase; if (caster_level > 50) - result += 2 * (caster_level - 50); + result += updownsign * 2 * (caster_level - 50); break; case 126: // check sign result = ubase; if (caster_level > 50) - result += 3 * (caster_level - 50); + result += updownsign * 3 * (caster_level - 50); break; case 127: // check sign result = ubase; if (caster_level > 50) - result += 4 * (caster_level - 50); + result += updownsign * 4 * (caster_level - 50); break; case 128: // check sign result = ubase; if (caster_level > 50) - result += 5 * (caster_level - 50); + result += updownsign * 5 * (caster_level - 50); break; case 129: // check sign result = ubase; if (caster_level > 50) - result += 10 * (caster_level - 50); + result += updownsign * 10 * (caster_level - 50); break; case 130: // check sign result = ubase; if (caster_level > 50) - result += 15 * (caster_level - 50); + result += updownsign * 15 * (caster_level - 50); break; case 131: // check sign result = ubase; if (caster_level > 50) - result += 20 * (caster_level - 50); + result += updownsign * 20 * (caster_level - 50); break; case 132: // check sign result = ubase; if (caster_level > 50) - result += 25 * (caster_level - 50); + result += updownsign * 25 * (caster_level - 50); break; case 137: // used in berserker AA desperation From 83367ee80605acd57147333c0d3e3f0c0bc4bd09 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 30 Sep 2014 16:18:25 -0400 Subject: [PATCH 185/368] Added clicky items from bags for clients that support it (RoF, currently) --- changelog.txt | 3 ++ common/clientversions.h | 44 ++++++++++++++++++++++++---- common/eq_dictionary.cpp | 20 +++++++++++++ common/eq_dictionary.h | 1 + common/item.cpp | 24 +++++++++++++++ common/item.h | 27 +++++++++++++++-- common/patches/client62_constants.h | 1 + common/patches/rof_constants.h | 1 + common/patches/sod_constants.h | 1 + common/patches/sof_constants.h | 1 + common/patches/titanium_constants.h | 1 + common/patches/underfoot_constants.h | 1 + zone/client_packet.cpp | 15 +++++++--- 13 files changed, 129 insertions(+), 11 deletions(-) diff --git a/changelog.txt b/changelog.txt index 4657226a2..41b5a810d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/30/2014 == +Uleat: Implemented click-casting from bag slots for clients that natively support it (RoF) + == 09/28/2014 == demonstar55: Add support for post June 18, 2014 Hundred Hands Effect spells (they changed the formula and stuff) set Spells:Jun182014HundredHandsRevamp to true if you're using a spell file from June 18, 2014+ diff --git a/common/clientversions.h b/common/clientversions.h index 47a7d0204..ee70d481c 100644 --- a/common/clientversions.h +++ b/common/clientversions.h @@ -17,7 +17,8 @@ static const uint32 BIT_RoFAndLater = 0xFFFFFFE0; static const uint32 BIT_RoF2AndLater = 0xFFFFFFC0; static const uint32 BIT_AllClients = 0xFFFFFFFF; -typedef enum { +typedef enum +{ EQClientUnknown = 0, EQClient62, // Build: 'Aug 4 2005 15:40:59' EQClientTitanium, // Build: 'Oct 31 2005 10:33:37' @@ -26,17 +27,50 @@ typedef enum { EQClientUnderfoot, // Build: 'Jun 8 2010 16:44:32' EQClientRoF, // Build: 'Dec 10 2012 17:35:44' EQClientRoF2, // Build: 'May 10 2013 23:30:08' - + _EQClientCount, // place new clients before this point (preferably, in release/attribute order) - + // Values below are not implemented, as yet... - + EmuNPC = _EQClientCount, EmuMerc, EmuBot, EmuPet, - + _EmuClientCount // array size for EQLimits } EQClientVersion; +static const char* EQClientVersionName(EQClientVersion version) +{ + switch (version) + { + case EQClientUnknown: + return "EQClientUnknown"; + case EQClient62: + return "EQClient62"; + case EQClientTitanium: + return "EQClientTitanium"; + case EQClientSoF: + return "EQClientSoF"; + case EQClientSoD: + return "EQClientSoD"; + case EQClientUnderfoot: + return "EQClientUnderfoot"; + case EQClientRoF: + return "EQClientRoF"; + case EQClientRoF2: + return "EQClientRoF2"; + case EmuNPC: + return "EmuNPC"; + case EmuMerc: + return "EmuMerc"; + case EmuBot: + return "EmuBot"; + case EmuPet: + return "EmuPet"; + default: + return "ERROR: Invalid EQClientVersion"; + }; +} + #endif /* CLIENTVERSIONS_H */ diff --git a/common/eq_dictionary.cpp b/common/eq_dictionary.cpp index 595d0cc5f..f85d3110f 100644 --- a/common/eq_dictionary.cpp +++ b/common/eq_dictionary.cpp @@ -1024,6 +1024,26 @@ bool EQLimits::AllowsEmptyBagInBag(uint32 version) { //return local[ValidateMobVersion(version)]; } +bool EQLimits::AllowsClickCastFromBag(uint32 version) { + static const bool local[_EmuClientCount] = { +/*Unknown*/ false, +/*62*/ Client62::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*Titanium*/ Titanium::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*SoF*/ SoF::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*SoD*/ SoD::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*Underfoot*/ Underfoot::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*RoF*/ RoF::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*RoF2*/ false, + +/*NPC*/ false, +/*Merc*/ false, +/*Bot*/ false, +/*Pet*/ false + }; + + return local[ValidateMobVersion(version)]; +} + // items uint16 EQLimits::ItemCommonSize(uint32 version) { static const uint16 local[_EmuClientCount] = { diff --git a/common/eq_dictionary.h b/common/eq_dictionary.h index 14f9d53d3..c08809a12 100644 --- a/common/eq_dictionary.h +++ b/common/eq_dictionary.h @@ -184,6 +184,7 @@ public: static uint64 CursorBitmask(uint32 version); static bool AllowsEmptyBagInBag(uint32 version); + static bool AllowsClickCastFromBag(uint32 version); // items static uint16 ItemCommonSize(uint32 version); diff --git a/common/item.cpp b/common/item.cpp index 513ddb23f..cfa52e0c4 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -911,6 +911,30 @@ bool Inventory::CanItemFitInContainer(const Item_Struct *ItemToTry, const Item_S return true; } +bool Inventory::SupportsClickCasting(int16 slot_id) +{ + // there are a few non-potion items that identify as ItemTypePotion..so, we still need to ubiquitously include the equipment range + if ((uint16)slot_id <= EmuConstants::GENERAL_END || slot_id == MainPowerSource) + { + return true; + } + else if (slot_id >= EmuConstants::GENERAL_BAGS_BEGIN && slot_id <= EmuConstants::GENERAL_BAGS_END) + { + if (EQLimits::AllowsClickCastFromBag(m_version)) + return true; + } + + return false; +} + +bool Inventory::SupportsPotionBeltCasting(int16 slot_id) +{ + if ((uint16)slot_id <= EmuConstants::GENERAL_END || slot_id == MainPowerSource || (slot_id >= EmuConstants::GENERAL_BAGS_BEGIN && slot_id <= EmuConstants::GENERAL_BAGS_END)) + return true; + + return false; +} + // Test whether a given slot can support a container item bool Inventory::SupportsContainers(int16 slot_id) { diff --git a/common/item.h b/common/item.h index c24c708b1..9be660117 100644 --- a/common/item.h +++ b/common/item.h @@ -121,8 +121,22 @@ public: // Public Methods /////////////////////////////// + Inventory() { m_version = EQClientUnknown; m_versionset = false; } ~Inventory(); + // Inventory v2 creep + bool SetInventoryVersion(EQClientVersion version) { + if (!m_versionset) { + m_version = version; + return (m_versionset = true); + } + else { + return false; + } + } + + EQClientVersion GetInventoryVersion() { return m_version; } + static void CleanDirty(); static void MarkDirty(ItemInst *inst); @@ -132,7 +146,7 @@ public: inline iter_queue cursor_begin() { return m_cursor.begin(); } inline iter_queue cursor_end() { return m_cursor.end(); } - inline bool CursorEmpty() { return (m_cursor.size() == 0); } + inline bool CursorEmpty() { return (m_cursor.size() == 0); } // Retrieve a read-only item from inventory inline const ItemInst* operator[](int16 slot_id) const { return GetItem(slot_id); } @@ -183,6 +197,10 @@ public: static bool CanItemFitInContainer(const Item_Struct *ItemToTry, const Item_Struct *Container); + // Test for valid inventory casting slot + bool SupportsClickCasting(int16 slot_id); + bool SupportsPotionBeltCasting(int16 slot_id); + // Test whether a given slot can support a container item static bool SupportsContainers(int16 slot_id); @@ -229,7 +247,12 @@ protected: std::map m_bank; // Items in character bank std::map m_shbank; // Items in character shared bank std::map m_trade; // Items in a trade session - ItemInstQueue m_cursor; // Items on cursor: FIFO + ItemInstQueue m_cursor; // Items on cursor: FIFO + +private: + // Active inventory version + EQClientVersion m_version; + bool m_versionset; }; class SharedDatabase; diff --git a/common/patches/client62_constants.h b/common/patches/client62_constants.h index 7d2125028..0e672a5de 100644 --- a/common/patches/client62_constants.h +++ b/common/patches/client62_constants.h @@ -180,6 +180,7 @@ namespace Client62 { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = true; } diff --git a/common/patches/rof_constants.h b/common/patches/rof_constants.h index 62a161dc5..97cb25aa5 100644 --- a/common/patches/rof_constants.h +++ b/common/patches/rof_constants.h @@ -184,6 +184,7 @@ namespace RoF { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = true; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = true; static const bool COIN_HAS_WEIGHT = false; } diff --git a/common/patches/sod_constants.h b/common/patches/sod_constants.h index 9a8907f13..8bdf45532 100644 --- a/common/patches/sod_constants.h +++ b/common/patches/sod_constants.h @@ -181,6 +181,7 @@ namespace SoD { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = false; } diff --git a/common/patches/sof_constants.h b/common/patches/sof_constants.h index d80fa2dcb..0b959ef3a 100644 --- a/common/patches/sof_constants.h +++ b/common/patches/sof_constants.h @@ -181,6 +181,7 @@ namespace SoF { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = true; } diff --git a/common/patches/titanium_constants.h b/common/patches/titanium_constants.h index 71625362a..85e525fe8 100644 --- a/common/patches/titanium_constants.h +++ b/common/patches/titanium_constants.h @@ -180,6 +180,7 @@ namespace Titanium { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = true; } diff --git a/common/patches/underfoot_constants.h b/common/patches/underfoot_constants.h index fdbe54086..b89a4f255 100644 --- a/common/patches/underfoot_constants.h +++ b/common/patches/underfoot_constants.h @@ -181,6 +181,7 @@ namespace Underfoot { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = false; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0061efff4..340723385 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1307,7 +1307,13 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) conn_state = ReceivedZoneEntry; ClientVersion = Connection()->ClientVersion(); - ClientVersionBit = 1 << (ClientVersion - 1); + if (ClientVersion != EQClientUnknown) + ClientVersionBit = 1 << (ClientVersion - 1); + else + ClientVersionBit = 0; + + bool siv = m_inv.SetInventoryVersion(ClientVersion); + LogFile->write(EQEMuLog::Debug, "%s inventory version to %s(%i)", (siv ? "Succeeded in setting" : "Failed to set"), EQClientVersionName(ClientVersion), ClientVersion); /* Antighost code tmp var is so the search doesnt find this object @@ -3999,15 +4005,16 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast { //discipline, using the item spell slot - if (castspell->inventoryslot == 0xFFFFFFFF) { + if (castspell->inventoryslot == INVALID_INDEX) { if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { LogFile->write(EQEMuLog::Debug, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); InterruptSpell(castspell->spell_id); } return; } - else if ((castspell->inventoryslot <= EmuConstants::GENERAL_END) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check + else if (m_inv.SupportsClickCasting(castspell->inventoryslot) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check { + // packet field types will be reviewed as packet transistions occur -U const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field //bool cancast = true; if (inst && inst->IsType(ItemClassCommon)) @@ -8555,7 +8562,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) LogFile->write(EQEMuLog::Debug, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id); - if ((slot_id < MainCursor) || (slot_id == MainPowerSource) || (slot_id > 250 && slot_id < 331 && ((item->ItemType == ItemTypePotion) || item->PotionBelt))) // sanity check + if (m_inv.SupportsClickCasting(slot_id) || ((item->ItemType == ItemTypePotion || item->PotionBelt) && m_inv.SupportsPotionBeltCasting(slot_id))) // sanity check { ItemInst* p_inst = (ItemInst*)inst; From 31f891dda56ed827f55eb5d0dd91878e5e683a84 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 30 Sep 2014 18:19:57 -0400 Subject: [PATCH 186/368] Fix error in GetBotOwnerCharacterID --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 462e9d0e5..8e875f70c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4941,7 +4941,7 @@ uint32 Bot::GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage) { std::string query = StringFormat("SELECT BotOwnerCharacterID FROM bots WHERE BotID = %u", botID); auto results = database.QueryDatabase(query); - if (results.Success()) { + if (!results.Success()) { *errorMessage = std::string(results.ErrorMessage()); return 0; } From cb49bae53c83143cbe83835d6d3c6ee754d05187 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 30 Sep 2014 18:41:03 -0400 Subject: [PATCH 187/368] Fix already in raid message --- zone/client_packet.cpp | 2 +- zone/string_ids.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 340723385..a68de63ba 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10693,7 +10693,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) Client *i = entity_list.GetClientByName(ri->player_name); if (i){ if (IsRaidGrouped()){ - i->Message_StringID(0, 5060); //group failed, must invite members not in raid... + i->Message_StringID(0, ALREADY_IN_RAID, ri->player_name); //group failed, must invite members not in raid... return; } Raid *r = entity_list.GetRaidByClient(i); diff --git a/zone/string_ids.h b/zone/string_ids.h index a443fabd9..56e1c4043 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -275,6 +275,7 @@ #define TOLD_NOT_ONLINE 5046 //%1 is not online at this time. #define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. #define PETITION_DELETED 5054 //Your petition was successfully deleted. +#define ALREADY_IN_RAID 5060 //%1 is already in a raid. #define GAIN_RAIDEXP 5085 //You gained raid experience! #define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there. #define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure. From 7326a3ea7aeaf078d394cbb7f49943a1c9170c1b Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 30 Sep 2014 19:20:38 -0400 Subject: [PATCH 188/368] Send the right person's name --- zone/client_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a68de63ba..2ef62c3d3 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10693,7 +10693,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) Client *i = entity_list.GetClientByName(ri->player_name); if (i){ if (IsRaidGrouped()){ - i->Message_StringID(0, ALREADY_IN_RAID, ri->player_name); //group failed, must invite members not in raid... + i->Message_StringID(0, ALREADY_IN_RAID, GetName()); //group failed, must invite members not in raid... return; } Raid *r = entity_list.GetRaidByClient(i); From cf8363f62ebb0bbe8f1fafe071d787c3e2d1cb50 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 30 Sep 2014 19:23:02 -0400 Subject: [PATCH 189/368] Fix another bot SQL error --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8e875f70c..2453a308a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4266,7 +4266,7 @@ uint32 Bot::GetBotItemBySlot(uint32 slotID) { std::string query = StringFormat("SELECT itemid FROM botinventory WHERE botid=%i AND slotid = %i", GetBotID(), slotID); auto results = database.QueryDatabase(query); - if(results.Success()) + if(!results.Success()) return 0; if(results.RowCount() != 1) From 1dfd3349b78f50bef27ae958f77e6576b76763e6 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 1 Oct 2014 01:15:08 -0400 Subject: [PATCH 190/368] Add Master Wu message --- zone/special_attacks.cpp | 54 ++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 901456cd2..f3527b997 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -26,6 +26,7 @@ #include "string_ids.h" #include "../common/misc_functions.h" #include "../common/rulesys.h" +#include "../common/string_util.h" int Mob::GetKickDamage() { @@ -346,15 +347,23 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { ReuseTime = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction; //Live AA - Technique of Master Wu - uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - if (bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0, 99))) { - - int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; - MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]); - - // always 1/4 of the double attack chance, 25% at rank 5 (100/4) - if ((bDoubleSpecialAttack / 4) > MakeRandomInt(0, 99)) - MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]); + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + if (wuchance) { + if (wuchance >= 100 || wuchance > MakeRandomInt(0, 99)) { + int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; + int extra = 1; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + if (wuchance / 4 > MakeRandomInt(0, 99)) + extra++; + // They didn't add a string ID for this. + std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); + // live uses 400 here -- not sure if it's the best for all clients though + SendColoredText(400, msg); + while (extra) { + MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]); + extra--; + } + } } if(ReuseTime < 100) { @@ -1743,18 +1752,21 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) return; //Live AA - Technique of Master Wu - uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - if( bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0,100))) { - - int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; - MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]); - - int TripleChance = 25; - if (bDoubleSpecialAttack > 100) - TripleChance += TripleChance*(100-bDoubleSpecialAttack)/100; - - if(TripleChance > MakeRandomInt(0,100)) { - MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]); + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + if (wuchance) { + if (wuchance >= 100 || wuchance > MakeRandomInt(0, 99)) { + int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; + int extra = 1; + if (wuchance / 4 > MakeRandomInt(0, 99)) + extra++; + // They didn't add a string ID for this. + std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); + // live uses 400 here -- not sure if it's the best for all clients though + SendColoredText(400, msg); + while (extra) { + MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0, 4)]); + extra--; + } } } } From be52d413db207dffe63069476f313035a2d03def Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 1 Oct 2014 02:11:38 -0400 Subject: [PATCH 191/368] Fix bazaar welcome message --- zone/trading.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/zone/trading.cpp b/zone/trading.cpp index 376d2cb14..738e763ed 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1615,36 +1615,36 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat } -void Client::SendBazaarWelcome(){ - +void Client::SendBazaarWelcome() +{ const std::string query = "SELECT COUNT(DISTINCT char_id), count(char_id) FROM trader"; - auto results = database.QueryDatabase(query); + auto results = database.QueryDatabase(query); if (results.Success() && results.RowCount() == 1){ - auto row = results.begin(); + auto row = results.begin(); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); - memset(outapp->pBuffer,0,outapp->size); + memset(outapp->pBuffer,0,outapp->size); - BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer; + BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer; - bws->Beginning.Action = BazaarWelcome; + bws->Beginning.Action = BazaarWelcome; - bws->Traders = atoi(row[0]); - bws->Items = atoi(row[1]); + bws->Traders = atoi(row[0]); + bws->Items = atoi(row[1]); - QueuePacket(outapp); + QueuePacket(outapp); - safe_delete(outapp); - } + safe_delete(outapp); + } - const std::string buyerCountQuery = "SELECT COUNT(DISTINCT charid) FROM buyer"; - results = database.QueryDatabase(query); + const std::string buyerCountQuery = "SELECT COUNT(DISTINCT charid) FROM buyer"; + results = database.QueryDatabase(buyerCountQuery); if (!results.Success() || results.RowCount() != 1) - return; + return; - auto row = results.begin(); - Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them, " + auto row = results.begin(); + Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them, " "or use /buyer to set up your own Buy Lines.", atoi(row[0])); } From a722e3f1121c078119c0c7b9ee7ccc6c2733c118 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 1 Oct 2014 13:24:38 -0400 Subject: [PATCH 192/368] Exported to PERL $client->SendColoredText(color, msg) --- zone/perl_client.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 03da3bc35..04becfdc8 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -6020,6 +6020,32 @@ XS(XS_Client_SendMarqueeMessage) XSRETURN_EMPTY; } +XS(XS_Client_SendColoredText); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendColoredText) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SendColoredText(color, message)"); + { + Client * THIS; + uint32 color = (uint32)SvUV(ST(1)); + std::string msg = (std::string)SvPV_nolen(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendColoredText(color, msg); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -6259,6 +6285,7 @@ XS(boot_Client) newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$"); newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$"); newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$"); + newXSproto(strcpy(buf, "SendColoredText"), XS_Client_SendColoredText, file, "$$$"); XSRETURN_YES; } From 50233b9a8c0e56db6d2067100e5bda7f22c43a86 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 1 Oct 2014 13:25:19 -0400 Subject: [PATCH 193/368] changelog --- changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.txt b/changelog.txt index 41b5a810d..a21a78717 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/01/2014 == +Kayen: Exported to PERL $client->SendColoredText(color, msg) + == 09/30/2014 == Uleat: Implemented click-casting from bag slots for clients that natively support it (RoF) From 7800cbbe964e6c5b8bdc96244d413436c31e019e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 1 Oct 2014 16:22:45 -0400 Subject: [PATCH 194/368] Initial work on OP_ItemRecastDelay --- common/emu_oplist.h | 1 + common/eq_packet_structs.h | 7 +++++++ common/opcode_dispatch.h | 1 + utils/patches/patch_RoF.conf | 2 ++ utils/patches/patch_SoD.conf | 2 ++ utils/patches/patch_Underfoot.conf | 2 ++ zone/spells.cpp | 13 +++++++++++++ 7 files changed, 28 insertions(+) diff --git a/common/emu_oplist.h b/common/emu_oplist.h index af871ec6a..8126fc773 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -542,4 +542,5 @@ N(OP_ZoneServerInfo), N(OP_ZoneServerReady), N(OP_ZoneSpawns), N(OP_ZoneUnavail), +N(OP_ItemRecastDelay), // mail and chat opcodes located in ../mail_oplist.h diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index ea0acaa90..2e4b29261 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4264,6 +4264,13 @@ struct ItemVerifyReply_Struct { /*012*/ }; +struct ItemRecastDelay_Struct { +/*000*/ uint32 recast_delay; // in seconds +/*004*/ uint32 recast_type; +/*008*/ uint32 unknown008; +/*012*/ +}; + /** * Shroud yourself. For yourself shrouding, this has your spawnId, spawnStruct, * bits of your charProfileStruct (no checksum, then charProfile up till diff --git a/common/opcode_dispatch.h b/common/opcode_dispatch.h index 3920dd3f7..46e8b9099 100644 --- a/common/opcode_dispatch.h +++ b/common/opcode_dispatch.h @@ -306,6 +306,7 @@ OUTz(OP_FinishWindow2); //OUTv(OP_AdventureMerchantResponse, strlen(msg)+2); OUTv(OP_ItemPacket, ItemPacket_Struct); OUTv(OP_ColoredText, ColoredText_Struct); +OUTv(OP_ItemRecastDelay, ItemRecastDelay_Struct); OUTv(OP_FormattedMessage, FormattedMessage_Struct); OUTv(OP_GuildMemberList, uint32); //variable length, but nasty OUTv(OP_InterruptCast, InterruptCast_Struct); diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 728676b2d..16197c0da 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -653,3 +653,5 @@ 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 + +OP_ItemRecastDelay=0x57ed diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 10df077f7..f0d223edb 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -660,3 +660,5 @@ 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 # + +OP_ItemRecastDelay=0x15c4 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index 4621a74a9..1b65a6481 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -654,3 +654,5 @@ 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 # + +OP_ItemRecastDelay=0x82d7 diff --git a/zone/spells.cpp b/zone/spells.cpp index a18eabe20..5205176a9 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -484,6 +484,19 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, return false; } + // This needs a bit more work for saving timer to PP for zoning etc + if (IsClient() && item_slot) { + ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot); + if (itm->GetItem()->RecastDelay) { + outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); + ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; + ird->recast_delay = itm->GetItem()->RecastDelay; + ird->recast_type = itm->GetItem()->RecastType; + CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + } + } + return(true); } From 8d050b8f3f2d06f133ac767f6764fbb81fa49a72 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 1 Oct 2014 18:06:02 -0400 Subject: [PATCH 195/368] Fix errors in Database::ExpireMail in ucs --- ucs/database.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ucs/database.cpp b/ucs/database.cpp index 43bb9e0e3..8130585d1 100644 --- a/ucs/database.cpp +++ b/ucs/database.cpp @@ -523,9 +523,9 @@ void Database::ExpireMail() { time(nullptr) - RuleI(Mail, ExpireTrash)); results = QueryDatabase(query); if(results.Success()) - _log(UCS__ERROR, "Error expiring trash messages, %s %s", query.c_str(), results.ErrorMessage().c_str()); - else _log(UCS__INIT, "Expired %i trash messages.", results.RowsAffected()); + else + _log(UCS__ERROR, "Error expiring trash messages, %s %s", query.c_str(), results.ErrorMessage().c_str()); } From e38268230f14df19cb3e28a435acae3cbbdbd2ef Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 1 Oct 2014 18:34:26 -0400 Subject: [PATCH 196/368] Export SendColoredText to lua --- changelog.txt | 1 + zone/lua_client.cpp | 6 ++++++ zone/lua_client.h | 1 + 3 files changed, 8 insertions(+) diff --git a/changelog.txt b/changelog.txt index a21a78717..cdbda16e0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 10/01/2014 == Kayen: Exported to PERL $client->SendColoredText(color, msg) +demonstar55: Exported SendColoredText to lua == 09/30/2014 == Uleat: Implemented click-casting from bag slots for clients that natively support it (RoF) diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 32affc06e..56abe5f09 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1239,6 +1239,11 @@ void Lua_Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in self->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg); } +void Lua_Client::SendColoredText(uint32 type, std::string msg) { + Lua_Safe_Call_Void(); + self->SendColoredText(type, msg); +} + void Lua_Client::PlayMP3(std::string file) { Lua_Safe_Call_Void(); @@ -1492,6 +1497,7 @@ luabind::scope lua_register_client() { .def("SetThirst", (void(Lua_Client::*)(int))&Lua_Client::SetThirst) .def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption) .def("SendMarqueeMessage", (void(Lua_Client::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_Client::SendMarqueeMessage) + .def("SendColoredText", (void(Lua_Client::*)(uint32, std::string))&Lua_Client::SendColoredText) .def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3); } diff --git a/zone/lua_client.h b/zone/lua_client.h index 8809779e9..ef2af1181 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -275,6 +275,7 @@ public: void SetThirst(int in_thirst); void SetConsumption(int in_hunger, int in_thirst); void SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg); + void SendColoredText(uint32 type, std::string msg); void PlayMP3(std::string file); }; From 1024e327b56f01c0768496d3d0376a1e73ea30d9 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 1 Oct 2014 20:48:15 -0400 Subject: [PATCH 197/368] Fix nullptr and item_slot check --- common/item.h | 3 +++ zone/spells.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/common/item.h b/common/item.h index 9be660117..4159e5ba3 100644 --- a/common/item.h +++ b/common/item.h @@ -391,6 +391,8 @@ public: void SetActivated(bool activated) { m_activated = activated; } int8 GetEvolveLvl() const { return m_evolveLvl; } void SetScaling(bool v) { m_scaling = v; } + void SetTimestamp(uint32 v) { m_timestamp = v; } + uint32 GetTimestamp() { return m_timestamp; } void Initialize(SharedDatabase *db = nullptr); void ScaleItem(); @@ -435,6 +437,7 @@ protected: Item_Struct* m_scaledItem; EvolveInfo* m_evolveInfo; bool m_scaling; + uint32 m_timestamp; // UNIX timestamp (GMT) of expiration // // Items inside of this item (augs or contents); diff --git a/zone/spells.cpp b/zone/spells.cpp index 5205176a9..606ce1816 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -485,9 +485,9 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, } // This needs a bit more work for saving timer to PP for zoning etc - if (IsClient() && item_slot) { + if (IsClient() && item_slot != INVALID_INDEX) { ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot); - if (itm->GetItem()->RecastDelay) { + if (itm && itm->GetItem()->RecastDelay) { outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; ird->recast_delay = itm->GetItem()->RecastDelay; From d8c6c69450556806c533a4a9a0d37579d6ed7502 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 1 Oct 2014 20:50:01 -0400 Subject: [PATCH 198/368] ITEM and PB slot checks too --- zone/spells.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 606ce1816..fb3cba454 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -485,7 +485,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, } // This needs a bit more work for saving timer to PP for zoning etc - if (IsClient() && item_slot != INVALID_INDEX) { + if (IsClient() && item_slot != INVALID_INDEX && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT))) { ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot); if (itm && itm->GetItem()->RecastDelay) { outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); From c7417d4b54e325496e3d3edbc2be1605ca02e4b9 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 1 Oct 2014 20:51:47 -0400 Subject: [PATCH 199/368] Didn't mean to push those .... --- common/item.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/common/item.h b/common/item.h index 4159e5ba3..9be660117 100644 --- a/common/item.h +++ b/common/item.h @@ -391,8 +391,6 @@ public: void SetActivated(bool activated) { m_activated = activated; } int8 GetEvolveLvl() const { return m_evolveLvl; } void SetScaling(bool v) { m_scaling = v; } - void SetTimestamp(uint32 v) { m_timestamp = v; } - uint32 GetTimestamp() { return m_timestamp; } void Initialize(SharedDatabase *db = nullptr); void ScaleItem(); @@ -437,7 +435,6 @@ protected: Item_Struct* m_scaledItem; EvolveInfo* m_evolveInfo; bool m_scaling; - uint32 m_timestamp; // UNIX timestamp (GMT) of expiration // // Items inside of this item (augs or contents); From fab091da2d7c3666c5124b3df42f7f0c6fd00a5b Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 1 Oct 2014 20:52:40 -0400 Subject: [PATCH 200/368] Move OP_ItemRecastDelay to the right spot --- common/emu_oplist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 8126fc773..6ddd59123 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -254,6 +254,7 @@ N(OP_ItemLinkText), N(OP_ItemName), N(OP_ItemPacket), N(OP_ItemPreview), +N(OP_ItemRecastDelay), N(OP_ItemVerifyReply), N(OP_ItemVerifyRequest), N(OP_ItemViewUnknown), @@ -542,5 +543,4 @@ N(OP_ZoneServerInfo), N(OP_ZoneServerReady), N(OP_ZoneSpawns), N(OP_ZoneUnavail), -N(OP_ItemRecastDelay), // mail and chat opcodes located in ../mail_oplist.h From 03fed0f42d8eee0820a9aa18ee8c2d9dc727b807 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 2 Oct 2014 14:10:48 -0400 Subject: [PATCH 201/368] Some AA updates from PEQ --- utils/sql/git/optional/2014_10_02_AAFixes.sql | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 utils/sql/git/optional/2014_10_02_AAFixes.sql diff --git a/utils/sql/git/optional/2014_10_02_AAFixes.sql b/utils/sql/git/optional/2014_10_02_AAFixes.sql new file mode 100644 index 000000000..1d6242b9f --- /dev/null +++ b/utils/sql/git/optional/2014_10_02_AAFixes.sql @@ -0,0 +1,13 @@ +-- Fix Salvage +UPDATE `aa_effects` SET `effectid` = '331' WHERE `aaid` IN (997, 998, 999); +-- Rapid Strikes missing entries +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('818', '1', '279', '17', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('819', '1', '279', '19', '0'); +-- Secondary Forte fixes client side display issues +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('691', '1', '248', '100', '0'); +-- Packrat this is what live uses +UPDATE `aa_effects` SET `base1` = '3' WHERE `aaid` = 678; +UPDATE `aa_effects` SET `base1` = '6' WHERE `aaid` = 679; +UPDATE `aa_effects` SET `base1` = '9' WHERE `aaid` = 680; +UPDATE `aa_effects` SET `base1` = '12' WHERE `aaid` = 681; +UPDATE `aa_effects` SET `base1` = '15' WHERE `aaid` = 682; From 272180ff0f1af852545337fb9a7b23bf22d3bf07 Mon Sep 17 00:00:00 2001 From: Uleat Date: Thu, 2 Oct 2014 20:18:54 -0400 Subject: [PATCH 202/368] Detached direct packet routing for some older client opcodes --- changelog.txt | 3 + common/eq_packet_structs.h | 29 ++-- common/patches/client62.cpp | 269 +++++++++++++++++++++++++++-- common/patches/client62_ops.h | 20 +++ common/patches/client62_structs.h | 55 +++--- common/patches/rof.cpp | 5 + common/patches/rof_structs.h | 30 ++-- common/patches/sod.cpp | 1 + common/patches/sod_structs.h | 34 ++-- common/patches/sof.cpp | 1 + common/patches/sof_structs.h | 34 ++-- common/patches/titanium.cpp | 267 ++++++++++++++++++++++++++-- common/patches/titanium_ops.h | 20 +++ common/patches/titanium_structs.h | 55 +++--- common/patches/underfoot.cpp | 1 + common/patches/underfoot_structs.h | 34 ++-- 16 files changed, 713 insertions(+), 145 deletions(-) diff --git a/changelog.txt b/changelog.txt index cdbda16e0..81233fcd7 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/02/2014 == +Uleat: First round of Ti/6.2 translators added - needed for re-enumeration + == 10/01/2014 == Kayen: Exported to PERL $client->SendColoredText(color, msg) demonstar55: Exported SendColoredText to lua diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 2e4b29261..47516b24a 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -1449,17 +1449,18 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; }; struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ }; struct MoveItem_Struct @@ -1467,16 +1468,18 @@ struct MoveItem_Struct /*0000*/ uint32 from_slot; /*0004*/ uint32 to_slot; /*0008*/ uint32 number_in_stack; +/*0012*/ }; // both MoveItem_Struct/DeleteItem_Struct server structures will be changing to a structure-based slot format..this will // be used for handling SoF/SoD/etc... time stamps sent using the MoveItem_Struct format. (nothing will be done with this -// info at the moment..but, it forwards it on to the server for handling/future use) +// info at the moment..but, it is forwarded on to the server for handling/future use) struct ClientTimeStamp_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ }; // @@ -3400,7 +3403,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; diff --git a/common/patches/client62.cpp b/common/patches/client62.cpp index 65ebe3600..bbc8757d0 100644 --- a/common/patches/client62.cpp +++ b/common/patches/client62.cpp @@ -18,15 +18,15 @@ namespace Client62 static OpcodeManager *opcodes = nullptr; static Strategy struct_strategy; - char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth); // server to client inventory location converters - static inline uint32 ServerToClient62Slot(uint32 ServerSlot); - static inline uint32 ServerToClient62CorpseSlot(uint32 ServerCorpse); + static inline int16 ServerToClient62Slot(uint32 ServerSlot); + static inline int16 ServerToClient62CorpseSlot(uint32 ServerCorpse); // client to server inventory location converters - static inline uint32 Client62ToServerSlot(uint32 Client62Slot); - static inline uint32 Client62ToServerCorpseSlot(uint32 Client62Corpse); + static inline uint32 Client62ToServerSlot(int16 Client62Slot); + static inline uint32 Client62ToServerCorpseSlot(int16 Client62Corpse); void Register(EQStreamIdentifier &into) { @@ -131,6 +131,31 @@ namespace Client62 FINISH_ENCODE(); } + ENCODE(OP_AdventureMerchantSell) + { + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToClient62Slot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ApplyPoison) + { + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToClient62Slot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); + } + ENCODE(OP_BazaarSearch) { EQApplicationPacket *in = *p; @@ -234,6 +259,20 @@ namespace Client62 dest->FastQueuePacket(&in, ack_req); } + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToClient62Slot(emu->from_slot); + eq->to_slot = ServerToClient62Slot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + ENCODE(OP_DeleteSpawn) { SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); @@ -400,6 +439,31 @@ namespace Client62 FINISH_ENCODE(); } + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = emu->slot_id; + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToClient62Slot(emu->from_slot); + eq->to_slot = ServerToClient62Slot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } ENCODE(OP_OnLevelMessage) @@ -616,6 +680,7 @@ namespace Client62 ENCODE(OP_ReadBook) { + // no apparent slot translation needed -U EQApplicationPacket *in = *p; *p = nullptr; @@ -732,6 +797,19 @@ namespace Client62 FINISH_ENCODE(); } + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToClient62Slot(emu->itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + ENCODE(OP_Track) { EQApplicationPacket *in = *p; @@ -764,6 +842,19 @@ namespace Client62 dest->FastQueuePacket(&in, ack_req); } + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToClient62Slot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + ENCODE(OP_WearChange) { ENCODE_LENGTH_EXACT(WearChange_Struct); @@ -875,6 +966,59 @@ namespace Client62 } // DECODE methods + DECODE(OP_AdventureMerchantSell) + { + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + IN(npcid); + emu->slot = Client62ToServerSlot(eq->slot); + IN(charges); + IN(sell_price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = Client62ToServerSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); + } + +#if 0 + // needs to be tested (and OpCode found) + DECODE(OP_AugmentInfo) { DECODE_FORWARD(OP_ReadBook); } +#endif + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = Client62ToServerSlot(eq->container_slot); + emu->augment_slot = eq->augment_slot; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + IN(slot); + IN(spell_id); + emu->inventoryslot = Client62ToServerSlot(eq->inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_CharacterCreate) { DECODE_LENGTH_EXACT(structs::CharCreate_Struct); @@ -903,6 +1047,30 @@ namespace Client62 FINISH_DIRECT_DECODE(); } + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = Client62ToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = Client62ToServerSlot(eq->from_slot); + emu->to_slot = Client62ToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_FaceChange) { DECODE_LENGTH_EXACT(structs::FaceChange_Struct); @@ -935,8 +1103,36 @@ namespace Client62 FINISH_DIRECT_DECODE(); } + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = eq->slot_id; + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); + + emu->from_slot = Client62ToServerSlot(eq->from_slot); + emu->to_slot = Client62ToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_ReadBook) { + // no apparent slot translation needed -U DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct); SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); @@ -964,6 +1160,43 @@ namespace Client62 FINISH_DIRECT_DECODE(); } + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = Client62ToServerSlot(eq->itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = Client62ToServerSlot(eq->container_slot); + IN(guildtribute_slot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = Client62ToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_WearChange) { DECODE_LENGTH_EXACT(structs::WearChange_Struct); @@ -992,13 +1225,14 @@ namespace Client62 } // file scope helper methods - char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth) + char *SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { char *serialization = nullptr; char *instance = nullptr; const char *protection = (const char *)"\\\\\\\\\\"; char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; bool stackable = inst->IsStackable(); + int16 slot_id = ServerToClient62Slot(slot_id_in); uint32 merchant_slot = inst->GetMerchantSlot(); int16 charges = inst->GetCharges(); const Item_Struct *item = inst->GetItem(); @@ -1009,7 +1243,8 @@ namespace Client62 "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", stackable ? charges : 1, 0, - (merchant_slot == 0) ? slot_id : merchant_slot, + //(merchant_slot == 0) ? slot_id : merchant_slot, // change when translator activated + (merchant_slot == 0) ? slot_id_in : merchant_slot, inst->GetPrice(), (merchant_slot == 0) ? 1 : inst->GetMerchantCount(), 0, @@ -1075,22 +1310,30 @@ namespace Client62 return serialization; } - static inline uint32 ServerToClient62Slot(uint32 ServerSlot) + static inline int16 ServerToClient62Slot(uint32 ServerSlot) { - //uint32 Client62Slot; + //int16 Client62Slot; + if (ServerSlot == INVALID_INDEX) + return INVALID_INDEX; + + return ServerSlot; // deprecated } - static inline uint32 ServerToClient62CorpseSlot(uint32 ServerCorpse) + static inline int16 ServerToClient62CorpseSlot(uint32 ServerCorpse) { - //uint32 Client62Corpse; + //int16 Client62Corpse; } - static inline uint32 Client62ToServerSlot(uint32 Client62Slot) + static inline uint32 Client62ToServerSlot(int16 Client62Slot) { //uint32 ServerSlot; + if (Client62Slot == INVALID_INDEX) + return INVALID_INDEX; + + return Client62Slot; // deprecated } - static inline uint32 Client62ToServerCorpseSlot(uint32 Client62Corpse) + static inline uint32 Client62ToServerCorpseSlot(int16 Client62Corpse) { //uint32 ServerCorpse; } diff --git a/common/patches/client62_ops.h b/common/patches/client62_ops.h index 8f544ccfc..0ff0cdaa7 100644 --- a/common/patches/client62_ops.h +++ b/common/patches/client62_ops.h @@ -1,8 +1,12 @@ // out-going packets that require an ENCODE translation: E(OP_Action) +E(OP_AdventureMerchantSell) +E(OP_ApplyPoison) E(OP_BazaarSearch) E(OP_BecomeTrader) E(OP_CharInventory) +E(OP_DeleteCharge) +E(OP_DeleteItem) E(OP_DeleteSpawn) E(OP_GuildMemberLevelUpdate) E(OP_GuildMemberList) @@ -10,6 +14,8 @@ E(OP_Illusion) E(OP_ItemLinkResponse) E(OP_ItemPacket) E(OP_LeadershipExpUpdate) +E(OP_LootItem) +E(OP_MoveItem) E(OP_NewSpawn) E(OP_OnLevelMessage) E(OP_PetBuffWindow) @@ -18,17 +24,31 @@ E(OP_ReadBook) E(OP_RespondAA) E(OP_SendAATable) E(OP_SendCharInfo) +E(OP_ShopPlayerSell) E(OP_Track) +E(OP_TributeItem) E(OP_WearChange) E(OP_ZoneEntry) E(OP_ZoneServerReady) E(OP_ZoneSpawns) // incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_ApplyPoison) +//D(OP_AugmentInfo) - needs opcode for conf file update +D(OP_AugmentItem) +D(OP_CastSpell) D(OP_CharacterCreate) +D(OP_Consume) +D(OP_DeleteItem) D(OP_FaceChange) D(OP_ItemLinkClick) +D(OP_LootItem) +D(OP_MoveItem) D(OP_ReadBook) D(OP_SetServerFilter) +D(OP_ShopPlayerSell) +D(OP_TradeSkillCombine) +D(OP_TributeItem) D(OP_WearChange) D(OP_WhoAllRequest) #undef E diff --git a/common/patches/client62_structs.h b/common/patches/client62_structs.h index 73726e165..70f05859e 100644 --- a/common/patches/client62_structs.h +++ b/common/patches/client62_structs.h @@ -406,7 +406,7 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint8 cs_unknown[4]; + uint8 cs_unknown[4]; }; /* @@ -1148,19 +1148,27 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; }; +struct DeleteItem_Struct +{ +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ +}; struct MoveItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ }; // @@ -1362,12 +1370,6 @@ struct CombatAbility_Struct { uint32 m_skill; }; -struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; -}; - //Instill Doubt struct Instill_Doubt_Struct { uint8 i_id; @@ -1521,6 +1523,14 @@ struct Adventure_Purchase_Struct { /*008*/ uint32 variable; }; +struct Adventure_Sell_Struct { +/*000*/ uint32 unknown000; //0x01 +/*004*/ uint32 npcid; +/*008*/ uint32 slot; +/*012*/ uint32 charges; +/*016*/ uint32 sell_price; +}; + struct AdventurePoints_Update_Struct { /*000*/ uint32 ldon_available_points; // Total available points /*004*/ uint8 unkown_apu004[20]; @@ -2524,10 +2534,10 @@ struct TributeInfo_Struct { }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { @@ -2563,7 +2573,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; @@ -3076,6 +3086,11 @@ struct AnnoyingZoneUnknown_Struct { uint32 value; //always 4 }; +struct ApplyPoison_Struct { + uint32 inventorySlot; + uint32 success; +}; + struct GuildMemberUpdate_Struct { /*00*/ uint32 guild_id; //not sure /*04*/ char member_name[64]; diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 2643b909e..581858381 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -4671,6 +4671,7 @@ namespace RoF slot_id = legacy::SLOT_TRADESKILL; // 1000 } emu->container_slot = slot_id; + emu->guildtribute_slot = RoFToServerSlot(eq->guildtribute_slot); // this should only return INVALID_INDEX until implemented -U FINISH_DIRECT_DECODE(); } @@ -5498,6 +5499,10 @@ namespace RoF ServerSlot = TempSlot; }*/ + else if (RoFSlot.SlotType == maps::MapGuildTribute) { + ServerSlot = INVALID_INDEX; + } + _log(NET__ERROR, "Convert RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, ServerSlot); return ServerSlot; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 79b4f21b8..9cec4f5fa 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -1678,7 +1678,7 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*000*/ ItemSlotStruct slot; +/*000*/ ItemSlotStruct slot; /*012*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click /*016*/ uint32 type; // 0x01=Food 0x02=Water /*020*/ uint32 c_unknown1; // Seen 2 @@ -1711,17 +1711,17 @@ struct ItemProperties_Struct { }; struct DeleteItem_Struct { -/*0000*/ ItemSlotStruct from_slot; -/*0004*/ ItemSlotStruct to_slot; -/*0008*/ uint32 number_in_stack; -/*0012*/ +/*0000*/ ItemSlotStruct from_slot; +/*0012*/ ItemSlotStruct to_slot; +/*0024*/ uint32 number_in_stack; +/*0028*/ }; struct MoveItem_Struct { -/*0000*/ ItemSlotStruct from_slot; -/*0004*/ ItemSlotStruct to_slot; -/*0008*/ uint32 number_in_stack; -/*0012*/ +/*0000*/ ItemSlotStruct from_slot; +/*0012*/ ItemSlotStruct to_slot; +/*0024*/ uint32 number_in_stack; +/*0028*/ }; // @@ -2045,7 +2045,7 @@ struct Merchant_Sell_Struct { struct Merchant_Purchase_Struct { /*000*/ uint32 npcid; // Merchant NPC's entity id -/*004*/ MainInvItemSlotStruct itemslot; +/*004*/ MainInvItemSlotStruct itemslot; /*012*/ uint32 quantity; /*016*/ uint32 price; /*020*/ @@ -3488,10 +3488,10 @@ struct TributeInfo_Struct { struct TributeItem_Struct { -/*00*/ ItemSlotStruct slot; -/*12*/ uint32 quantity; -/*16*/ uint32 tribute_master_id; -/*20*/ int32 tribute_points; +/*00*/ ItemSlotStruct slot; +/*12*/ uint32 quantity; +/*16*/ uint32 tribute_master_id; +/*20*/ int32 tribute_points; /*24*/ }; @@ -3527,7 +3527,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ ItemSlotStruct container_slot; -/*12*/ ItemSlotStruct unknown_slot; // Slot type is 8? +/*12*/ ItemSlotStruct guildtribute_slot; // Slot type is 8? (MapGuildTribute = 8 -U) /*24*/ }; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 69ec9f1cb..90c5644fe 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -3074,6 +3074,7 @@ namespace SoD SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); emu->container_slot = SoDToServerSlot(eq->container_slot); + IN(guildtribute_slot); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index ffd6e9ecb..ea4726a62 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -519,7 +519,7 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint8 cs_unknown[4]; + uint8 cs_unknown[4]; }; /* @@ -1469,11 +1469,11 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; /*0016*/ }; @@ -1503,17 +1503,17 @@ struct ItemProperties_Struct { }; struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; struct MoveItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; @@ -3090,10 +3090,10 @@ struct TributeInfo_Struct { }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { @@ -3129,7 +3129,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 5e00a2870..861edcb3d 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -2398,6 +2398,7 @@ namespace SoF SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); emu->container_slot = SoFToServerSlot(eq->container_slot); + IN(guildtribute_slot); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 7d250a568..0beb17db6 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -497,7 +497,7 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint8 cs_unknown[4]; + uint8 cs_unknown[4]; }; /* @@ -1445,11 +1445,11 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; /*0016*/ }; @@ -1479,17 +1479,17 @@ struct ItemProperties_Struct { }; struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; struct MoveItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; @@ -2953,10 +2953,10 @@ struct TributeInfo_Struct { }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { @@ -2992,7 +2992,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index eaa7b3b10..92695f7a1 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -18,15 +18,15 @@ namespace Titanium static OpcodeManager *opcodes = nullptr; static Strategy struct_strategy; - char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth); // server to client inventory location converters - static inline uint32 ServerToTitaniumSlot(uint32 ServerSlot); - static inline uint32 ServerToTitaniumCorpseSlot(uint32 ServerCorpse); + static inline int16 ServerToTitaniumSlot(uint32 ServerSlot); + static inline int16 ServerToTitaniumCorpseSlot(uint32 ServerCorpse); // client to server inventory location converters - static inline uint32 TitaniumToServerSlot(uint32 TitaniumSlot); - static inline uint32 TitaniumToServerCorpseSlot(uint32 TitaniumCorpse); + static inline uint32 TitaniumToServerSlot(int16 TitaniumSlot); + static inline uint32 TitaniumToServerCorpseSlot(int16 TitaniumCorpse); void Register(EQStreamIdentifier &into) { @@ -133,6 +133,31 @@ namespace Titanium FINISH_ENCODE(); } + ENCODE(OP_AdventureMerchantSell) + { + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToTitaniumSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ApplyPoison) + { + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToTitaniumSlot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); + } + ENCODE(OP_BazaarSearch) { if (((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) { @@ -237,6 +262,20 @@ namespace Titanium dest->FastQueuePacket(&in, ack_req); } + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToTitaniumSlot(emu->from_slot); + eq->to_slot = ServerToTitaniumSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + ENCODE(OP_DeleteSpawn) { SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); @@ -608,6 +647,31 @@ namespace Titanium delete in; } + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = emu->slot_id; + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToTitaniumSlot(emu->from_slot); + eq->to_slot = ServerToTitaniumSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } ENCODE(OP_OnLevelMessage) @@ -870,6 +934,7 @@ namespace Titanium ENCODE(OP_ReadBook) { + // no apparent slot translation needed -U EQApplicationPacket *in = *p; *p = nullptr; @@ -992,6 +1057,19 @@ namespace Titanium FINISH_ENCODE(); } + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToTitaniumSlot(emu->itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + ENCODE(OP_Track) { EQApplicationPacket *in = *p; @@ -1052,6 +1130,19 @@ namespace Titanium FINISH_ENCODE(); } + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToTitaniumSlot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + ENCODE(OP_VetRewardsAvaliable) { EQApplicationPacket *inapp = *p; @@ -1232,6 +1323,57 @@ namespace Titanium } // DECODE methods + DECODE(OP_AdventureMerchantSell) + { + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + IN(npcid); + emu->slot = TitaniumToServerSlot(eq->slot); + IN(charges); + IN(sell_price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = TitaniumToServerSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + // needs to be tested + DECODE(OP_AugmentInfo) { DECODE_FORWARD(OP_ReadBook); } + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = TitaniumToServerSlot(eq->container_slot); + emu->augment_slot = eq->augment_slot; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + IN(slot); + IN(spell_id); + emu->inventoryslot = TitaniumToServerSlot(eq->inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_CharacterCreate) { DECODE_LENGTH_EXACT(structs::CharCreate_Struct); @@ -1260,6 +1402,30 @@ namespace Titanium FINISH_DIRECT_DECODE(); } + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = TitaniumToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = TitaniumToServerSlot(eq->from_slot); + emu->to_slot =TitaniumToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_FaceChange) { DECODE_LENGTH_EXACT(structs::FaceChange_Struct); @@ -1347,8 +1513,36 @@ namespace Titanium FINISH_DIRECT_DECODE(); } + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = eq->slot_id; + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); + + emu->from_slot = TitaniumToServerSlot(eq->from_slot); + emu->to_slot = TitaniumToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_ReadBook) { + // no apparent slot translation needed -U DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct); SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); @@ -1372,6 +1566,19 @@ namespace Titanium FINISH_DIRECT_DECODE(); } + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = TitaniumToServerSlot(eq->itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_TraderBuy) { DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); @@ -1388,6 +1595,30 @@ namespace Titanium FINISH_DIRECT_DECODE(); } + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = TitaniumToServerSlot(eq->container_slot); + IN(guildtribute_slot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = TitaniumToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_WearChange) { DECODE_LENGTH_EXACT(structs::WearChange_Struct); @@ -1422,13 +1653,14 @@ namespace Titanium } // file scope helper methods - char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth) + char *SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { char *serialization = nullptr; char *instance = nullptr; const char *protection = (const char *)"\\\\\\\\\\"; char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; bool stackable = inst->IsStackable(); + int16 slot_id = ServerToTitaniumSlot(slot_id_in); uint32 merchant_slot = inst->GetMerchantSlot(); int16 charges = inst->GetCharges(); const Item_Struct *item = inst->GetItem(); @@ -1439,7 +1671,8 @@ namespace Titanium "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", stackable ? charges : 0, 0, - (merchant_slot == 0) ? slot_id : merchant_slot, + //(merchant_slot == 0) ? slot_id : merchant_slot, // change when translator activated + (merchant_slot == 0) ? slot_id_in : merchant_slot, inst->GetPrice(), (merchant_slot == 0) ? 1 : inst->GetMerchantCount(), 0, @@ -1503,22 +1736,30 @@ namespace Titanium return serialization; } - static inline uint32 ServerToTitaniumSlot(uint32 ServerSlot) + static inline int16 ServerToTitaniumSlot(uint32 ServerSlot) { - //uint32 TitaniumSlot; + //int16 TitaniumSlot; + if (ServerSlot == INVALID_INDEX) + return INVALID_INDEX; + + return ServerSlot; // deprecated } - static inline uint32 ServerToTitaniumCorpseSlot(uint32 ServerCorpse) + static inline int16 ServerToTitaniumCorpseSlot(uint32 ServerCorpse) { - //uint32 TitaniumCorpse; + //int16 TitaniumCorpse; } - static inline uint32 TitaniumToServerSlot(uint32 TitaniumSlot) + static inline uint32 TitaniumToServerSlot(int16 TitaniumSlot) { //uint32 ServerSlot; + if (TitaniumSlot == INVALID_INDEX) + return INVALID_INDEX; + + return TitaniumSlot; // deprecated } - static inline uint32 TitaniumToServerCorpseSlot(uint32 TitaniumCorpse) + static inline uint32 TitaniumToServerCorpseSlot(int16 TitaniumCorpse) { //uint32 ServerCorpse; } diff --git a/common/patches/titanium_ops.h b/common/patches/titanium_ops.h index 40651a652..2aa3d8071 100644 --- a/common/patches/titanium_ops.h +++ b/common/patches/titanium_ops.h @@ -1,8 +1,12 @@ // out-going packets that require an ENCODE translation: E(OP_Action) +E(OP_AdventureMerchantSell) +E(OP_ApplyPoison) E(OP_BazaarSearch) E(OP_BecomeTrader) E(OP_CharInventory) +E(OP_DeleteCharge) +E(OP_DeleteItem) E(OP_DeleteSpawn) E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) @@ -20,6 +24,8 @@ E(OP_ItemLinkResponse) E(OP_ItemPacket) E(OP_LeadershipExpUpdate) E(OP_LFGuild) +E(OP_LootItem) +E(OP_MoveItem) E(OP_OnLevelMessage) E(OP_PetBuffWindow) E(OP_PlayerProfile) @@ -28,24 +34,38 @@ E(OP_ReadBook) E(OP_RespondAA) E(OP_SendCharInfo) E(OP_SendAATable) +E(OP_ShopPlayerSell) E(OP_Track) E(OP_Trader) E(OP_TraderBuy) +E(OP_TributeItem) E(OP_VetRewardsAvaliable) E(OP_WearChange) E(OP_ZoneEntry) E(OP_ZoneServerReady) E(OP_ZoneSpawns) // incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_ApplyPoison) +D(OP_AugmentInfo) +D(OP_AugmentItem) +D(OP_CastSpell) D(OP_CharacterCreate) +D(OP_Consume) +D(OP_DeleteItem) D(OP_FaceChange) D(OP_InspectAnswer) D(OP_InspectRequest) D(OP_ItemLinkClick) D(OP_LFGuild) +D(OP_LootItem) +D(OP_MoveItem) D(OP_ReadBook) D(OP_SetServerFilter) +D(OP_ShopPlayerSell) D(OP_TraderBuy) +D(OP_TradeSkillCombine) +D(OP_TributeItem) D(OP_WearChange) D(OP_WhoAllRequest) #undef E diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 1fcd31b77..0e74af75e 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -419,7 +419,7 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint8 cs_unknown[4]; + uint8 cs_unknown[4]; }; /* @@ -1217,19 +1217,27 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; }; +struct DeleteItem_Struct +{ +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ +}; struct MoveItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ }; // @@ -1431,12 +1439,6 @@ struct CombatAbility_Struct { uint32 m_skill; }; -struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; -}; - //Instill Doubt struct Instill_Doubt_Struct { uint8 i_id; @@ -1590,6 +1592,14 @@ struct Adventure_Purchase_Struct { /*008*/ uint32 variable; }; +struct Adventure_Sell_Struct { +/*000*/ uint32 unknown000; //0x01 +/*004*/ uint32 npcid; +/*008*/ uint32 slot; +/*012*/ uint32 charges; +/*016*/ uint32 sell_price; +}; + struct AdventurePoints_Update_Struct { /*000*/ uint32 ldon_available_points; // Total available points /*004*/ uint8 unkown_apu004[20]; @@ -2583,10 +2593,10 @@ struct TributeInfo_Struct { }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { @@ -2622,7 +2632,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; @@ -3154,6 +3164,11 @@ struct AnnoyingZoneUnknown_Struct { uint32 value; //always 4 }; +struct ApplyPoison_Struct { + uint32 inventorySlot; + uint32 success; +}; + struct GuildMemberUpdate_Struct { /*000*/ uint32 guild_id; //not sure /*004*/ char member_name[64]; diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index a1d16f077..0cec336b2 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -3472,6 +3472,7 @@ namespace Underfoot SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); emu->container_slot = UnderfootToServerSlot(eq->container_slot); + IN(guildtribute_slot); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index 8226e8fd7..f7716834b 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -519,7 +519,7 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint32 cs_unknown[5]; + uint32 cs_unknown[5]; }; /* @@ -1505,11 +1505,11 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; /*0016*/ }; @@ -1539,17 +1539,17 @@ struct ItemProperties_Struct { }; struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; struct MoveItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; @@ -3129,10 +3129,10 @@ struct TributeInfo_Struct { }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { @@ -3168,7 +3168,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; From 968127c414e2413dc43b3ca9ec5b26b51a252605 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 2 Oct 2014 22:08:12 -0400 Subject: [PATCH 203/368] Exported to PERL $client->SendSpellAnim(targetid, spellid) This function sends the spell graphic of a spell without actually having to cast the spell. --- changelog.txt | 4 ++++ zone/client.h | 1 + zone/perl_client.cpp | 28 ++++++++++++++++++++++++++++ zone/spells.cpp | 16 ++++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/changelog.txt b/changelog.txt index 81233fcd7..693393bac 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/02/2014 == +Kayen: Exported to PERL $client->SendSpellAnim(targetid, spellid) +This function sends the spell graphic of a spell without actually having to cast the spell. + == 10/02/2014 == Uleat: First round of Ti/6.2 translators added - needed for re-enumeration diff --git a/zone/client.h b/zone/client.h index 235861396..d600b51c5 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1155,6 +1155,7 @@ public: const char* GetClassPlural(Client* client); void SendWebLink(const char* website); void SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg); + void SendSpellAnim(uint16 targetid, uint16 spell_id); void DuplicateLoreMessage(uint32 ItemID); void GarbleMessage(char *, uint8); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 04becfdc8..a33e9a8b9 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -6046,6 +6046,32 @@ XS(XS_Client_SendColoredText) XSRETURN_EMPTY; } +XS(XS_Client_SendSpellAnim); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendSpellAnim) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: SendSpellAnim(uint16 spell_id, uint32 seq)"); + { + Client * THIS; + uint16 targetid = (uint16)SvUV(ST(1)); + uint16 spell_id = (uint16)SvUV(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendSpellAnim(targetid,spell_id); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -6286,6 +6312,8 @@ XS(boot_Client) newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$"); newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$"); newXSproto(strcpy(buf, "SendColoredText"), XS_Client_SendColoredText, file, "$$$"); + newXSproto(strcpy(buf, "SendSpellAnim"), XS_Client_SendSpellAnim, file, "$$$"); + XSRETURN_YES; } diff --git a/zone/spells.cpp b/zone/spells.cpp index fb3cba454..913ea0f26 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5329,3 +5329,19 @@ void NPC::UninitializeBuffSlots() safe_delete_array(buffs); } +void Client::SendSpellAnim(uint16 targetid, uint16 spell_id) +{ + if (!targetid || !IsValidSpell(spell_id)) + return; + + EQApplicationPacket app(OP_Action, sizeof(Action_Struct)); + Action_Struct* a = (Action_Struct*)app.pBuffer; + a->target = targetid; + a->source = this->GetID(); + a->type = 231; + a->spell = spell_id; + a->sequence = 231; + + app.priority = 1; + entity_list.QueueCloseClients(this, &app); +} From b3d8e22539b1a9bc97e9f5bc211a7649d85c8616 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 2 Oct 2014 22:52:25 -0400 Subject: [PATCH 204/368] Make ZoneDatabase::FillAAEffects get the data from memory Note: This should probably be moved out of ZoneDatabase --- zone/aa.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/zone/aa.cpp b/zone/aa.cpp index 1f933b30e..8af3896d3 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1751,19 +1751,15 @@ void ZoneDatabase::FillAAEffects(SendAA_Struct* aa_struct){ if(!aa_struct) return; - std::string query = StringFormat("SELECT effectid, base1, base2, slot from aa_effects where aaid=%i order by slot asc", aa_struct->id); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in Client::FillAAEffects query: '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - return; - } - - int index = 0; - for (auto row = results.begin(); row != results.end(); ++row, ++index) { - aa_struct->abilities[index].skill_id=atoi(row[0]); - aa_struct->abilities[index].base1=atoi(row[1]); - aa_struct->abilities[index].base2=atoi(row[2]); - aa_struct->abilities[index].slot=atoi(row[3]); + auto it = aa_effects.find(aa_struct->id); + if (it != aa_effects.end()) { + for (int slot = 0; slot < aa_struct->total_abilities; slot++) { + // aa_effects is a map of a map, so the slot reference does not start at 0 + aa_struct->abilities[slot].skill_id = it->second[slot + 1].skill_id; + aa_struct->abilities[slot].base1 = it->second[slot + 1].base1; + aa_struct->abilities[slot].base2 = it->second[slot + 1].base2; + aa_struct->abilities[slot].slot = it->second[slot + 1].slot; + } } } From 0438dee22e125d53e0edb2c270151d5f505bf2de Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 3 Oct 2014 01:37:51 -0400 Subject: [PATCH 205/368] Rework ZoneDatabase::SavePetInfo -- should be better Worst case before some something like 96 queries. --- zone/zonedb.cpp | 136 ++++++++++++++++++++---------------------------- 1 file changed, 57 insertions(+), 79 deletions(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index b49837704..7815b5b96 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2895,96 +2895,74 @@ void ZoneDatabase::LoadBuffs(Client *client) { } } -void ZoneDatabase::SavePetInfo(Client *client) { +void ZoneDatabase::SavePetInfo(Client *client) +{ + PetInfo *petinfo = nullptr; - PetInfo *petinfo = client->GetPetInfo(0); - PetInfo *suspended = client->GetPetInfo(1); - - std::string query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID()); - auto results = database.QueryDatabase(query); - if(!results.Success()) + std::string query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) return; - query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID()); + query = StringFormat("DELETE FROM `character_pet_inventory` WHERE `char_id` = %u", client->CharacterID()); results = database.QueryDatabase(query); - if(!results.Success()) + if (!results.Success()) return; - query = StringFormat("INSERT INTO `character_pet_info` " - "(`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " - "VALUES (%u, 0, '%s', %i, %u, %u, %u, %f) " - "ON DUPLICATE KEY UPDATE `petname` = '%s', `petpower` = %i, `spell_id` = %u, " - "`hp` = %u, `mana` = %u, `size` = %f", - client->CharacterID(), petinfo->Name, petinfo->petpower, petinfo->SpellID, - petinfo->HP, petinfo->Mana, petinfo->size, petinfo->Name, petinfo->petpower, - petinfo->SpellID, petinfo->HP, petinfo->Mana, petinfo->size); - results = database.QueryDatabase(query); - if(!results.Success()) - return; + for (int pet = 0; pet < 2; pet++) { + petinfo = client->GetPetInfo(pet); + if (!petinfo) + continue; - for(int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) { - if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0) - continue; + query = StringFormat("INSERT INTO `character_pet_info` " + "(`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " + "VALUES (%u, %u, '%s', %i, %u, %u, %u, %f) " + "ON DUPLICATE KEY UPDATE `petname` = '%s', `petpower` = %i, `spell_id` = %u, " + "`hp` = %u, `mana` = %u, `size` = %f", + client->CharacterID(), pet, petinfo->Name, petinfo->petpower, petinfo->SpellID, + petinfo->HP, petinfo->Mana, petinfo->size, // and now the ON DUPLICATE ENTRIES + petinfo->Name, petinfo->petpower, petinfo->SpellID, petinfo->HP, petinfo->Mana, petinfo->size); + results = database.QueryDatabase(query); + if (!results.Success()) + return; + query.clear(); - query = StringFormat("INSERT INTO `character_pet_buffs` " - "(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " - "`ticsremaining`, `counters`) " - "VALUES (%u, 0, %u, %u, %u, %u, %d)", - client->CharacterID(), index, petinfo->Buffs[index].spellid, - petinfo->Buffs[index].level, petinfo->Buffs[index].duration, - petinfo->Buffs[index].counters); - database.QueryDatabase(query); - } + // pet buffs! + for (int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) { + if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0) + continue; + if (query.length() == 0) + query = StringFormat("INSERT INTO `character_pet_buffs` " + "(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " + "`ticsremaining`, `counters`) " + "VALUES (%u, %u, %u, %u, %u, %u, %d)", + client->CharacterID(), pet, index, petinfo->Buffs[index].spellid, + petinfo->Buffs[index].level, petinfo->Buffs[index].duration, + petinfo->Buffs[index].counters); + else + query += StringFormat(", (%u, %u, %u, %u, %u, %u, %d)", + client->CharacterID(), pet, index, petinfo->Buffs[index].spellid, + petinfo->Buffs[index].level, petinfo->Buffs[index].duration, + petinfo->Buffs[index].counters); + } + database.QueryDatabase(query); + query.clear(); - for(int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) { - if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0) - continue; + // pet inventory! + for (int index = EmuConstants::EQUIPMENT_BEGIN; index <= EmuConstants::EQUIPMENT_END; index++) { + if (!petinfo->Items[index]) + continue; - query = StringFormat("INSERT INTO `character_pet_buffs` " - "(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " - "`ticsremaining`, `counters`) " - "VALUES (%u, 1, %u, %u, %u, %u, %d)", - client->CharacterID(), index, suspended->Buffs[index].spellid, - suspended->Buffs[index].level, suspended->Buffs[index].duration, - suspended->Buffs[index].counters); - database.QueryDatabase(query); + if (query.length() == 0) + query = StringFormat("INSERT INTO `character_pet_inventory` " + "(`char_id`, `pet`, `slot`, `item_id`) " + "VALUES (%u, %u, %u, %u)", + client->CharacterID(), pet, index, petinfo->Items[index]); + else + query += StringFormat(", (%u, %u, %u, %u)", client->CharacterID(), pet, index, petinfo->Items[index]); + } + database.QueryDatabase(query); } - - for (int index = EmuConstants::EQUIPMENT_BEGIN; index <= EmuConstants::EQUIPMENT_END; index++) { - if(!petinfo->Items[index]) - continue; - - query = StringFormat("INSERT INTO `character_pet_inventory` " - "(`char_id`, `pet`, `slot`, `item_id`) " - "VALUES (%u, 0, %u, %u)", - client->CharacterID(), index, petinfo->Items[index]); - database.QueryDatabase(query); - } - - query = StringFormat("INSERT INTO `character_pet_info` " - "(`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " - "VALUES (%u, 1, '%s', %u, %u, %u, %u, %f) " - "ON DUPLICATE KEY UPDATE " - "`petname`='%s', `petpower`=%i, `spell_id`=%u, `hp`=%u, `mana`=%u, `size`=%f", - client->CharacterID(), suspended->Name, suspended->petpower, suspended->SpellID, - suspended->HP, suspended->Mana, suspended->size, suspended->Name, suspended->petpower, - suspended->SpellID, suspended->HP, suspended->Mana, suspended->size); - results = database.QueryDatabase(query); - if(!results.Success()) - return; - - - for (int index = EmuConstants::EQUIPMENT_BEGIN; index <= EmuConstants::EQUIPMENT_END; index++) { - if(!suspended->Items[index]) - continue; - - query = StringFormat("INSERT INTO `character_pet_inventory` " - "(`char_id`, `pet`, `slot`, `item_id`) " - "VALUES (%u, 1, %u, %u)", - client->CharacterID(), index, suspended->Items[index]); - database.QueryDatabase(query); - } - } void ZoneDatabase::RemoveTempFactions(Client *client) { From bb2bed7b00dc17e55516c9d412a534b5c8861eca Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 3 Oct 2014 02:03:48 -0400 Subject: [PATCH 206/368] Fixed OP_AugmentInfo packet processing that I broked'd --- changelog.txt | 3 +++ common/eq_packet_structs.h | 6 +++--- common/patches/client62.cpp | 5 ----- common/patches/client62_ops.h | 1 - common/patches/client62_structs.h | 5 ----- common/patches/rof_structs.h | 6 +++--- common/patches/sod_structs.h | 6 +++--- common/patches/sof_structs.h | 6 +++--- common/patches/titanium.cpp | 3 --- common/patches/titanium_ops.h | 1 - common/patches/underfoot_structs.h | 6 +++--- 11 files changed, 18 insertions(+), 30 deletions(-) diff --git a/changelog.txt b/changelog.txt index 81233fcd7..b31afa449 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/03/2014 == +Uleat: Fixed Ti(6.2) OP_AugmentInfo translation that I broke (does not currently need and I mis-read a process) + == 10/02/2014 == Uleat: First round of Ti/6.2 translators added - needed for re-enumeration diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 47516b24a..8e5588e5c 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4312,9 +4312,9 @@ struct ControlBoat_Struct { struct AugmentInfo_Struct { -/*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[67]; // total packet length 72, all the rest were always 00 +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint8 window; // window to display the information in +/*005*/ uint8 unknown005[67]; // total packet length 72, all the rest were always 00 /*072*/ }; diff --git a/common/patches/client62.cpp b/common/patches/client62.cpp index bbc8757d0..d9def3dbb 100644 --- a/common/patches/client62.cpp +++ b/common/patches/client62.cpp @@ -990,11 +990,6 @@ namespace Client62 FINISH_DIRECT_DECODE(); } -#if 0 - // needs to be tested (and OpCode found) - DECODE(OP_AugmentInfo) { DECODE_FORWARD(OP_ReadBook); } -#endif - DECODE(OP_AugmentItem) { DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); diff --git a/common/patches/client62_ops.h b/common/patches/client62_ops.h index 0ff0cdaa7..eb7ff2205 100644 --- a/common/patches/client62_ops.h +++ b/common/patches/client62_ops.h @@ -34,7 +34,6 @@ E(OP_ZoneSpawns) // incoming packets that require a DECODE translation: D(OP_AdventureMerchantSell) D(OP_ApplyPoison) -//D(OP_AugmentInfo) - needs opcode for conf file update D(OP_AugmentItem) D(OP_CastSpell) D(OP_CharacterCreate) diff --git a/common/patches/client62_structs.h b/common/patches/client62_structs.h index 70f05859e..24e273682 100644 --- a/common/patches/client62_structs.h +++ b/common/patches/client62_structs.h @@ -3099,11 +3099,6 @@ struct GuildMemberUpdate_Struct { /*72*/ uint32 unknown072; }; - - - - - }; //end namespace structs }; //end namespace Client62 diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 9cec4f5fa..0549b4b58 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4585,9 +4585,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { -/*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint8 window; // window to display the information in +/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 /*076*/ }; diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index ea4726a62..64d907c5b 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -4109,9 +4109,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { -/*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint8 window; // window to display the information in +/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 /*076*/ }; diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 0beb17db6..5164af3eb 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -3963,9 +3963,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { -/*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint8 window; // window to display the information in +/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 /*076*/ }; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 92695f7a1..d0235049d 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1347,9 +1347,6 @@ namespace Titanium FINISH_DIRECT_DECODE(); } - // needs to be tested - DECODE(OP_AugmentInfo) { DECODE_FORWARD(OP_ReadBook); } - DECODE(OP_AugmentItem) { DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); diff --git a/common/patches/titanium_ops.h b/common/patches/titanium_ops.h index 2aa3d8071..338558602 100644 --- a/common/patches/titanium_ops.h +++ b/common/patches/titanium_ops.h @@ -47,7 +47,6 @@ E(OP_ZoneSpawns) // incoming packets that require a DECODE translation: D(OP_AdventureMerchantSell) D(OP_ApplyPoison) -D(OP_AugmentInfo) D(OP_AugmentItem) D(OP_CastSpell) D(OP_CharacterCreate) diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index f7716834b..ab263d6c4 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -4160,9 +4160,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { -/*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint8 window; // window to display the information in +/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 /*076*/ }; From 3a270dd96abbe9fd2ccd3a3e5bdfbcaf0de52712 Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 3 Oct 2014 15:05:20 -0400 Subject: [PATCH 207/368] Moved OP_LootItem slot translation to external handlers in client patch files --- changelog.txt | 1 + common/patches/client62.cpp | 6 ++++-- common/patches/rof.cpp | 6 ++++-- common/patches/sod.cpp | 6 ++++-- common/patches/sof.cpp | 6 ++++-- common/patches/titanium.cpp | 6 ++++-- common/patches/underfoot.cpp | 6 ++++-- 7 files changed, 25 insertions(+), 12 deletions(-) diff --git a/changelog.txt b/changelog.txt index b31afa449..6272d4a69 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 10/03/2014 == Uleat: Fixed Ti(6.2) OP_AugmentInfo translation that I broke (does not currently need and I mis-read a process) +Uleat: Moved client patch OP_LootItem slot translation to external handlers == 10/02/2014 == Uleat: First round of Ti/6.2 translators added - needed for re-enumeration diff --git a/common/patches/client62.cpp b/common/patches/client62.cpp index d9def3dbb..2da492145 100644 --- a/common/patches/client62.cpp +++ b/common/patches/client62.cpp @@ -446,7 +446,7 @@ namespace Client62 OUT(lootee); OUT(looter); - eq->slot_id = emu->slot_id; + eq->slot_id = ServerToClient62CorpseSlot(emu->slot_id); OUT(auto_loot); FINISH_ENCODE(); @@ -1105,7 +1105,7 @@ namespace Client62 IN(lootee); IN(looter); - emu->slot_id = eq->slot_id; + emu->slot_id = Client62ToServerCorpseSlot(eq->slot_id); IN(auto_loot); FINISH_DIRECT_DECODE(); @@ -1317,6 +1317,7 @@ namespace Client62 static inline int16 ServerToClient62CorpseSlot(uint32 ServerCorpse) { //int16 Client62Corpse; + return ServerCorpse; } static inline uint32 Client62ToServerSlot(int16 Client62Slot) @@ -1331,6 +1332,7 @@ namespace Client62 static inline uint32 Client62ToServerCorpseSlot(int16 Client62Corpse) { //uint32 ServerCorpse; + return Client62Corpse; } } // end namespace Client62 diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 581858381..d6eb858e3 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -1423,7 +1423,7 @@ namespace RoF OUT(lootee); OUT(looter); - eq->slot_id = emu->slot_id + 1; + eq->slot_id = ServerToRoFCorpseSlot(emu->slot_id); OUT(auto_loot); FINISH_ENCODE(); @@ -4405,7 +4405,7 @@ namespace RoF IN(lootee); IN(looter); - emu->slot_id = eq->slot_id - 1; + emu->slot_id = RoFToServerCorpseSlot(eq->slot_id); IN(auto_loot); FINISH_DIRECT_DECODE(); @@ -5406,6 +5406,7 @@ namespace RoF static inline uint32 ServerToRoFCorpseSlot(uint32 ServerCorpse) { //uint32 RoFCorpse; + return (ServerCorpse + 1); } static inline uint32 RoFToServerSlot(structs::ItemSlotStruct RoFSlot) @@ -5546,6 +5547,7 @@ namespace RoF static inline uint32 RoFToServerCorpseSlot(uint32 RoFCorpse) { //uint32 ServerCorpse; + return (RoFCorpse - 1); } } // end namespace RoF diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 90c5644fe..3501e73f0 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -984,7 +984,7 @@ namespace SoD OUT(lootee); OUT(looter); - eq->slot_id = emu->slot_id + 1; + eq->slot_id = ServerToSoDCorpseSlot(emu->slot_id); OUT(auto_loot); FINISH_ENCODE(); @@ -2956,7 +2956,7 @@ namespace SoD IN(lootee); IN(looter); - emu->slot_id = eq->slot_id - 1; + emu->slot_id = SoDToServerCorpseSlot(eq->slot_id); IN(auto_loot); FINISH_DIRECT_DECODE(); @@ -3599,6 +3599,7 @@ namespace SoD static inline uint32 ServerToSoDCorpseSlot(uint32 ServerCorpse) { //uint32 SoDCorpse; + return (ServerCorpse + 1); } static inline uint32 SoDToServerSlot(uint32 SoDSlot) @@ -3623,6 +3624,7 @@ namespace SoD static inline uint32 SoDToServerCorpseSlot(uint32 SoDCorpse) { //uint32 ServerCorpse; + return (SoDCorpse - 1); } } // end namespace SoD diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 861edcb3d..80c0cb7cf 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -783,7 +783,7 @@ namespace SoF OUT(lootee); OUT(looter); - eq->slot_id = emu->slot_id + 1; + eq->slot_id = ServerToSoFCorpseSlot(emu->slot_id); OUT(auto_loot); FINISH_ENCODE(); @@ -2294,7 +2294,7 @@ namespace SoF IN(lootee); IN(looter); - emu->slot_id = eq->slot_id - 1; + emu->slot_id = SoFToServerCorpseSlot(eq->slot_id); IN(auto_loot); FINISH_DIRECT_DECODE(); @@ -2920,6 +2920,7 @@ namespace SoF static inline uint32 ServerToSoFCorpseSlot(uint32 ServerCorpse) { //uint32 SoFCorpse; + return (ServerCorpse + 1); } static inline uint32 SoFToServerSlot(uint32 SoFSlot) @@ -2945,6 +2946,7 @@ namespace SoF static inline uint32 SoFToServerCorpseSlot(uint32 SoFCorpse) { //uint32 ServerCorpse; + return (SoFCorpse - 1); } } // end namespace SoF diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index d0235049d..7c990751d 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -654,7 +654,7 @@ namespace Titanium OUT(lootee); OUT(looter); - eq->slot_id = emu->slot_id; + eq->slot_id = ServerToTitaniumCorpseSlot(emu->slot_id); OUT(auto_loot); FINISH_ENCODE(); @@ -1517,7 +1517,7 @@ namespace Titanium IN(lootee); IN(looter); - emu->slot_id = eq->slot_id; + emu->slot_id = TitaniumToServerCorpseSlot(eq->slot_id); IN(auto_loot); FINISH_DIRECT_DECODE(); @@ -1745,6 +1745,7 @@ namespace Titanium static inline int16 ServerToTitaniumCorpseSlot(uint32 ServerCorpse) { //int16 TitaniumCorpse; + return ServerCorpse; } static inline uint32 TitaniumToServerSlot(int16 TitaniumSlot) @@ -1759,6 +1760,7 @@ namespace Titanium static inline uint32 TitaniumToServerCorpseSlot(int16 TitaniumCorpse) { //uint32 ServerCorpse; + return TitaniumCorpse; } } // end namespace Titanium diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index 0cec336b2..7c1aa940b 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -1234,7 +1234,7 @@ namespace Underfoot OUT(lootee); OUT(looter); - eq->slot_id = emu->slot_id + 1; + eq->slot_id = ServerToUnderFootCorpseSlot(emu->slot_id); OUT(auto_loot); FINISH_ENCODE(); @@ -3294,7 +3294,7 @@ namespace Underfoot IN(lootee); IN(looter); - emu->slot_id = eq->slot_id - 1; + emu->slot_id = UnderfootToServerCorpseSlot(eq->slot_id); IN(auto_loot); FINISH_DIRECT_DECODE(); @@ -4021,6 +4021,7 @@ namespace Underfoot static inline uint32 ServerToUnderFootCorpseSlot(uint32 ServerCorpse) { //uint32 UnderfootCorpse; + return (ServerCorpse + 1); } static inline uint32 UnderfootToServerSlot(uint32 UnderfootSlot) @@ -4046,6 +4047,7 @@ namespace Underfoot static inline uint32 UnderfootToServerCorpseSlot(uint32 UnderfootCorpse) { //uint32 ServerCorpse; + return (UnderfootCorpse - 1); } } // end namespace Underfoot From 61545beff243e272cf6c1db5795450c1328ea450 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 3 Oct 2014 13:42:39 -0700 Subject: [PATCH 208/368] Hunger and thirst clamping within larger ranges --- zone/client.cpp | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 964f228d1..5b7c0d597 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -39,15 +39,10 @@ extern volatile bool RunLoops; #include "../common/features.h" -#include "masterentity.h" -#include "worldserver.h" #include "../common/misc.h" -#include "zonedb.h" #include "../common/spdat.h" -#include "net.h" #include "../common/packet_dump.h" #include "../common/packet_functions.h" -#include "petitions.h" #include "../common/serverinfo.h" #include "../common/zone_numbers.h" #include "../common/moremath.h" @@ -55,6 +50,12 @@ extern volatile bool RunLoops; #include "../common/breakdowns.h" #include "../common/rulesys.h" #include "../common/string_util.h" +#include "../common/data_verification.h" +#include "net.h" +#include "masterentity.h" +#include "worldserver.h" +#include "zonedb.h" +#include "petitions.h" #include "forage.h" #include "command.h" #include "string_ids.h" @@ -594,19 +595,8 @@ bool Client::Save(uint8 iCommitNow) { database.SaveCharacterTribute(this->CharacterID(), &m_pp); SaveTaskState(); /* Save Character Task */ - if(m_pp.hunger_level < 0) { - m_pp.hunger_level = 0; - } - else if(m_pp.hunger_level > 6000) { - m_pp.hunger_level = 6000; - } - - if(m_pp.thirst_level < 0) { - m_pp.thirst_level = 0; - } - else if(m_pp.thirst_level > 6000) { - m_pp.thirst_level = 6000; - } + m_pp.hunger_level = EQEmu::Clamp(m_pp.hunger_level, 0, 50000); + m_pp.thirst_level = EQEmu::Clamp(m_pp.thirst_level, 0, 50000); database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp, &m_epp); /* Save Character Data */ return true; From e0e305949880536392ecfac7b8c23299ae2a4b75 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 3 Oct 2014 14:57:01 -0700 Subject: [PATCH 209/368] Turn off user literal error in clang --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f9c5429b..a975ef533 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,7 +259,10 @@ OPTION(EQEMU_BUILD_CLIENT_FILES "Build Client Import/Export Data Programs." ON) #C++11 stuff IF(NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reserved-user-defined-literal") + ENDIF() ENDIF(NOT MSVC) #Various definitions From e753685ceb3ec1b4a24d80ee657cc89ede7729ce Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 4 Oct 2014 03:23:42 -0400 Subject: [PATCH 210/368] Implement number of hit indicators for UF and RoF There is a small display bug with the initial cast of the spell, but it updates quickly enough that it shouldn't be too noticeable This still needs to be fixed though Changed SendBuffDurationPacket to take a Buffs_Struct by reference to allow more of the data to be obtained without more params Added Client::SendBuffNumHitPacket(Buffs_Struct &buff, int slot) --- common/eq_packet_structs.h | 4 +- common/patches/rof.cpp | 7 +-- common/patches/rof_structs.h | 26 ++++++++++- common/patches/underfoot.cpp | 5 ++- common/patches/underfoot_structs.h | 21 ++++++++- zone/client.h | 3 +- zone/spell_effects.cpp | 70 +++++++++++++----------------- zone/spells.cpp | 38 ++++++++++++---- 8 files changed, 118 insertions(+), 56 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 8e5588e5c..a022626e1 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -534,7 +534,7 @@ struct SpellBuffFade_Struct { /*007*/ uint8 unknown7; /*008*/ uint32 spellid; /*012*/ uint32 duration; -/*016*/ uint32 unknown016; +/*016*/ uint32 num_hits; /*020*/ uint32 unknown020; //prolly global player ID /*024*/ uint32 slotid; /*028*/ uint32 bufffade; @@ -4602,11 +4602,13 @@ struct BuffIconEntry_Struct uint32 buff_slot; uint32 spell_id; uint32 tics_remaining; + uint32 num_hits; }; struct BuffIcon_Struct { uint32 entity_id; + uint8 all_buffs; uint16 count; BuffIconEntry_Struct entries[0]; }; diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index d6eb858e3..a107a1246 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -376,6 +376,7 @@ namespace RoF OUT(duration); eq->playerId = 0x7cde; OUT(slotid); + OUT(num_hits); if (emu->bufffade == 1) eq->bufffade = 1; else @@ -414,7 +415,7 @@ namespace RoF __packet->WriteUInt32(emu->entity_id); __packet->WriteUInt32(0); // PlayerID ? - __packet->WriteUInt8(1); // 1 indicates all buffs on the player (0 to add or remove a single buff) + __packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff) __packet->WriteUInt16(emu->count); for (uint16 i = 0; i < emu->count; ++i) @@ -429,10 +430,10 @@ namespace RoF __packet->WriteUInt32(buffslot); __packet->WriteUInt32(emu->entries[i].spell_id); __packet->WriteUInt32(emu->entries[i].tics_remaining); - __packet->WriteUInt32(0); // Unknown + __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown __packet->WriteString(""); } - __packet->WriteUInt8(0); // Unknown + __packet->WriteUInt8(!emu->all_buffs); // Unknown FINISH_ENCODE(); } diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 0549b4b58..5f1d2aac4 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -712,7 +712,8 @@ struct SpellBuffFade_Struct_Live { /*012*/ uint32 spellid; /*016*/ uint32 duration; /*020*/ uint32 playerId; // Global player ID? -/*024*/ uint8 unknown0028[68]; +/*024*/ uint32 num_hits; +/*028*/ uint8 unknown0028[64]; /*092*/ uint32 slotid; /*096*/ uint32 bufffade; /*100*/ @@ -726,7 +727,7 @@ struct SpellBuffFade_Struct { /*007*/ uint8 unknown7; /*008*/ uint32 spellid; /*012*/ uint32 duration; -/*016*/ uint32 unknown016; +/*016*/ uint32 num_hits; /*020*/ uint32 unknown020; // Global player ID? /*024*/ uint32 playerId; // Player id who cast the buff /*028*/ uint32 slotid; @@ -741,6 +742,27 @@ struct BuffRemoveRequest_Struct /*08*/ }; +#if 0 +// not in use +struct BuffIconEntry_Struct { +/*000*/ uint32 buff_slot; +/*004*/ uint32 spell_id; +/*008*/ uint32 tics_remaining; +/*012*/ uint32 num_hits; +// char name[0]; caster name is also here sometimes +// uint8 unknownend; 1 when single, 0 when all opposite of all_buffs? +}; + +// not in use +struct BuffIcon_Struct { +/*000*/ uint32 entity_id; +/*004*/ uint32 unknown004; +/*008*/ uint8 all_buffs; // 1 when updating all buffs, 0 when doing one +/*009*/ uint16 count; +/*011*/ BuffIconEntry_Struct entires[0]; +}; +#endif + struct GMTrainee_Struct { /*000*/ uint32 npcid; diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index 7c1aa940b..6291d3077 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -330,6 +330,7 @@ namespace Underfoot OUT(duration); OUT(slotid); OUT(bufffade); // Live (October 2011) sends a 2 rather than 0 when a buff is created, but it doesn't seem to matter. + OUT(num_hits); eq->unknown008 = 1.0f; FINISH_ENCODE(); @@ -348,7 +349,7 @@ namespace Underfoot *((uint32*)ptr) = emu->entity_id; ptr += sizeof(uint32); ptr += sizeof(uint32); - *((uint8*)ptr) = 1; + *((uint8*)ptr) = emu->all_buffs; ptr += sizeof(uchar); *((uint16*)ptr) = emu->count; ptr += sizeof(uint16); @@ -371,7 +372,9 @@ namespace Underfoot ptr += sizeof(uint32); *((uint32*)ptr) = emu->entries[i].tics_remaining; ptr += sizeof(uint32); + *((uint32*)ptr) = emu->entries[i].num_hits; ptr += sizeof(uint32); + *((uint8*)ptr) = !emu->all_buffs; ptr += 1; } diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index ab263d6c4..89101ca2e 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -564,7 +564,7 @@ struct SpellBuffFade_Struct_Underfoot { /*008*/ float unknown008; /*012*/ uint32 spellid; /*016*/ uint32 duration; -/*020*/ uint32 unknown016; +/*020*/ uint32 num_hits; /*024*/ uint32 playerId; // Global player ID? /*028*/ uint32 unknown020; /*032*/ uint8 unknown0028[48]; @@ -589,6 +589,25 @@ struct SpellBuffFade_Struct { /*036*/ }; +#if 0 +struct BuffIconEntry_Struct { +/*000*/ uint32 buff_slot; +/*004*/ uint32 spell_id; +/*008*/ uint32 tics_remaining; +/*012*/ uint32 num_hits; +// char name[0]; caster name is also here sometimes +// uint8 unknownend; 1 when single, 0 when all opposite of all_buffs? +}; + +struct BuffIcon_Struct { +/*000*/ uint32 entity_id; +/*004*/ uint32 unknown004; +/*008*/ uint8 all_buffs; // 1 when updating all buffs, 0 when doing one +/*009*/ uint16 count; +/*011*/ BuffIconEntry_Struct entires[0]; +}; +#endif + struct BuffRemoveRequest_Struct { /*00*/ uint32 SlotID; diff --git a/zone/client.h b/zone/client.h index 235861396..fe05194bc 100644 --- a/zone/client.h +++ b/zone/client.h @@ -896,7 +896,8 @@ public: //This is used to later set the buff duration of the spell, in slot to duration. //Doesn't appear to work directly after the client recieves an action packet. - void SendBuffDurationPacket(uint16 spell_id, int duration, int inlevel); + void SendBuffDurationPacket(Buffs_Struct &buff); + void SendBuffNumHitPacket(Buffs_Struct &buff, int slot); void ProcessInspectRequest(Client* requestee, Client* requester); bool ClientFinishedLoading() { return (conn_state == ClientConnectFinished); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index add06714b..f86f43bf4 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3395,7 +3395,7 @@ void Mob::BuffProcess() { if(buffs[buffs_i].UpdateClient == true) { - CastToClient()->SendBuffDurationPacket(buffs[buffs_i].spellid, buffs[buffs_i].ticsremaining, buffs[buffs_i].casterlevel); + CastToClient()->SendBuffDurationPacket(buffs[buffs_i]); buffs[buffs_i].UpdateClient = false; } } @@ -5559,64 +5559,56 @@ void Mob::CheckNumHitsRemaining(uint8 type, uint32 buff_slot, uint16 spell_id) uint32 buff_max = GetMaxTotalSlots(); //Spell specific procs [Type 7,10,11] - if (IsValidSpell(spell_id)){ - - for(uint32 d = 0; d < buff_max; d++) { - - if((buffs[d].spellid == spell_id) && (buffs[d].numhits > 0) && (spells[buffs[d].spellid].numhitstype == type)){ - - if(--buffs[d].numhits == 0) { + if (IsValidSpell(spell_id)) { + for (uint32 d = 0; d < buff_max; d++) { + if (buffs[d].spellid == spell_id && buffs[d].numhits > 0 && + spells[buffs[d].spellid].numhitstype == type) { + if (--buffs[d].numhits == 0) { CastOnNumHitFade(buffs[d].spellid); - if(!TryFadeEffect(d)) + if (!TryFadeEffect(d)) BuffFadeBySlot(d, true); + } else if (IsClient()) { // still have numhits and client, update + CastToClient()->SendBuffNumHitPacket(buffs[d], d); } } } - } - - else if (type == 7){ - if (buff_slot > 0){ - - if(--buffs[buff_slot].numhits == 0) { + } else if (type == 7) { + if (buff_slot > 0) { + if (--buffs[buff_slot].numhits == 0) { CastOnNumHitFade(buffs[buff_slot].spellid); - if(!TryFadeEffect(buff_slot)) + if (!TryFadeEffect(buff_slot)) BuffFadeBySlot(buff_slot , true); - } + } else if (IsClient()) { // still have numhits and client, update + CastToClient()->SendBuffNumHitPacket(buffs[buff_slot], buff_slot); } - - else { - - for(int d = 0; d < buff_max; d++) { - - if(!m_spellHitsLeft[d]) + } else { + for (int d = 0; d < buff_max; d++) { + if (!m_spellHitsLeft[d]) continue; - if ((IsValidSpell(buffs[d].spellid)) && (m_spellHitsLeft[d] == buffs[d].spellid)) { - if(--buffs[d].numhits == 0) { + if (IsValidSpell(buffs[d].spellid) && m_spellHitsLeft[d] == buffs[d].spellid) { + if (--buffs[d].numhits == 0) { CastOnNumHitFade(buffs[d].spellid); m_spellHitsLeft[d] = 0; - if(!TryFadeEffect(d)) + if (!TryFadeEffect(d)) BuffFadeBySlot(d, true); + } else if (IsClient()) { // still have numhits and client, update + CastToClient()->SendBuffNumHitPacket(buffs[d], d); } } } } - } - - - else{ - - for(uint32 d = 0; d < buff_max; d++) { - - if((IsValidSpell(buffs[d].spellid)) && (buffs[d].numhits > 0) && (spells[buffs[d].spellid].numhitstype == type)){ - - if(--buffs[d].numhits == 0) { + } else { + for (uint32 d = 0; d < buff_max; d++) { + if (IsValidSpell(buffs[d].spellid) && buffs[d].numhits > 0 && + spells[buffs[d].spellid].numhitstype == type) { + if (--buffs[d].numhits == 0) { CastOnNumHitFade(buffs[d].spellid); - if(!TryFadeEffect(d)){ + if (!TryFadeEffect(d)) BuffFadeBySlot(d, true); - } + } else if (IsClient()) { // still have numhits and client, update + CastToClient()->SendBuffNumHitPacket(buffs[d], d); } - } } } diff --git a/zone/spells.cpp b/zone/spells.cpp index fb3cba454..310bc35bd 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5164,20 +5164,40 @@ void Mob::_StopSong() //Thus I use this in the buff process to update the correct duration once after casting //this allows AAs and focus effects that increase buff duration to work correctly, but could probably //be used for other things as well -void Client::SendBuffDurationPacket(uint16 spell_id, int duration, int inlevel) +void Client::SendBuffDurationPacket(Buffs_Struct &buff) { EQApplicationPacket* outapp; outapp = new EQApplicationPacket(OP_Buff, sizeof(SpellBuffFade_Struct)); SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*) outapp->pBuffer; sbf->entityid = GetID(); - sbf->slot=2; - sbf->spellid=spell_id; - sbf->slotid=0; - sbf->effect = inlevel > 0 ? inlevel : GetLevel(); - sbf->level = inlevel > 0 ? inlevel : GetLevel(); + sbf->slot = 2; + sbf->spellid = buff.spellid; + sbf->slotid = 0; + sbf->effect = buff.casterlevel > 0 ? buff.casterlevel : GetLevel(); + sbf->level = buff.casterlevel > 0 ? buff.casterlevel : GetLevel(); sbf->bufffade = 0; - sbf->duration = duration; + sbf->duration = buff.ticsremaining; + sbf->num_hits = buff.numhits; + FastQueuePacket(&outapp); +} + +void Client::SendBuffNumHitPacket(Buffs_Struct &buff, int slot) +{ + // UF+ use this packet + if (GetClientVersion() < EQClientUnderfoot) + return; + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_BuffCreate, sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct)); + BuffIcon_Struct *bi = (BuffIcon_Struct *)outapp->pBuffer; + bi->entity_id = GetID(); + bi->count = 1; + bi->all_buffs = 0; + + bi->entries[0].buff_slot = slot; + bi->entries[0].spell_id = buff.spellid; + bi->entries[0].tics_remaining = buff.ticsremaining; + bi->entries[0].num_hits = buff.numhits; FastQueuePacket(&outapp); } @@ -5252,6 +5272,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target) BuffIcon_Struct *buff = (BuffIcon_Struct*)outapp->pBuffer; buff->entity_id = GetID(); buff->count = count; + buff->all_buffs = 1; uint32 index = 0; for(unsigned int i = 0; i < buff_count; ++i) @@ -5261,6 +5282,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target) buff->entries[index].buff_slot = i; buff->entries[index].spell_id = buffs[i].spellid; buff->entries[index].tics_remaining = buffs[i].ticsremaining; + buff->entries[index].num_hits = buffs[i].numhits; ++index; } } @@ -5278,7 +5300,7 @@ void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration) buffs[i].ticsremaining = newDuration; if(IsClient()) { - CastToClient()->SendBuffDurationPacket(buffs[i].spellid, buffs[i].ticsremaining, buffs[i].casterlevel); + CastToClient()->SendBuffDurationPacket(buffs[i]); } } } From 4d768474e883cc0fef962bcb4265239b72de6b61 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 09:58:55 -0700 Subject: [PATCH 211/368] command_setfaction converted to QueryDatabase --- zone/command.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 2844e9daf..bc2813221 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -588,7 +588,7 @@ int command_realdispatch(Client *c, const char *message) /* QS: Player_Log_Issued_Commands */ if (RuleB(QueryServ, PlayerLogIssuedCommandes)){ std::string event_desc = StringFormat("Issued command :: '%s' in zoneid:%i instid:%i", message, c->GetZoneID(), c->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Issued_Commands, c->CharacterID(), event_desc); + QServ->PlayerLogEvent(Player_Log_Issued_Commands, c->CharacterID(), event_desc); } #ifdef COMMANDS_LOGGING @@ -815,17 +815,18 @@ void command_version(Client *c, const Seperator *sep) void command_setfaction(Client *c, const Seperator *sep) { - if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) + if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) { c->Message(0, "Usage: #setfaction [faction number]"); - else - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"Setting NPC %u to faction %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->argplus[1])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set npc_faction_id=%i where id=%i",atoi(sep->argplus[1]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } + return; + } + + auto npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); + c->Message(15,"Setting NPC %u to faction %i", npcTypeID, atoi(sep->argplus[1])); + + std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", + atoi(sep->argplus[1]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); } void command_serversidename(Client *c, const Seperator *sep) @@ -2397,8 +2398,8 @@ void command_showskills(Client *c, const Seperator *sep) c->Message(0, "Skills for %s", t->GetName()); for (SkillUseTypes i=Skill1HBlunt; i <= HIGHEST_SKILL; i=(SkillUseTypes)(i+1)) - c->Message(0, "Skill [%d] is at [%d] - %u", i, t->GetSkill(i), t->GetRawSkill(i)); -} + c->Message(0, "Skill [%d] is at [%d] - %u", i, t->GetSkill(i), t->GetRawSkill(i)); +} void command_findspell(Client *c, const Seperator *sep) { From 114d0ae11b8e2a38bde0518de3b7233f5199d02c Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:01:59 -0700 Subject: [PATCH 212/368] command_viewpetition converted to QueryDatabase --- zone/command.cpp | 48 ++++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index bc2813221..dfb7267b1 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1510,33 +1510,29 @@ void command_movechar(Client *c, const Seperator *sep) void command_viewpetition(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) + if (sep->arg[1][0] == 0) { c->Message(0, "Usage: #viewpetition (petition number) Type #listpetition for a list"); - else - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - int queryfound = 0; - MYSQL_RES *result; - MYSQL_ROW row; - c->Message(13," ID : Character Name , Petition Text"); - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT petid, charname, petitiontext from petitions order by petid"), errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) - { - if (strcasecmp(row[0],sep->argplus[1])== 0) - { - queryfound=1; - c->Message(15, " %s: %s , %s ",row[0],row[1],row[2]); - } - } - LogFile->write(EQEMuLog::Normal,"View petition request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); - if (queryfound==0) - c->Message(13,"There was an error in your request: ID not found! Please check the Id and try again."); - mysql_free_result(result); - } - safe_delete_array(query); - } + return; + } + + c->Message(13," ID : Character Name , Petition Text"); + + std::string query = "SELECT petid, charname, petitiontext FROM petitions ORDER BY petid"; + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + LogFile->write(EQEMuLog::Normal,"View petition request from %s, petition number: %i", c->GetName(), atoi(sep->argplus[1]) ); + + if (results.RowCount() == 0) { + c->Message(13,"There was an error in your request: ID not found! Please check the Id and try again."); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + if (strcasecmp(row[0], sep->argplus[1]) == 0) + c->Message(15, " %s: %s , %s ", row[0], row[1], row[2]); + } void command_petitioninfo(Client *c, const Seperator *sep) From 905a264744c58787611338a82a88d203cc7fcb52 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:03:07 -0700 Subject: [PATCH 213/368] command_petitioninfo converted to QueryDatabase --- zone/command.cpp | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index dfb7267b1..294934f46 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1537,31 +1537,27 @@ void command_viewpetition(Client *c, const Seperator *sep) void command_petitioninfo(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) + if (sep->arg[1][0] == 0) { c->Message(0, "Usage: #petitioninfo (petition number) Type #listpetition for a list"); - else - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - int queryfound = 0; - MYSQL_RES *result; - MYSQL_ROW row; + return; + } + + std::string query = "SELECT petid, charname, accountname, zone, charclass, charrace, charlevel FROM petitions ORDER BY petid"; + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + LogFile->write(EQEMuLog::Normal,"Petition information request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); + + if (results.RowCount() == 0) { + c->Message(13,"There was an error in your request: ID not found! Please check the Id and try again."); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + if (strcasecmp(row[0],sep->argplus[1])== 0) + c->Message(13," ID : %s Character Name: %s Account Name: %s Zone: %s Character Class: %s Character Race: %s Character Level: %s",row[0],row[1],row[2],row[3],row[4],row[5],row[6]); - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT petid, charname, accountname, zone, charclass, charrace, charlevel from petitions order by petid"), errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) - if (strcasecmp(row[0],sep->argplus[1])== 0) - { - queryfound=1; - c->Message(13," ID : %s Character Name: %s Account Name: %s Zone: %s Character Class: %s Character Race: %s Character Level: %s",row[0],row[1],row[2],row[3],row[4],row[5],row[6]); - } - LogFile->write(EQEMuLog::Normal,"Petition information request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); - if (queryfound==0) - c->Message(13,"There was an error in your request: ID not found! Please check the Id and try again."); - mysql_free_result(result); - } - safe_delete_array(query); - } } void command_delpetition(Client *c, const Seperator *sep) From ca2c2ccfac0b14e9640c5217b14a90e812c64edd Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:04:39 -0700 Subject: [PATCH 214/368] command_delpetition converted to QueryDatabase --- zone/command.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 294934f46..96daf6fd1 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1562,17 +1562,19 @@ void command_petitioninfo(Client *c, const Seperator *sep) void command_delpetition(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) + if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*") == 0) { c->Message(0, "Usage: #delpetition (petition number) Type #listpetition for a list"); - else { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(13,"Attempting to delete petition number: %i",atoi(sep->argplus[1])); - if (database.RunQuery(query, MakeAnyLenString(&query, "DELETE from petitions where petid=%i",atoi(sep->argplus[1])), errbuf)) { - LogFile->write(EQEMuLog::Normal,"Delete petition request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); - } - safe_delete_array(query); - } + return; + } + + c->Message(13,"Attempting to delete petition number: %i",atoi(sep->argplus[1])); + std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", atoi(sep->argplus[1])); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + LogFile->write(EQEMuLog::Normal,"Delete petition request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); + } void command_listnpcs(Client *c, const Seperator *sep) From 61cd48ff449e201274c967b10dfa294ea43cb211 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:07:25 -0700 Subject: [PATCH 215/368] command_findnpctype converted to QueryDatabase --- zone/command.cpp | 86 +++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 53 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 96daf6fd1..e652f3377 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -3096,67 +3096,47 @@ void command_peekinv(Client *c, const Seperator *sep) void command_findnpctype(Client *c, const Seperator *sep) { - if(sep->arg[1][0] == 0) + if(sep->arg[1][0] == 0) { c->Message(0, "Usage: #findnpctype [search criteria]"); - else - { - int id; - int count; - const int maxrows = 20; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query; - MYSQL_RES *result; - MYSQL_ROW row; + return; + } - query = new char[256]; + std::string query; - // If id evaluates to 0, then search as if user entered a string. - if ((id = atoi((const char *)sep->arg[1])) == 0) - MakeAnyLenString(&query, - "SELECT id,name" - " FROM npc_types WHERE name LIKE '%%%s%%'", - sep->arg[1]); - // Otherwise, look for just that npc id. - else - MakeAnyLenString(&query, - "SELECT id,name FROM npc_types WHERE id=%i", id); + int id = atoi((const char *)sep->arg[1]); + if (id == 0) // If id evaluates to 0, then search as if user entered a string. + query = StringFormat("SELECT id, name FROM npc_types WHERE name LIKE '%%%s%%'", sep->arg[1]); + else // Otherwise, look for just that npc id. + query = StringFormat("SELECT id, name FROM npc_types WHERE id = %i", id); - // If query runs successfully. - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - count = 0; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message (0, "Error querying database."); + c->Message (0, query.c_str()); + } - // Process each row returned. - while((row = mysql_fetch_row(result))) - { - // Limit to returning maxrows rows. - if (++count > maxrows) - { - c->Message (0, - "%i npc types shown. Too many results.", maxrows); - break; - } - c->Message (0, " %s: %s", row[0], row[1]); - } + if (results.RowCount() == 0) // No matches found. + c->Message (0, "No matches found for %s.", sep->arg[1]); - // If we did not hit the maxrows limit. - if (count <= maxrows) - c->Message (0, "Query complete. %i rows shown.", count); - // No matches found. - else if (count == 0) - c->Message (0, "No matches found for %s.", sep->arg[1]); + // If query runs successfully. + int count = 0; + const int maxrows = 20; - mysql_free_result(result); - } - // If query failed. - else - { - c->Message (0, "Error querying database."); - c->Message (0, query); - } + // Process each row returned. + for (auto row = results.begin(); row != results.end(); ++row) { + // Limit to returning maxrows rows. + if (++count > maxrows) { + c->Message (0, "%i npc types shown. Too many results.", maxrows); + break; + } + + c->Message (0, " %s: %s", row[0], row[1]); + } + + // If we did not hit the maxrows limit. + if (count <= maxrows) + c->Message (0, "Query complete. %i rows shown.", count); - safe_delete_array(query); - } } void command_findzone(Client *c, const Seperator *sep) From 5f5fba1117afae3b4b9e2f82cc051e3c65f9daa4 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:09:20 -0700 Subject: [PATCH 216/368] command_findzone converted to QueryDatabase --- zone/command.cpp | 83 ++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 49 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index e652f3377..8143189d7 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -3141,63 +3141,48 @@ void command_findnpctype(Client *c, const Seperator *sep) void command_findzone(Client *c, const Seperator *sep) { - if(sep->arg[1][0] == 0) + if(sep->arg[1][0] == 0) { c->Message(0, "Usage: #findzone [search criteria]"); - else - { - int id; - int count; - const int maxrows = 20; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query; - MYSQL_RES *result; - MYSQL_ROW row; + return; + } - query = new char[256]; + std::string query; + int id = atoi((const char *)sep->arg[1]); + if (id == 0) { // If id evaluates to 0, then search as if user entered a string. + char *escName = new char[strlen(sep->arg[1]) * 2 + 1]; + database.DoEscapeString(escName, sep->arg[1], strlen(sep->arg[1])); - // If id evaluates to 0, then search as if user entered a string. - if ((id = atoi((const char *)sep->arg[1])) == 0) - { - char *EscName = new char[strlen(sep->arg[1]) * 2 + 1]; - database.DoEscapeString(EscName, sep->arg[1], strlen(sep->arg[1])); + query = StringFormat("SELECT zoneidnumber, short_name, long_name FROM zone " + "WHERE long_name RLIKE '%s' AND version = 0", escName); + safe_delete_array(escName); + } + else // Otherwise, look for just that zoneidnumber. + query = StringFormat("SELECT zoneidnumber, short_name, long_name FROM zone " + "WHERE zoneidnumber = %i AND version = 0", id); - MakeAnyLenString(&query, "SELECT zoneidnumber,short_name,long_name FROM zone WHERE long_name rLIKE '%s' AND version=0", - EscName); - safe_delete_array(EscName); - } - // Otherwise, look for just that zoneidnumber. - else - MakeAnyLenString(&query, "SELECT zoneidnumber,short_name,long_name FROM zone WHERE zoneidnumber=%i AND version=0", id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message (0, "Error querying database."); + c->Message (0, query.c_str()); + return; + } - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - count = 0; + int count = 0; + const int maxrows = 20; - while((row = mysql_fetch_row(result))) - { - if (++count > maxrows) - { - c->Message (0, "%i zones shown. Too many results.", maxrows); - break; - } - c->Message (0, " %s: %s, %s", row[0], row[1], row[2]); - } + for(auto row = results.begin(); row != results.end(); ++row) { + if (++count > maxrows) { + c->Message (0, "%i zones shown. Too many results.", maxrows); + break; + } - if (count <= maxrows) - c->Message (0, "Query complete. %i rows shown.", count); - else if (count == 0) - c->Message (0, "No matches found for %s.", sep->arg[1]); + c->Message (0, " %s: %s, %s", row[0], row[1], row[2]); + } - mysql_free_result(result); - } - else - { - c->Message (0, "Error querying database."); - c->Message (0, query); - } - - safe_delete_array(query); - } + if (count <= maxrows) + c->Message (0, "Query complete. %i rows shown.", count); + else if (count == 0) + c->Message (0, "No matches found for %s.", sep->arg[1]); } void command_viewnpctype(Client *c, const Seperator *sep) From accedf7184fcfd8ce4e44d2ead019cced53a72eb Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:12:39 -0700 Subject: [PATCH 217/368] command_listpetition converted to QueryDatabase --- zone/command.cpp | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 8143189d7..b9b70a25e 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -3373,23 +3373,20 @@ void command_motd(Client *c, const Seperator *sep) void command_listpetition(Client *c, const Seperator *sep) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - bool header = false; - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT petid, charname, accountname from petitions order by petid"), errbuf, &result)) { - LogFile->write(EQEMuLog::Normal,"Petition list requested by %s", c->GetName()); - while ((row = mysql_fetch_row(result))) { - if(!header) { - header = true; - c->Message(13," ID : Character Name , Account Name"); - } - c->Message(15, " %s: %s , %s ",row[0],row[1],row[2]); - } - mysql_free_result(result); - } - safe_delete_array(query); + std::string query = "SELECT petid, charname, accountname FROM petitions ORDER BY petid"; + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + LogFile->write(EQEMuLog::Normal,"Petition list requested by %s", c->GetName()); + + if (results.RowCount() == 0) + return; + + c->Message(13," ID : Character Name , Account Name"); + + for (auto row = results.begin(); row != results.end(); ++row) + c->Message(15, " %s: %s , %s ",row[0],row[1],row[2]); } void command_equipitem(Client *c, const Seperator *sep) From d86fc1c1418855e832eb27061087dfc44749df07 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:16:26 -0700 Subject: [PATCH 218/368] command_spawnfix converted to QueryDatabase --- zone/command.cpp | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index b9b70a25e..05917fe5a 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -4511,33 +4511,31 @@ void command_npcspawn(Client *c, const Seperator *sep) } void command_spawnfix(Client *c, const Seperator *sep) { - Mob *t = c->GetTarget(); - if (!t || !t->IsNPC()) + Mob *targetMob = c->GetTarget(); + if (!targetMob || !targetMob->IsNPC()) { c->Message(0, "Error: #spawnfix: Need an NPC target."); - else { - Spawn2* s2 = t->CastToNPC()->respawn2; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + return; + } - if(!s2) { - c->Message(0, "#spawnfix FAILED -- cannot determine which spawn entry in the database this mob came from."); - } - else - { - if(database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawn2 SET x='%f', y='%f', z='%f', heading='%f' WHERE id='%i'",c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()), errbuf)) - { - c->LogSQL(query); - c->Message(0, "Updating coordinates successful."); - t->Depop(false); - } - else - { - c->Message(13, "Update failed! MySQL gave the following error:"); - c->Message(13, errbuf); - } - safe_delete_array(query); - } - } + Spawn2* s2 = targetMob->CastToNPC()->respawn2; + + if(!s2) { + c->Message(0, "#spawnfix FAILED -- cannot determine which spawn entry in the database this mob came from."); + return; + } + + std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'", + c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(13, "Update failed! MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Updating coordinates successful."); + targetMob->Depop(false); } void command_loc(Client *c, const Seperator *sep) From b3789c261b51ae87509e67c48ff96daa44c4113f Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:34:44 -0700 Subject: [PATCH 219/368] GetStartZone converted to QueryDatabase --- world/worlddb.cpp | 98 +++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 55 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 32c4ada48..cec7536ed 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -35,7 +35,7 @@ extern std::vector character_create_race_class_combos; // solar: the current stuff is at the bottom of this function void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs, uint32 ClientVersion) { Inventory *inv; - uint8 has_home = 0; + uint8 has_home = 0; uint8 has_bind = 0; /* Initialize Variables */ @@ -49,7 +49,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* /* Get Character Info */ std::string cquery = StringFormat( - "SELECT " + "SELECT " "`id`, " // 0 "name, " // 1 "gender, " // 2 @@ -70,8 +70,8 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* "drakkin_tattoo, " // 17 "drakkin_details, " // 18 "zone_id " // 19 - "FROM " - "character_data " + "FROM " + "character_data " "WHERE `account_id` = %i ORDER BY `name` LIMIT 10 ", account_id); auto results = database.QueryDatabase(cquery); int char_num = 0; for (auto row = results.begin(); row != results.end(); ++row) { @@ -108,11 +108,11 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* } /* Set Bind Point Data for any character that may possibly be missing it for any reason */ - cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %i LIMIT 2", character_id); + cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %i LIMIT 2", character_id); auto results_bind = database.QueryDatabase(cquery); has_home = 0; has_bind = 0; for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { if (row_b[6] && atoi(row_b[6]) == 1){ has_home = 1; } - if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; } + if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; } } if (has_home == 0 || has_bind == 0){ @@ -121,34 +121,34 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* auto results_bind = database.QueryDatabase(cquery); for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { /* If a bind_id is specified, make them start there */ - if (atoi(row_d[1]) != 0) { + if (atoi(row_d[1]) != 0) { pp.binds[4].zoneId = (uint32)atoi(row_d[1]); GetSafePoints(pp.binds[4].zoneId, 0, &pp.binds[4].x, &pp.binds[4].y, &pp.binds[4].z); } /* Otherwise, use the zone and coordinates given */ - else { + else { pp.binds[4].zoneId = (uint32)atoi(row_d[0]); - float x = atof(row_d[2]); - float y = atof(row_d[3]); - float z = atof(row_d[4]); - if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); } + float x = atof(row_d[2]); + float y = atof(row_d[3]); + float z = atof(row_d[4]); + if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); } pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z; - + } } pp.binds[0] = pp.binds[4]; /* If no home bind set, set it */ if (has_home == 0){ std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" - " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", - character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 1); + " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", + character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 1); auto results_bset = QueryDatabase(query); ThrowDBError(results_bset.ErrorMessage(), "WorldDatabase::GetCharSelectInfo Set Home Point", query); } /* If no regular bind set, set it */ if (has_bind == 0){ std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" - " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", - character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0); + " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", + character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0); auto results_bset = QueryDatabase(query); ThrowDBError(results_bset.ErrorMessage(), "WorldDatabase::GetCharSelectInfo Set Bind Point", query); } } @@ -166,7 +166,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); auto results_b = database.QueryDatabase(cquery); uint8 slot = 0; for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { - slot = atoi(row_b[0]); + slot = atoi(row_b[0]); pp.item_tint[slot].rgb.red = atoi(row_b[1]); pp.item_tint[slot].rgb.green = atoi(row_b[2]); pp.item_tint[slot].rgb.blue = atoi(row_b[3]); @@ -187,7 +187,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* if (pp.item_tint[material].rgb.use_tint){ color = pp.item_tint[material].color; } else{ color = item->GetItem()->Color; } - cs->cs_colors[char_num][material].color = color; + cs->cs_colors[char_num][material].color = color; /* Weapons are handled a bit differently */ if ((material == MaterialPrimary) || (material == MaterialSecondary)) { @@ -227,49 +227,28 @@ int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum) { bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row = 0; - int rows; - if(!in_pp || !in_cc) return false; in_pp->x = in_pp->y = in_pp->z = in_pp->heading = in_pp->zone_id = 0; in_pp->binds[0].x = in_pp->binds[0].y = in_pp->binds[0].z = in_pp->binds[0].zoneId = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "SELECT x,y,z,heading,zone_id,bind_id FROM start_zones WHERE player_choice=%i AND player_class=%i " - "AND player_deity=%i AND player_race=%i", - in_cc->start_zone, - in_cc->class_, - in_cc->deity, - in_cc->race), errbuf, &result)) - { - LogFile->write(EQEMuLog::Error, "Start zone query failed: %s : %s\n", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT x, y, z, heading, zone_id, bind_id " + "FROM start_zones WHERE player_choice = % i " + "AND player_class = %i AND player_deity = %i " + "AND player_race = %i", + in_cc->start_zone, in_cc->class_, in_cc->deity, + in_cc->race); + auto results = QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Start zone query failed: %s : %s\n", query.c_str(), results.ErrorMessage().c_str()); return false; } - LogFile->write(EQEMuLog::Status, "Start zone query: %s\n", query); - safe_delete_array(query); + LogFile->write(EQEMuLog::Status, "Start zone query: %s\n", query.c_str()); - if((rows = mysql_num_rows(result)) > 0) - row = mysql_fetch_row(result); - - if(row) - { - LogFile->write(EQEMuLog::Status, "Found starting location in start_zones"); - in_pp->x = atof(row[0]); - in_pp->y = atof(row[1]); - in_pp->z = atof(row[2]); - in_pp->heading = atof(row[3]); - in_pp->zone_id = atoi(row[4]); - in_pp->binds[0].zoneId = atoi(row[5]); - } - else - { - printf("No start_zones entry in database, using defaults\n"); + if (results.RowCount() == 0) { + printf("No start_zones entry in database, using defaults\n"); switch(in_cc->start_zone) { case 0: @@ -357,6 +336,16 @@ bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* break; } } + } + else { + LogFile->write(EQEMuLog::Status, "Found starting location in start_zones"); + auto row = results.begin(); + in_pp->x = atof(row[0]); + in_pp->y = atof(row[1]); + in_pp->z = atof(row[2]); + in_pp->heading = atof(row[3]); + in_pp->zone_id = atoi(row[4]); + in_pp->binds[0].zoneId = atoi(row[5]); } if(in_pp->x == 0 && in_pp->y == 0 && in_pp->z == 0) @@ -364,8 +353,7 @@ bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* if(in_pp->binds[0].x == 0 && in_pp->binds[0].y == 0 && in_pp->binds[0].z == 0) database.GetSafePoints(in_pp->binds[0].zoneId, 0, &in_pp->binds[0].x, &in_pp->binds[0].y, &in_pp->binds[0].z); - if(result) - mysql_free_result(result); + return true; } @@ -395,7 +383,7 @@ bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Stru " FROM start_zones " " WHERE zone_id = %i " " AND player_class = %i " - " AND player_deity = %i" + " AND player_deity = %i" " AND player_race = %i", in_cc->start_zone, in_cc->class_, From 7f5f805c10f07784bef1dc51e49eccd05b0320dd Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:36:04 -0700 Subject: [PATCH 220/368] GetStartZoneSoF converted to QueryDatabase --- world/worlddb.cpp | 61 ++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index cec7536ed..2e486eb56 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -359,7 +359,6 @@ bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc) { - // SoF doesn't send the player_choice field in character creation, it now sends the real zoneID instead. // // For SoF, search for an entry in start_zones with a matching zone_id, class, race and deity. @@ -367,53 +366,25 @@ bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Stru // For now, if no row matching row is found, send them to Crescent Reach, as that is probably the most likely // reason for no match being found. // - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row = 0; - int rows; - if(!in_pp || !in_cc) return false; in_pp->x = in_pp->y = in_pp->z = in_pp->heading = in_pp->zone_id = 0; in_pp->binds[0].x = in_pp->binds[0].y = in_pp->binds[0].z = in_pp->binds[0].zoneId = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "SELECT x, y, z, heading, bind_id " - " FROM start_zones " - " WHERE zone_id = %i " - " AND player_class = %i " - " AND player_deity = %i" - " AND player_race = %i", - in_cc->start_zone, - in_cc->class_, - in_cc->deity, - in_cc->race), errbuf, &result)) - { - LogFile->write(EQEMuLog::Status, "SoF Start zone query failed: %s : %s\n", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT x, y, z, heading, bind_id FROM start_zones WHERE zone_id = %i " + "AND player_class = %i AND player_deity = %i AND player_race = %i", + in_cc->start_zone, in_cc->class_, in_cc->deity, in_cc->race); + auto results = QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Status, "SoF Start zone query failed: %s : %s\n", query.c_str(), results.ErrorMessage().c_str()); return false; } - LogFile->write(EQEMuLog::Status, "SoF Start zone query: %s\n", query); - safe_delete_array(query); + LogFile->write(EQEMuLog::Status, "SoF Start zone query: %s\n", query.c_str()); - if((rows = mysql_num_rows(result)) > 0) - row = mysql_fetch_row(result); - - if(row) - { - LogFile->write(EQEMuLog::Status, "Found starting location in start_zones"); - in_pp->x = atof(row[0]); - in_pp->y = atof(row[1]); - in_pp->z = atof(row[2]); - in_pp->heading = atof(row[3]); - in_pp->zone_id = in_cc->start_zone; - in_pp->binds[0].zoneId = atoi(row[4]); - } - else - { - printf("No start_zones entry in database, using defaults\n"); + if (results.RowCount() == 0) { + printf("No start_zones entry in database, using defaults\n"); if(in_cc->start_zone == RuleI(World, TutorialZoneID)) in_pp->zone_id = in_cc->start_zone; @@ -423,7 +394,16 @@ bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Stru in_pp->z = in_pp->binds[0].z = 0.79; in_pp->zone_id = in_pp->binds[0].zoneId = 394; // Crescent Reach. } - + } + else { + LogFile->write(EQEMuLog::Status, "Found starting location in start_zones"); + auto row = results.begin(); + in_pp->x = atof(row[0]); + in_pp->y = atof(row[1]); + in_pp->z = atof(row[2]); + in_pp->heading = atof(row[3]); + in_pp->zone_id = in_cc->start_zone; + in_pp->binds[0].zoneId = atoi(row[4]); } if(in_pp->x == 0 && in_pp->y == 0 && in_pp->z == 0) @@ -431,8 +411,7 @@ bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Stru if(in_pp->binds[0].x == 0 && in_pp->binds[0].y == 0 && in_pp->binds[0].z == 0) database.GetSafePoints(in_pp->binds[0].zoneId, 0, &in_pp->binds[0].x, &in_pp->binds[0].y, &in_pp->binds[0].z); - if(result) - mysql_free_result(result); + return true; } From 28ec84a6c9c658eebd9157c55f3b4a8d6e17512e Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:37:19 -0700 Subject: [PATCH 221/368] GetLauncherList converted to QueryDatabase --- world/worlddb.cpp | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 2e486eb56..cac9fd7e8 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -416,26 +416,18 @@ bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Stru } void WorldDatabase::GetLauncherList(std::vector &rl) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - rl.clear(); - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT name FROM launcher" ) - , errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) { - rl.push_back(row[0]); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "WorldDatabase::GetLauncherList: %s", errbuf); - } - safe_delete_array(query); + const std::string query = "SELECT name FROM launcher"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "WorldDatabase::GetLauncherList: %s", results.ErrorMessage().c_str()); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + rl.push_back(row[0]); + } void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) { From 0b352ff0f78986c17e98aa55558311b78f3e981f Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:38:14 -0700 Subject: [PATCH 222/368] SetMailKey converted to QueryDatabase fix SetMailKey --- world/worlddb.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index cac9fd7e8..d780571d5 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -432,9 +432,6 @@ void WorldDatabase::GetLauncherList(std::vector &rl) { void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char MailKeyString[17]; if(RuleB(Chat, EnableMailKeyIPVerification) == true) @@ -442,12 +439,11 @@ void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) { else sprintf(MailKeyString, "%08X", MailKey); - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE `character_data` SET mailkey = '%s' WHERE id='%i'", - MailKeyString, CharID), errbuf)) - - LogFile->write(EQEMuLog::Error, "WorldDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, errbuf); - - safe_delete_array(query); + std::string query = StringFormat("UPDATE character_data SET mailkey = '%s' WHERE id = '%i'", + MailKeyString, CharID); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "WorldDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, results.ErrorMessage().c_str()); } From 2d8e9bf058fdd9f536587cd5885a0b5b52c457f8 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:43:13 -0700 Subject: [PATCH 223/368] GetCharacterLevel converted to QueryDatabase --- world/worlddb.cpp | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index d780571d5..b42b7dddb 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -449,28 +449,20 @@ void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) { bool WorldDatabase::GetCharacterLevel(const char *name, int &level) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT level FROM character_data WHERE name = '%s'", name); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "WorldDatabase::GetCharacterLevel: %s", results.ErrorMessage().c_str()); + return false; + } - if(RunQuery(query, MakeAnyLenString(&query, "SELECT `level` FROM `character_data` WHERE `name` = '%s'", name), errbuf, &result)) - { - if(row = mysql_fetch_row(result)) - { - level = atoi(row[0]); - mysql_free_result(result); - safe_delete_array(query); - return true; - } - mysql_free_result(result); - } - else - { - LogFile->write(EQEMuLog::Error, "WorldDatabase::GetCharacterLevel: %s", errbuf); - } - safe_delete_array(query); - return false; + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + level = atoi(row[0]); + + return true; } bool WorldDatabase::LoadCharacterCreateAllocations() { From c4de954b1538cca3635724cdc0bdb1eecbfbdfb1 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:44:29 -0700 Subject: [PATCH 224/368] LoadCharacterCreateAllocations converted to QueryDatabase --- world/worlddb.cpp | 56 +++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index b42b7dddb..289a919cd 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -468,37 +468,31 @@ bool WorldDatabase::GetCharacterLevel(const char *name, int &level) bool WorldDatabase::LoadCharacterCreateAllocations() { character_create_allocations.clear(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, MakeAnyLenString(&query, "SELECT * FROM char_create_point_allocations order by id"), errbuf, &result)) { - safe_delete_array(query); - while(row = mysql_fetch_row(result)) { - RaceClassAllocation allocate; - int r = 0; - allocate.Index = atoi(row[r++]); - allocate.BaseStats[0] = atoi(row[r++]); - allocate.BaseStats[3] = atoi(row[r++]); - allocate.BaseStats[1] = atoi(row[r++]); - allocate.BaseStats[2] = atoi(row[r++]); - allocate.BaseStats[4] = atoi(row[r++]); - allocate.BaseStats[5] = atoi(row[r++]); - allocate.BaseStats[6] = atoi(row[r++]); - allocate.DefaultPointAllocation[0] = atoi(row[r++]); - allocate.DefaultPointAllocation[3] = atoi(row[r++]); - allocate.DefaultPointAllocation[1] = atoi(row[r++]); - allocate.DefaultPointAllocation[2] = atoi(row[r++]); - allocate.DefaultPointAllocation[4] = atoi(row[r++]); - allocate.DefaultPointAllocation[5] = atoi(row[r++]); - allocate.DefaultPointAllocation[6] = atoi(row[r++]); - character_create_allocations.push_back(allocate); - } - mysql_free_result(result); - } else { - safe_delete_array(query); - return false; - } + std::string query = "SELECT * FROM char_create_point_allocations ORDER BY id"; + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + for (auto row = results.begin(); row != results.end(); ++row) { + RaceClassAllocation allocate; + allocate.Index = atoi(row[0]); + allocate.BaseStats[0] = atoi(row[1]); + allocate.BaseStats[3] = atoi(row[2]); + allocate.BaseStats[1] = atoi(row[3]); + allocate.BaseStats[2] = atoi(row[4]); + allocate.BaseStats[4] = atoi(row[5]); + allocate.BaseStats[5] = atoi(row[6]); + allocate.BaseStats[6] = atoi(row[7]); + allocate.DefaultPointAllocation[0] = atoi(row[8]); + allocate.DefaultPointAllocation[3] = atoi(row[9]); + allocate.DefaultPointAllocation[1] = atoi(row[10]); + allocate.DefaultPointAllocation[2] = atoi(row[11]); + allocate.DefaultPointAllocation[4] = atoi(row[12]); + allocate.DefaultPointAllocation[5] = atoi(row[13]); + allocate.DefaultPointAllocation[6] = atoi(row[14]); + + character_create_allocations.push_back(allocate); + } return true; } From eeb9a6ab65668b1b0f65fea3f85c8ad60549fb2a Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:45:50 -0700 Subject: [PATCH 225/368] LoadCharacterCreateCombos converted to QueryDatabase --- world/worlddb.cpp | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 289a919cd..be82130e3 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -500,27 +500,21 @@ bool WorldDatabase::LoadCharacterCreateAllocations() { bool WorldDatabase::LoadCharacterCreateCombos() { character_create_race_class_combos.clear(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, MakeAnyLenString(&query, "select * from char_create_combinations order by race, class, deity, start_zone"), errbuf, &result)) { - safe_delete_array(query); - while(row = mysql_fetch_row(result)) { - RaceClassCombos combo; - int r = 0; - combo.AllocationIndex = atoi(row[r++]); - combo.Race = atoi(row[r++]); - combo.Class = atoi(row[r++]); - combo.Deity = atoi(row[r++]); - combo.Zone = atoi(row[r++]); - combo.ExpansionRequired = atoi(row[r++]); - character_create_race_class_combos.push_back(combo); - } - mysql_free_result(result); - } else { - safe_delete_array(query); - return false; + std::string query = "SELECT * FROM char_create_combinations ORDER BY race, class, deity, start_zone"; + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + for (auto row = results.begin(); row != results.end(); ++row) { + RaceClassCombos combo; + combo.AllocationIndex = atoi(row[0]); + combo.Race = atoi(row[1]); + combo.Class = atoi(row[2]); + combo.Deity = atoi(row[3]); + combo.Zone = atoi(row[4]); + combo.ExpansionRequired = atoi(row[5]); + + character_create_race_class_combos.push_back(combo); } return true; From b379bdd36e4dde4a84d81dbbc6eed1a747fdf4f2 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 13:23:52 -0700 Subject: [PATCH 226/368] SetHideMe converted to QueryDatabase --- common/shareddb.cpp | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index aecfa0488..1a2f1d81f 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -48,18 +48,14 @@ SharedDatabase::~SharedDatabase() { bool SharedDatabase::SetHideMe(uint32 account_id, uint8 hideme) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET hideme = %i where id = %i", hideme, account_id), errbuf)) { - std::cerr << "Error in SetGMSpeed query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("UPDATE account SET hideme = %i WHERE id = %i", hideme, account_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetGMSpeed query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); return true; - } uint8 SharedDatabase::GetGMSpeed(uint32 account_id) @@ -357,13 +353,13 @@ bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, " AND (`zoneid` = %i OR `zoneid` = 0)" " AND gm <= %i ORDER BY id", si_race, si_class, si_deity, si_current_zone, admin_level); - auto results = QueryDatabase(query); + auto results = QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { - itemid = atoi(row[0]); + itemid = atoi(row[0]); charges = atoi(row[1]); slot = atoi(row[2]); myitem = GetItem(itemid); - if(!myitem) + if(!myitem) continue; ItemInst* myinst = CreateBaseItem(myitem, charges); if(slot < 0) @@ -604,7 +600,7 @@ bool SharedDatabase::GetInventory(uint32 account_id, char* name, Inventory* inv) bool ret = false; // Retrieve character inventory - if (RunQuery(query, MakeAnyLenString(&query, + if (RunQuery(query, MakeAnyLenString(&query, " SELECT `slotid`, `itemid`, `charges`, `color`, `augslot1`, `augslot2`, `augslot3`, `augslot4`, `augslot5`, `instnodrop`, `custom_data`" " FROM `inventory`" " INNER JOIN `character_data` ch ON ch.id = charid" @@ -1688,7 +1684,7 @@ bool SharedDatabase::LoadBaseData() { EQEmu::IPCMutex mutex("base_data"); mutex.Lock(); base_data_mmf = new EQEmu::MemoryMappedFile("shared/base_data"); - + int size = 16 * (GetMaxBaseDataLevel() + 1) * sizeof(BaseDataStruct); if(size == 0) { EQ_EXCEPT("SharedDatabase", "Base Data size is zero"); @@ -1713,9 +1709,9 @@ void SharedDatabase::LoadBaseData(void *data, int max_level) { const char *query = "SELECT * FROM base_data ORDER BY level, class ASC"; MYSQL_RES *result; MYSQL_ROW row; - + if(RunQuery(query, strlen(query), errbuf, &result)) { - + int lvl = 0; int cl = 0; while (row = mysql_fetch_row(result)) { @@ -1994,10 +1990,10 @@ const LootDrop_Struct* SharedDatabase::GetLootDrop(uint32 lootdrop_id) { return nullptr; } -void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message) { +void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message) { std::string query = StringFormat("SELECT `inspect_message` FROM `character_inspect_messages` WHERE `id` = %u LIMIT 1", character_id); auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "SharedDatabase::LoadCharacterInspectMessage", query); - auto row = results.begin(); + auto row = results.begin(); memcpy(message, "", sizeof(InspectMessage_Struct)); for (auto row = results.begin(); row != results.end(); ++row) { memcpy(message, row[0], sizeof(InspectMessage_Struct)); @@ -2006,7 +2002,7 @@ void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMes void SharedDatabase::SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message) { std::string query = StringFormat("REPLACE INTO `character_inspect_messages` (id, inspect_message) VALUES (%u, '%s')", character_id, EscapeString(message->text).c_str()); - auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "SharedDatabase::SaveCharacterInspectMessage", query); + auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "SharedDatabase::SaveCharacterInspectMessage", query); } void SharedDatabase::GetBotInspectMessage(uint32 botid, InspectMessage_Struct* message) { From edc2567b1029114a47bfe18a327ded43c00a9230 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 13:26:45 -0700 Subject: [PATCH 227/368] GetGMSpeed converted to QueryDatabase --- common/shareddb.cpp | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 1a2f1d81f..b1527bffe 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -60,37 +60,19 @@ bool SharedDatabase::SetHideMe(uint32 account_id, uint8 hideme) uint8 SharedDatabase::GetGMSpeed(uint32 account_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT gmspeed FROM account where id='%i'", account_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint8 gmspeed = atoi(row[0]); - mysql_free_result(result); - return gmspeed; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else - { - - std::cerr << "Error in GetGMSpeed query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; + std::string query = StringFormat("SELECT gmspeed FROM account WHERE id = '%i'", account_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetGMSpeed query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - return 0; + if (results.RowCount() != 1) + return 0; + auto row = results.begin(); + return atoi(row[0]); } bool SharedDatabase::SetGMSpeed(uint32 account_id, uint8 gmspeed) From bcf282da913ed4cde6f839f7af6e33637a65f3c5 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 13:27:26 -0700 Subject: [PATCH 228/368] SetGMSpeed converted to QueryDatabase --- common/shareddb.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index b1527bffe..e1166689c 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -77,18 +77,14 @@ uint8 SharedDatabase::GetGMSpeed(uint32 account_id) bool SharedDatabase::SetGMSpeed(uint32 account_id, uint8 gmspeed) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET gmspeed = %i where id = %i", gmspeed, account_id), errbuf)) { - std::cerr << "Error in SetGMSpeed query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("UPDATE account SET gmspeed = %i WHERE id = %i", gmspeed, account_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetGMSpeed query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); return true; - } uint32 SharedDatabase::GetTotalTimeEntitledOnAccount(uint32 AccountID) { From 95208eb24e53d3d396db84c8b7921fbc76e99be9 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 13:31:06 -0700 Subject: [PATCH 229/368] SaveCursor --- common/shareddb.cpp | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index e1166689c..0f94dc2b8 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -99,27 +99,26 @@ uint32 SharedDatabase::GetTotalTimeEntitledOnAccount(uint32 AccountID) { bool SharedDatabase::SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end) { - iter_queue it; - int i; - bool ret = true; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; // Delete cursor items - if ((ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid = %i AND ((slotid >= 8000 AND slotid <= 8999) OR slotid = %i OR (slotid >= %i AND slotid <= %i))", - char_id, MainCursor, EmuConstants::CURSOR_BAG_BEGIN, EmuConstants::CURSOR_BAG_END), errbuf))) { + std::string query = StringFormat("DELETE FROM inventory WHERE charid = %i " + "AND ((slotid >= 8000 AND slotid <= 8999) " + "OR slotid = %i OR (slotid >= %i AND slotid <= %i) )", + char_id, MainCursor, + EmuConstants::CURSOR_BAG_BEGIN, EmuConstants::CURSOR_BAG_END); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cout << "Clearing cursor failed: " << results.ErrorMessage() << std::endl; + return false; + } - for (it = start, i = 8000; it != end; ++it, i++) { - ItemInst *inst = *it; - if (!(ret = SaveInventory(char_id, inst, (i == 8000) ? MainCursor : i))) - break; - } - } - else { - std::cout << "Clearing cursor failed: " << errbuf << std::endl; - } - safe_delete_array(query); + int i = 8000; + for(auto it = start; it != end; ++it, i++) { + ItemInst *inst = *it; + if (!SaveInventory(char_id,inst,(i == 8000) ? MainCursor : i)) + return false; + } - return ret; + return true; } bool SharedDatabase::VerifyInventory(uint32 account_id, int16 slot_id, const ItemInst* inst) From 2fbd170188e0ca547a58f7711c6c870ca0b8e0e9 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 13:32:01 -0700 Subject: [PATCH 230/368] VerifyInventory converted to QueryDatabase --- common/shareddb.cpp | 51 +++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 0f94dc2b8..ee1ee271c 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -123,39 +123,36 @@ bool SharedDatabase::SaveCursor(uint32 char_id, std::list::const_iter bool SharedDatabase::VerifyInventory(uint32 account_id, int16 slot_id, const ItemInst* inst) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; // Delete cursor items - if (!RunQuery(query, MakeAnyLenString(&query, - "SELECT itemid,charges FROM sharedbank " - "WHERE acctid=%d AND slotid=%d", - account_id, slot_id), errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error runing inventory verification query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT itemid, charges FROM sharedbank " + "WHERE acctid = %d AND slotid = %d", + account_id, slot_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error runing inventory verification query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); //returning true is less harmful in the face of a query error - return(true); + return true; } - safe_delete_array(query); - row = mysql_fetch_row(result); - bool found = false; - if(row) { - uint32 id = atoi(row[0]); - uint16 charges = atoi(row[1]); + if (results.RowCount() == 0) + return false; - uint16 expect_charges = 0; - if(inst->GetCharges() >= 0) - expect_charges = inst->GetCharges(); - else - expect_charges = 0x7FFF; + auto row = results.begin(); - if(id == inst->GetItem()->ID && charges == expect_charges) - found = true; - } - mysql_free_result(result); - return(found); + uint32 id = atoi(row[0]); + uint16 charges = atoi(row[1]); + + uint16 expect_charges = 0; + + if(inst->GetCharges() >= 0) + expect_charges = inst->GetCharges(); + else + expect_charges = 0x7FFF; + + if(id != inst->GetItem()->ID || charges != expect_charges) + return false; + + return true; } bool SharedDatabase::SaveInventory(uint32 char_id, const ItemInst* inst, int16 slot_id) { From a3347579e6a64fe5a7768264fa1567dcc84f7950 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 13:45:50 -0700 Subject: [PATCH 231/368] SaveInventory converted into 4 methods, converted to QueryDatabase --- common/shareddb.cpp | 234 ++++++++++++++++++++++++++------------------ common/shareddb.h | 4 + 2 files changed, 145 insertions(+), 93 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index ee1ee271c..cfe9e0939 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -156,118 +156,166 @@ bool SharedDatabase::VerifyInventory(uint32 account_id, int16 slot_id, const Ite } bool SharedDatabase::SaveInventory(uint32 char_id, const ItemInst* inst, int16 slot_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - bool ret = false; - uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; //never save tribute slots: if(slot_id >= EmuConstants::TRIBUTE_BEGIN && slot_id <= EmuConstants::TRIBUTE_END) - return(true); + return true; - if (inst && inst->IsType(ItemClassCommon)) { + if (slot_id >= EmuConstants::SHARED_BANK_BEGIN && slot_id <= EmuConstants::SHARED_BANK_BAGS_END) { + // Shared bank inventory + if (!inst) + return DeleteSharedBankSlot(char_id, slot_id); + else + return UpdateSharedBankSlot(char_id, inst, slot_id); + } + else if (!inst) // All other inventory + return DeleteInventorySlot(char_id, slot_id); + + return UpdateInventorySlot(char_id, inst, slot_id); +} + +bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const ItemInst* inst, int16 slot_id) { + + uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; + if (inst->IsType(ItemClassCommon)) for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { ItemInst *auginst=inst->GetItem(i); augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : NO_ITEM; } - } - if (slot_id >= EmuConstants::SHARED_BANK_BEGIN && slot_id <= EmuConstants::SHARED_BANK_BAGS_END) { // Shared bank inventory - if (!inst) { - // Delete item - uint32 account_id = GetAccountIDByChar(char_id); - uint32 len_query = MakeAnyLenString(&query, "DELETE FROM sharedbank WHERE acctid=%i AND slotid=%i", - account_id, slot_id); + uint16 charges = 0; + if(inst->GetCharges() >= 0) + charges = inst->GetCharges(); + else + charges = 0x7FFF; - ret = RunQuery(query, len_query, errbuf); + // Update/Insert item + std::string query = StringFormat("REPLACE INTO inventory " + "(charid, slotid, itemid, charges, instnodrop, custom_data, color, " + "augslot1, augslot2, augslot3, augslot4, augslot5) " + "VALUES( %lu, %lu, %lu, %lu, %lu, '%s', %lu, " + "%lu, %lu, %lu, %lu, %lu)", + (unsigned long)char_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, + (unsigned long)charges, (unsigned long)(inst->IsInstNoDrop()? 1: 0), + inst->GetCustomDataString().c_str(), (unsigned long)inst->GetColor(), + (unsigned long)augslot[0], (unsigned long)augslot[1], (unsigned long)augslot[2], + (unsigned long)augslot[3],(unsigned long)augslot[4]); + auto results = QueryDatabase(query); - // Delete bag slots, if need be - if (ret && Inventory::SupportsContainers(slot_id)) { - safe_delete_array(query); - int16 base_slot_id = Inventory::CalcSlotId(slot_id, SUB_BEGIN); - ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM sharedbank WHERE acctid=%i AND slotid>=%i AND slotid<%i", - account_id, base_slot_id, (base_slot_id+10)), errbuf); - } - - // @merth: need to delete augments here - } - else { - // Update/Insert item - uint32 account_id = GetAccountIDByChar(char_id); - uint16 charges = 0; - if(inst->GetCharges() >= 0) - charges = inst->GetCharges(); - else - charges = 0x7FFF; - - uint32 len_query = MakeAnyLenString(&query, - "REPLACE INTO sharedbank " - " (acctid,slotid,itemid,charges,custom_data," - " augslot1,augslot2,augslot3,augslot4,augslot5)" - " VALUES(%lu,%lu,%lu,%lu,'%s'," - " %lu,%lu,%lu,%lu,%lu)", - (unsigned long)account_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, (unsigned long)charges, - inst->GetCustomDataString().c_str(), - (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4]); - - - ret = RunQuery(query, len_query, errbuf); - } - } - else { // All other inventory - if (!inst) { - // Delete item - ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND slotid=%i", - char_id, slot_id), errbuf); - - // Delete bag slots, if need be - if (ret && Inventory::SupportsContainers(slot_id)) { - safe_delete_array(query); - int16 base_slot_id = Inventory::CalcSlotId(slot_id, SUB_BEGIN); - ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND slotid>=%i AND slotid<%i", - char_id, base_slot_id, (base_slot_id+10)), errbuf); - } - - // @merth: need to delete augments here - } - else { - uint16 charges = 0; - if(inst->GetCharges() >= 0) - charges = inst->GetCharges(); - else - charges = 0x7FFF; - // Update/Insert item - uint32 len_query = MakeAnyLenString(&query, - "REPLACE INTO inventory " - " (charid,slotid,itemid,charges,instnodrop,custom_data,color," - " augslot1,augslot2,augslot3,augslot4,augslot5)" - " VALUES(%lu,%lu,%lu,%lu,%lu,'%s',%lu," - " %lu,%lu,%lu,%lu,%lu)", - (unsigned long)char_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, (unsigned long)charges, - (unsigned long)(inst->IsInstNoDrop() ? 1:0),inst->GetCustomDataString().c_str(),(unsigned long)inst->GetColor(), - (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4] ); - - ret = RunQuery(query, len_query, errbuf); - } - } - - if (!ret) - LogFile->write(EQEMuLog::Error, "SaveInventory query '%s': %s", query, errbuf); - safe_delete_array(query); - - // Save bag contents, if slot supports bag contents - if (inst && inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) { + // Save bag contents, if slot supports bag contents + if (inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) for (uint8 idx = SUB_BEGIN; idx < EmuConstants::ITEM_CONTAINER_SIZE; idx++) { const ItemInst* baginst = inst->GetItem(idx); SaveInventory(char_id, baginst, Inventory::CalcSlotId(slot_id, idx)); } - } - // @merth: need to save augments here + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "UpdateInventorySlot query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } - return ret; + return true; } +bool SharedDatabase::UpdateSharedBankSlot(uint32 char_id, const ItemInst* inst, int16 slot_id) { + + uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; + if (inst->IsType(ItemClassCommon)) + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { + ItemInst *auginst=inst->GetItem(i); + augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : NO_ITEM; + } + +// Update/Insert item + uint32 account_id = GetAccountIDByChar(char_id); + uint16 charges = 0; + if(inst->GetCharges() >= 0) + charges = inst->GetCharges(); + else + charges = 0x7FFF; + + std::string query = StringFormat("REPLACE INTO sharedbank " + "(acctid, slotid, itemid, charges, custom_data, " + "augslot1, augslot2, augslot3, augslot4, augslot5) " + "VALUES( %lu, %lu, %lu, %lu, '%s', " + "%lu, %lu, %lu, %lu, %lu)", + (unsigned long)account_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, + (unsigned long)charges, inst->GetCustomDataString().c_str(), (unsigned long)augslot[0], + (unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4]); + auto results = QueryDatabase(query); + + // Save bag contents, if slot supports bag contents + if (inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) + for (uint8 idx = SUB_BEGIN; idx < EmuConstants::ITEM_CONTAINER_SIZE; idx++) { + const ItemInst* baginst = inst->GetItem(idx); + SaveInventory(char_id, baginst, Inventory::CalcSlotId(slot_id, idx)); + } + + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "UpdateSharedBankSlot query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + return true; +} + +bool SharedDatabase::DeleteInventorySlot(uint32 char_id, int16 slot_id) { + + // Delete item + std::string query = StringFormat("DELETE FROM inventory WHERE charid = %i AND slotid = %i", char_id, slot_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "DeleteInventorySlot query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + // Delete bag slots, if need be + if (!Inventory::SupportsContainers(slot_id)) + return true; + + int16 base_slot_id = Inventory::CalcSlotId(slot_id, SUB_BEGIN); + query = StringFormat("DELETE FROM inventory WHERE charid = %i AND slotid >= %i AND slotid < %i", + char_id, base_slot_id, (base_slot_id+10)); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "DeleteInventorySlot, bags query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + // @merth: need to delete augments here + return true; +} + +bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) { + + // Delete item + uint32 account_id = GetAccountIDByChar(char_id); + std::string query = StringFormat("DELETE FROM sharedbank WHERE acctid=%i AND slotid=%i", account_id, slot_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "DeleteSharedBankSlot query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + // Delete bag slots, if need be + if (!Inventory::SupportsContainers(slot_id)) + return true; + + int16 base_slot_id = Inventory::CalcSlotId(slot_id, SUB_BEGIN); + query = StringFormat("DELETE FROM sharedbank WHERE acctid = %i " + "AND slotid >= %i AND slotid < %i", + account_id, base_slot_id, (base_slot_id+10)); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "DeleteSharedBankSlot, bags query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + // @merth: need to delete augments here + return true; +} + + int32 SharedDatabase::GetSharedPlatinum(uint32 account_id) { char errbuf[MYSQL_ERRMSG_SIZE]; diff --git a/common/shareddb.h b/common/shareddb.h index eeef6125a..14793f792 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -54,6 +54,10 @@ public: */ bool SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end); bool SaveInventory(uint32 char_id, const ItemInst* inst, int16 slot_id); + bool DeleteSharedBankSlot(uint32 char_id, int16 slot_id); + bool DeleteInventorySlot(uint32 char_id, int16 slot_id); + bool UpdateInventorySlot(uint32 char_id, const ItemInst* inst, int16 slot_id); + bool UpdateSharedBankSlot(uint32 char_id, const ItemInst* inst, int16 slot_id); bool VerifyInventory(uint32 account_id, int16 slot_id, const ItemInst* inst); bool GetSharedBank(uint32 id, Inventory* inv, bool is_charid); int32 GetSharedPlatinum(uint32 account_id); From cbce5e5eb6085845d49038d39983848b8dd36ce9 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 13:50:01 -0700 Subject: [PATCH 232/368] GetSharedPlatinum converted to QueryDatabase --- common/shareddb.cpp | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index cfe9e0939..a3aa999d5 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -318,34 +318,19 @@ bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) { int32 SharedDatabase::GetSharedPlatinum(uint32 account_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT sharedplat FROM account WHERE id='%i'", account_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint32 shared_platinum = atoi(row[0]); - mysql_free_result(result); - return shared_platinum; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in GetSharedPlatinum query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT sharedplat FROM account WHERE id = '%i'", account_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetSharedPlatinum query '" << query << "' " << results.ErrorMessage().c_str() << std::endl; return false; - } + } - return 0; + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) { From 211b3a135ef5f3821013bff68011ba1fa946506a Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 13:53:28 -0700 Subject: [PATCH 233/368] SetSharedPlatinum converted to QueryDatabase --- common/shareddb.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index a3aa999d5..c87efc96e 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -334,16 +334,13 @@ int32 SharedDatabase::GetSharedPlatinum(uint32 account_id) } bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE `account` SET `sharedplat` = `sharedplat` + %i WHERE id = %i", amount_to_add, account_id), errbuf)) { - std::cerr << "Error in SetSharedPlatinum query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("UPDATE account SET sharedplat = sharedplat + %i WHERE id = %i", amount_to_add, account_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetSharedPlatinum query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); return true; } From 4a3f94e68840e8e9cea5f7c2310edd50477d9961 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 13:54:33 -0700 Subject: [PATCH 234/368] SetStartingItems converted to QueryDatabase --- common/shareddb.cpp | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index c87efc96e..b5a26cf13 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -345,32 +345,37 @@ bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) { } bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) { + const Item_Struct* myitem; - uint32 itemid = 0; - int32 charges = 0; - int32 slot = 0; - auto query = StringFormat( - "SELECT `itemid`, `item_charges`, `slot` FROM `starting_items`" - " WHERE (`race` = %i OR `race` = 0)" - " AND (`class` = %i OR `class` = 0)" - " AND (`deityid` = %i OR `deityid` = 0)" - " AND (`zoneid` = %i OR `zoneid` = 0)" - " AND gm <= %i ORDER BY id", - si_race, si_class, si_deity, si_current_zone, admin_level); - auto results = QueryDatabase(query); + + std::string query = StringFormat("SELECT itemid, item_charges, slot FROM starting_items " + "WHERE (race = %i or race = 0) AND (class = %i or class = 0) AND " + "(deityid = %i or deityid = 0) AND (zoneid = %i or zoneid = 0) AND " + "gm <= %i ORDER BY id", + si_race, si_class, si_deity, si_current_zone, admin_level); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + for (auto row = results.begin(); row != results.end(); ++row) { - itemid = atoi(row[0]); - charges = atoi(row[1]); - slot = atoi(row[2]); + int32 itemid = atoi(row[0]); + int32 charges = atoi(row[1]); + int32 slot = atoi(row[2]); myitem = GetItem(itemid); + if(!myitem) continue; + ItemInst* myinst = CreateBaseItem(myitem, charges); + if(slot < 0) - slot = inv->FindFreeSlot(0,0); + slot = inv->FindFreeSlot(0, 0); + inv->PutItem(slot, *myinst); safe_delete(myinst); } + return true; } From aa780ceb8c8dcb039445d92c9e78563355889ba3 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 13:55:43 -0700 Subject: [PATCH 235/368] GetSharedBank converted to QueryDatabase --- common/shareddb.cpp | 169 +++++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 87 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index b5a26cf13..96c947c9b 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -382,103 +382,98 @@ bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, // Retrieve shared bank inventory based on either account or character bool SharedDatabase::GetSharedBank(uint32 id, Inventory* inv, bool is_charid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 len_query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - bool ret = false; + std::string query; - if (is_charid) { - len_query = MakeAnyLenString(&query, - "SELECT sb.slotid,sb.itemid,sb.charges,sb.augslot1,sb.augslot2,sb.augslot3,sb.augslot4,sb.augslot5,sb.custom_data from sharedbank sb " - "INNER JOIN character_data ch ON ch.account_id=sb.acctid " - "WHERE ch.id=%i", id); - } - else { - len_query = MakeAnyLenString(&query, - "SELECT slotid,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5,custom_data from sharedbank WHERE acctid=%i", id); - } + if (is_charid) + query = StringFormat("SELECT sb.slotid, sb.itemid, sb.charges, " + "sb.augslot1, sb.augslot2, sb.augslot3, " + "sb.augslot4, sb.augslot5, sb.custom_data " + "FROM sharedbank sb INNER JOIN character_data ch " + "ON ch.account_id=sb.acctid WHERE ch.id = %i", id); + else + query = StringFormat("SELECT slotid, itemid, charges, " + "augslot1, augslot2, augslot3, " + "augslot4, augslot5, custom_data " + "FROM sharedbank WHERE acctid=%i", id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Database::GetSharedBank(uint32 account_id): %s", results.ErrorMessage().c_str()); + return false; + } - if (RunQuery(query, len_query, errbuf, &result)) { - while ((row = mysql_fetch_row(result))) { - int16 slot_id = (int16)atoi(row[0]); - uint32 item_id = (uint32)atoi(row[1]); - int8 charges = (int8)atoi(row[2]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoi(row[3]); - aug[1] = (uint32)atoi(row[4]); - aug[2] = (uint32)atoi(row[5]); - aug[3] = (uint32)atoi(row[6]); - aug[4] = (uint32)atoi(row[7]); - const Item_Struct* item = GetItem(item_id); + for (auto row = results.begin(); row != results.end(); ++row) { + int16 slot_id = (int16)atoi(row[0]); + uint32 item_id = (uint32)atoi(row[1]); + int8 charges = (int8)atoi(row[2]); - if (item) { - int16 put_slot_id = INVALID_INDEX; + uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; + aug[0] = (uint32)atoi(row[3]); + aug[1] = (uint32)atoi(row[4]); + aug[2] = (uint32)atoi(row[5]); + aug[3] = (uint32)atoi(row[6]); + aug[4] = (uint32)atoi(row[7]); - ItemInst* inst = CreateBaseItem(item, charges); - if (item->ItemClass == ItemClassCommon) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); - } - } - } - if(row[8]) { - std::string data_str(row[8]); - std::string id; - std::string value; - bool use_id = true; + const Item_Struct* item = GetItem(item_id); - for(int i = 0; i < data_str.length(); ++i) { - if(data_str[i] == '^') { - if(!use_id) { - inst->SetCustomData(id, value); - id.clear(); - value.clear(); - } - use_id = !use_id; - } - else { - char v = data_str[i]; - if(use_id) { - id.push_back(v); - } else { - value.push_back(v); - } - } - } - } - - put_slot_id = inv->PutItem(slot_id, *inst); - safe_delete(inst); - - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in shared bank inventory: %s=%i, item_id=%i, slot_id=%i", - ((is_charid==true) ? "charid" : "acctid"), id, item_id, slot_id); - - if (is_charid) - SaveInventory(id, nullptr, slot_id); - } - } - else { - LogFile->write(EQEMuLog::Error, + if (!item) { + LogFile->write(EQEMuLog::Error, "Warning: %s %i has an invalid item_id %i in inventory slot %i", ((is_charid==true) ? "charid" : "acctid"), id, item_id, slot_id); - } - } + continue; + } - mysql_free_result(result); - ret = true; - } - else { - LogFile->write(EQEMuLog::Error, "Database::GetSharedBank(uint32 account_id): %s", errbuf); + int16 put_slot_id = INVALID_INDEX; + + ItemInst* inst = CreateBaseItem(item, charges); + if (item->ItemClass == ItemClassCommon) { + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { + if (aug[i]) { + inst->PutAugment(this, i, aug[i]); + } + } + } + + if(!row[8]) + continue; + + std::string data_str(row[8]); + std::string idAsString; + std::string value; + bool use_id = true; + + for(int i = 0; i < data_str.length(); ++i) { + if(data_str[i] == '^') { + if(!use_id) { + inst->SetCustomData(idAsString, value); + idAsString.clear(); + value.clear(); + } + use_id = !use_id; + continue; + } + + char v = data_str[i]; + if(use_id) + idAsString.push_back(v); + else + value.push_back(v); + } + + put_slot_id = inv->PutItem(slot_id, *inst); + safe_delete(inst); + + // Save ptr to item in inventory + if (put_slot_id != INVALID_INDEX) + continue; + + LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in shared bank inventory: %s=%i, item_id=%i, slot_id=%i", + ((is_charid==true)? "charid": "acctid"), id, item_id, slot_id); + + if (is_charid) + SaveInventory(id, nullptr, slot_id); } - safe_delete_array(query); - return ret; + return true; } From cb897786bc5b2df6a2b69b9afe45a34b023033fb Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:02:43 -0700 Subject: [PATCH 236/368] GetInventory(account_id, name, inv) converted to QueryDatabase --- common/shareddb.cpp | 178 +++++++++++++++++++++----------------------- 1 file changed, 84 insertions(+), 94 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 96c947c9b..1c835ba33 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -597,104 +597,94 @@ bool SharedDatabase::GetInventory(uint32 char_id, Inventory* inv) { // Overloaded: Retrieve character inventory based on account_id and character name bool SharedDatabase::GetInventory(uint32 account_id, char* name, Inventory* inv) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - bool ret = false; - // Retrieve character inventory - if (RunQuery(query, MakeAnyLenString(&query, - " SELECT `slotid`, `itemid`, `charges`, `color`, `augslot1`, `augslot2`, `augslot3`, `augslot4`, `augslot5`, `instnodrop`, `custom_data`" - " FROM `inventory`" - " INNER JOIN `character_data` ch ON ch.id = charid" - " WHERE ch.NAME = '%s'" - " AND ch.account_id = % i" - " ORDER BY `slotid`", - name, account_id), errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) { - int16 slot_id = atoi(row[0]); - uint32 item_id = atoi(row[1]); - int8 charges = atoi(row[2]); - uint32 color = atoul(row[3]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoi(row[4]); - aug[1] = (uint32)atoi(row[5]); - aug[2] = (uint32)atoi(row[6]); - aug[3] = (uint32)atoi(row[7]); - aug[4] = (uint32)atoi(row[8]); - bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; - const Item_Struct* item = GetItem(item_id); - int16 put_slot_id = INVALID_INDEX; - if(!item) - continue; - - ItemInst* inst = CreateBaseItem(item, charges); - inst->SetInstNoDrop(instnodrop); - - if(row[10]) { - std::string data_str(row[10]); - std::string id; - std::string value; - bool use_id = true; - - for(int i = 0; i < data_str.length(); ++i) { - if(data_str[i] == '^') { - if(!use_id) { - inst->SetCustomData(id, value); - id.clear(); - value.clear(); - } - use_id = !use_id; - } - else { - char v = data_str[i]; - if(use_id) { - id.push_back(v); - } else { - value.push_back(v); - } - } - } - } - - if (color > 0) - inst->SetColor(color); - inst->SetCharges(charges); - - if (item->ItemClass == ItemClassCommon) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); - } - } - } - if (slot_id >= 8000 && slot_id <= 8999) - put_slot_id = inv->PushCursor(*inst); - else - put_slot_id = inv->PutItem(slot_id, *inst); - safe_delete(inst); - - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in inventory: name=%s, acctid=%i, item_id=%i, slot_id=%i", - name, account_id, item_id, slot_id); - } - } - mysql_free_result(result); - - // Retrieve shared inventory - ret = GetSharedBank(account_id, inv, false); - } - else { - LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query, errbuf); + std::string query = StringFormat("SELECT slotid, itemid, charges, color, augslot1, " + "augslot2, augslot3, augslot4, augslot5, instnodrop, custom_data " + "FROM inventory INNER JOIN character_data ch " + "ON ch.id = charid WHERE ch.name = '%s' AND ch.account_id = %i ORDER BY slotid", + name, account_id); + auto results = QueryDatabase(query); + if (!results.Success()){ + LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); LogFile->write(EQEMuLog::Error, "If you got an error related to the 'instnodrop' field, run the following SQL Queries:\nalter table inventory add instnodrop tinyint(1) unsigned default 0 not null;\n"); + return false; } - safe_delete_array(query); - return ret; + + for (auto row = results.begin(); row != results.end(); ++row) { + int16 slot_id = atoi(row[0]); + uint32 item_id = atoi(row[1]); + int8 charges = atoi(row[2]); + uint32 color = atoul(row[3]); + + uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; + aug[0] = (uint32)atoi(row[4]); + aug[1] = (uint32)atoi(row[5]); + aug[2] = (uint32)atoi(row[6]); + aug[3] = (uint32)atoi(row[7]); + aug[4] = (uint32)atoi(row[8]); + + bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; + const Item_Struct* item = GetItem(item_id); + int16 put_slot_id = INVALID_INDEX; + if(!item) + continue; + + ItemInst* inst = CreateBaseItem(item, charges); + inst->SetInstNoDrop(instnodrop); + + if(row[10]) { + std::string data_str(row[10]); + std::string idAsString; + std::string value; + bool use_id = true; + + for(int i = 0; i < data_str.length(); ++i) { + if(data_str[i] == '^') { + if(!use_id) { + inst->SetCustomData(idAsString, value); + idAsString.clear(); + value.clear(); + } + + use_id = !use_id; + continue; + } + + char v = data_str[i]; + if(use_id) + idAsString.push_back(v); + else + value.push_back(v); + + } + } + + if (color > 0) + inst->SetColor(color); + + inst->SetCharges(charges); + + if (item->ItemClass == ItemClassCommon) + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) + if (aug[i]) + inst->PutAugment(this, i, aug[i]); + + if (slot_id>=8000 && slot_id <= 8999) + put_slot_id = inv->PushCursor(*inst); + else + put_slot_id = inv->PutItem(slot_id, *inst); + + safe_delete(inst); + + // Save ptr to item in inventory + if (put_slot_id == INVALID_INDEX) + LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in inventory: name=%s, acctid=%i, item_id=%i, slot_id=%i", name, account_id, item_id, slot_id); + + } + + // Retrieve shared inventory + return GetSharedBank(account_id, inv, false); } From 3b75d4fa8f27454c0388dd666c3cfb766aa06b64 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:04:19 -0700 Subject: [PATCH 237/368] GetInventory char_id, inv) converted to QueryDatabase --- common/shareddb.cpp | 185 ++++++++++++++++++++------------------------ 1 file changed, 85 insertions(+), 100 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 1c835ba33..372872900 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -479,120 +479,105 @@ bool SharedDatabase::GetSharedBank(uint32 id, Inventory* inv, bool is_charid) { // Overloaded: Retrieve character inventory based on character id bool SharedDatabase::GetInventory(uint32 char_id, Inventory* inv) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - bool ret = false; - // Retrieve character inventory - if (RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5," - "instnodrop,custom_data FROM inventory WHERE charid=%i ORDER BY slotid", char_id), errbuf, &result)) { + std::string query = StringFormat("SELECT slotid, itemid, charges, color, augslot1, " + "augslot2, augslot3, augslot4, augslot5, instnodrop, custom_data " + "FROM inventory WHERE charid = %i ORDER BY slotid", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + LogFile->write(EQEMuLog::Error, "If you got an error related to the 'instnodrop' field, run the following SQL Queries:\nalter table inventory add instnodrop tinyint(1) unsigned default 0 not null;\n"); + return false; + } - while ((row = mysql_fetch_row(result))) { - int16 slot_id = atoi(row[0]); - uint32 item_id = atoi(row[1]); - uint16 charges = atoi(row[2]); - uint32 color = atoul(row[3]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoul(row[4]); - aug[1] = (uint32)atoul(row[5]); - aug[2] = (uint32)atoul(row[6]); - aug[3] = (uint32)atoul(row[7]); - aug[4] = (uint32)atoul(row[8]); - bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; + for (auto row = results.begin(); row != results.end(); ++row) { + int16 slot_id = atoi(row[0]); + uint32 item_id = atoi(row[1]); + uint16 charges = atoi(row[2]); + uint32 color = atoul(row[3]); - const Item_Struct* item = GetItem(item_id); + uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - if (item) { - int16 put_slot_id = INVALID_INDEX; + aug[0] = (uint32)atoul(row[4]); + aug[1] = (uint32)atoul(row[5]); + aug[2] = (uint32)atoul(row[6]); + aug[3] = (uint32)atoul(row[7]); + aug[4] = (uint32)atoul(row[8]); - ItemInst* inst = CreateBaseItem(item, charges); + bool instnodrop = (row[9] && (uint16)atoi(row[9]))? true: false; - if(row[10]) { - std::string data_str(row[10]); - std::string id; - std::string value; - bool use_id = true; + const Item_Struct* item = GetItem(item_id); - for(int i = 0; i < data_str.length(); ++i) { - if(data_str[i] == '^') { - if(!use_id) { - inst->SetCustomData(id, value); - id.clear(); - value.clear(); - } - use_id = !use_id; - } - else { - char v = data_str[i]; - if(use_id) { - id.push_back(v); - } else { - value.push_back(v); - } - } - } - } + if (!item) { + LogFile->write(EQEMuLog::Error,"Warning: charid %i has an invalid item_id %i in inventory slot %i", char_id, item_id, slot_id); + continue; + } - if (instnodrop || (((slot_id >= EmuConstants::EQUIPMENT_BEGIN && slot_id <= EmuConstants::EQUIPMENT_END) || slot_id == MainPowerSource) && inst->GetItem()->Attuneable)) - inst->SetInstNoDrop(true); - if (color > 0) - inst->SetColor(color); - if(charges==0x7FFF) - inst->SetCharges(-1); - else - inst->SetCharges(charges); + int16 put_slot_id = INVALID_INDEX; - if (item->ItemClass == ItemClassCommon) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); - } - } - } + ItemInst* inst = CreateBaseItem(item, charges); - if (slot_id >= 8000 && slot_id <= 8999) { - put_slot_id = inv->PushCursor(*inst); - } - // Admins: please report any occurrences of this error - else if (slot_id >= 3111 && slot_id <= 3179) { - LogFile->write(EQEMuLog::Error, - "Warning: Defunct location for item in inventory: charid=%i, item_id=%i, slot_id=%i .. pushing to cursor...", - char_id, item_id, slot_id); - put_slot_id = inv->PushCursor(*inst); - } - else { - put_slot_id = inv->PutItem(slot_id, *inst); - } + if(row[10]) { + std::string data_str(row[10]); + std::string idAsString; + std::string value; + bool use_id = true; - safe_delete(inst); + for(int i = 0; i < data_str.length(); ++i) { + if(data_str[i] == '^') { + if(!use_id) { + inst->SetCustomData(idAsString, value); + idAsString.clear(); + value.clear(); + } - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in inventory: charid=%i, item_id=%i, slot_id=%i", - char_id, item_id, slot_id); - } - } - else { - LogFile->write(EQEMuLog::Error, - "Warning: charid %i has an invalid item_id %i in inventory slot %i", - char_id, item_id, slot_id); - } - } - mysql_free_result(result); + use_id = !use_id; + continue; + } - // Retrieve shared inventory - ret = GetSharedBank(char_id, inv, true); - } - else { - LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query, errbuf); - LogFile->write(EQEMuLog::Error, "If you got an error related to the 'instnodrop' field, run the following SQL Queries:\nalter table inventory add instnodrop tinyint(1) unsigned default 0 not null;\n"); - } + char v = data_str[i]; + if(use_id) + idAsString.push_back(v); + else + value.push_back(v); + } + } - safe_delete_array(query); - return ret; + if (instnodrop || (((slot_id >= EmuConstants::EQUIPMENT_BEGIN && slot_id <= EmuConstants::EQUIPMENT_END) || slot_id == MainPowerSource) && inst->GetItem()->Attuneable)) + inst->SetInstNoDrop(true); + + if (color > 0) + inst->SetColor(color); + + if(charges==0x7FFF) + inst->SetCharges(-1); + else + inst->SetCharges(charges); + + if (item->ItemClass == ItemClassCommon) + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) + if (aug[i]) + inst->PutAugment(this, i, aug[i]); + + if (slot_id >= 8000 && slot_id <= 8999) + put_slot_id = inv->PushCursor(*inst); + else if (slot_id >= 3111 && slot_id <= 3179) { + // Admins: please report any occurrences of this error + LogFile->write(EQEMuLog::Error, "Warning: Defunct location for item in inventory: charid=%i, item_id=%i, slot_id=%i .. pushing to cursor...", char_id, item_id, slot_id); + put_slot_id = inv->PushCursor(*inst); + } else + put_slot_id = inv->PutItem(slot_id, *inst); + + safe_delete(inst); + + // Save ptr to item in inventory + if (put_slot_id == INVALID_INDEX) { + LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in inventory: charid=%i, item_id=%i, slot_id=%i",char_id, item_id, slot_id); + } + } + + // Retrieve shared inventory + return GetSharedBank(char_id, inv, true); } // Overloaded: Retrieve character inventory based on account_id and character name From 638d121b75ff30182dc057f79c840b7da543960d Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:05:07 -0700 Subject: [PATCH 238/368] GetItemsCount converted to QueryDatabase --- common/shareddb.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 372872900..62344ff19 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -674,25 +674,26 @@ bool SharedDatabase::GetInventory(uint32 account_id, char* name, Inventory* inv) void SharedDatabase::GetItemsCount(int32 &item_count, uint32 &max_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; item_count = -1; max_id = 0; - char query[] = "SELECT MAX(id), count(*) FROM items"; - if (RunQuery(query, static_cast(strlen(query)), errbuf, &result)) { - row = mysql_fetch_row(result); - if (row != nullptr && row[1] != 0) { - item_count = atoi(row[1]); - if(row[0]) - max_id = atoi(row[0]); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetItemsCount '%s': '%s'", query, errbuf); + const std::string query = "SELECT MAX(id), count(*) FROM items"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetItemsCount '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + return; } + + if (results.RowCount() == 0) + return; + + auto row = results.begin(); + + if(row[0]) + max_id = atoi(row[0]); + + if (row[1]) + item_count = atoi(row[1]); } bool SharedDatabase::LoadItems() { From 2a73a572d303658befae18d52a91d055e3093531 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:06:37 -0700 Subject: [PATCH 239/368] LoadItems converted to QueryDatabase --- common/shareddb.cpp | 428 ++++++++++++++++++++++---------------------- 1 file changed, 217 insertions(+), 211 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 62344ff19..f7895a485 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -729,9 +729,6 @@ bool SharedDatabase::LoadItems() { void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_item_id) { EQEmu::FixedMemoryHashSet hash(reinterpret_cast(data), size, items, max_item_id); - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; char ndbuffer[4]; bool disableNoRent = false; @@ -759,220 +756,229 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_ } } - char query[] = "select source," + Item_Struct item; + + const std::string query = "SELECT source," #define F(x) "`"#x"`," #include "item_fieldlist.h" #undef F - "updated" - " from items order by id"; - Item_Struct item; - if(RunQuery(query, sizeof(query), errbuf, &result)) { - while((row = mysql_fetch_row(result))) { - memset(&item, 0, sizeof(Item_Struct)); + "updated FROM items ORDER BY id"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "LoadItems '%s', %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - item.ItemClass = (uint8)atoi(row[ItemField::itemclass]); - strcpy(item.Name,row[ItemField::name]); - strcpy(item.Lore,row[ItemField::lore]); - strcpy(item.IDFile,row[ItemField::idfile]); - item.ID = (uint32)atoul(row[ItemField::id]); - item.Weight = (uint8)atoi(row[ItemField::weight]); - item.NoRent = disableNoRent ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::norent]); - item.NoDrop = disableNoDrop ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::nodrop]); - item.Size = (uint8)atoi(row[ItemField::size]); - item.Slots = (uint32)atoul(row[ItemField::slots]); - item.Price = (uint32)atoul(row[ItemField::price]); - item.Icon = (uint32)atoul(row[ItemField::icon]); - item.BenefitFlag = (atoul(row[ItemField::benefitflag]) != 0); - item.Tradeskills = (atoi(row[ItemField::tradeskills])==0) ? false : true; - item.CR = (int8)atoi(row[ItemField::cr]); - item.DR = (int8)atoi(row[ItemField::dr]); - item.PR = (int8)atoi(row[ItemField::pr]); - item.MR = (int8)atoi(row[ItemField::mr]); - item.FR = (int8)atoi(row[ItemField::fr]); - item.AStr = (int8)atoi(row[ItemField::astr]); - item.ASta = (int8)atoi(row[ItemField::asta]); - item.AAgi = (int8)atoi(row[ItemField::aagi]); - item.ADex = (int8)atoi(row[ItemField::adex]); - item.ACha = (int8)atoi(row[ItemField::acha]); - item.AInt = (int8)atoi(row[ItemField::aint]); - item.AWis = (int8)atoi(row[ItemField::awis]); - item.HP = (int32)atoul(row[ItemField::hp]); - item.Mana = (int32)atoul(row[ItemField::mana]); - item.AC = (int32)atoul(row[ItemField::ac]); - item.Deity = (uint32)atoul(row[ItemField::deity]); - item.SkillModValue = (int32)atoul(row[ItemField::skillmodvalue]); - //item.Unk033 = (int32)atoul(row[ItemField::UNK033]); - item.SkillModType = (uint32)atoul(row[ItemField::skillmodtype]); - item.BaneDmgRace = (uint32)atoul(row[ItemField::banedmgrace]); - item.BaneDmgAmt = (int8)atoi(row[ItemField::banedmgamt]); - item.BaneDmgBody = (uint32)atoul(row[ItemField::banedmgbody]); - item.Magic = (atoi(row[ItemField::magic])==0) ? false : true; - item.CastTime_ = (int32)atoul(row[ItemField::casttime_]); - item.ReqLevel = (uint8)atoi(row[ItemField::reqlevel]); - item.BardType = (uint32)atoul(row[ItemField::bardtype]); - item.BardValue = (int32)atoul(row[ItemField::bardvalue]); - item.Light = (int8)atoi(row[ItemField::light]); - item.Delay = (uint8)atoi(row[ItemField::delay]); - item.RecLevel = (uint8)atoi(row[ItemField::reclevel]); - item.RecSkill = (uint8)atoi(row[ItemField::recskill]); - item.ElemDmgType = (uint8)atoi(row[ItemField::elemdmgtype]); - item.ElemDmgAmt = (uint8)atoi(row[ItemField::elemdmgamt]); - item.Range = (uint8)atoi(row[ItemField::range]); - item.Damage = (uint32)atoi(row[ItemField::damage]); - item.Color = (uint32)atoul(row[ItemField::color]); - item.Classes = (uint32)atoul(row[ItemField::classes]); - item.Races = (uint32)atoul(row[ItemField::races]); - //item.Unk054 = (uint32)atoul(row[ItemField::UNK054]); - item.MaxCharges = (int16)atoi(row[ItemField::maxcharges]); - item.ItemType = (uint8)atoi(row[ItemField::itemtype]); - item.Material = (uint8)atoi(row[ItemField::material]); - item.SellRate = (float)atof(row[ItemField::sellrate]); - //item.Unk059 = (uint32)atoul(row[ItemField::UNK059]); - item.CastTime = (uint32)atoul(row[ItemField::casttime]); - item.EliteMaterial = (uint32)atoul(row[ItemField::elitematerial]); - item.ProcRate = (int32)atoi(row[ItemField::procrate]); - item.CombatEffects = (int8)atoi(row[ItemField::combateffects]); - item.Shielding = (int8)atoi(row[ItemField::shielding]); - item.StunResist = (int8)atoi(row[ItemField::stunresist]); - item.StrikeThrough = (int8)atoi(row[ItemField::strikethrough]); - item.ExtraDmgSkill = (uint32)atoul(row[ItemField::extradmgskill]); - item.ExtraDmgAmt = (uint32)atoul(row[ItemField::extradmgamt]); - item.SpellShield = (int8)atoi(row[ItemField::spellshield]); - item.Avoidance = (int8)atoi(row[ItemField::avoidance]); - item.Accuracy = (int8)atoi(row[ItemField::accuracy]); - item.CharmFileID = (uint32)atoul(row[ItemField::charmfileid]); - item.FactionMod1 = (int32)atoul(row[ItemField::factionmod1]); - item.FactionMod2 = (int32)atoul(row[ItemField::factionmod2]); - item.FactionMod3 = (int32)atoul(row[ItemField::factionmod3]); - item.FactionMod4 = (int32)atoul(row[ItemField::factionmod4]); - item.FactionAmt1 = (int32)atoul(row[ItemField::factionamt1]); - item.FactionAmt2 = (int32)atoul(row[ItemField::factionamt2]); - item.FactionAmt3 = (int32)atoul(row[ItemField::factionamt3]); - item.FactionAmt4 = (int32)atoul(row[ItemField::factionamt4]); - strcpy(item.CharmFile,row[ItemField::charmfile]); - item.AugType = (uint32)atoul(row[ItemField::augtype]); - item.AugSlotType[0] = (uint8)atoi(row[ItemField::augslot1type]); - item.AugSlotVisible[0] = (uint8)atoi(row[ItemField::augslot1visible]); - item.AugSlotUnk2[0] = 0; - item.AugSlotType[1] = (uint8)atoi(row[ItemField::augslot2type]); - item.AugSlotVisible[1] = (uint8)atoi(row[ItemField::augslot2visible]); - item.AugSlotUnk2[1] = 0; - item.AugSlotType[2] = (uint8)atoi(row[ItemField::augslot3type]); - item.AugSlotVisible[2] = (uint8)atoi(row[ItemField::augslot3visible]); - item.AugSlotUnk2[2] = 0; - item.AugSlotType[3] = (uint8)atoi(row[ItemField::augslot4type]); - item.AugSlotVisible[3] = (uint8)atoi(row[ItemField::augslot4visible]); - item.AugSlotUnk2[3] = 0; - item.AugSlotType[4] = (uint8)atoi(row[ItemField::augslot5type]); - item.AugSlotVisible[4] = (uint8)atoi(row[ItemField::augslot5visible]); - item.AugSlotUnk2[4] = 0; - item.LDoNTheme = (uint32)atoul(row[ItemField::ldontheme]); - item.LDoNPrice = (uint32)atoul(row[ItemField::ldonprice]); - item.LDoNSold = (uint32)atoul(row[ItemField::ldonsold]); - item.BagType = (uint8)atoi(row[ItemField::bagtype]); - item.BagSlots = (uint8)atoi(row[ItemField::bagslots]); - item.BagSize = (uint8)atoi(row[ItemField::bagsize]); - item.BagWR = (uint8)atoi(row[ItemField::bagwr]); - item.Book = (uint8)atoi(row[ItemField::book]); - item.BookType = (uint32)atoul(row[ItemField::booktype]); - strcpy(item.Filename,row[ItemField::filename]); - item.BaneDmgRaceAmt = (uint32)atoul(row[ItemField::banedmgraceamt]); - item.AugRestrict = (uint32)atoul(row[ItemField::augrestrict]); - item.LoreGroup = disableLoreGroup ? (uint8)atoi("0") : atoi(row[ItemField::loregroup]); - item.LoreFlag = item.LoreGroup!=0; - item.PendingLoreFlag = (atoi(row[ItemField::pendingloreflag])==0) ? false : true; - item.ArtifactFlag = (atoi(row[ItemField::artifactflag])==0) ? false : true; - item.SummonedFlag = (atoi(row[ItemField::summonedflag])==0) ? false : true; - item.Favor = (uint32)atoul(row[ItemField::favor]); - item.FVNoDrop = (atoi(row[ItemField::fvnodrop])==0) ? false : true; - item.Endur = (uint32)atoul(row[ItemField::endur]); - item.DotShielding = (uint32)atoul(row[ItemField::dotshielding]); - item.Attack = (uint32)atoul(row[ItemField::attack]); - item.Regen = (uint32)atoul(row[ItemField::regen]); - item.ManaRegen = (uint32)atoul(row[ItemField::manaregen]); - item.EnduranceRegen = (uint32)atoul(row[ItemField::enduranceregen]); - item.Haste = (uint32)atoul(row[ItemField::haste]); - item.DamageShield = (uint32)atoul(row[ItemField::damageshield]); - item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]); - item.RecastType = (uint32)atoul(row[ItemField::recasttype]); - item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]); - item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]); - item.Attuneable = (atoi(row[ItemField::attuneable])==0) ? false : true; - item.NoPet = (atoi(row[ItemField::nopet])==0) ? false : true; - item.PointType = (uint32)atoul(row[ItemField::pointtype]); - item.PotionBelt = (atoi(row[ItemField::potionbelt])==0) ? false : true; - item.PotionBeltSlots = (atoi(row[ItemField::potionbeltslots])==0) ? false : true; - item.StackSize = (uint16)atoi(row[ItemField::stacksize]); - item.NoTransfer = disableNoTransfer ? false : (atoi(row[ItemField::notransfer])==0) ? false : true; - item.Stackable = (atoi(row[ItemField::stackable])==0) ? false : true; - item.Click.Effect = (uint32)atoul(row[ItemField::clickeffect]); - item.Click.Type = (uint8)atoul(row[ItemField::clicktype]); - item.Click.Level = (uint8)atoul(row[ItemField::clicklevel]); - item.Click.Level2 = (uint8)atoul(row[ItemField::clicklevel2]); - strcpy(item.CharmFile,row[ItemField::charmfile]); - item.Proc.Effect = (uint16)atoul(row[ItemField::proceffect]); - item.Proc.Type = (uint8)atoul(row[ItemField::proctype]); - item.Proc.Level = (uint8)atoul(row[ItemField::proclevel]); - item.Proc.Level2 = (uint8)atoul(row[ItemField::proclevel2]); - item.Worn.Effect = (uint16)atoul(row[ItemField::worneffect]); - item.Worn.Type = (uint8)atoul(row[ItemField::worntype]); - item.Worn.Level = (uint8)atoul(row[ItemField::wornlevel]); - item.Worn.Level2 = (uint8)atoul(row[ItemField::wornlevel2]); - item.Focus.Effect = (uint16)atoul(row[ItemField::focuseffect]); - item.Focus.Type = (uint8)atoul(row[ItemField::focustype]); - item.Focus.Level = (uint8)atoul(row[ItemField::focuslevel]); - item.Focus.Level2 = (uint8)atoul(row[ItemField::focuslevel2]); - item.Scroll.Effect = (uint16)atoul(row[ItemField::scrolleffect]); - item.Scroll.Type = (uint8)atoul(row[ItemField::scrolltype]); - item.Scroll.Level = (uint8)atoul(row[ItemField::scrolllevel]); - item.Scroll.Level2 = (uint8)atoul(row[ItemField::scrolllevel2]); - item.Bard.Effect = (uint16)atoul(row[ItemField::bardeffect]); - item.Bard.Type = (uint8)atoul(row[ItemField::bardtype]); - item.Bard.Level = (uint8)atoul(row[ItemField::bardlevel]); - item.Bard.Level2 = (uint8)atoul(row[ItemField::bardlevel2]); - item.QuestItemFlag = (atoi(row[ItemField::questitemflag])==0) ? false : true; - item.SVCorruption = (int32)atoi(row[ItemField::svcorruption]); - item.Purity = (uint32)atoul(row[ItemField::purity]); - item.BackstabDmg = (uint32)atoul(row[ItemField::backstabdmg]); - item.DSMitigation = (uint32)atoul(row[ItemField::dsmitigation]); - item.HeroicStr = (int32)atoi(row[ItemField::heroic_str]); - item.HeroicInt = (int32)atoi(row[ItemField::heroic_int]); - item.HeroicWis = (int32)atoi(row[ItemField::heroic_wis]); - item.HeroicAgi = (int32)atoi(row[ItemField::heroic_agi]); - item.HeroicDex = (int32)atoi(row[ItemField::heroic_dex]); - item.HeroicSta = (int32)atoi(row[ItemField::heroic_sta]); - item.HeroicCha = (int32)atoi(row[ItemField::heroic_cha]); - item.HeroicMR = (int32)atoi(row[ItemField::heroic_mr]); - item.HeroicFR = (int32)atoi(row[ItemField::heroic_fr]); - item.HeroicCR = (int32)atoi(row[ItemField::heroic_cr]); - item.HeroicDR = (int32)atoi(row[ItemField::heroic_dr]); - item.HeroicPR = (int32)atoi(row[ItemField::heroic_pr]); - item.HeroicSVCorrup = (int32)atoi(row[ItemField::heroic_svcorrup]); - item.HealAmt = (int32)atoi(row[ItemField::healamt]); - item.SpellDmg = (int32)atoi(row[ItemField::spelldmg]); - item.LDoNSellBackRate = (uint32)atoul(row[ItemField::ldonsellbackrate]); - item.ScriptFileID = (uint32)atoul(row[ItemField::scriptfileid]); - item.ExpendableArrow = (uint16)atoul(row[ItemField::expendablearrow]); - item.Clairvoyance = (uint32)atoul(row[ItemField::clairvoyance]); - strcpy(item.ClickName,row[ItemField::clickname]); - strcpy(item.ProcName,row[ItemField::procname]); - strcpy(item.WornName,row[ItemField::wornname]); - strcpy(item.FocusName,row[ItemField::focusname]); - strcpy(item.ScrollName,row[ItemField::scrollname]); + for(auto row = results.begin(); row != results.end(); ++row) { + memset(&item, 0, sizeof(Item_Struct)); - try { - hash.insert(item.ID, item); - } catch(std::exception &ex) { - LogFile->write(EQEMuLog::Error, "Database::LoadItems: %s", ex.what()); - break; - } - } + item.ItemClass = (uint8)atoi(row[ItemField::itemclass]); + strcpy(item.Name,row[ItemField::name]); + strcpy(item.Lore,row[ItemField::lore]); + strcpy(item.IDFile,row[ItemField::idfile]); + + item.ID = (uint32)atoul(row[ItemField::id]); + item.Weight = (uint8)atoi(row[ItemField::weight]); + item.NoRent = disableNoRent ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::norent]); + item.NoDrop = disableNoDrop ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::nodrop]); + item.Size = (uint8)atoi(row[ItemField::size]); + item.Slots = (uint32)atoul(row[ItemField::slots]); + item.Price = (uint32)atoul(row[ItemField::price]); + item.Icon = (uint32)atoul(row[ItemField::icon]); + item.BenefitFlag = (atoul(row[ItemField::benefitflag]) != 0); + item.Tradeskills = (atoi(row[ItemField::tradeskills])==0) ? false : true; + item.CR = (int8)atoi(row[ItemField::cr]); + item.DR = (int8)atoi(row[ItemField::dr]); + item.PR = (int8)atoi(row[ItemField::pr]); + item.MR = (int8)atoi(row[ItemField::mr]); + item.FR = (int8)atoi(row[ItemField::fr]); + item.AStr = (int8)atoi(row[ItemField::astr]); + item.ASta = (int8)atoi(row[ItemField::asta]); + item.AAgi = (int8)atoi(row[ItemField::aagi]); + item.ADex = (int8)atoi(row[ItemField::adex]); + item.ACha = (int8)atoi(row[ItemField::acha]); + item.AInt = (int8)atoi(row[ItemField::aint]); + item.AWis = (int8)atoi(row[ItemField::awis]); + item.HP = (int32)atoul(row[ItemField::hp]); + item.Mana = (int32)atoul(row[ItemField::mana]); + item.AC = (int32)atoul(row[ItemField::ac]); + item.Deity = (uint32)atoul(row[ItemField::deity]); + item.SkillModValue = (int32)atoul(row[ItemField::skillmodvalue]); + + item.SkillModType = (uint32)atoul(row[ItemField::skillmodtype]); + item.BaneDmgRace = (uint32)atoul(row[ItemField::banedmgrace]); + item.BaneDmgAmt = (int8)atoi(row[ItemField::banedmgamt]); + item.BaneDmgBody = (uint32)atoul(row[ItemField::banedmgbody]); + item.Magic = (atoi(row[ItemField::magic])==0) ? false : true; + item.CastTime_ = (int32)atoul(row[ItemField::casttime_]); + item.ReqLevel = (uint8)atoi(row[ItemField::reqlevel]); + item.BardType = (uint32)atoul(row[ItemField::bardtype]); + item.BardValue = (int32)atoul(row[ItemField::bardvalue]); + item.Light = (int8)atoi(row[ItemField::light]); + item.Delay = (uint8)atoi(row[ItemField::delay]); + item.RecLevel = (uint8)atoi(row[ItemField::reclevel]); + item.RecSkill = (uint8)atoi(row[ItemField::recskill]); + item.ElemDmgType = (uint8)atoi(row[ItemField::elemdmgtype]); + item.ElemDmgAmt = (uint8)atoi(row[ItemField::elemdmgamt]); + item.Range = (uint8)atoi(row[ItemField::range]); + item.Damage = (uint32)atoi(row[ItemField::damage]); + item.Color = (uint32)atoul(row[ItemField::color]); + item.Classes = (uint32)atoul(row[ItemField::classes]); + item.Races = (uint32)atoul(row[ItemField::races]); + + item.MaxCharges = (int16)atoi(row[ItemField::maxcharges]); + item.ItemType = (uint8)atoi(row[ItemField::itemtype]); + item.Material = (uint8)atoi(row[ItemField::material]); + item.SellRate = (float)atof(row[ItemField::sellrate]); + + item.CastTime = (uint32)atoul(row[ItemField::casttime]); + item.EliteMaterial = (uint32)atoul(row[ItemField::elitematerial]); + item.ProcRate = (int32)atoi(row[ItemField::procrate]); + item.CombatEffects = (int8)atoi(row[ItemField::combateffects]); + item.Shielding = (int8)atoi(row[ItemField::shielding]); + item.StunResist = (int8)atoi(row[ItemField::stunresist]); + item.StrikeThrough = (int8)atoi(row[ItemField::strikethrough]); + item.ExtraDmgSkill = (uint32)atoul(row[ItemField::extradmgskill]); + item.ExtraDmgAmt = (uint32)atoul(row[ItemField::extradmgamt]); + item.SpellShield = (int8)atoi(row[ItemField::spellshield]); + item.Avoidance = (int8)atoi(row[ItemField::avoidance]); + item.Accuracy = (int8)atoi(row[ItemField::accuracy]); + item.CharmFileID = (uint32)atoul(row[ItemField::charmfileid]); + item.FactionMod1 = (int32)atoul(row[ItemField::factionmod1]); + item.FactionMod2 = (int32)atoul(row[ItemField::factionmod2]); + item.FactionMod3 = (int32)atoul(row[ItemField::factionmod3]); + item.FactionMod4 = (int32)atoul(row[ItemField::factionmod4]); + item.FactionAmt1 = (int32)atoul(row[ItemField::factionamt1]); + item.FactionAmt2 = (int32)atoul(row[ItemField::factionamt2]); + item.FactionAmt3 = (int32)atoul(row[ItemField::factionamt3]); + item.FactionAmt4 = (int32)atoul(row[ItemField::factionamt4]); + + strcpy(item.CharmFile,row[ItemField::charmfile]); + + item.AugType = (uint32)atoul(row[ItemField::augtype]); + item.AugSlotType[0] = (uint8)atoi(row[ItemField::augslot1type]); + item.AugSlotVisible[0] = (uint8)atoi(row[ItemField::augslot1visible]); + item.AugSlotUnk2[0] = 0; + item.AugSlotType[1] = (uint8)atoi(row[ItemField::augslot2type]); + item.AugSlotVisible[1] = (uint8)atoi(row[ItemField::augslot2visible]); + item.AugSlotUnk2[1] = 0; + item.AugSlotType[2] = (uint8)atoi(row[ItemField::augslot3type]); + item.AugSlotVisible[2] = (uint8)atoi(row[ItemField::augslot3visible]); + item.AugSlotUnk2[2] = 0; + item.AugSlotType[3] = (uint8)atoi(row[ItemField::augslot4type]); + item.AugSlotVisible[3] = (uint8)atoi(row[ItemField::augslot4visible]); + item.AugSlotUnk2[3] = 0; + item.AugSlotType[4] = (uint8)atoi(row[ItemField::augslot5type]); + item.AugSlotVisible[4] = (uint8)atoi(row[ItemField::augslot5visible]); + item.AugSlotUnk2[4] = 0; + + item.LDoNTheme = (uint32)atoul(row[ItemField::ldontheme]); + item.LDoNPrice = (uint32)atoul(row[ItemField::ldonprice]); + item.LDoNSold = (uint32)atoul(row[ItemField::ldonsold]); + item.BagType = (uint8)atoi(row[ItemField::bagtype]); + item.BagSlots = (uint8)atoi(row[ItemField::bagslots]); + item.BagSize = (uint8)atoi(row[ItemField::bagsize]); + item.BagWR = (uint8)atoi(row[ItemField::bagwr]); + item.Book = (uint8)atoi(row[ItemField::book]); + item.BookType = (uint32)atoul(row[ItemField::booktype]); + + strcpy(item.Filename,row[ItemField::filename]); + + item.BaneDmgRaceAmt = (uint32)atoul(row[ItemField::banedmgraceamt]); + item.AugRestrict = (uint32)atoul(row[ItemField::augrestrict]); + item.LoreGroup = disableLoreGroup ? (uint8)atoi("0") : atoi(row[ItemField::loregroup]); + item.LoreFlag = item.LoreGroup!=0; + item.PendingLoreFlag = (atoi(row[ItemField::pendingloreflag])==0) ? false : true; + item.ArtifactFlag = (atoi(row[ItemField::artifactflag])==0) ? false : true; + item.SummonedFlag = (atoi(row[ItemField::summonedflag])==0) ? false : true; + item.Favor = (uint32)atoul(row[ItemField::favor]); + item.FVNoDrop = (atoi(row[ItemField::fvnodrop])==0) ? false : true; + item.Endur = (uint32)atoul(row[ItemField::endur]); + item.DotShielding = (uint32)atoul(row[ItemField::dotshielding]); + item.Attack = (uint32)atoul(row[ItemField::attack]); + item.Regen = (uint32)atoul(row[ItemField::regen]); + item.ManaRegen = (uint32)atoul(row[ItemField::manaregen]); + item.EnduranceRegen = (uint32)atoul(row[ItemField::enduranceregen]); + item.Haste = (uint32)atoul(row[ItemField::haste]); + item.DamageShield = (uint32)atoul(row[ItemField::damageshield]); + item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]); + item.RecastType = (uint32)atoul(row[ItemField::recasttype]); + item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]); + item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]); + item.Attuneable = (atoi(row[ItemField::attuneable])==0) ? false : true; + item.NoPet = (atoi(row[ItemField::nopet])==0) ? false : true; + item.PointType = (uint32)atoul(row[ItemField::pointtype]); + item.PotionBelt = (atoi(row[ItemField::potionbelt])==0) ? false : true; + item.PotionBeltSlots = (atoi(row[ItemField::potionbeltslots])==0) ? false : true; + item.StackSize = (uint16)atoi(row[ItemField::stacksize]); + item.NoTransfer = disableNoTransfer ? false : (atoi(row[ItemField::notransfer])==0) ? false : true; + item.Stackable = (atoi(row[ItemField::stackable])==0) ? false : true; + item.Click.Effect = (uint32)atoul(row[ItemField::clickeffect]); + item.Click.Type = (uint8)atoul(row[ItemField::clicktype]); + item.Click.Level = (uint8)atoul(row[ItemField::clicklevel]); + item.Click.Level2 = (uint8)atoul(row[ItemField::clicklevel2]); + + strcpy(item.CharmFile,row[ItemField::charmfile]); + + item.Proc.Effect = (uint16)atoul(row[ItemField::proceffect]); + item.Proc.Type = (uint8)atoul(row[ItemField::proctype]); + item.Proc.Level = (uint8)atoul(row[ItemField::proclevel]); + item.Proc.Level2 = (uint8)atoul(row[ItemField::proclevel2]); + item.Worn.Effect = (uint16)atoul(row[ItemField::worneffect]); + item.Worn.Type = (uint8)atoul(row[ItemField::worntype]); + item.Worn.Level = (uint8)atoul(row[ItemField::wornlevel]); + item.Worn.Level2 = (uint8)atoul(row[ItemField::wornlevel2]); + item.Focus.Effect = (uint16)atoul(row[ItemField::focuseffect]); + item.Focus.Type = (uint8)atoul(row[ItemField::focustype]); + item.Focus.Level = (uint8)atoul(row[ItemField::focuslevel]); + item.Focus.Level2 = (uint8)atoul(row[ItemField::focuslevel2]); + item.Scroll.Effect = (uint16)atoul(row[ItemField::scrolleffect]); + item.Scroll.Type = (uint8)atoul(row[ItemField::scrolltype]); + item.Scroll.Level = (uint8)atoul(row[ItemField::scrolllevel]); + item.Scroll.Level2 = (uint8)atoul(row[ItemField::scrolllevel2]); + item.Bard.Effect = (uint16)atoul(row[ItemField::bardeffect]); + item.Bard.Type = (uint8)atoul(row[ItemField::bardtype]); + item.Bard.Level = (uint8)atoul(row[ItemField::bardlevel]); + item.Bard.Level2 = (uint8)atoul(row[ItemField::bardlevel2]); + item.QuestItemFlag = (atoi(row[ItemField::questitemflag])==0) ? false : true; + item.SVCorruption = (int32)atoi(row[ItemField::svcorruption]); + item.Purity = (uint32)atoul(row[ItemField::purity]); + item.BackstabDmg = (uint32)atoul(row[ItemField::backstabdmg]); + item.DSMitigation = (uint32)atoul(row[ItemField::dsmitigation]); + item.HeroicStr = (int32)atoi(row[ItemField::heroic_str]); + item.HeroicInt = (int32)atoi(row[ItemField::heroic_int]); + item.HeroicWis = (int32)atoi(row[ItemField::heroic_wis]); + item.HeroicAgi = (int32)atoi(row[ItemField::heroic_agi]); + item.HeroicDex = (int32)atoi(row[ItemField::heroic_dex]); + item.HeroicSta = (int32)atoi(row[ItemField::heroic_sta]); + item.HeroicCha = (int32)atoi(row[ItemField::heroic_cha]); + item.HeroicMR = (int32)atoi(row[ItemField::heroic_mr]); + item.HeroicFR = (int32)atoi(row[ItemField::heroic_fr]); + item.HeroicCR = (int32)atoi(row[ItemField::heroic_cr]); + item.HeroicDR = (int32)atoi(row[ItemField::heroic_dr]); + item.HeroicPR = (int32)atoi(row[ItemField::heroic_pr]); + item.HeroicSVCorrup = (int32)atoi(row[ItemField::heroic_svcorrup]); + item.HealAmt = (int32)atoi(row[ItemField::healamt]); + item.SpellDmg = (int32)atoi(row[ItemField::spelldmg]); + item.LDoNSellBackRate = (uint32)atoul(row[ItemField::ldonsellbackrate]); + item.ScriptFileID = (uint32)atoul(row[ItemField::scriptfileid]); + item.ExpendableArrow = (uint16)atoul(row[ItemField::expendablearrow]); + item.Clairvoyance = (uint32)atoul(row[ItemField::clairvoyance]); + + strcpy(item.ClickName,row[ItemField::clickname]); + strcpy(item.ProcName,row[ItemField::procname]); + strcpy(item.WornName,row[ItemField::wornname]); + strcpy(item.FocusName,row[ItemField::focusname]); + strcpy(item.ScrollName,row[ItemField::scrollname]); + + try { + hash.insert(item.ID, item); + } catch(std::exception &ex) { + LogFile->write(EQEMuLog::Error, "Database::LoadItems: %s", ex.what()); + break; + } + } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "LoadItems '%s', %s", query, errbuf); - } } const Item_Struct* SharedDatabase::GetItem(uint32 id) { From 0ef428d28688a390ffcf2567daf96dd0925c6e1f Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:07:55 -0700 Subject: [PATCH 240/368] GetBook converted to QueryDatabase --- common/shareddb.cpp | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index f7895a485..38925027e 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1015,35 +1015,28 @@ const Item_Struct* SharedDatabase::IterateItems(uint32* id) { std::string SharedDatabase::GetBook(const char *txtfile) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; char txtfile2[20]; std::string txtout; - strcpy(txtfile2,txtfile); - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT txtfile FROM books where name='%s'", txtfile2), errbuf, &result)) { - std::cerr << "Error in GetBook query '" << query << "' " << errbuf << std::endl; - if (query != 0) - safe_delete_array(query); + strcpy(txtfile2, txtfile); + + std::string query = StringFormat("SELECT txtfile FROM books WHERE name = '%s'", txtfile2); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetBook query '" << query << "' " << results.ErrorMessage() << std::endl; txtout.assign(" ",1); return txtout; } - else { - safe_delete_array(query); - if (mysql_num_rows(result) == 0) { - mysql_free_result(result); - LogFile->write(EQEMuLog::Error, "No book to send, (%s)", txtfile); - txtout.assign(" ",1); - return txtout; - } - else { - row = mysql_fetch_row(result); - txtout.assign(row[0],strlen(row[0])); - mysql_free_result(result); - return txtout; - } - } + + if (results.RowCount() == 0) { + LogFile->write(EQEMuLog::Error, "No book to send, (%s)", txtfile); + txtout.assign(" ",1); + return txtout; + } + + auto row = results.begin(); + txtout.assign(row[0],strlen(row[0])); + + return txtout; } void SharedDatabase::GetFactionListInfo(uint32 &list_count, uint32 &max_lists) { From 7d0b316e74194e020a7b18d27f910816d8ee3dd0 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:08:53 -0700 Subject: [PATCH 241/368] GetFactionListInfo converted to QueryDatabase --- common/shareddb.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 38925027e..6c727ef28 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1042,20 +1042,21 @@ std::string SharedDatabase::GetBook(const char *txtfile) void SharedDatabase::GetFactionListInfo(uint32 &list_count, uint32 &max_lists) { list_count = 0; max_lists = 0; - const char *query = "SELECT COUNT(*), MAX(id) FROM npc_faction"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, strlen(query), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - list_count = static_cast(atoul(row[0])); - max_lists = static_cast(atoul(row[1])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query, errbuf); + const std::string query = "SELECT COUNT(*), MAX(id) FROM npc_faction"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; } + + if (results.RowCount() == 0) + return; + + auto row = results.begin(); + + list_count = static_cast(atoul(row[0])); + max_lists = static_cast(atoul(row[1])); } const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) { From e19db3f447a741f1ce337aaff36172711e7d2730 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:10:00 -0700 Subject: [PATCH 242/368] LoadNPCFactionLists converted to QueryDatabase --- common/shareddb.cpp | 81 +++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 6c727ef28..6441145aa 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1073,57 +1073,52 @@ const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) { void SharedDatabase::LoadNPCFactionLists(void *data, uint32 size, uint32 list_count, uint32 max_lists) { EQEmu::FixedMemoryHashSet hash(reinterpret_cast(data), size, list_count, max_lists); - const char *query = "SELECT npc_faction.id, npc_faction.primaryfaction, npc_faction.ignore_primary_assist, " - "npc_faction_entries.faction_id, npc_faction_entries.value, npc_faction_entries.npc_value, npc_faction_entries.temp " - "FROM npc_faction LEFT JOIN npc_faction_entries ON npc_faction.id = npc_faction_entries.npc_faction_id ORDER BY " - "npc_faction.id;"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; NPCFactionList faction; - if(RunQuery(query, strlen(query), errbuf, &result)) { - uint32 current_id = 0; - uint32 current_entry = 0; - while(row = mysql_fetch_row(result)) { - uint32 id = static_cast(atoul(row[0])); - if(id != current_id) { - if(current_id != 0) { - hash.insert(current_id, faction); - } + const std::string query = "SELECT npc_faction.id, npc_faction.primaryfaction, npc_faction.ignore_primary_assist, " + "npc_faction_entries.faction_id, npc_faction_entries.value, npc_faction_entries.npc_value, " + "npc_faction_entries.temp FROM npc_faction LEFT JOIN npc_faction_entries " + "ON npc_faction.id = npc_faction_entries.npc_faction_id ORDER BY npc_faction.id;"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - memset(&faction, 0, sizeof(faction)); - current_entry = 0; - current_id = id; - faction.id = id; - faction.primaryfaction = static_cast(atoul(row[1])); - faction.assistprimaryfaction = (atoi(row[2]) == 0); - } + uint32 current_id = 0; + uint32 current_entry = 0; - if(!row[3]) { + for(auto row = results.begin(); row != results.end(); ++row) { + uint32 id = static_cast(atoul(row[0])); + if(id != current_id) { + if(current_id != 0) { + hash.insert(current_id, faction); + } + + memset(&faction, 0, sizeof(faction)); + current_entry = 0; + current_id = id; + faction.id = id; + faction.primaryfaction = static_cast(atoul(row[1])); + faction.assistprimaryfaction = (atoi(row[2]) == 0); + } + + if(!row[3]) + continue; + + if(current_entry >= MAX_NPC_FACTIONS) continue; - } - if(current_entry >= MAX_NPC_FACTIONS) { - continue; - } + faction.factionid[current_entry] = static_cast(atoul(row[3])); + faction.factionvalue[current_entry] = static_cast(atoi(row[4])); + faction.factionnpcvalue[current_entry] = static_cast(atoi(row[5])); + faction.factiontemp[current_entry] = static_cast(atoi(row[6])); + ++current_entry; + } - faction.factionid[current_entry] = static_cast(atoul(row[3])); - faction.factionvalue[current_entry] = static_cast(atoi(row[4])); - faction.factionnpcvalue[current_entry] = static_cast(atoi(row[5])); - faction.factiontemp[current_entry] = static_cast(atoi(row[6])); - ++current_entry; - } + if(current_id != 0) + hash.insert(current_id, faction); - if(current_id != 0) { - hash.insert(current_id, faction); - } - - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query, errbuf); -} } bool SharedDatabase::LoadNPCFactionLists() { From e2c84c5f392fdabaf161afad0a3ce018910b7d69 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:10:57 -0700 Subject: [PATCH 243/368] DeleteStalePlayerCorpses converted to QueryDatabase --- common/shareddb.cpp | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 6441145aa..44e6084e9 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1207,33 +1207,25 @@ ItemInst* SharedDatabase::CreateBaseItem(const Item_Struct* item, int16 charges) } int32 SharedDatabase::DeleteStalePlayerCorpses() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if(RuleB(Zone, EnableShadowrest)) { - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE player_corpses SET IsBurried = 1 WHERE IsBurried=0 and " - "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > %d and not timeofdeath=0", - (RuleI(Character, CorpseDecayTimeMS) / 1000)), errbuf, 0, &affected_rows)) - { - safe_delete_array(query); + std::string query = StringFormat("UPDATE player_corpses SET IsBurried = 1 WHERE IsBurried = 0 AND " + "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > %d AND NOT timeofdeath = 0", + (RuleI(Character, CorpseDecayTimeMS) / 1000)); + auto results = QueryDatabase(query); + if (!results.Success()) return -1; - } - } - else - { - if (!RunQuery(query, MakeAnyLenString(&query, "Delete from player_corpses where (UNIX_TIMESTAMP() - " - "UNIX_TIMESTAMP(timeofdeath)) > %d and not timeofdeath=0", (RuleI(Character, CorpseDecayTimeMS) / 1000)), - errbuf, 0, &affected_rows)) - { - safe_delete_array(query); - return -1; - } + + return results.RowsAffected(); } - safe_delete_array(query); - return affected_rows; + std::string query = StringFormat("DELETE FROM player_corpses WHERE (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > %d " + "AND NOT timeofdeath = 0", (RuleI(Character, CorpseDecayTimeMS) / 1000)); + auto results = QueryDatabase(query); + if (!results.Success()) + return -1; + + return results.RowsAffected(); } int32 SharedDatabase::DeleteStalePlayerBackups() { From c12805accebf55477d5ba11e731ef044a19f7c0a Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:11:40 -0700 Subject: [PATCH 244/368] DeleteStalePlayerBackups converted to QueryDatabase --- common/shareddb.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 44e6084e9..ba4d34413 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1229,18 +1229,13 @@ int32 SharedDatabase::DeleteStalePlayerCorpses() { } int32 SharedDatabase::DeleteStalePlayerBackups() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - // 1209600 seconds = 2 weeks - if (!RunQuery(query, MakeAnyLenString(&query, "Delete from player_corpses_backup where (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > 1209600"), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - return -1; - } - safe_delete_array(query); + const std::string query = "DELETE FROM player_corpses_backup WHERE (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > 1209600"; + auto results = QueryDatabase(query); + if (!results.Success()) + return -1; - return affected_rows; + return results.RowsAffected(); } bool SharedDatabase::GetCommandSettings(std::map &commands) { From 5cbae2b83336b7824ca4714c11cb4cce558c7f97 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:12:16 -0700 Subject: [PATCH 245/368] GetcommandSettings converted to QueryDatabase --- common/shareddb.cpp | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index ba4d34413..3a4e59f86 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1239,27 +1239,20 @@ int32 SharedDatabase::DeleteStalePlayerBackups() { } bool SharedDatabase::GetCommandSettings(std::map &commands) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - strcpy(query, "SELECT command,access from commands"); - commands.clear(); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - commands[row[0]]=atoi(row[1]); - } - mysql_free_result(result); - return true; - } else { - std::cerr << "Error in GetCommands query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + const std::string query = "SELECT command, access FROM commands"; + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetCommands query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - return false; + commands.clear(); + + for (auto row = results.begin(); row != results.end(); ++row) + commands[row[0]]=atoi(row[1]); + + return true; } bool SharedDatabase::LoadSkillCaps() { From 13667749312dd2accead8c8e36ff4e04aceee489 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:13:05 -0700 Subject: [PATCH 246/368] LoadSkillCaps converted to QueryDatabase --- common/shareddb.cpp | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 3a4e59f86..db9f6a3bf 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1287,31 +1287,25 @@ void SharedDatabase::LoadSkillCaps(void *data) { uint32 level_count = HARD_LEVEL_CAP + 1; uint16 *skill_caps_table = reinterpret_cast(data); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, MakeAnyLenString(&query, - "SELECT skillID, class, level, cap FROM skill_caps ORDER BY skillID, class, level"), - errbuf, &result)) { - safe_delete_array(query); - - while((row = mysql_fetch_row(result))) { - uint8 skillID = atoi(row[0]); - uint8 class_ = atoi(row[1]) - 1; - uint8 level = atoi(row[2]); - uint16 cap = atoi(row[3]); - if(skillID >= skill_count || class_ >= class_count || level >= level_count) - continue; - - uint32 index = (((class_ * skill_count) + skillID) * level_count) + level; - skill_caps_table[index] = cap; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error loading skill caps from database: %s", errbuf); - safe_delete_array(query); + const std::string query = "SELECT skillID, class, level, cap FROM skill_caps ORDER BY skillID, class, level"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error loading skill caps from database: %s", results.ErrorMessage().c_str()); + return; } + + for(auto row = results.begin(); row != results.end(); ++row) { + uint8 skillID = atoi(row[0]); + uint8 class_ = atoi(row[1]) - 1; + uint8 level = atoi(row[2]); + uint16 cap = atoi(row[3]); + + if(skillID >= skill_count || class_ >= class_count || level >= level_count) + continue; + + uint32 index = (((class_ * skill_count) + skillID) * level_count) + level; + skill_caps_table[index] = cap; + } } uint16 SharedDatabase::GetSkillCap(uint8 Class_, SkillUseTypes Skill, uint8 Level) { From 0622f02470789c659261d8c90f4ee8f47470ac21 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:14:27 -0700 Subject: [PATCH 247/368] LoadDamageShieldTypes converted to queryDatabase --- common/shareddb.cpp | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index db9f6a3bf..4408840d5 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1388,32 +1388,20 @@ uint8 SharedDatabase::GetTrainLevel(uint8 Class_, SkillUseTypes Skill, uint8 Lev void SharedDatabase::LoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpellID) { - const char *DSQuery = "SELECT `spellid`, `type` from `damageshieldtypes` WHERE `spellid` > 0 " - "AND `spellid` <= %i"; + std::string query = StringFormat("SELECT `spellid`, `type` FROM `damageshieldtypes` WHERE `spellid` > 0 " + "AND `spellid` <= %i", iMaxSpellID); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadDamageShieldTypes: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - const char *ERR_MYSQLERROR = "Error in LoadDamageShieldTypes: %s %s"; + for(auto row = results.begin(); row != results.end(); ++row) { + int spellID = atoi(row[0]); + if((spellID > 0) && (spellID <= iMaxSpellID)) + sp[spellID].DamageShieldType = atoi(row[1]); + } - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(RunQuery(query,MakeAnyLenString(&query,DSQuery,iMaxSpellID),errbuf,&result)) { - - while((row = mysql_fetch_row(result))) { - - int SpellID = atoi(row[0]); - if((SpellID > 0) && (SpellID <= iMaxSpellID)) { - sp[SpellID].DamageShieldType = atoi(row[1]); - } - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - safe_delete_array(query); - } } const EvolveInfo* SharedDatabase::GetEvolveInfo(uint32 loregroup) { From 1c04dccf53a1f961f0dae4aad4b4ae97bb69ddc8 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:16:00 -0700 Subject: [PATCH 248/368] GetMaxSpellID converted to QueryDatabase --- common/shareddb.cpp | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 4408840d5..6b4a43a13 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1409,23 +1409,16 @@ const EvolveInfo* SharedDatabase::GetEvolveInfo(uint32 loregroup) { } int SharedDatabase::GetMaxSpellID() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; - MYSQL_ROW row; - int32 ret = 0; - if(RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM spells_new"), - errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - ret = atoi(row[0]); - mysql_free_result(result); - } else { - _log(SPELLS__LOAD_ERR, "Error in GetMaxSpellID query '%s' %s", query, errbuf); - safe_delete_array(query); - ret = -1; - } - return ret; + std::string query = "SELECT MAX(id) FROM spells_new"; + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(SPELLS__LOAD_ERR, "Error in GetMaxSpellID query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return -1; + } + + auto row = results.begin(); + + return atoi(row[0]); } void SharedDatabase::LoadSpells(void *data, int max_spells) { From 938322a3d14fc607a87fd7434ecac849162673ca Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:16:54 -0700 Subject: [PATCH 249/368] LoadSpells converted to QueryDatabase --- common/shareddb.cpp | 269 ++++++++++++++++++++++---------------------- 1 file changed, 133 insertions(+), 136 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 6b4a43a13..b90097a1e 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1423,164 +1423,161 @@ int SharedDatabase::GetMaxSpellID() { void SharedDatabase::LoadSpells(void *data, int max_spells) { SPDat_Spell_Struct *sp = reinterpret_cast(data); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, MakeAnyLenString(&query, - "SELECT * FROM spells_new ORDER BY id ASC"), - errbuf, &result)) { - safe_delete_array(query); + const std::string query = "SELECT * FROM spells_new ORDER BY id ASC"; + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(SPELLS__LOAD_ERR, "Error in LoadSpells query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - int tempid = 0; - int counter = 0; + if(results.ColumnCount() <= SPELL_LOAD_FIELD_COUNT) { + _log(SPELLS__LOAD_ERR, "Fatal error loading spells: Spell field count < SPELL_LOAD_FIELD_COUNT(%u)", SPELL_LOAD_FIELD_COUNT); + return; + } - if(result && mysql_field_count(getMySQL()) <= SPELL_LOAD_FIELD_COUNT) { - _log(SPELLS__LOAD_ERR, "Fatal error loading spells: Spell field count < SPELL_LOAD_FIELD_COUNT(%u)", SPELL_LOAD_FIELD_COUNT); - mysql_free_result(result); - return; - } + int tempid = 0; + int counter = 0; - while (row = mysql_fetch_row(result)) { - tempid = atoi(row[0]); - if(tempid >= max_spells) { - _log(SPELLS__LOAD_ERR, "Non fatal error: spell.id >= max_spells, ignoring."); - continue; - } + for (auto row = results.begin(); row != results.end(); ++row) { + tempid = atoi(row[0]); + if(tempid >= max_spells) { + _log(SPELLS__LOAD_ERR, "Non fatal error: spell.id >= max_spells, ignoring."); + continue; + } - ++counter; - sp[tempid].id = tempid; - strn0cpy(sp[tempid].name, row[1], sizeof(sp[tempid].name)); - strn0cpy(sp[tempid].player_1, row[2], sizeof(sp[tempid].player_1)); - strn0cpy(sp[tempid].teleport_zone, row[3], sizeof(sp[tempid].teleport_zone)); - strn0cpy(sp[tempid].you_cast, row[4], sizeof(sp[tempid].you_cast)); - strn0cpy(sp[tempid].other_casts, row[5], sizeof(sp[tempid].other_casts)); - strn0cpy(sp[tempid].cast_on_you, row[6], sizeof(sp[tempid].cast_on_you)); - strn0cpy(sp[tempid].cast_on_other, row[7], sizeof(sp[tempid].cast_on_other)); - strn0cpy(sp[tempid].spell_fades, row[8], sizeof(sp[tempid].spell_fades)); + ++counter; + sp[tempid].id = tempid; + strn0cpy(sp[tempid].name, row[1], sizeof(sp[tempid].name)); + strn0cpy(sp[tempid].player_1, row[2], sizeof(sp[tempid].player_1)); + strn0cpy(sp[tempid].teleport_zone, row[3], sizeof(sp[tempid].teleport_zone)); + strn0cpy(sp[tempid].you_cast, row[4], sizeof(sp[tempid].you_cast)); + strn0cpy(sp[tempid].other_casts, row[5], sizeof(sp[tempid].other_casts)); + strn0cpy(sp[tempid].cast_on_you, row[6], sizeof(sp[tempid].cast_on_you)); + strn0cpy(sp[tempid].cast_on_other, row[7], sizeof(sp[tempid].cast_on_other)); + strn0cpy(sp[tempid].spell_fades, row[8], sizeof(sp[tempid].spell_fades)); - sp[tempid].range=static_cast(atof(row[9])); - sp[tempid].aoerange=static_cast(atof(row[10])); - sp[tempid].pushback=static_cast(atof(row[11])); - sp[tempid].pushup=static_cast(atof(row[12])); - sp[tempid].cast_time=atoi(row[13]); - sp[tempid].recovery_time=atoi(row[14]); - sp[tempid].recast_time=atoi(row[15]); - sp[tempid].buffdurationformula=atoi(row[16]); - sp[tempid].buffduration=atoi(row[17]); - sp[tempid].AEDuration=atoi(row[18]); - sp[tempid].mana=atoi(row[19]); + sp[tempid].range=static_cast(atof(row[9])); + sp[tempid].aoerange=static_cast(atof(row[10])); + sp[tempid].pushback=static_cast(atof(row[11])); + sp[tempid].pushup=static_cast(atof(row[12])); + sp[tempid].cast_time=atoi(row[13]); + sp[tempid].recovery_time=atoi(row[14]); + sp[tempid].recast_time=atoi(row[15]); + sp[tempid].buffdurationformula=atoi(row[16]); + sp[tempid].buffduration=atoi(row[17]); + sp[tempid].AEDuration=atoi(row[18]); + sp[tempid].mana=atoi(row[19]); - int y=0; - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].base[y]=atoi(row[20+y]); // effect_base_value - for(y=0; y < EFFECT_COUNT; y++) - sp[tempid].base2[y]=atoi(row[32+y]); // effect_limit_value - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].max[y]=atoi(row[44+y]); + int y=0; + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].base[y]=atoi(row[20+y]); // effect_base_value - for(y=0; y< 4;y++) - sp[tempid].components[y]=atoi(row[58+y]); + for(y=0; y < EFFECT_COUNT; y++) + sp[tempid].base2[y]=atoi(row[32+y]); // effect_limit_value - for(y=0; y< 4;y++) - sp[tempid].component_counts[y]=atoi(row[62+y]); + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].max[y]=atoi(row[44+y]); - for(y=0; y< 4;y++) - sp[tempid].NoexpendReagent[y]=atoi(row[66+y]); + for(y=0; y< 4;y++) + sp[tempid].components[y]=atoi(row[58+y]); - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].formula[y]=atoi(row[70+y]); + for(y=0; y< 4;y++) + sp[tempid].component_counts[y]=atoi(row[62+y]); - sp[tempid].goodEffect=atoi(row[83]); - sp[tempid].Activated=atoi(row[84]); - sp[tempid].resisttype=atoi(row[85]); + for(y=0; y< 4;y++) + sp[tempid].NoexpendReagent[y]=atoi(row[66+y]); - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].effectid[y]=atoi(row[86+y]); + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].formula[y]=atoi(row[70+y]); - sp[tempid].targettype = (SpellTargetType) atoi(row[98]); - sp[tempid].basediff=atoi(row[99]); - int tmp_skill = atoi(row[100]);; - if(tmp_skill < 0 || tmp_skill > HIGHEST_SKILL) - sp[tempid].skill = SkillBegging; /* not much better we can do. */ // can probably be changed to client-based 'SkillNone' once activated - else - sp[tempid].skill = (SkillUseTypes) tmp_skill; - sp[tempid].zonetype=atoi(row[101]); - sp[tempid].EnvironmentType=atoi(row[102]); - sp[tempid].TimeOfDay=atoi(row[103]); + sp[tempid].goodEffect=atoi(row[83]); + sp[tempid].Activated=atoi(row[84]); + sp[tempid].resisttype=atoi(row[85]); - for(y=0; y < PLAYER_CLASS_COUNT;y++) - sp[tempid].classes[y]=atoi(row[104+y]); + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].effectid[y]=atoi(row[86+y]); - sp[tempid].CastingAnim=atoi(row[120]); - sp[tempid].SpellAffectIndex=atoi(row[123]); - sp[tempid].disallow_sit=atoi(row[124]); + sp[tempid].targettype = (SpellTargetType) atoi(row[98]); + sp[tempid].basediff=atoi(row[99]); - for (y = 0; y < 16; y++) - sp[tempid].deities[y]=atoi(row[126+y]); + int tmp_skill = atoi(row[100]);; - sp[tempid].uninterruptable=atoi(row[146]) != 0; - sp[tempid].ResistDiff=atoi(row[147]); - sp[tempid].dot_stacking_exempt=atoi(row[148]); - sp[tempid].RecourseLink = atoi(row[150]); - sp[tempid].no_partial_resist = atoi(row[151]) != 0; + if(tmp_skill < 0 || tmp_skill > HIGHEST_SKILL) + sp[tempid].skill = SkillBegging; /* not much better we can do. */ // can probably be changed to client-based 'SkillNone' once activated + else + sp[tempid].skill = (SkillUseTypes) tmp_skill; - sp[tempid].short_buff_box = atoi(row[154]); - sp[tempid].descnum = atoi(row[155]); - sp[tempid].effectdescnum = atoi(row[157]); + sp[tempid].zonetype=atoi(row[101]); + sp[tempid].EnvironmentType=atoi(row[102]); + sp[tempid].TimeOfDay=atoi(row[103]); - sp[tempid].npc_no_los = atoi(row[159]) != 0; - sp[tempid].reflectable = atoi(row[161]) != 0; - sp[tempid].bonushate=atoi(row[162]); + for(y=0; y < PLAYER_CLASS_COUNT;y++) + sp[tempid].classes[y]=atoi(row[104+y]); - sp[tempid].EndurCost=atoi(row[166]); - sp[tempid].EndurTimerIndex=atoi(row[167]); - sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0; - sp[tempid].HateAdded=atoi(row[173]); - sp[tempid].EndurUpkeep=atoi(row[174]); - sp[tempid].numhitstype = atoi(row[175]); - sp[tempid].numhits = atoi(row[176]); - sp[tempid].pvpresistbase=atoi(row[177]); - sp[tempid].pvpresistcalc=atoi(row[178]); - sp[tempid].pvpresistcap=atoi(row[179]); - sp[tempid].spell_category=atoi(row[180]); - sp[tempid].can_mgb=atoi(row[185]); - sp[tempid].dispel_flag = atoi(row[186]); - sp[tempid].MinResist = atoi(row[189]); - sp[tempid].MaxResist = atoi(row[190]); - sp[tempid].viral_targets = atoi(row[191]); - sp[tempid].viral_timer = atoi(row[192]); - sp[tempid].NimbusEffect = atoi(row[193]); - sp[tempid].directional_start = static_cast(atoi(row[194])); - sp[tempid].directional_end = static_cast(atoi(row[195])); - sp[tempid].not_extendable = atoi(row[197]) != 0; - sp[tempid].suspendable = atoi(row[200]) != 0; - sp[tempid].viral_range = atoi(row[201]); - sp[tempid].spellgroup=atoi(row[207]); - sp[tempid].rank = atoi(row[208]); - sp[tempid].powerful_flag=atoi(row[209]); - sp[tempid].CastRestriction = atoi(row[211]); - sp[tempid].AllowRest = atoi(row[212]) != 0; - sp[tempid].InCombat = atoi(row[213]) != 0; - sp[tempid].OutofCombat = atoi(row[214]) != 0; - sp[tempid].aemaxtargets = atoi(row[218]); - sp[tempid].maxtargets = atoi(row[219]); - sp[tempid].persistdeath = atoi(row[224]) != 0; - sp[tempid].min_dist = atof(row[227]); - sp[tempid].min_dist_mod = atof(row[228]); - sp[tempid].max_dist = atof(row[229]); - sp[tempid].max_dist_mod = atof(row[230]); - sp[tempid].min_range = static_cast(atoi(row[231])); - sp[tempid].DamageShieldType = 0; - } - mysql_free_result(result); + sp[tempid].CastingAnim=atoi(row[120]); + sp[tempid].SpellAffectIndex=atoi(row[123]); + sp[tempid].disallow_sit=atoi(row[124]); - LoadDamageShieldTypes(sp, max_spells); - } else { - _log(SPELLS__LOAD_ERR, "Error in LoadSpells query '%s' %s", query, errbuf); - safe_delete_array(query); - } + for (y = 0; y < 16; y++) + sp[tempid].deities[y]=atoi(row[126+y]); + + sp[tempid].uninterruptable=atoi(row[146]) != 0; + sp[tempid].ResistDiff=atoi(row[147]); + sp[tempid].dot_stacking_exempt=atoi(row[148]); + sp[tempid].RecourseLink = atoi(row[150]); + sp[tempid].no_partial_resist = atoi(row[151]) != 0; + + sp[tempid].short_buff_box = atoi(row[154]); + sp[tempid].descnum = atoi(row[155]); + sp[tempid].effectdescnum = atoi(row[157]); + + sp[tempid].npc_no_los = atoi(row[159]) != 0; + sp[tempid].reflectable = atoi(row[161]) != 0; + sp[tempid].bonushate=atoi(row[162]); + + sp[tempid].EndurCost=atoi(row[166]); + sp[tempid].EndurTimerIndex=atoi(row[167]); + sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0; + sp[tempid].HateAdded=atoi(row[173]); + sp[tempid].EndurUpkeep=atoi(row[174]); + sp[tempid].numhitstype = atoi(row[175]); + sp[tempid].numhits = atoi(row[176]); + sp[tempid].pvpresistbase=atoi(row[177]); + sp[tempid].pvpresistcalc=atoi(row[178]); + sp[tempid].pvpresistcap=atoi(row[179]); + sp[tempid].spell_category=atoi(row[180]); + sp[tempid].can_mgb=atoi(row[185]); + sp[tempid].dispel_flag = atoi(row[186]); + sp[tempid].MinResist = atoi(row[189]); + sp[tempid].MaxResist = atoi(row[190]); + sp[tempid].viral_targets = atoi(row[191]); + sp[tempid].viral_timer = atoi(row[192]); + sp[tempid].NimbusEffect = atoi(row[193]); + sp[tempid].directional_start = static_cast(atoi(row[194])); + sp[tempid].directional_end = static_cast(atoi(row[195])); + sp[tempid].not_extendable = atoi(row[197]) != 0; + sp[tempid].suspendable = atoi(row[200]) != 0; + sp[tempid].viral_range = atoi(row[201]); + sp[tempid].spellgroup=atoi(row[207]); + sp[tempid].rank = atoi(row[208]); + sp[tempid].powerful_flag=atoi(row[209]); + sp[tempid].CastRestriction = atoi(row[211]); + sp[tempid].AllowRest = atoi(row[212]) != 0; + sp[tempid].InCombat = atoi(row[213]) != 0; + sp[tempid].OutofCombat = atoi(row[214]) != 0; + sp[tempid].aemaxtargets = atoi(row[218]); + sp[tempid].maxtargets = atoi(row[219]); + sp[tempid].persistdeath = atoi(row[224]) != 0; + sp[tempid].min_dist = atof(row[227]); + sp[tempid].min_dist_mod = atof(row[228]); + sp[tempid].max_dist = atof(row[229]); + sp[tempid].max_dist_mod = atof(row[230]); + sp[tempid].min_range = static_cast(atoi(row[231])); + sp[tempid].DamageShieldType = 0; + } + + LoadDamageShieldTypes(sp, max_spells); } int SharedDatabase::GetMaxBaseDataLevel() { From cecd9b89ba9c623a07e6ae7601f7b725f74cc0c2 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:18:11 -0700 Subject: [PATCH 250/368] GetMaxBaseDataLevel converted to QueryDatabase --- common/shareddb.cpp | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index b90097a1e..0522d146a 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1581,25 +1581,19 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { } int SharedDatabase::GetMaxBaseDataLevel() { - char errbuf[MYSQL_ERRMSG_SIZE]; - const char *query = "SELECT MAX(level) FROM base_data"; - MYSQL_RES *result; - MYSQL_ROW row; - int32 ret = 0; - if(RunQuery(query, strlen(query), errbuf, &result)) { - row = mysql_fetch_row(result); - if(row) { - ret = atoi(row[0]); - mysql_free_result(result); - } else { - ret = -1; - mysql_free_result(result); - } - } else { - LogFile->write(EQEMuLog::Error, "Error in GetMaxBaseDataLevel query '%s' %s", query, errbuf); - ret = -1; + const std::string query = "SELECT MAX(level) FROM base_data"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetMaxBaseDataLevel query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return -1; } - return ret; + + if (results.RowCount() == 0) + return -1; + + auto row = results.begin(); + + return atoi(row[0]); } bool SharedDatabase::LoadBaseData() { From 81b9d9a57eefa7fd0fcb5375bd8e3a0052ce6233 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:19:04 -0700 Subject: [PATCH 251/368] LoadBaseData converted to QueryDatabase --- common/shareddb.cpp | 89 ++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 0522d146a..2621e61bc 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1626,53 +1626,52 @@ bool SharedDatabase::LoadBaseData() { void SharedDatabase::LoadBaseData(void *data, int max_level) { char *base_ptr = reinterpret_cast(data); - char errbuf[MYSQL_ERRMSG_SIZE]; - const char *query = "SELECT * FROM base_data ORDER BY level, class ASC"; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, strlen(query), errbuf, &result)) { - - int lvl = 0; - int cl = 0; - while (row = mysql_fetch_row(result)) { - lvl = atoi(row[0]); - cl = atoi(row[1]); - if(lvl <= 0) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.level <= 0, ignoring."); - continue; - } - - if(lvl >= max_level) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.level >= max_level, ignoring."); - continue; - } - - if(cl <= 0) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.cl <= 0, ignoring."); - continue; - } - - if(cl > 16) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.class > 16, ignoring."); - continue; - } - - BaseDataStruct *bd = reinterpret_cast(base_ptr + (((16 * (lvl - 1)) + (cl - 1)) * sizeof(BaseDataStruct))); - bd->base_hp = atof(row[2]); - bd->base_mana = atof(row[3]); - bd->base_end = atof(row[4]); - bd->unk1 = atof(row[5]); - bd->unk2 = atof(row[6]); - bd->hp_factor = atof(row[7]); - bd->mana_factor = atof(row[8]); - bd->endurance_factor = atof(row[9]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadBaseData query '%s' %s", query, errbuf); - safe_delete_array(query); + const std::string query = "SELECT * FROM base_data ORDER BY level, class ASC"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadBaseData query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return; } + + int lvl = 0; + int cl = 0; + + for (auto row = results.begin(); row != results.end(); ++row) { + lvl = atoi(row[0]); + cl = atoi(row[1]); + + if(lvl <= 0) { + LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.level <= 0, ignoring."); + continue; + } + + if(lvl >= max_level) { + LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.level >= max_level, ignoring."); + continue; + } + + if(cl <= 0) { + LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.cl <= 0, ignoring."); + continue; + } + + if(cl > 16) { + LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.class > 16, ignoring."); + continue; + } + + BaseDataStruct *bd = reinterpret_cast(base_ptr + (((16 * (lvl - 1)) + (cl - 1)) * sizeof(BaseDataStruct))); + bd->base_hp = atof(row[2]); + bd->base_mana = atof(row[3]); + bd->base_end = atof(row[4]); + bd->unk1 = atof(row[5]); + bd->unk2 = atof(row[6]); + bd->hp_factor = atof(row[7]); + bd->mana_factor = atof(row[8]); + bd->endurance_factor = atof(row[9]); + } + } const BaseDataStruct* SharedDatabase::GetBaseData(int lvl, int cl) { From aa9601352dc6386b04421ee2f3cdea7d92763523 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:20:46 -0700 Subject: [PATCH 252/368] GetLootDropInfo converted to QueryDatabase --- common/shareddb.cpp | 57 +++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 2621e61bc..dd71bf11a 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1707,42 +1707,43 @@ void SharedDatabase::GetLootTableInfo(uint32 &loot_table_count, uint32 &max_loot loot_table_count = 0; max_loot_table = 0; loot_table_entries = 0; - const char *query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM loottable_entries) FROM loottable"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; + const std::string query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM loottable_entries) FROM loottable"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - if(RunQuery(query, strlen(query), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - loot_table_count = static_cast(atoul(row[0])); - max_loot_table = static_cast(atoul(row[1])); - loot_table_entries = static_cast(atoul(row[2])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); - } + if (results.RowCount() == 0) + return; + + auto row = results.begin(); + + loot_table_count = static_cast(atoul(row[0])); + max_loot_table = static_cast(atoul(row[1])); + loot_table_entries = static_cast(atoul(row[2])); } void SharedDatabase::GetLootDropInfo(uint32 &loot_drop_count, uint32 &max_loot_drop, uint32 &loot_drop_entries) { loot_drop_count = 0; max_loot_drop = 0; loot_drop_entries = 0; - const char *query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM lootdrop_entries) FROM lootdrop"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, strlen(query), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - loot_drop_count = static_cast(atoul(row[0])); - max_loot_drop = static_cast(atoul(row[1])); - loot_drop_entries = static_cast(atoul(row[2])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); - } + const std::string query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM lootdrop_entries) FROM lootdrop"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + if (results.RowCount() == 0) + return; + + auto row =results.begin(); + + loot_drop_count = static_cast(atoul(row[0])); + max_loot_drop = static_cast(atoul(row[1])); + loot_drop_entries = static_cast(atoul(row[2])); } void SharedDatabase::LoadLootTables(void *data, uint32 size) { From c7faf8a03c2d7efa27a8b5a2e2bee75b631bd510 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:21:25 -0700 Subject: [PATCH 253/368] LoadLootTables converted to QueryDatabase --- common/shareddb.cpp | 87 +++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index dd71bf11a..80d1e4383 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1748,61 +1748,56 @@ void SharedDatabase::GetLootDropInfo(uint32 &loot_drop_count, uint32 &max_loot_d void SharedDatabase::LoadLootTables(void *data, uint32 size) { EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); - const char *query = "SELECT loottable.id, loottable.mincash, loottable.maxcash, loottable.avgcoin," - " loottable_entries.lootdrop_id, loottable_entries.multiplier, loottable_entries.droplimit, " - "loottable_entries.mindrop, loottable_entries.probability FROM loottable LEFT JOIN loottable_entries" - " ON loottable.id = loottable_entries.loottable_id ORDER BY id"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; + uint8 loot_table[sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)]; LootTable_Struct *lt = reinterpret_cast(loot_table); - if(RunQuery(query, strlen(query), errbuf, &result)) { - uint32 current_id = 0; - uint32 current_entry = 0; - while(row = mysql_fetch_row(result)) { - uint32 id = static_cast(atoul(row[0])); - if(id != current_id) { - if(current_id != 0) { - hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + - (sizeof(LootTableEntries_Struct) * lt->NumEntries))); - } + const std::string query = "SELECT loottable.id, loottable.mincash, loottable.maxcash, loottable.avgcoin, " + "loottable_entries.lootdrop_id, loottable_entries.multiplier, loottable_entries.droplimit, " + "loottable_entries.mindrop, loottable_entries.probability FROM loottable LEFT JOIN loottable_entries " + "ON loottable.id = loottable_entries.loottable_id ORDER BY id"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - memset(loot_table, 0, sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)); - current_entry = 0; - current_id = id; - lt->mincash = static_cast(atoul(row[1])); - lt->maxcash = static_cast(atoul(row[2])); - lt->avgcoin = static_cast(atoul(row[3])); - } + uint32 current_id = 0; + uint32 current_entry = 0; - if(current_entry > 128) { - continue; - } + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = static_cast(atoul(row[0])); + if(id != current_id) { + if(current_id != 0) + hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lt->NumEntries))); - if(!row[4]) { - continue; - } + memset(loot_table, 0, sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)); + current_entry = 0; + current_id = id; + lt->mincash = static_cast(atoul(row[1])); + lt->maxcash = static_cast(atoul(row[2])); + lt->avgcoin = static_cast(atoul(row[3])); + } - lt->Entries[current_entry].lootdrop_id = static_cast(atoul(row[4])); - lt->Entries[current_entry].multiplier = static_cast(atoi(row[5])); - lt->Entries[current_entry].droplimit = static_cast(atoi(row[6])); - lt->Entries[current_entry].mindrop = static_cast(atoi(row[7])); - lt->Entries[current_entry].probability = static_cast(atof(row[8])); + if(current_entry > 128) + continue; - ++(lt->NumEntries); - ++current_entry; - } - if(current_id != 0) { - hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + - (sizeof(LootTableEntries_Struct) * lt->NumEntries))); - } + if(!row[4]) + continue; + + lt->Entries[current_entry].lootdrop_id = static_cast(atoul(row[4])); + lt->Entries[current_entry].multiplier = static_cast(atoi(row[5])); + lt->Entries[current_entry].droplimit = static_cast(atoi(row[6])); + lt->Entries[current_entry].mindrop = static_cast(atoi(row[7])); + lt->Entries[current_entry].probability = static_cast(atof(row[8])); + + ++(lt->NumEntries); + ++current_entry; + } + + if(current_id != 0) + hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lt->NumEntries))); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); - } } void SharedDatabase::LoadLootDrops(void *data, uint32 size) { From 74b8f3803022cf823dec2fde7ffd5b6f79465546 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:22:27 -0700 Subject: [PATCH 254/368] LoadLootDrops converted to QueryDatabase --- common/shareddb.cpp | 81 +++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 80d1e4383..8e79985db 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1801,57 +1801,52 @@ void SharedDatabase::LoadLootTables(void *data, uint32 size) { } void SharedDatabase::LoadLootDrops(void *data, uint32 size) { - EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); - const char *query = "SELECT lootdrop.id, lootdrop_entries.item_id, lootdrop_entries.item_charges, " - "lootdrop_entries.equip_item, lootdrop_entries.chance, lootdrop_entries.minlevel, " - "lootdrop_entries.maxlevel, lootdrop_entries.multiplier FROM lootdrop JOIN lootdrop_entries " - "ON lootdrop.id = lootdrop_entries.lootdrop_id ORDER BY lootdrop_id"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; + EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); uint8 loot_drop[sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)]; LootDrop_Struct *ld = reinterpret_cast(loot_drop); - if(RunQuery(query, strlen(query), errbuf, &result)) { - uint32 current_id = 0; - uint32 current_entry = 0; - while(row = mysql_fetch_row(result)) { - uint32 id = static_cast(atoul(row[0])); - if(id != current_id) { - if(current_id != 0) { - hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) + - (sizeof(LootDropEntries_Struct) * ld->NumEntries))); - } - memset(loot_drop, 0, sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)); - current_entry = 0; - current_id = id; - } + const std::string query = "SELECT lootdrop.id, lootdrop_entries.item_id, lootdrop_entries.item_charges, " + "lootdrop_entries.equip_item, lootdrop_entries.chance, lootdrop_entries.minlevel, " + "lootdrop_entries.maxlevel, lootdrop_entries.multiplier FROM lootdrop JOIN lootdrop_entries " + "ON lootdrop.id = lootdrop_entries.lootdrop_id ORDER BY lootdrop_id"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting loot drop info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + } - if(current_entry >= 1260) { - continue; - } + uint32 current_id = 0; + uint32 current_entry = 0; - ld->Entries[current_entry].item_id = static_cast(atoul(row[1])); - ld->Entries[current_entry].item_charges = static_cast(atoi(row[2])); - ld->Entries[current_entry].equip_item = static_cast(atoi(row[3])); - ld->Entries[current_entry].chance = static_cast(atof(row[4])); - ld->Entries[current_entry].minlevel = static_cast(atoi(row[5])); - ld->Entries[current_entry].maxlevel = static_cast(atoi(row[6])); - ld->Entries[current_entry].multiplier = static_cast(atoi(row[7])); + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = static_cast(atoul(row[0])); + if(id != current_id) { + if(current_id != 0) + hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) +(sizeof(LootDropEntries_Struct) * ld->NumEntries))); - ++(ld->NumEntries); - ++current_entry; - } - if(current_id != 0) { - hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) + - (sizeof(LootDropEntries_Struct) * ld->NumEntries))); - } + memset(loot_drop, 0, sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)); + current_entry = 0; + current_id = id; + } + + if(current_entry >= 1260) + continue; + + ld->Entries[current_entry].item_id = static_cast(atoul(row[1])); + ld->Entries[current_entry].item_charges = static_cast(atoi(row[2])); + ld->Entries[current_entry].equip_item = static_cast(atoi(row[3])); + ld->Entries[current_entry].chance = static_cast(atof(row[4])); + ld->Entries[current_entry].minlevel = static_cast(atoi(row[5])); + ld->Entries[current_entry].maxlevel = static_cast(atoi(row[6])); + ld->Entries[current_entry].multiplier = static_cast(atoi(row[7])); + + ++(ld->NumEntries); + ++current_entry; + } + + if(current_id != 0) + hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * ld->NumEntries))); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot drop info from database: %s, %s", query, errbuf); - } } bool SharedDatabase::LoadLoot() { From 64641a339088fca8c2248d1f49004b3b0d713653 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:23:53 -0700 Subject: [PATCH 255/368] GetBotInspectMessage converted to QueryDatabase --- common/shareddb.cpp | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 8e79985db..6d58f70b9 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1918,25 +1918,19 @@ void SharedDatabase::SaveCharacterInspectMessage(uint32 character_id, const Insp void SharedDatabase::GetBotInspectMessage(uint32 botid, InspectMessage_Struct* message) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT BotInspectMessage FROM bots WHERE BotID=%i", botid), errbuf, &result)) { - safe_delete_array(query); - - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - memcpy(message, row[0], sizeof(InspectMessage_Struct)); - } - - mysql_free_result(result); - } - else { - std::cerr << "Error in GetBotInspectMessage query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT BotInspectMessage FROM bots WHERE BotID = %i", botid); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetBotInspectMessage query '" << query << "' " << results.ErrorMessage() << std::endl; + return; } + + if (results.RowCount() != 1) + return; + + auto row = results.begin(); + memcpy(message, row[0], sizeof(InspectMessage_Struct)); + } void SharedDatabase::SetBotInspectMessage(uint32 botid, const InspectMessage_Struct* message) { From 528f16bdaf89a56cea3d9b46494fc669ab3dac9c Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:24:29 -0700 Subject: [PATCH 256/368] SetBotInspectMessage converted to QueryDatabase --- common/shareddb.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 6d58f70b9..660b37940 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1935,13 +1935,10 @@ void SharedDatabase::GetBotInspectMessage(uint32 botid, InspectMessage_Struct* m void SharedDatabase::SetBotInspectMessage(uint32 botid, const InspectMessage_Struct* message) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - std::string msg = EscapeString(message->text); - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE bots SET BotInspectMessage='%s' WHERE BotID=%i", msg.c_str(), botid), errbuf)) { - std::cerr << "Error in SetBotInspectMessage query '" << query << "' " << errbuf << std::endl; - } + std::string query = StringFormat("UPDATE bots SET BotInspectMessage = '%s' WHERE BotID = %i", msg.c_str(), botid); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in SetBotInspectMessage query '" << query << "' " << results.ErrorMessage() << std::endl; - safe_delete_array(query); } From 6d6516d80d233469376da5acde201b1008a10382 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:43:02 -0700 Subject: [PATCH 257/368] IsDiscovered converted to QueryDatabase --- zone/client.cpp | 84 ++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 47 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 5b7c0d597..3f2c40b66 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -324,7 +324,7 @@ Client::Client(EQStreamInterface* ieqs) initial_respawn_selection = 0; alternate_currency_loaded = false; - + EngagedRaidTarget = false; SavedRaidRestTimer = 0; } @@ -491,7 +491,7 @@ bool Client::SaveAA(){ } if (points > 0) { SendAA_Struct* curAA = zone->FindAA(aa[a]->AA - aa[a]->value + 1); - if (curAA) { + if (curAA) { for (int rank = 0; rank::iterator RequiredLevel = AARequiredLevelAndCost.find(aa[a]->AA - aa[a]->value + 1 + rank); if (RequiredLevel != AARequiredLevelAndCost.end()) { @@ -519,7 +519,7 @@ bool Client::SaveAA(){ } bool Client::Save(uint8 iCommitNow) { - if(!ClientDataLoaded()) + if(!ClientDataLoaded()) return false; /* Wrote current basics to PP for saves */ @@ -533,7 +533,7 @@ bool Client::Save(uint8 iCommitNow) { if (GetHP() <= 0) { m_pp.cur_hp = GetMaxHP(); } - else { + else { m_pp.cur_hp = GetHP(); } @@ -564,7 +564,7 @@ bool Client::Save(uint8 iCommitNow) { GetMercInfo().MercTimerRemaining = GetMercTimer()->GetRemainingTime(); } - if (!(GetMerc() && !dead)) { + if (!(GetMerc() && !dead)) { memset(&m_mercinfo, 0, sizeof(struct MercInfo)); } @@ -583,9 +583,9 @@ bool Client::Save(uint8 iCommitNow) { } database.SavePetInfo(this); - if(tribute_timer.Enabled()) { + if(tribute_timer.Enabled()) { m_pp.tribute_time_remaining = tribute_timer.GetRemainingTime(); - } + } else { m_pp.tribute_time_remaining = 0xFFFFFFFF; m_pp.tribute_active = 0; } @@ -989,7 +989,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s Message(13, "Command '%s' not recognized.", message); } } else { - if(!RuleB(Chat, SuppressCommandErrors)) + if(!RuleB(Chat, SuppressCommandErrors)) Message(13, "Command '%s' not recognized.", message); } } @@ -1362,14 +1362,14 @@ bool Client::UpdateLDoNPoints(int32 points, uint32 theme) void Client::SetSkill(SkillUseTypes skillid, uint16 value) { if (skillid > HIGHEST_SKILL) - return; - m_pp.skills[skillid] = value; // We need to be able to #setskill 254 and 255 to reset skills + return; + m_pp.skills[skillid] = value; // We need to be able to #setskill 254 and 255 to reset skills database.SaveCharacterSkill(this->CharacterID(), skillid, value); EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; - skill->skillId=skillid; + skill->skillId=skillid; skill->value=value; QueuePacket(outapp); safe_delete(outapp); @@ -1390,7 +1390,7 @@ void Client::IncreaseLanguageSkill(int skill_id, int value) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; skill->skillId = 100 + skill_id; - skill->value = m_pp.languages[skill_id]; + skill->value = m_pp.languages[skill_id]; QueuePacket(outapp); safe_delete(outapp); @@ -2141,7 +2141,7 @@ void Client::AddMoneyToPP(uint64 copper, bool updateclient){ /* Add Amount of Platinum */ tmp2 = tmp/1000; int32 new_val = m_pp.platinum + tmp2; - if(new_val < 0) { m_pp.platinum = 0; } + if(new_val < 0) { m_pp.platinum = 0; } else { m_pp.platinum = m_pp.platinum + tmp2; } tmp-=tmp2*1000; @@ -2151,7 +2151,7 @@ void Client::AddMoneyToPP(uint64 copper, bool updateclient){ /* Add Amount of Gold */ tmp2 = tmp/100; new_val = m_pp.gold + tmp2; - if(new_val < 0) { m_pp.gold = 0; } + if(new_val < 0) { m_pp.gold = 0; } else { m_pp.gold = m_pp.gold + tmp2; } tmp-=tmp2*100; @@ -4031,28 +4031,18 @@ void Client::KeyRingList() bool Client::IsDiscovered(uint32 itemid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT count(*) FROM discovered_items WHERE item_id = '%lu'", itemid); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in IsDiscovered query '" << query << "' " << results.ErrorMessage() << std::endl; + return false; + } - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT count(*) FROM discovered_items WHERE item_id = '%lu'", itemid), errbuf, &result)) - { - row = mysql_fetch_row(result); - if (atoi(row[0])) - { - mysql_free_result(result); - safe_delete_array(query); - return true; - } - } - else - { - std::cerr << "Error in IsDiscovered query '" << query << "' " << errbuf << std::endl; - } - mysql_free_result(result); - safe_delete_array(query); - return false; + auto row = results.begin(); + if (!atoi(row[0])) + return false; + + return true; } void Client::DiscoverItem(uint32 itemid) { @@ -4290,15 +4280,15 @@ void Client::IncrementAggroCount() { if(!RuleI(Character, RestRegenPercent)) return; - + // If we already had aggro before this method was called, the combat indicator should already be up for SoF clients, // so we don't need to send it again. // if(AggroCount > 1) return; - + // Pause the rest timer - if (AggroCount == 1) + if (AggroCount == 1) SavedRaidRestTimer = rest_timer.GetRemainingTime(); if(GetClientVersion() >= EQClientSoF) { @@ -4343,9 +4333,9 @@ void Client::DecrementAggroCount() { time_until_rest = RuleI(Character, RestRegenTimeToActivate) * 1000; } } - + rest_timer.Start(time_until_rest); - + if(GetClientVersion() >= EQClientSoF) { EQApplicationPacket *outapp = new EQApplicationPacket(OP_RestState, 5); @@ -4450,7 +4440,7 @@ void Client::SendRespawnBinds() int num_options = respawn_options.size(); uint32 PacketLength = 17 + (26 * num_options); //Header size + per-option invariant size - + std::list::iterator itr; RespawnOption* opt; @@ -7630,7 +7620,7 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui tmpValue = current_value + mod + npc_value[i]; int16 FactionModPct = spellbonuses.FactionModPct + itembonuses.FactionModPct + aabonuses.FactionModPct; - tmpValue += (tmpValue * FactionModPct) / 100; + tmpValue += (tmpValue * FactionModPct) / 100; // Make sure faction hits don't go to GMs... if (m_pp.gm==1 && (tmpValue < current_value)) { @@ -7944,7 +7934,7 @@ void Client::TryItemTimer(int slot) } ++it_iter; } - + if(slot > EmuConstants::EQUIPMENT_END) { return; } @@ -7980,7 +7970,7 @@ void Client::RefundAA() { for(int j = 0; j < cur; j++) { m_pp.aapoints += curaa->cost + (curaa->cost_inc * j); refunded = true; - } + } } else { @@ -8268,12 +8258,12 @@ void Client::ExpeditionSay(const char *str, int ExpID) { while((row = mysql_fetch_row(result))) { const char* CharName = row[0]; if(strcmp(CharName, this->GetCleanName()) != 0) - worldserver.SendEmoteMessage(CharName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str); + worldserver.SendEmoteMessage(CharName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str); // ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3])); - } + } mysql_free_result(result); - + } void Client::ShowNumHits() From c7c92182108869fa2b9ecd54d219871d0ffd569d Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:46:19 -0700 Subject: [PATCH 258/368] DiscoverItem converted to QueryDatabase --- zone/client.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 3f2c40b66..112afddff 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -4047,14 +4047,11 @@ bool Client::IsDiscovered(uint32 itemid) { void Client::DiscoverItem(uint32 itemid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO discovered_items SET item_id=%lu, char_name='%s', discovered_date=UNIX_TIMESTAMP(), account_status=%i", itemid, GetName(), Admin()), errbuf, &result)) - { - mysql_free_result(result); - } - safe_delete_array(query); + std::string query = StringFormat("INSERT INTO discovered_items " + "SET item_id = %lu, char_name = '%s', " + "discovered_date = UNIX_TIMESTAMP(), account_status = %i", + itemid, GetName(), Admin()); + auto results = database.QueryDatabase(query); parse->EventPlayer(EVENT_DISCOVER_ITEM, this, "", itemid); } From fe1c67b8b4442b6b1ac1dfeb7e5d0b88e911cf81 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 14:55:55 -0700 Subject: [PATCH 259/368] SendRewards converted to QueryDatabase --- zone/client.cpp | 98 +++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 57 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 112afddff..8b172c461 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -5223,67 +5223,51 @@ const bool Client::IsMQExemptedArea(uint32 zoneID, float x, float y, float z) co void Client::SendRewards() { std::vector rewards; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT reward_id, amount FROM" - " account_rewards WHERE account_id=%i ORDER by reward_id", AccountID()), - errbuf,&result)) - { - while((row = mysql_fetch_row(result))) - { - ClientReward cr; - cr.id = atoi(row[0]); - cr.amount = atoi(row[1]); - rewards.push_back(cr); - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Client::SendRewards(): %s (%s)", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT reward_id, amount " + "FROM account_rewards " + "WHERE account_id = %i " + "ORDER BY reward_id", AccountID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Client::SendRewards(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); return; - } + } - if(rewards.size() > 0) - { - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(InternalVeteranReward) * rewards.size())); - uchar *data = vetapp->pBuffer; - for(int i = 0; i < rewards.size(); ++i) - { - InternalVeteranReward *ivr = (InternalVeteranReward*)data; - ivr->claim_id = rewards[i].id; - ivr->number_available = rewards[i].amount; - std::list::iterator iter = zone->VeteranRewards.begin(); - while(iter != zone->VeteranRewards.end()) - { - if((*iter).claim_id == rewards[i].id) - { - break; - } - ++iter; - } + for (auto row = results.begin(); row != results.end(); ++row) { + ClientReward cr; + cr.id = atoi(row[0]); + cr.amount = atoi(row[1]); + rewards.push_back(cr); + } - if(iter != zone->VeteranRewards.end()) - { - InternalVeteranReward ivro = (*iter); - ivr->claim_count = ivro.claim_count; - for(int x = 0; x < ivro.claim_count; ++x) - { - ivr->items[x].item_id = ivro.items[x].item_id; - ivr->items[x].charges = ivro.items[x].charges; - strcpy(ivr->items[x].item_name, ivro.items[x].item_name); - } - } + if(rewards.size() == 0) + return; - data += sizeof(InternalVeteranReward); - } - FastQueuePacket(&vetapp); - } + EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(InternalVeteranReward) * rewards.size())); + uchar *data = vetapp->pBuffer; + for(int i = 0; i < rewards.size(); ++i) { + InternalVeteranReward *ivr = (InternalVeteranReward*)data; + ivr->claim_id = rewards[i].id; + ivr->number_available = rewards[i].amount; + auto iter = zone->VeteranRewards.begin(); + for (;iter != zone->VeteranRewards.end(); ++iter) + if((*iter).claim_id == rewards[i].id) + break; + + if(iter != zone->VeteranRewards.end()) { + InternalVeteranReward ivro = (*iter); + ivr->claim_count = ivro.claim_count; + for(int x = 0; x < ivro.claim_count; ++x) { + ivr->items[x].item_id = ivro.items[x].item_id; + ivr->items[x].charges = ivro.items[x].charges; + strcpy(ivr->items[x].item_name, ivro.items[x].item_name); + } + } + + data += sizeof(InternalVeteranReward); + } + + FastQueuePacket(&vetapp); } bool Client::TryReward(uint32 claim_id) From 037df28b7c94211cb1bf1033078969f989efd7e5 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 15:14:50 -0700 Subject: [PATCH 260/368] TryReward converted to QueryDatabase --- zone/client.cpp | 166 ++++++++++++++++-------------------------------- 1 file changed, 56 insertions(+), 110 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 8b172c461..56837805e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -5270,8 +5270,7 @@ void Client::SendRewards() FastQueuePacket(&vetapp); } -bool Client::TryReward(uint32 claim_id) -{ +bool Client::TryReward(uint32 claim_id) { //Make sure we have an open spot //Make sure we have it in our acct and count > 0 //Make sure the entry was found @@ -5281,143 +5280,90 @@ bool Client::TryReward(uint32 claim_id) //save uint32 free_slot = 0xFFFFFFFF; - for(int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) - { + for(int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) { ItemInst *item = GetInv().GetItem(i); - if(!item) - { + if(!item) { free_slot = i; break; } } if(free_slot == 0xFFFFFFFF) - { return false; - } char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 amt = 0; - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT amount FROM" - " account_rewards WHERE account_id=%i AND reward_id=%i", AccountID(), claim_id), - errbuf,&result)) - { - row = mysql_fetch_row(result); - if(row) - { - amt = atoi(row[0]); - } - else - { - mysql_free_result(result); - safe_delete_array(query); - return false; - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT amount FROM account_rewards " + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); return false; - } + } + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + + uint32 amt = atoi(row[0]); if(amt == 0) - { return false; - } std::list::iterator iter = zone->VeteranRewards.begin(); - while(iter != zone->VeteranRewards.end()) - { + for (; iter != zone->VeteranRewards.end(); ++row) if((*iter).claim_id == claim_id) - { break; - } - ++iter; - } if(iter == zone->VeteranRewards.end()) - { return false; - } - if(amt == 1) - { - if(!database.RunQuery(query,MakeAnyLenString(&query,"DELETE FROM" - " account_rewards WHERE account_id=%i AND reward_id=%i", AccountID(), claim_id), - errbuf)) - { - LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query, errbuf); - safe_delete_array(query); - } - else - { - safe_delete_array(query); - } + if(amt == 1) { + query = StringFormat("DELETE FROM account_rewards " + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); } - else - { - if(!database.RunQuery(query,MakeAnyLenString(&query,"UPDATE account_rewards SET amount=(amount-1)" - " WHERE account_id=%i AND reward_id=%i", AccountID(), claim_id), - errbuf)) - { - LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query, errbuf); - safe_delete_array(query); - } - else - { - safe_delete_array(query); - } + else { + query = StringFormat("UPDATE account_rewards SET amount = (amount-1) " + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); } InternalVeteranReward ivr = (*iter); ItemInst *claim = database.CreateItem(ivr.items[0].item_id, ivr.items[0].charges); - if(claim) - { - bool lore_conflict = false; - if(CheckLoreConflict(claim->GetItem())) - { - lore_conflict = true; - } - - for(int y = 1; y < 8; y++) - { - if(ivr.items[y].item_id) - { - if(claim->GetItem()->ItemClass == 1) - { - ItemInst *item_temp = database.CreateItem(ivr.items[y].item_id, ivr.items[y].charges); - if(item_temp) - { - if(CheckLoreConflict(item_temp->GetItem())) - { - lore_conflict = true; - DuplicateLoreMessage(ivr.items[y].item_id); - } - claim->PutItem(y-1, *item_temp); - } - } - } - } - - if(lore_conflict) - { - safe_delete(claim); - return true; - } - else - { - PutItemInInventory(free_slot, *claim); - SendItemPacket(free_slot, claim, ItemPacketTrade); - } + if(!claim) { + Save(); + return true; } + bool lore_conflict = CheckLoreConflict(claim->GetItem()); + + for(int y = 1; y < 8; y++) + if(ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) { + ItemInst *item_temp = database.CreateItem(ivr.items[y].item_id, ivr.items[y].charges); + if(item_temp) { + if(CheckLoreConflict(item_temp->GetItem())) { + lore_conflict = true; + DuplicateLoreMessage(ivr.items[y].item_id); + } + claim->PutItem(y-1, *item_temp); + } + } + + if(lore_conflict) { + safe_delete(claim); + return true; + } + + PutItemInInventory(free_slot, *claim); + SendItemPacket(free_slot, claim, ItemPacketTrade); + Save(); return true; } From 81a641c96c2791143e2c350cd8b519e82bc2ad7f Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 15:21:05 -0700 Subject: [PATCH 261/368] LoadAccountFlags converted to QueryDatabase --- zone/client.cpp | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 56837805e..b3493f7e4 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7722,28 +7722,19 @@ void Client::SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 totalval void Client::LoadAccountFlags() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; accountflags.clear(); - MakeAnyLenString(&query, "SELECT p_flag, p_value FROM account_flags WHERE p_accid = '%d'", account_id); - if(database.RunQuery(query, strlen(query), errbuf, &result)) - { - while(row = mysql_fetch_row(result)) - { - std::string fname(row[0]); - std::string fval(row[1]); - accountflags[fname] = fval; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in LoadAccountFlags query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); + std::string query = StringFormat("SELECT p_flag, p_value " + "FROM account_flags WHERE p_accid = '%d'", + account_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in LoadAccountFlags query '" << query << "' " << results.ErrorMessage() << std::endl; + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + accountflags[row[0]] = row[1]; } void Client::SetAccountFlag(std::string flag, std::string val) From 9c3f1937a76420a36ad2533ac9ed51cf3843c541 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 15:24:11 -0700 Subject: [PATCH 262/368] SetAccountFlag converted to QueryDatabase --- zone/client.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index b3493f7e4..3dc69d563 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7737,17 +7737,16 @@ void Client::LoadAccountFlags() accountflags[row[0]] = row[1]; } -void Client::SetAccountFlag(std::string flag, std::string val) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; +void Client::SetAccountFlag(std::string flag, std::string val) { - MakeAnyLenString(&query, "REPLACE INTO account_flags (p_accid, p_flag, p_value) VALUES( '%d', '%s', '%s')", account_id, flag.c_str(), val.c_str()); - if(!database.RunQuery(query, strlen(query), errbuf)) - { - std::cerr << "Error in SetAccountFlags query '" << query << "' " << errbuf << std::endl; + std::string query = StringFormat("REPLACE INTO account_flags (p_accid, p_flag, p_value) " + "VALUES( '%d', '%s', '%s')", + account_id, flag.c_str(), val.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + std::cerr << "Error in SetAccountFlags query '" << query << "' " << results.ErrorMessage() << std::endl; + return; } - safe_delete_array(query); accountflags[flag] = val; } From 1b1c5b5f9f83d346cef3b5104dbc818590ca2825 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 15:29:02 -0700 Subject: [PATCH 263/368] ExpeditionSay converted to QueryDatabase --- zone/client.cpp | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 3dc69d563..a277b9858 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8157,29 +8157,25 @@ void Client::PlayMP3(const char* fname) } void Client::ExpeditionSay(const char *str, int ExpID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (!database.RunQuery(query,MakeAnyLenString(&query, "SELECT `player_name` FROM `cust_inst_players` WHERE `inst_id` = %i", ExpID),errbuf,&result)){ - safe_delete_array(query); + std::string query = StringFormat("SELECT `player_name` FROM `cust_inst_players` " + "WHERE `inst_id` = %i", ExpID); + auto results = database.QueryDatabase(query); + if (!results.Success()) return; - } - safe_delete_array(query); + if(results.RowCount() == 0) { + this->Message(14, "You say to the expedition, '%s'", str); + return; + } - if(result) - this->Message(14, "You say to the expedition, '%s'", str); - - while((row = mysql_fetch_row(result))) { - const char* CharName = row[0]; - if(strcmp(CharName, this->GetCleanName()) != 0) - worldserver.SendEmoteMessage(CharName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str); + for(auto row = results.begin(); row != results.end(); ++row) { + const char* charName = row[0]; + if(strcmp(charName, this->GetCleanName()) != 0) + worldserver.SendEmoteMessage(charName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str); // ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3])); } - mysql_free_result(result); } From ce3532aa197ac22ce64a97a94241cd1ba69534e0 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:20:33 -0700 Subject: [PATCH 264/368] command_suspend converted to QueryDatabase --- zone/command.cpp | 110 +++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 05917fe5a..cd59f4cb6 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6180,76 +6180,76 @@ void command_ban(Client *c, const Seperator *sep) void command_suspend(Client *c, const Seperator *sep) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; + if((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0)) { + c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); + return; + } - if((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0)) - c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); - else - { - int Duration = atoi(sep->arg[2]); + int duration = atoi(sep->arg[2]); - if(Duration < 0) - Duration = 0; + if(duration < 0) + duration = 0; - std::string message; - if(Duration > 0) { - int i = 3; - while(1) { - if(sep->arg[i][0] == 0) { - break; - } + std::string message; - if(message.length() > 0) { - message.push_back(' '); - } + if(duration > 0) { + int i = 3; + while(1) { + if(sep->arg[i][0] == 0) { + break; + } - message += sep->arg[i]; - ++i; - } + if(message.length() > 0) { + message.push_back(' '); + } - if(message.length() == 0) { - c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); - return; - } - } + message += sep->arg[i]; + ++i; + } - int AccountID; + if(message.length() == 0) { + c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); + return; + } + } - if((AccountID = database.GetAccountIDByChar(sep->arg[1])) > 0) - { - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), " - "suspend_reason = '%s' WHERE `id` = %i", Duration, EscapeString(message).c_str(), AccountID), errbuf, 0); + char *escName = new char[strlen(sep->arg[1]) * 2 + 1]; + database.DoEscapeString(escName, sep->arg[1], strlen(sep->arg[1])); + int accountID = database.GetAccountIDByChar(escName); + safe_delete_array(escName); - if(Duration) - c->Message(13, "Account number %i with the character %s has been temporarily suspended for %i day(s) with the message: \"%s\"", AccountID, sep->arg[1], - Duration, message.c_str()); - else - c->Message(13, "Account number %i with the character %s is no longer suspended.", AccountID, sep->arg[1]); + if (accountID <= 0) { + c->Message(13,"Character does not exist."); + return; + } - safe_delete_array(query); + std::string query = StringFormat("UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), " + "suspend_reason = '%s' WHERE `id` = %i", + duration, EscapeString(message).c_str(), accountID); + auto results = database.QueryDatabase(query); - Client *BannedClient = entity_list.GetClientByName(sep->arg[1]); + if(duration) + c->Message(13,"Account number %i with the character %s has been temporarily suspended for %i day(s).", accountID, sep->arg[1], duration); + else + c->Message(13,"Account number %i with the character %s is no longer suspended.", accountID, sep->arg[1]); - if(BannedClient) - BannedClient->WorldKick(); - else - { - ServerPacket pack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*)pack.pBuffer; + Client *bannedClient = entity_list.GetClientByName(sep->arg[1]); - strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname)); - strn0cpy(sks->name, sep->arg[1], sizeof(sks->name)); - sks->adminrank = c->Admin(); + if(bannedClient) { + bannedClient->WorldKick(); + return; + } - worldserver.SendPacket(&pack); - } + ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*) pack->pBuffer; - } - else { - c->Message(13, "Character does not exist."); - } - } + strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname)); + strn0cpy(sks->name, sep->arg[1], sizeof(sks->name)); + sks->adminrank = c->Admin(); + + worldserver.SendPacket(pack); + + safe_delete(pack); } void command_ipban(Client *c, const Seperator *sep) From f1039ab0e9fc95138fd33e08ddd8ab012b1f8b4d Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:26:19 -0700 Subject: [PATCH 265/368] command_revoke converted to QueryDatabase --- zone/command.cpp | 71 +++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index cd59f4cb6..5d95906ab 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6268,50 +6268,41 @@ void command_ipban(Client *c, const Seperator *sep) void command_revoke(Client *c, const Seperator *sep) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) - { + if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { c->Message(0, "Usage: #revoke [charname] [1/0]"); + return; } - else - { - uint32 tmp = database.GetAccountIDByChar(sep->arg[1]); - if(tmp) - { - int flag = sep->arg[2][0] == '1' ? true : false; - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE account set revoked=%d where id = %i", flag, tmp), errbuf, 0); - c->Message(13,"%s account number %i with the character %s.", flag?"Revoking":"Unrevoking", tmp, sep->arg[1]); - Client* revokee = entity_list.GetClientByAccID(tmp); - if(revokee) - { - c->Message(0, "Found %s in this zone.", revokee->GetName()); - revokee->SetRevoked(flag); - } - else - { + + uint32 characterID = database.GetAccountIDByChar(sep->arg[1]); + if(characterID == 0) { + c->Message(13,"Character does not exist."); + return; + } + + int flag = sep->arg[2][0] == '1' ? true : false; + std::string query = StringFormat("UPDATE account SET revoked = %d WHERE id = %i", flag, characterID); + auto results = database.QueryDatabase(query); + + c->Message(13,"%s account number %i with the character %s.", flag? "Revoking": "Unrevoking", characterID, sep->arg[1]); + + Client* revokee = entity_list.GetClientByAccID(characterID); + if(revokee) { + c->Message(0, "Found %s in this zone.", revokee->GetName()); + revokee->SetRevoked(flag); + return; + } + #if EQDEBUG >= 6 - c->Message(0, "Couldn't find %s in this zone, passing request to worldserver.", sep->arg[1]); + c->Message(0, "Couldn't find %s in this zone, passing request to worldserver.", sep->arg[1]); #endif - ServerPacket * outapp = new ServerPacket (ServerOP_Revoke,sizeof(RevokeStruct)); - RevokeStruct* revoke = (RevokeStruct*)outapp->pBuffer; - strn0cpy(revoke->adminname, c->GetName(), 64); - strn0cpy(revoke->name, sep->arg[1], 64); - revoke->toggle = flag; - worldserver.SendPacket(outapp); - safe_delete(outapp); - } - } - else { - c->Message(13,"Character does not exist."); - } - if(query) - { - safe_delete_array(query); - query=nullptr; - } - } + + ServerPacket * outapp = new ServerPacket (ServerOP_Revoke,sizeof(RevokeStruct)); + RevokeStruct* revoke = (RevokeStruct*)outapp->pBuffer; + strn0cpy(revoke->adminname, c->GetName(), 64); + strn0cpy(revoke->name, sep->arg[1], 64); + revoke->toggle = flag; + worldserver.SendPacket(outapp); + safe_delete(outapp); } void command_oocmute(Client *c, const Seperator *sep) From e2382137e6ea281b6c5c7d14e40dd4c6f2319adc Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:28:34 -0700 Subject: [PATCH 266/368] command_repop converted to QueryDatabase --- zone/command.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 5d95906ab..639e99c81 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -4148,25 +4148,24 @@ void command_repop(Client *c, const Seperator *sep) LinkedListIterator iterator(zone->spawn2_list); iterator.Reset(); - while (iterator.MoreElements()) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM respawn_times WHERE id=%lu" - " AND instance_id=%lu",(unsigned long)iterator.GetData()->GetID(), (unsigned long)zone->GetInstanceID()), errbuf); - safe_delete_array(query); + while (iterator.MoreElements()) { + std::string query = StringFormat("DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", + (unsigned long)iterator.GetData()->GetID(), + (unsigned long)zone->GetInstanceID()); + auto results = database.QueryDatabase(query); iterator.Advance(); } c->Message(0, "Zone depop: Force resetting spawn timers."); } - if (sep->IsNumber(timearg)) { - c->Message(0, "Zone depoped. Repop in %i seconds", atoi(sep->arg[timearg])); - zone->Repop(atoi(sep->arg[timearg])*1000); - } - else { - c->Message(0, "Zone depoped. Repoping now."); + + if (!sep->IsNumber(timearg)) { + c->Message(0, "Zone depoped. Repoping now."); zone->Repop(); + return; } + + c->Message(0, "Zone depoped. Repop in %i seconds", atoi(sep->arg[timearg])); + zone->Repop(atoi(sep->arg[timearg])*1000); } void command_spawnstatus(Client *c, const Seperator *sep) From 1d08b1687fe61d59c35c82b43217ab30924e2e27 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 10:50:04 -0700 Subject: [PATCH 267/368] command_ban converted to QueryDatabase --- zone/command.cpp | 100 ++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 54 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 639e99c81..ac45dd612 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6111,70 +6111,62 @@ void command_stun(Client *c, const Seperator *sep) void command_ban(Client *c, const Seperator *sep) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) - { +if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { c->Message(0, "Usage: #ban "); + return; } - else - { - auto account_id = database.GetAccountIDByChar(sep->arg[1]); - std::string message; - int i = 2; - while(1) { - if(sep->arg[i][0] == 0) { - break; - } + auto account_id = database.GetAccountIDByChar(sep->arg[1]); - if(message.length() > 0) { - message.push_back(' '); - } + std::string message; + int i = 2; + while(1) { + if(sep->arg[i][0] == 0) { + break; + } - message += sep->arg[i]; - ++i; - } + if(message.length() > 0) { + message.push_back(' '); + } - if(message.length() == 0) { - c->Message(0, "Usage: #ban "); - return; - } + message += sep->arg[i]; + ++i; + } - if(account_id > 0) - { - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE account set status = -2, ban_reason = '%s' where id = %i", EscapeString(message).c_str(), account_id), errbuf, 0); - c->Message(13, "Account number %i with the character %s has been banned with message: \"%s\"", account_id, sep->arg[1], message.c_str()); + if(message.length() == 0) { + c->Message(0, "Usage: #ban "); + return; + } - ServerPacket pack(ServerOP_FlagUpdate, 6); - *((uint32*)&pack.pBuffer[0]) = account_id; - *((int16*)&pack.pBuffer[4]) = -2; - worldserver.SendPacket(&pack); + if(account_id == 0) { + c->Message(13, "Character does not exist."); + return; + } - Client *client = nullptr; - client = entity_list.GetClientByName(sep->arg[1]); - if(client) - { - client->WorldKick(); - } - else - { - ServerPacket pack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)pack.pBuffer; - strcpy(skp->adminname, c->GetName()); - strcpy(skp->name, sep->arg[1]); - skp->adminrank = c->Admin(); - worldserver.SendPacket(&pack); - } - } - else - { - c->Message(13, "Character does not exist."); - } + std::string query = StringFormat("UPDATE account SET status = -2, ban_reason = '%s' " + "WHERE id = %i", EscapeString(message).c_str(), account_id); + auto results = database.QueryDatabase(query); - safe_delete_array(query); - } + c->Message(13, "Account number %i with the character %s has been banned with message: \"%s\"", account_id, sep->arg[1], message.c_str()); + + ServerPacket flagUpdatePack(ServerOP_FlagUpdate, 6); + *((uint32*)&flagUpdatePack.pBuffer[0]) = account_id; + *((int16*)&flagUpdatePack.pBuffer[4]) = -2; + worldserver.SendPacket(&flagUpdatePack); + + Client *client = nullptr; + client = entity_list.GetClientByName(sep->arg[1]); + if(client) { + client->WorldKick(); + return; + } + + ServerPacket kickPlayerPack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)kickPlayerPack.pBuffer; + strcpy(skp->adminname, c->GetName()); + strcpy(skp->name, sep->arg[1]); + skp->adminrank = c->Admin(); + worldserver.SendPacket(&kickPlayerPack); } void command_suspend(Client *c, const Seperator *sep) From 6be2f6a7e434514ac12265c57942b4d57b6eee61 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 11:52:40 -0700 Subject: [PATCH 268/368] command_npcedit converted to QueryDatabase --- zone/command.cpp | 1355 ++++++++++++++++++++++++---------------------- 1 file changed, 711 insertions(+), 644 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index ac45dd612..f3adf5856 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6403,12 +6403,11 @@ void command_npcemote(Client *c, const Seperator *sep) } void command_npcedit(Client *c, const Seperator *sep) -{ - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) - { +{ if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { c->Message(0, "Error: Must have NPC targeted"); return; } + if ( strcasecmp( sep->arg[1], "help" ) == 0 ) { c->Message(0, "Help File for #npcedit. Syntax for commands are:"); @@ -6481,663 +6480,731 @@ void command_npcedit(Client *c, const Seperator *sep) c->Message(0, "#npcedit version - Set an NPC's version"); } - else if ( strcasecmp( sep->arg[1], "name" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has the name %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set name='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); + + uint32 npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); + + if ( strcasecmp( sep->arg[1], "name" ) == 0 ) { + c->Message(15,"NPCID %u now has the name %s.",npcTypeID, sep->argplus[2]); + + std::string query = StringFormat("UPDATE npc_types SET name = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; } - else if ( strcasecmp( sep->arg[1], "lastname" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has the lastname %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set lastname='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "race" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has the race %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set race=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "class" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now class %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set class=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "bodytype" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has type %i bodytype.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set bodytype=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "hp" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Hitpoints.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set hp=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "gender" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now gender %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set gender=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "texture" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now uses texture %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set texture=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "helmtexture" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now uses helmtexture %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set helmtexture=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "size" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now size %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set size=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "hpregen" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now regens %i hitpoints per tick.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set hp_regen_rate=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "manaregen" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now regens %i mana per tick.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set mana_regen_rate=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "loottable" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now on loottable_id %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set loottable_id=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "merchantid" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now merchant_id %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set merchant_id=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "alt_currency_id" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'alt_currency_id' set to %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set alt_currency_id='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "npc_spells_effects_id" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'npc_spells_effects_id' set to %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set npc_spells_effects_id='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "adventure_template_id" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'adventure_template_id' set to %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set adventure_template_id='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "trap_template" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'trap_template' set to %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set trap_template='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "special_abilities" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'special_abilities' set to %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set special_abilities='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "spell" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now uses spell list %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set npc_spells_id=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "faction" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now faction %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set npc_faction_id=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "mindmg" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now hits for a min of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set mindmg=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "maxdmg" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now hits for a max of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set maxdmg=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "aggroradius" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has an aggro radius of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set aggroradius=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "assistradius" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has an assist radius of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set assistradius=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "social" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u social status is now %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set social=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "runspeed" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now runs at %f",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atof(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set runspeed=%f where id=%i",atof(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "AGI" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Agility.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set AGI=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "CHA" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Charisma.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set CHA=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "DEX" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Dexterity.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set DEX=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "INT" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Intelligence.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set _INT=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "STA" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Stamina.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set STA=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "STR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Strength.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set STR=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "WIS" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Magic Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set WIS=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "MR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Magic Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set MR=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "DR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Disease Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set DR=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "CR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Cold Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set CR=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "FR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Fire Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set FR=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "PR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Poison Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set PR=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "Corrup" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Corruption Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set corrup=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "PhR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u now has a Physical Resistance of %i.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set PhR=%i where id=%i", atoi(sep->argplus[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "seeinvis" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has seeinvis set to %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set see_invis=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "seeinvisundead" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has seeinvisundead set to %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set see_invis_undead=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "seehide" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has seehide set to %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set see_hide=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "seeimprovedhide" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has seeimprovedhide set to %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set see_improved_hide=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "AC" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Armor Class.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set ac=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "ATK" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Attack.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set atk=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "Accuracy" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Accuracy.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set accuracy=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "level" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now level %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set level=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "maxlevel" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a maximum level of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set maxlevel=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "qglobal" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "Quest globals have been %s for NPCID %u", atoi(sep->arg[2]) == 0 ? "disabled" : "enabled", c->GetTarget()->CastToNPC()->GetNPCTypeID()); - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET qglobal=%i WHERE id=%i", atoi(sep->argplus[2]) == 0 ? 0 : 1, c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "npcaggro" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u will now %s other NPCs with negative faction npc_value",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])==0?"not aggro":"aggro"); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set npc_aggro=%i where id=%i",atoi(sep->argplus[2])==0?0:1,c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "limit" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a spawn limit of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set limit=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "Attackspeed" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has attack_speed set to %f",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atof(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set attack_speed=%f where id=%i",atof(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "Attackdelay" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has attack_delay set to %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set attack_delay=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "findable" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now %s",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])==0?"not findable":"findable"); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set findable=%i where id=%i",atoi(sep->argplus[2])==0?0:1,c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "wep1" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u will have item graphic %i set to his primary on repop.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set d_meele_texture1=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "wep2" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u will have item graphic %i set to his secondary on repop.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set d_meele_texture2=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "featuresave" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u saved with all current facial feature settings",c->GetTarget()->CastToNPC()->GetNPCTypeID()); + if ( strcasecmp( sep->arg[1], "lastname" ) == 0 ) { + c->Message(15,"NPCID %u now has the lastname %s.",npcTypeID, sep->argplus[2]); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set luclin_haircolor=%i where id=%i",c->GetTarget()->GetHairColor(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set luclin_beardcolor=%i where id=%i",c->GetTarget()->GetBeardColor(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set luclin_hairstyle=%i where id=%i",c->GetTarget()->GetHairStyle(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set luclin_beard=%i where id=%i",c->GetTarget()->GetBeard(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set face=%i where id=%i",c->GetTarget()->GetLuclinFace(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set drakkin_heritage=%i where id=%i",c->GetTarget()->GetDrakkinHeritage(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set drakkin_tattoo=%i where id=%i",c->GetTarget()->GetDrakkinTattoo(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set drakkin_details=%i where id=%i",c->GetTarget()->GetDrakkinDetails(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); + std::string query = StringFormat("UPDATE npc_types SET lastname = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + if ( strcasecmp( sep->arg[1], "race" ) == 0 ) { + c->Message(15,"NPCID %u now has the race %i.",npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET race = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; } - else if ( strcasecmp( sep->arg[1], "color" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i red, %i green, and %i blue tinting on their armor.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set armortint_red=%i, armortint_green=%i, armortint_blue=%i where id=%i", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); + + if ( strcasecmp( sep->arg[1], "class" ) == 0 ) { + c->Message(15,"NPCID %u is now class %i.",npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET class = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; } - else if ( strcasecmp( sep->arg[1], "armortint_id" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'armortint_id' set to %s",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set armortint_id='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); + + if ( strcasecmp( sep->arg[1], "bodytype" ) == 0 ) { + c->Message(15,"NPCID %u now has type %i bodytype.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET bodytype = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; } - else if ( strcasecmp( sep->arg[1], "setanimation" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - int Animation = 0; - if(sep->arg[2] && atoi(sep->arg[2]) <= 4){ - if((strcasecmp( sep->arg[2], "stand" ) == 0) || atoi(sep->arg[2]) == 0){ - Animation = 0; //Stand - } - if((strcasecmp( sep->arg[2], "sit" ) == 0) || atoi(sep->arg[2]) == 1){ - Animation = 1; //Sit - } - if((strcasecmp( sep->arg[2], "crouch" ) == 0) || atoi(sep->arg[2]) == 2){ - Animation = 2; //Crouch - } - if((strcasecmp( sep->arg[2], "dead" ) == 0) || atoi(sep->arg[2]) == 3){ - Animation = 3; //Dead - } - if((strcasecmp( sep->arg[2], "loot" ) == 0) || atoi(sep->arg[2]) == 4){ - Animation = 4; //Looting Animation - } + + if ( strcasecmp( sep->arg[1], "hp" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Hitpoints.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET hp = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "gender" ) == 0 ) { + c->Message(15,"NPCID %u is now gender %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET gender = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "texture" ) == 0 ) { + c->Message(15,"NPCID %u now uses texture %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET texture = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "helmtexture" ) == 0 ) { + c->Message(15,"NPCID %u now uses helmtexture %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET helmtexture = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "size" ) == 0 ) { + c->Message(15,"NPCID %u is now size %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET size = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "hpregen" ) == 0 ) { + c->Message(15,"NPCID %u now regens %i hitpoints per tick.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET hp_regen_rate = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "manaregen" ) == 0 ) { + c->Message(15,"NPCID %u now regens %i mana per tick.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET mana_regen_rate = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "loottable" ) == 0 ) { + c->Message(15,"NPCID %u is now on loottable_id %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET loottable_id = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "merchantid" ) == 0 ) { + c->Message(15,"NPCID %u is now merchant_id %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET merchant_id = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "alt_currency_id" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'alt_currency_id' set to %s.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET alt_currency_id = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "npc_spells_effects_id" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'npc_spells_effects_id' set to %s.", npcTypeID, sep->argplus[2]); + + std::string query = StringFormat("UPDATE npc_types SET npc_spells_effects_id = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "adventure_template_id" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'adventure_template_id' set to %s.", npcTypeID, sep->argplus[2]); + + std::string query = StringFormat("UPDATE npc_types SET adventure_template_id = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "trap_template" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'trap_template' set to %s.", npcTypeID, sep->argplus[2]); + + std::string query = StringFormat("UPDATE npc_types SET trap_template = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "special_abilities" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'special_abilities' set to %s.", npcTypeID, sep->argplus[2]); + + std::string query = StringFormat("UPDATE npc_types SET special_abilities = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "spell" ) == 0 ) { + c->Message(15,"NPCID %u now uses spell list %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET npc_spells_id = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "faction" ) == 0 ) { + c->Message(15,"NPCID %u is now faction %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "mindmg" ) == 0 ) { + c->Message(15,"NPCID %u now hits for a min of %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET mindmg = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "maxdmg" ) == 0 ) { + c->Message(15,"NPCID %u now hits for a max of %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET maxdmg = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "aggroradius" ) == 0 ) { + c->Message(15,"NPCID %u now has an aggro radius of %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET aggroradius = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "assistradius" ) == 0 ) { + c->Message(15,"NPCID %u now has an assist radius of %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET assistradius = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "social" ) == 0 ) { + c->Message(15,"NPCID %u social status is now %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET social = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "runspeed" ) == 0 ) { + c->Message(15,"NPCID %u now runs at %f", npcTypeID, atof(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET runspeed = %f WHERE id = %i", + atof(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "AGI" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Agility.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET AGI = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "CHA" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Charisma.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET CHA = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "DEX" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Dexterity.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET DEX = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "INT" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Intelligence.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET _INT = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "STA" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Stamina.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET STA = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "STR" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Strength.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET STR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "WIS" ) == 0 ) { + c->Message(15,"NPCID %u now has a Magic Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET WIS = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "MR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Magic Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET MR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "DR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Disease Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET DR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "CR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Cold Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET CR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "FR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Fire Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET FR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "PR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Poison Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET PR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "Corrup" ) == 0 ) { + c->Message(15,"NPCID %u now has a Corruption Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET corrup = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "PhR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Physical Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET PhR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "seeinvis" ) == 0 ) { + c->Message(15,"NPCID %u now has seeinvis set to %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET see_invis = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "seeinvisundead" ) == 0 ) { + c->Message(15,"NPCID %u now has seeinvisundead set to %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET see_invis_undead = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "seehide" ) == 0 ) { + c->Message(15,"NPCID %u now has seehide set to %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET see_hide = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "seeimprovedhide" ) == 0 ) { + c->Message(15,"NPCID %u now has seeimprovedhide set to %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET see_improved_hide = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "AC" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Armor Class.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET ac = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "ATK" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Attack.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET atk = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "Accuracy" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Accuracy.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET accuracy = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "level" ) == 0 ) { + c->Message(15,"NPCID %u is now level %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET level = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "maxlevel" ) == 0 ) { + c->Message(15,"NPCID %u now has a maximum level of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET maxlevel = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "qglobal" ) == 0 ) { + c->Message(15,"Quest globals have been %s for NPCID %u", + atoi(sep->arg[2]) == 0 ? "disabled" : "enabled", npcTypeID); + + std::string query = StringFormat("UPDATE npc_types SET qglobal = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "npcaggro" ) == 0 ) { + c->Message(15,"NPCID %u will now %s other NPCs with negative faction npc_value", + npcTypeID, atoi(sep->arg[2]) == 0? "not aggro": "aggro"); + + std::string query = StringFormat("UPDATE npc_types SET npc_aggro = %i WHERE id = %i", + atoi(sep->argplus[2]) == 0? 0: 1, npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "limit" ) == 0 ) { + c->Message(15,"NPCID %u now has a spawn limit of %i", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET limit = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "Attackspeed" ) == 0 ) { + c->Message(15,"NPCID %u now has attack_speed set to %f", + npcTypeID, atof(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET attack_speed = %f WHERE id = %i", + atof(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "Attackdelay" ) == 0 ) { + c->Message(15,"NPCID %u now has attack_delay set to %i",npcTypeID,atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET attack_delay = %i WHERE id = %i",atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "findable" ) == 0 ) { + c->Message(15,"NPCID %u is now %s", npcTypeID, atoi(sep->arg[2]) == 0? "not findable": "findable"); + + std::string query = StringFormat("UPDATE npc_types SET findable = %i WHERE id = %i", + atoi(sep->argplus[2]) == 0? 0: 1, npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "wep1" ) == 0 ) { + c->Message(15,"NPCID %u will have item graphic %i set to his primary on repop.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET d_meele_texture1 = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "wep2" ) == 0 ) { + c->Message(15,"NPCID %u will have item graphic %i set to his secondary on repop.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET d_meele_texture2 = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "featuresave" ) == 0 ) { + c->Message(15,"NPCID %u saved with all current facial feature settings", + npcTypeID); + + Mob* target = c->GetTarget(); + + std::string query = StringFormat("UPDATE npc_types " + "SET luclin_haircolor = %i, luclin_beardcolor = %i, " + "luclin_hairstyle = %i, luclin_beard = %i, " + "face = %i, drakkin_heritage = %i, " + "drakkin_tattoo = %i, drakkin_details = %i, " + "WHERE id = %i", + target->GetHairColor(), target->GetBeardColor(), + target->GetHairStyle(), target->GetBeard(), + target->GetLuclinFace(), target->GetDrakkinHeritage(), + target->GetDrakkinTattoo(), target->GetDrakkinDetails(), + npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "color" ) == 0 ) { + c->Message(15,"NPCID %u now has %i red, %i green, and %i blue tinting on their armor.", + npcTypeID, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); + + std::string query = StringFormat("UPDATE npc_types " + "SET armortint_red = %i, armortint_green = %i, armortint_blue = %i " + "WHERE id = %i", + atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "armortint_id" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'armortint_id' set to %s", + npcTypeID, sep->arg[2]); + + std::string query = StringFormat("UPDATE npc_types SET armortint_id = '%s' WHERE id = %i", + sep->argplus[2], npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "setanimation" ) == 0 ) { + int animation = 0; + if(sep->arg[2] && atoi(sep->arg[2]) <= 4) { + if((strcasecmp( sep->arg[2], "stand" ) == 0) || atoi(sep->arg[2]) == 0) + animation = 0; //Stand + if((strcasecmp( sep->arg[2], "sit" ) == 0) || atoi(sep->arg[2]) == 1) + animation = 1; //Sit + if((strcasecmp( sep->arg[2], "crouch" ) == 0) || atoi(sep->arg[2]) == 2) + animation = 2; //Crouch + if((strcasecmp( sep->arg[2], "dead" ) == 0) || atoi(sep->arg[2]) == 3) + animation = 3; //Dead + if((strcasecmp( sep->arg[2], "loot" ) == 0) || atoi(sep->arg[2]) == 4) + animation = 4; //Looting Animation } - else{ + else { c->Message(0, "You must specifiy an animation stand, sit, crouch, dead, loot (0-4)"); c->Message(0, "Example: #npcedit setanimation sit"); c->Message(0, "Example: #npcedit setanimation 0"); return; } - c->Message(15,"NPCID %u now has the animation set to %i on spawn with spawngroup %i", c->GetTarget()->CastToNPC()->GetNPCTypeID(), Animation, c->GetTarget()->CastToNPC()->GetSp2() ); - database.RunQuery(query, MakeAnyLenString(&query, "update spawn2 set animation = %i where spawngroupID=%i", Animation, c->GetTarget()->CastToNPC()->GetSp2()), errbuf); - c->GetTarget()->SetAppearance(EmuAppearance(Animation)); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "scalerate" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u now has a scaling rate of %i.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set scalerate=%i where id=%i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "healscale" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u now has a heal scaling rate of %i.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set healscale=%i where id=%i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "spellscale" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u now has a spell scaling rate of %i.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set spellscale=%i where id=%i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "no_target" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u is now %s.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2]) == 0 ? "targetable" : "untargetable"); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set no_target_hotkey=%i where id=%i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "version" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u is now version %i.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set version=%i where id=%i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); + + c->Message(15,"NPCID %u now has the animation set to %i on spawn with spawngroup %i", npcTypeID, animation, c->GetTarget()->CastToNPC()->GetSp2() ); + + std::string query = StringFormat("UPDATE spawn2 SET animation = %i " + "WHERE spawngroupID = %i", + animation, c->GetTarget()->CastToNPC()->GetSp2()); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + + c->GetTarget()->SetAppearance(EmuAppearance(animation)); + return; } - else if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) - { - c->Message(0, "Type #npcedit help for more info"); + if ( strcasecmp( sep->arg[1], "scalerate" ) == 0 ) { + c->Message(15,"NPCID %u now has a scaling rate of %i.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET scalerate = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; } + + if ( strcasecmp( sep->arg[1], "healscale" ) == 0 ) { + c->Message(15, "NPCID %u now has a heal scaling rate of %i.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET healscale = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "spellscale" ) == 0 ) { + c->Message(15, "NPCID %u now has a spell scaling rate of %i.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET spellscale = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "no_target" ) == 0 ) { + c->Message(15, "NPCID %u is now %s.", + npcTypeID, atoi(sep->arg[2]) == 0? "targetable": "untargetable"); + + std::string query = StringFormat("UPDATE npc_types SET no_target_hotkey = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "version" ) == 0 ) { + c->Message(15, "NPCID %u is now version %i.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET version = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) + c->Message(0, "Type #npcedit help for more info"); + } #ifdef PACKET_PROFILER From a61b930bd9a0769f05d541726f508df4814aa8c8 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 11:58:24 -0700 Subject: [PATCH 269/368] command_qglobal converted to QueryDatabase --- zone/command.cpp | 68 +++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index f3adf5856..cfb10c564 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -7309,56 +7309,58 @@ void command_nologs(Client *c, const Seperator *sep) void command_qglobal(Client *c, const Seperator *sep) { //In-game switch for qglobal column - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; if(sep->arg[1][0] == 0) { c->Message(0, "Syntax: #qglobal [on/off/view]. Requires NPC target."); return; } - Mob *t = c->GetTarget(); - if(!t || !t->IsNPC()) { + + Mob *target = c->GetTarget(); + + if(!target || !target->IsNPC()) { c->Message(13, "NPC Target Required!"); return; } - if(!strcasecmp(sep->arg[1], "on")) - { - if(!database.RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET qglobal=1 WHERE id='%i'", t->GetNPCTypeID()), errbuf, 0)) - { + + if(!strcasecmp(sep->arg[1], "on")) { + std::string query = StringFormat("UPDATE npc_types SET qglobal = 1 WHERE id = '%i'", + target->GetNPCTypeID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { c->Message(15, "Could not update database."); + return; } - else - { - c->LogSQL(query); - c->Message(15, "Success! Changes take effect on zone reboot."); - } - safe_delete(query); + + c->LogSQL(query.c_str()); + c->Message(15, "Success! Changes take effect on zone reboot."); + return; } - else if(!strcasecmp(sep->arg[1], "off")) - { - if(!database.RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET qglobal=0 WHERE id='%i'", t->GetNPCTypeID()), errbuf, 0)) - { + + if(!strcasecmp(sep->arg[1], "off")) { + std::string query = StringFormat("UPDATE npc_types SET qglobal = 0 WHERE id = '%i'", + target->GetNPCTypeID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { c->Message(15, "Could not update database."); + return; } - else - { - c->LogSQL(query); - c->Message(15, "Success! Changes take effect on zone reboot."); - } - safe_delete(query); + + c->LogSQL(query.c_str()); + c->Message(15, "Success! Changes take effect on zone reboot."); + return; } - else if(!strcasecmp(sep->arg[1], "view")) - { - const NPCType *type = database.GetNPCType(t->GetNPCTypeID()); - if(!type) { + + if(!strcasecmp(sep->arg[1], "view")) { + const NPCType *type = database.GetNPCType(target->GetNPCTypeID()); + if(!type) c->Message(15, "Invalid NPC type."); - } else if(type->qglobal) { + else if(type->qglobal) c->Message(15, "This NPC has quest globals active."); - } else { + else c->Message(15, "This NPC has quest globals disabled."); - } - } else { - c->Message(15, "Invalid action specified."); + return; } + + c->Message(15, "Invalid action specified."); } void command_path(Client *c, const Seperator *sep) From 6a078279eb5e5ed3a26894106a5c3932288693f7 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:01:30 -0700 Subject: [PATCH 270/368] command_flagedit converted to QueryDatabase --- zone/command.cpp | 94 ++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index cfb10c564..4c617a572 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -7867,8 +7867,6 @@ void command_flags(Client *c, const Seperator *sep) { void command_flagedit(Client *c, const Seperator *sep) { //super-command for editing zone flags - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { c->Message(0, "Syntax: #flagedit [lockzone|unlockzone|listzones|give|take]."); c->Message(0, "...lockzone [zone id/short] [flag name] - Set the specified flag name on the zone, locking the zone"); @@ -7901,17 +7899,21 @@ void command_flagedit(Client *c, const Seperator *sep) { database.DoEscapeString(flag_name, sep->argplus[3], 64); flag_name[127] = '\0'; - if(!database.RunQuery(query, MakeAnyLenString(&query, - "UPDATE zone SET flag_needed='%s' WHERE zoneidnumber=%d AND version=%d", - flag_name, zoneid, zone->GetInstanceVersion()), errbuf)) - { - c->Message(13, "Error updating zone: %s", errbuf); - } else { - c->LogSQL(query); - c->Message(15, "Success! Zone %s now requires a flag, named %s", database.GetZoneName(zoneid), flag_name); + std::string query = StringFormat("UPDATE zone SET flag_needed = '%s' " + "WHERE zoneidnumber = %d AND version = %d", + flag_name, zoneid, zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + c->Message(13, "Error updating zone: %s", results.ErrorMessage().c_str()); + return; } - safe_delete(query); - } else if(!strcasecmp(sep->arg[1], "unlockzone")) { + + c->LogSQL(query.c_str()); + c->Message(15, "Success! Zone %s now requires a flag, named %s", database.GetZoneName(zoneid), flag_name); + return; + } + + if(!strcasecmp(sep->arg[1], "unlockzone")) { uint32 zoneid = 0; if(sep->arg[2][0] != '\0') { zoneid = atoi(sep->arg[2]); @@ -7919,39 +7921,43 @@ void command_flagedit(Client *c, const Seperator *sep) { zoneid = database.GetZoneID(sep->arg[2]); } } + if(zoneid < 1) { c->Message(13, "zone required. see help."); return; } - if(!database.RunQuery(query, MakeAnyLenString(&query, - "UPDATE zone SET flag_needed='' WHERE zoneidnumber=%d AND version=%d", - zoneid, zone->GetInstanceVersion()), errbuf)) - { - c->Message(15, "Error updating zone: %s", errbuf); - } else { - c->LogSQL(query); - c->Message(15, "Success! Zone %s no longer requires a flag.", database.GetZoneName(zoneid)); + std::string query = StringFormat("UPDATE zone SET flag_needed = '' " + "WHERE zoneidnumber = %d AND version = %d", + zoneid, zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + c->Message(15, "Error updating zone: %s", results.ErrorMessage().c_str()); + return; } - safe_delete(query); - } else if(!strcasecmp(sep->arg[1], "listzones")) { - MYSQL_RES *result; - MYSQL_ROW row; - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT zoneidnumber,short_name,long_name,version,flag_needed FROM zone WHERE flag_needed != ''" - ), errbuf, &result)) - { - c->Message(0, "Zones which require flags:"); - while ((row = mysql_fetch_row(result))) - { - c->Message(0, "Zone %s (%s,%s) version %s requires key %s", row[2], row[0], row[1], row[3], row[4]); - } - mysql_free_result(result); - } else { - c->Message(13, "Unable to query zone flags: %s", errbuf); - } - safe_delete_array(query); - } else if(!strcasecmp(sep->arg[1], "give")) { + + c->LogSQL(query.c_str()); + c->Message(15, "Success! Zone %s no longer requires a flag.", database.GetZoneName(zoneid)); + return; + } + + if(!strcasecmp(sep->arg[1], "listzones")) { + std::string query = "SELECT zoneidnumber, short_name, long_name, version, flag_needed " + "FROM zone WHERE flag_needed != ''"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(13, "Unable to query zone flags: %s", results.ErrorMessage().c_str()); + return; + } + + c->Message(0, "Zones which require flags:"); + for (auto row = results.begin(); row != results.end(); ++row) + c->Message(0, "Zone %s (%s,%s) version %s requires key %s", row[2], row[0], row[1], row[3], row[4]); + + return; + } + + if(!strcasecmp(sep->arg[1], "give")) { uint32 zoneid = 0; if(sep->arg[2][0] != '\0') { zoneid = atoi(sep->arg[2]); @@ -7971,7 +7977,10 @@ void command_flagedit(Client *c, const Seperator *sep) { } t->CastToClient()->SetZoneFlag(zoneid); - } else if(!strcasecmp(sep->arg[1], "give")) { + return; + } + + if(!strcasecmp(sep->arg[1], "give")) { uint32 zoneid = 0; if(sep->arg[2][0] != '\0') { zoneid = atoi(sep->arg[2]); @@ -7991,9 +8000,10 @@ void command_flagedit(Client *c, const Seperator *sep) { } t->CastToClient()->ClearZoneFlag(zoneid); - } else { - c->Message(15, "Invalid action specified. use '#flagedit help' for help"); + return; } + + c->Message(15, "Invalid action specified. use '#flagedit help' for help"); } void command_mlog(Client *c, const Seperator *sep) { From d5f5b38d26ea1162def7224a7c866c507edf5df9 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:03:25 -0700 Subject: [PATCH 271/368] command_advnpcspawn converted to QueryDatabase --- zone/command.cpp | 470 +++++++++++++++++++++++++---------------------- 1 file changed, 249 insertions(+), 221 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 4c617a572..727696486 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -8752,233 +8752,261 @@ void command_refreshgroup(Client *c, const Seperator *sep) void command_advnpcspawn(Client *c, const Seperator *sep) { Mob *target=c->GetTarget(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 last_insert_id = 0; - if (strcasecmp(sep->arg[1], "maketype") == 0){ - if(target && target->IsNPC()) - { - database.NPCSpawnDB(6, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); - } - else - c->Message(0, "Target Required!"); - } - else if (strcasecmp(sep->arg[1], "makegroup") == 0) { - if(sep->arg[2]) - { - if (!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (name,spawn_limit,dist,max_x,min_x,max_y,min_y,delay) VALUES (\"%s\",%i,%f,%f,%f,%f,%f,%i)", sep->arg[2], (sep->arg[3]?atoi(sep->arg[3]):0), (sep->arg[4]?atof(sep->arg[4]):0), (sep->arg[5]?atof(sep->arg[5]):0), (sep->arg[6]?atof(sep->arg[6]):0), (sep->arg[7]?atof(sep->arg[7]):0), (sep->arg[8]?atof(sep->arg[8]):0), (sep->arg[9]?atoi(sep->arg[9]):0)), errbuf, 0, 0, &last_insert_id)) - { - c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(13, errbuf); - } - else - { - c->LogSQL(query); - c->Message(0, "Group ID %i created successfully!", last_insert_id); - } - safe_delete_array(query); - } - else - { - c->Message(0, "Format: #advnpdspawn makegroup [spawn limit] [dist] [max x] [min x] [max y] [min y] [delay]"); - } - } - else if (strcasecmp(sep->arg[1], "addgroupentry") == 0) { - if(atoi(sep->arg[2]) && atoi(sep->arg[3]) && atoi(sep->arg[4])) - { - if (!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID,npcID,chance) VALUES (%i,%i,%i)", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), errbuf, 0, 0, &last_insert_id))) - { - c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(13, errbuf); - } - else - { - c->LogSQL(query); - c->Message(0, "NPC %i added to group %i with %i chance!", atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[4]) ); - } - safe_delete(query); - } - else - { - c->Message(0, "Format: #advnpdspawn addgroupentry "); - } - } - else if (strcasecmp(sep->arg[1], "editgroupbox") == 0) { - if(atof(sep->arg[2]) && atof(sep->arg[3]) && atof(sep->arg[4]) && atof(sep->arg[5]) && atof(sep->arg[6]) && atof(sep->arg[7]) && atof(sep->arg[8])) - { - if (!database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawngroup SET dist='%f',max_x='%f',min_x='%f',max_y='%f',min_y='%f',delay='%i' WHERE id='%i'", atof(sep->arg[3]),atof(sep->arg[4]),atof(sep->arg[5]),atof(sep->arg[6]),atof(sep->arg[7]),atoi(sep->arg[8]),atoi(sep->arg[2]), errbuf, 0, 0, &last_insert_id))) - { - c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(13, errbuf); - } - else - { - c->LogSQL(query); - c->Message(0, "Group ID %i created successfully!", last_insert_id); - } - safe_delete_array(query); - } - else - { - c->Message(0, "Format: #advnpdspawn editgroupbox "); - } - } - else if (strcasecmp(sep->arg[1], "cleargroupbox") == 0) { - if(atoi(sep->arg[2])) - { - if (!database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawngroup SET dist='0',max_x='0',min_x='0',max_y='0',min_y='0',delay='0' WHERE id='%i'",atoi(sep->arg[2])), errbuf, 0, 0, &last_insert_id)) - { - c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(13, errbuf); - } - else - { - c->LogSQL(query); - c->Message(0, "Group ID %i created successfully!", last_insert_id); - } - safe_delete_array(query); - } - else - { - c->Message(0, "Format: #advnpdspawn cleargroupbox "); - } - } - else if (strcasecmp(sep->arg[1], "addgroupspawn") == 0 && atoi(sep->arg[2])!=0) { - database.NPCSpawnDB(5, zone->GetShortName(), zone->GetInstanceVersion(), c, 0, atoi(sep->arg[2])); - c->Message(0, "Mob of group %i added successfully!", atoi(sep->arg[2])); - } - else if (strcasecmp(sep->arg[1], "removegroupspawn") == 0) { - if (!target || !target->IsNPC()) - c->Message(0, "Error: Need an NPC target."); - else { - Spawn2* s2 = target->CastToNPC()->respawn2; + if (strcasecmp(sep->arg[1], "maketype") == 0) { + if(!target || !target->IsNPC()) { + c->Message(0, "Target Required!"); + return; + } - if(!s2) { - c->Message(0, "removegroupspawn FAILED -- cannot determine which spawn entry in the database this mob came from."); - } - else - { - if(database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'",s2->GetID()), errbuf)) - { - c->LogSQL(query); - c->Message(0, "Spawnpoint Removed successfully."); - target->Depop(false); - } - else - { - c->Message(13, "Update failed! MySQL gave the following error:"); - c->Message(13, errbuf); - } - safe_delete_array(query); - } - } - } - else if (strcasecmp(sep->arg[1], "movespawn") == 0) { - if (!target || !target->IsNPC()) - c->Message(0, "Error: Need an NPC target."); - else { - Spawn2* s2 = target->CastToNPC()->respawn2; + database.NPCSpawnDB(6, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); + return; + } - if(!s2) { - c->Message(0, "movespawn FAILED -- cannot determine which spawn entry in the database this mob came from."); - } - else - { - if(database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawn2 SET x='%f', y='%f', z='%f', heading='%f' WHERE id='%i'",c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()), errbuf)) - { - c->LogSQL(query); - c->Message(0, "Updating coordinates successful."); - target->CastToNPC()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); - target->CastToNPC()->SaveGuardSpot(true); - target->SendPosition(); - } - else - { - c->Message(13, "Update failed! MySQL gave the following error:"); - c->Message(13, errbuf); - } - safe_delete_array(query); - } - } - } - else if (strcasecmp(sep->arg[1], "editrespawn") == 0) { - if (!target || !target->IsNPC()) - c->Message(0, "Error: Need an NPC target."); - else { - Spawn2* s2 = target->CastToNPC()->respawn2; + if (strcasecmp(sep->arg[1], "makegroup") == 0) { + if(!sep->arg[2]) { + c->Message(0, "Format: #advnpdspawn makegroup [spawn limit] [dist] [max x] [min x] [max y] [min y] [delay]"); + return; + } - uint32 new_rs = 0; - uint32 new_var = s2->GetVariance(); - if(!sep->IsNumber(2)) - { - c->Message(0, "editrespawn FAILED -- cannot set respawn to be 0"); - return; - } - else - { - new_rs = atoi(sep->arg[2]); - } + std::string query = StringFormat("INSERT INTO spawngroup " + "(name, spawn_limit, dist, max_x, min_x, max_y, min_y, delay) " + "VALUES (\"%s\", %i, %f, %f, %f, %f, %f, %i)", + sep->arg[2], + (sep->arg[3]? atoi(sep->arg[3]): 0), + (sep->arg[4]? atof(sep->arg[4]): 0), + (sep->arg[5]? atof(sep->arg[5]): 0), + (sep->arg[6]? atof(sep->arg[6]): 0), + (sep->arg[7]? atof(sep->arg[7]): 0), + (sep->arg[8]? atof(sep->arg[8]): 0), + (sep->arg[9]? atoi(sep->arg[9]): 0)); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } - if(sep->IsNumber(3)) - { - new_var = atoi(sep->arg[3]); - } + c->LogSQL(query.c_str()); + c->Message(0, "Group ID %i created successfully!", results.LastInsertedID()); + return; + } - if(!s2) { - c->Message(0, "editrespawn FAILED -- cannot determine which spawn entry in the database this mob came from."); - } - else - { - if(database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawn2 SET respawntime=%u, variance=%u WHERE id='%i'", new_rs, new_var, s2->GetID()), errbuf)) - { - c->LogSQL(query); - c->Message(0, "Updating respawn timer successful."); - s2->SetRespawnTimer(new_rs); - s2->SetVariance(new_var); - } - else - { - c->Message(13, "Update failed! MySQL gave the following error:"); - c->Message(13, errbuf); - } - safe_delete_array(query); - } - } - } - else if (strcasecmp(sep->arg[1], "setversion") == 0) { - int16 Version = 0; - if (!target || !target->IsNPC()) - c->Message(0, "Error: Need an NPC target."); - else { - if(sep->IsNumber(2)){ - Version = atoi(sep->arg[2]); - if(database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawn2 SET version=%i WHERE spawngroupID='%i'", Version, c->GetTarget()->CastToNPC()->GetSp2()), errbuf)){ - c->LogSQL(query); - c->Message(0, "Version change to %i was successful from SpawnGroupID %i", Version, c->GetTarget()->CastToNPC()->GetSp2()); - c->GetTarget()->Depop(false); - } - else{ - c->Message(13, "Update failed! MySQL gave the following error:"); - c->Message(13, errbuf); - } - safe_delete_array(query); - } - else{ - c->Message(0, "setversion FAILED -- You must set a version number"); - return; - } - } - } - else if (strcasecmp(sep->arg[1], "testload") == 0 && atoi(sep->arg[2])!=0) { - database.LoadSpawnGroupsByID(atoi(sep->arg[2]),&zone->spawn_group_list); - c->Message(0, "Group %i loaded successfully!", atoi(sep->arg[2])); - } - else { - c->Message(0, "Error: #advnpcspawn: Invalid command."); - c->Message(0, "Usage: #advnpcspawn [maketype|makegroup|addgroupentry|addgroupspawn|setversion]"); - c->Message(0, "Usage: #advnpcspawn [removegroupspawn|movespawn|editrespawn|editgroupbox|cleargroupbox]"); - } + if (strcasecmp(sep->arg[1], "addgroupentry") == 0) { + if(!atoi(sep->arg[2]) || !atoi(sep->arg[3]) || !atoi(sep->arg[4])) { + c->Message(0, "Format: #advnpdspawn addgroupentry "); + return; + } + + std::string query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) " + "VALUES (%i, %i, %i)", + atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "NPC %i added to group %i with %i chance!", atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[4]) ); + + return; + } + + if (strcasecmp(sep->arg[1], "editgroupbox") == 0) { + if(!atof(sep->arg[2]) || !atof(sep->arg[3]) || !atof(sep->arg[4]) || !atof(sep->arg[5]) || !atof(sep->arg[6]) || !atof(sep->arg[7]) || !atof(sep->arg[8])) { + c->Message(0, "Format: #advnpdspawn editgroupbox "); + return; + } + + std::string query = StringFormat("UPDATE spawngroup SET dist = '%f', max_x = '%f', min_x = '%f', " + "max_y = '%f', min_y = '%f', delay = '%i' WHERE id = '%i'", + atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), + atof(sep->arg[6]), atof(sep->arg[7]), atoi(sep->arg[8]), + atoi(sep->arg[2])); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Group ID %i created successfully!", results.LastInsertedID()); + + return; + } + + if (strcasecmp(sep->arg[1], "cleargroupbox") == 0) { + if(!atoi(sep->arg[2])) { + c->Message(0, "Format: #advnpdspawn cleargroupbox "); + return; + } + + std::string query = StringFormat("UPDATE spawngroup " + "SET dist = '0', max_x = '0', min_x = '0', " + "max_y = '0', min_y = '0', delay = '0' " + "WHERE id = '%i' ", atoi(sep->arg[2])); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Group ID %i created successfully!", results.LastInsertedID()); + + return; + } + + if (strcasecmp(sep->arg[1], "addgroupspawn") == 0 && atoi(sep->arg[2])!=0) { + database.NPCSpawnDB(5, zone->GetShortName(), zone->GetInstanceVersion(), c, 0, atoi(sep->arg[2])); + c->Message(0, "Mob of group %i added successfully!", atoi(sep->arg[2])); + return; + } + + if (strcasecmp(sep->arg[1], "removegroupspawn") == 0) { + if (!target || !target->IsNPC()) { + c->Message(0, "Error: Need an NPC target."); + return; + } + + Spawn2* s2 = target->CastToNPC()->respawn2; + + if(!s2) { + c->Message(0, "removegroupspawn FAILED -- cannot determine which spawn entry in the database this mob came from."); + return; + } + + std::string query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", s2->GetID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + c->Message(13, "Update failed! MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Spawnpoint Removed successfully."); + target->Depop(false); + + return; + } + + if (strcasecmp(sep->arg[1], "movespawn") == 0) { + if (!target || !target->IsNPC()) { + c->Message(0, "Error: Need an NPC target."); + return; + } + + Spawn2* s2 = target->CastToNPC()->respawn2; + + if(!s2) { + c->Message(0, "movespawn FAILED -- cannot determine which spawn entry in the database this mob came from."); + return; + } + + std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' " + "WHERE id = '%i'", + c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(13, "Update failed! MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Updating coordinates successful."); + target->CastToNPC()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); + target->CastToNPC()->SaveGuardSpot(true); + target->SendPosition(); + + return; + } + + if (strcasecmp(sep->arg[1], "editrespawn") == 0) { + if (!target || !target->IsNPC()) { + c->Message(0, "Error: Need an NPC target."); + return; + } + + Spawn2* s2 = target->CastToNPC()->respawn2; + + uint32 new_rs = 0; + uint32 new_var = s2->GetVariance(); + if(!sep->IsNumber(2)) { + c->Message(0, "editrespawn FAILED -- cannot set respawn to be 0"); + return; + } + + new_rs = atoi(sep->arg[2]); + + if(sep->IsNumber(3)) + new_var = atoi(sep->arg[3]); + + if(!s2) { + c->Message(0, "editrespawn FAILED -- cannot determine which spawn entry in the database this mob came from."); + return; + } + + std::string query = StringFormat("UPDATE spawn2 SET respawntime = %u, variance = %u " + "WHERE id = '%i'", new_rs, new_var, s2->GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(13, "Update failed! MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Updating respawn timer successful."); + s2->SetRespawnTimer(new_rs); + s2->SetVariance(new_var); + + return; + } + + if (strcasecmp(sep->arg[1], "setversion") == 0) { + if (!target || !target->IsNPC()) { + c->Message(0, "Error: Need an NPC target."); + return; + } + + if(!sep->IsNumber(2)) { + c->Message(0, "setversion FAILED -- You must set a version number"); + return; + } + + int16 version = atoi(sep->arg[2]); + std::string query = StringFormat("UPDATE spawn2 SET version = %i " + "WHERE spawngroupID = '%i'", + version, c->GetTarget()->CastToNPC()->GetSp2()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(13, "Update failed! MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Version change to %i was successful from SpawnGroupID %i", version, c->GetTarget()->CastToNPC()->GetSp2()); + c->GetTarget()->Depop(false); + + return; + } + + if (strcasecmp(sep->arg[1], "testload") == 0 && atoi(sep->arg[2])!=0) { + database.LoadSpawnGroupsByID(atoi(sep->arg[2]),&zone->spawn_group_list); + c->Message(0, "Group %i loaded successfully!", atoi(sep->arg[2])); + return; + } + + c->Message(0, "Error: #advnpcspawn: Invalid command."); + c->Message(0, "Usage: #advnpcspawn [maketype|makegroup|addgroupentry|addgroupspawn|setversion]"); + c->Message(0, "Usage: #advnpcspawn [removegroupspawn|movespawn|editrespawn|editgroupbox|cleargroupbox]"); } void command_aggrozone(Client *c, const Seperator *sep) { From bf6b9aaef8af9ffa697a6d55961562d349901bc2 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:07:33 -0700 Subject: [PATCH 272/368] command_object converted to QueryDatabase --- zone/command.cpp | 2391 ++++++++++++++++++++-------------------------- 1 file changed, 1031 insertions(+), 1360 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 727696486..b03cb3d93 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -9287,28 +9287,15 @@ void command_netstats(Client *c, const Seperator *sep) void command_object(Client *c, const Seperator *sep) { if (!c) - { return; // Crash Suppressant: No client. How did we get here? - } // Save it here. We sometimes have need to refer to it in multiple places. const char* usage_string = "Usage: #object List|Add|Edit|Move|Rotate|Save|Copy|Delete|Undo"; - if ((!sep) || (sep->argnum == 0)) - { - // Crash Suppressant: Shouldn't be able to get here, either, but fail gracefully if we do. + if ((!sep) || (sep->argnum == 0)) { c->Message(0, usage_string); - return; - } - - char errbuf[MYSQL_ERRMSG_SIZE]; - char query[512]; - uint32 col; - MYSQL_RES *result; - MYSQL_ROW row; - int iObjectsFound = 0; - int len; + } Object* o = nullptr; Object_Struct od; @@ -9325,1361 +9312,1045 @@ void command_object(Client *c, const Seperator *sep) bool bNewObject = false; - errbuf[0] = '\0'; - float x2; float y2; // Temporary object type for static objects to allow manipulation // NOTE: Zone::LoadZoneObjects() currently loads this as an uint8, so max value is 255! - static const uint32 TempStaticType = 255; + static const uint32 staticType = 255; // Case insensitive commands (List == list == LIST) strlwr(sep->arg[1]); - // Protip: We only really care about the first letter. You can abbreviate Delete to just D if desired. - switch (sep->arg[1][0]) - { - case 'l': // List Objects - // Insufficient or invalid args - if ((sep->argnum < 2) || (sep->arg[2][0] < '0') || ((sep->arg[2][0] > '9') && ((sep->arg[2][0] & 0xDF) != 'A'))) - { - c->Message(0, "Usage: #object List All|(radius)"); - - return; - } - - if ((sep->arg[2][0] & 0xDF) == 'A') - { - radius = 0; // List All - } - else if ((radius = atoi(sep->arg[2])) <= 0) - { - radius = 500; // Invalid radius. Default to 500 units. - } - - if (radius == 0) - { - c->Message(0, "Objects within this zone:"); - } - else - { - c->Message(0, "Objects within %u units of your current location:", radius); - } - - if (radius) - { - len = snprintf(query, sizeof(query), - "SELECT id, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20" - " FROM object" - " WHERE (zoneid=%u)" - " AND (version=%u)" - " AND (xpos BETWEEN %.1f AND %.1f)" - " AND (ypos BETWEEN %.1f AND %.1f)" - " AND (zpos BETWEEN %.1f AND %.1f)" - " ORDER BY id", - zone->GetZoneID(), - zone->GetInstanceVersion(), - c->GetX() - radius, // Yes, we're actually using a bounding box instead of a radius. - c->GetX() + radius, // Much less processing power used this way. - c->GetY() - radius, - c->GetY() + radius, - c->GetZ() - radius, - c->GetZ() + radius); - } - else - { - len = snprintf(query, sizeof(query), - "SELECT id, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20" - " FROM object" - " WHERE (zoneid=%u)" - " AND (version=%u)" - " ORDER BY id", - zone->GetZoneID(), - zone->GetInstanceVersion()); - } - - if (database.RunQuery(query, len, errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) - { - col = 0; - id = atoi(row[col++]); - od.x = atof(row[col++]); - od.y = atof(row[col++]); - od.z = atof(row[col++]); - od.heading = atof(row[col++]); - itemid = atoi(row[col++]); - strn0cpy(od.object_name, row[col++], sizeof(od.object_name)); - od.object_name[sizeof(od.object_name) - 1] = '\0'; // Required if strlen(row[col++]) exactly == sizeof(object_name) - - od.object_type = atoi(row[col++]); - icon = atoi(row[col++]); - od.unknown008 = atoi(row[col++]); - od.unknown010 = atoi(row[col++]); - od.unknown020 = atoi(row[col++]); - - switch (od.object_type) - { - case 0: // Static Object - case TempStaticType: // Static Object unlocked for changes - if (od.unknown008 == 0) // Unknown08 field is optional Size parameter for static objects - { - od.unknown008 = 100; // Static object default Size is 100% - } - - c->Message(0, - "- STATIC Object (%s): id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, size %u, solidtype %u, incline %u", - (od.object_type == 0) ? "locked" : "unlocked", id, od.x, od.y, od.z, od.heading, od.object_name, od.unknown008, od.unknown010, od.unknown020); - break; - case OT_DROPPEDITEM: // Ground Spawn - c->Message(0, - "- TEMPORARY Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, itemid %u, model %s, icon %u", - id, od.x, od.y, od.z, od.heading, itemid, od.object_name, icon); - break; - default: // All others == Tradeskill Objects - c->Message(0, - "- TRADESKILL Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, type %u, icon %u", - id, od.x, od.y, od.z, od.heading, od.object_name, od.object_type, icon); - break; - } - - iObjectsFound++; - } - - mysql_free_result(result); - } - - c->Message(0, "%u object%s found", iObjectsFound, (iObjectsFound == 1) ? "" : "s"); - break; - case 'a': // Add Object - // Insufficient or invalid arguments - if ((sep->argnum < 3) || ((sep->arg[3][0] == '\0') && (sep->arg[4][0] < '0') && (sep->arg[4][0] > '9'))) - { - c->Message(0, "Usage: (Static Object): #object Add [ObjectID] 0 Model [SizePercent] [SolidType] [Incline]"); - c->Message(0, "Usage: (Tradeskill Object): #object Add [ObjectID] TypeNum Model Icon"); - c->Message(0, "- Notes: Model must start with a letter, max length 16. SolidTypes = 0 (Solid), 1 (Sometimes Non-Solid)"); - - return; - } - - if (sep->argnum > 3) - { - // Model name in arg3? - if ((sep->arg[3][0] <= '9') && (sep->arg[3][0] >= '0')) - { - // Nope, user must have specified ObjectID. Extract it. - id = atoi(sep->arg[2]); - - col = 1; // Bump all other arguments one to the right. Model is in arg4. - } - else - { - // Yep, arg3 is non-numeric, ObjectID must be omitted and model must be arg3 - id = 0; - col = 0; - } - } - else - { - // Nope, only 3 args. Object ID must be omitted and arg3 must be model. - id = 0; - col = 0; - } - - memset(&od, 0, sizeof(od)); - - od.object_type = atoi(sep->arg[2 + col]); - - switch (od.object_type) - { - case 0: // Static Object - if ((sep->argnum - col) > 3) - { - od.unknown008 = atoi(sep->arg[4 + col]); // Size specified - - if ((sep->argnum - col) > 4) - { - od.unknown010 = atoi(sep->arg[5 + col]); // SolidType specified - - if ((sep->argnum - col) > 5) - { - od.unknown020 = atoi(sep->arg[6 + col]); // Incline specified - } - } - } - break; - case 1: // Ground Spawn - c->Message(0, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped items, which are not supported with #object. See the 'ground_spawns' table in the database."); - - return; - break; - default: // Everything else == Tradeskill Object - icon = ((sep->argnum - col) > 3) ? atoi(sep->arg[4 + col]) : 0; - - if (icon == 0) - { - c->Message(0, "ERROR: Required property 'Icon' not specified for Tradeskill Object"); - - return; - } - break; - } - - od.x = c->GetX(); - od.y = c->GetY(); - od.z = c->GetZ() - (c->GetSize() * 0.625f); - od.heading = c->GetHeading() * 2.0f; // GetHeading() is half of actual. Compensate by doubling. - - if (id) - { - // ID specified. Verify that it doesn't already exist. - - len = snprintf(query, sizeof(query), "SELECT COUNT(*) FROM object WHERE ID=%u", id); - - // Already in database? - if (database.RunQuery(query, len, errbuf, &result)) - { - if ((row = mysql_fetch_row(result)) != nullptr) - { - if (atoi(row[0]) > 0) - { - // Yep, in database already. - - id = 0; - } - } - - mysql_free_result(result); - } - - if (id) - { - // Not in database. Already spawned, just not saved? - if (entity_list.FindObject(id)) - { - // Yep, already spawned. - - id = 0; - } - } - - if (id == 0) - { - c->Message(0, "ERROR: An object already exists with the id %u", atoi(sep->arg[2])); - - return; - } - } - - // Verify no other objects already in this spot (accidental double-click of Hotkey?) - len = snprintf(query, sizeof(query), - "SELECT COUNT(*) FROM object " - "WHERE (zoneid=%u) " - "AND (version=%u) " - "AND (posx BETWEEN %.1f AND %.1f) " - "AND (posy BETWEEN %.1f AND %.1f) " - "AND (posz BETWEEN %.1f AND %.1f)", - zone->GetZoneID(), - zone->GetInstanceVersion(), - od.x - 0.2f, od.x + 0.2f, // Yes, we're actually using a bounding box instead of a radius. - od.y - 0.2f, od.y + 0.2f, // Much less processing power used this way. - od.z - 0.2f, od.z + 0.2f); // It's pretty forgiving, though, allowing for close-proximity objects - - iObjectsFound = 0; - if (database.RunQuery(query, len, errbuf, &result)) - { - if ((row = mysql_fetch_row(result)) != nullptr) - { - iObjectsFound = atoi(row[0]); // Number of nearby objects from database - } - - mysql_free_result(result); - } - - if (iObjectsFound == 0) - { - // No objects found in database too close. How about spawned but not yet saved? - if (entity_list.FindNearbyObject(od.x, od.y, od.z, 0.2f)) - { - iObjectsFound++; - } - } - - if (iObjectsFound) - { - c->Message(0, "ERROR: Object already at this location."); - - return; - } - - // Strip any single quotes from objectname (SQL injection FTL!) - strn0cpy(od.object_name, sep->arg[3 + col], sizeof(od.object_name)); - - len = strlen(od.object_name); - for (col = 0; col < (uint32)len; col++) - { - if (od.object_name[col] == '\'') - { - // Uh oh, 1337 h4x0r monkeying around! Strip that apostrophe! - memcpy(&od.object_name[col], &od.object_name[col + 1], len - col); - - len--; - col--; - } - } - - strupr(od.object_name); // Model names are always upper-case. - - if ((od.object_name[0] < 'A') || (od.object_name[0] > 'Z')) - { - c->Message(0, "ERROR: Model name must start with a letter."); - - return; - } - - if (id == 0) - { - // No ID specified. Get a best-guess next number from the database - - // If there's a problem retrieving an ID from the database, it'll end up being object # 1. No biggie. - - strn0cpy(query, "SELECT MAX(id) FROM object", sizeof(query)); - - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - if (row = mysql_fetch_row(result)) - { - id = atoi(row[0]); - } - - mysql_free_result(result); - } - - id++; - } - - // Make sure not to overwrite already-spawned objects that haven't been saved yet. - while (o = entity_list.FindObject(id)) - { - id++; - } - - if (od.object_type == 0) // Static object - { - od.object_type = TempStaticType; // Temporary. We'll make it 0 when we Save - } - - od.zone_id = zone->GetZoneID(); - od.zone_instance = zone->GetInstanceVersion(); - - o = new Object(id, od.object_type, icon, od, nullptr); - - // Add to our zone entity list and spawn immediately for all clients - entity_list.AddObject(o, true); - - // Bump player back to avoid getting stuck inside new object - - // GetHeading() returns half of the actual heading, for some reason, so we'll double it here for computation - x2 = 10.0f * sin(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); - y2 = 10.0f * cos(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); - c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading() * 2); - - c->Message(0, "Spawning object with tentative id %u at location (%.1f, %.1f, %.1f heading %.1f). Use '#object Save' to save to database when satisfied with placement.", id, od.x, od.y, od.z, od.heading); - - if (od.object_type == TempStaticType) // Temporary Static Object - { - c->Message(0, "- Note: Static Object will act like a tradeskill container and will not reflect size, solidtype, or incline values until you commit with '#object Save', after which it will be unchangeable until you use '#object Edit' and zone back in."); - } - break; - case 'e': // Edit - // Insufficient or invalid arguments - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) < 1)) - { - c->Message(0, "Usage: #object Edit (ObjectID) [PropertyName] [NewValue]"); - c->Message(0, "- Static Object (Type 0) Properties: model, type, size, solidtype, incline"); - c->Message(0, "- Tradeskill Object (Type 2+) Properties: model, type, icon"); - - return; - } - - o = entity_list.FindObject(id); - - // Object already available in-zone? - if (o) - { - // Yep, looks like we can make real-time changes. - if (sep->argnum < 4) - { - // Or not. '#object Edit (ObjectID)' called without PropertyName and NewValue - - c->Message(0, "Note: Object %u already unlocked and ready for changes", id); - - return; - } - } - else - { - // Object not found in-zone in a modifiable form. Check for valid matching circumstances. - - len = snprintf(query, sizeof(query), "SELECT zoneid, version, type FROM object WHERE id=%u", id); - - iObjectsFound = 0; - if (database.RunQuery(query, len, errbuf, &result)) - { - if (row = mysql_fetch_row(result)) - { - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - - iObjectsFound++; - } - - mysql_free_result(result); - } - - // Object ID not found? - if (iObjectsFound == 0) - { - c->Message(0, "ERROR: Object %u not found", id); - - return; - } - - // Object not in this zone? - if (od.zone_id != zone->GetZoneID()) - { - c->Message(0, "ERROR: Object %u not in this zone.", id); - - return; - } - - // Object not in this instance? - if (od.zone_instance != zone->GetInstanceVersion()) - { - c->Message(0, "ERROR: Object %u not part of this instance version.", id); - - return; - } - - switch (od.object_type) - { - case 0: // Static object needing unlocking + if (strcasecmp(sep->arg[1], "list") == 0) { + // Insufficient or invalid args + if ((sep->argnum < 2) || (sep->arg[2][0] < '0') || ((sep->arg[2][0] > '9') && ((sep->arg[2][0] & 0xDF) != 'A'))) { + c->Message(0, "Usage: #object List All|(radius)"); + return; + } + + if ((sep->arg[2][0] & 0xDF) == 'A') + radius = 0; // List All + else if ((radius = atoi(sep->arg[2])) <= 0) + radius = 500; // Invalid radius. Default to 500 units. + + if (radius == 0) + c->Message(0, "Objects within this zone:"); + else + c->Message(0, "Objects within %u units of your current location:", radius); + + std::string query; + if (radius) + query = StringFormat("SELECT id, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u AND version = %u " + "AND (xpos BETWEEN %.1f AND %.1f) " + "AND (ypos BETWEEN %.1f AND %.1f) " + "AND (zpos BETWEEN %.1f AND %.1f) " + "ORDER BY id", + zone->GetZoneID(), zone->GetInstanceVersion(), + c->GetX() - radius, // Yes, we're actually using a bounding box instead of a radius. + c->GetX() + radius, // Much less processing power used this way. + c->GetY() - radius, + c->GetY() + radius, + c->GetZ() - radius, + c->GetZ() + radius); + else + query = StringFormat("SELECT id, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u AND version = %u " + "ORDER BY id", + zone->GetZoneID(), zone->GetInstanceVersion()); + + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Error in objects query"); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + id = atoi(row[0]); + od.x = atof(row[1]); + od.y = atof(row[2]); + od.z = atof(row[3]); + od.heading = atof(row[4]); + itemid = atoi(row[5]); + strn0cpy(od.object_name, row[6], sizeof(od.object_name)); + od.object_name[sizeof(od.object_name) - 1] = '\0'; // Required if strlen(row[col++]) exactly == sizeof(object_name) + + od.object_type = atoi(row[7]); + icon = atoi(row[8]); + od.unknown008 = atoi(row[9]); + od.unknown010 = atoi(row[10]); + od.unknown020 = atoi(row[11]); + + switch (od.object_type) + { + case 0: // Static Object + case staticType: // Static Object unlocked for changes + if (od.unknown008 == 0) // Unknown08 field is optional Size parameter for static objects + od.unknown008 = 100; // Static object default Size is 100% + + c->Message(0,"- STATIC Object (%s): id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, size %u, solidtype %u, incline %u", (od.object_type == 0) ? "locked" : "unlocked", id, od.x, od.y, od.z, od.heading, od.object_name, od.unknown008, od.unknown010, od.unknown020); + break; + + case OT_DROPPEDITEM: // Ground Spawn + c->Message(0,"- TEMPORARY Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, itemid %u, model %s, icon %u",id, od.x, od.y, od.z, od.heading, itemid, od.object_name, icon); + break; + + default: // All others == Tradeskill Objects + c->Message(0, "- TRADESKILL Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, type %u, icon %u",id, od.x, od.y, od.z, od.heading, od.object_name, od.object_type, icon); + break; + } + } + + c->Message(0, "%u object%s found", results.RowCount(), (results.RowCount() == 1)? "": "s"); + return; + } + + if (strcasecmp(sep->arg[1], "add") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || ((sep->arg[3][0] == '\0') && (sep->arg[4][0] < '0') && (sep->arg[4][0] > '9'))) { + c->Message(0, "Usage: (Static Object): #object Add [ObjectID] 0 Model [SizePercent] [SolidType] [Incline]"); + c->Message(0, "Usage: (Tradeskill Object): #object Add [ObjectID] TypeNum Model Icon"); + c->Message(0, "- Notes: Model must start with a letter, max length 16. SolidTypes = 0 (Solid), 1 (Sometimes Non-Solid)"); + return; + } + + int col; + + if (sep->argnum > 3) { // Model name in arg3? + if ((sep->arg[3][0] <= '9') && (sep->arg[3][0] >= '0')) { + // Nope, user must have specified ObjectID. Extract it. + id = atoi(sep->arg[2]); + col = 1; // Bump all other arguments one to the right. Model is in arg4. + } + else { + // Yep, arg3 is non-numeric, ObjectID must be omitted and model must be arg3 + id = 0; + col = 0; + } + } + else { + // Nope, only 3 args. Object ID must be omitted and arg3 must be model. + id = 0; + col = 0; + } + + memset(&od, 0, sizeof(od)); + + od.object_type = atoi(sep->arg[2 + col]); + + switch (od.object_type) { + case 0: // Static Object + if ((sep->argnum - col) > 3) { + od.unknown008 = atoi(sep->arg[4 + col]); // Size specified + + if ((sep->argnum - col) > 4) { + od.unknown010 = atoi(sep->arg[5 + col]); // SolidType specified + + if ((sep->argnum - col) > 5) + od.unknown020 = atoi(sep->arg[6 + col]); // Incline specified + } + } + break; + + case 1: // Ground Spawn + c->Message(0, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped items, which are not supported with #object. See the 'ground_spawns' table in the database."); + return; + + default: // Everything else == Tradeskill Object + icon = ((sep->argnum - col) > 3) ? atoi(sep->arg[4 + col]) : 0; + + if (icon == 0) { + c->Message(0, "ERROR: Required property 'Icon' not specified for Tradeskill Object"); + return; + } + + break; + } + + od.x = c->GetX(); + od.y = c->GetY(); + od.z = c->GetZ() - (c->GetSize() * 0.625f); + od.heading = c->GetHeading() * 2.0f; // GetHeading() is half of actual. Compensate by doubling. + + std::string query; + if (id) { + // ID specified. Verify that it doesn't already exist. + query = StringFormat("SELECT COUNT(*) FROM object WHERE ID = %u", id); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + if (atoi(row[0]) > 0) // Yep, in database already. + id = 0; + } + + // Not in database. Already spawned, just not saved? + // Yep, already spawned. + if (id && entity_list.FindObject(id)) + id = 0; + + if (id == 0) { + c->Message(0, "ERROR: An object already exists with the id %u", atoi(sep->arg[2])); + return; + } + } + + int objectsFound = 0; + // Verify no other objects already in this spot (accidental double-click of Hotkey?) + query = StringFormat("SELECT COUNT(*) FROM object WHERE zoneid = %u " + "AND version=%u AND (posx BETWEEN %.1f AND %.1f) " + "AND (posy BETWEEN %.1f AND %.1f) " + "AND (posz BETWEEN %.1f AND %.1f)", + zone->GetZoneID(), zone->GetInstanceVersion(), + od.x - 0.2f, od.x + 0.2f, // Yes, we're actually using a bounding box instead of a radius. + od.y - 0.2f, od.y + 0.2f, // Much less processing power used this way. + od.z - 0.2f, od.z + 0.2f); // It's pretty forgiving, though, allowing for close-proximity objects + + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + objectsFound = atoi(row[0]); // Number of nearby objects from database + } + + // No objects found in database too close. How about spawned but not yet saved? + if (objectsFound == 0 && entity_list.FindNearbyObject(od.x, od.y, od.z, 0.2f)) + objectsFound = 1; + + if (objectsFound) { + c->Message(0, "ERROR: Object already at this location."); + return; + } + + // Strip any single quotes from objectname (SQL injection FTL!) + strn0cpy(od.object_name, sep->arg[3 + col], sizeof(od.object_name)); + + uint32 len = strlen(od.object_name); + for (col = 0; col < (uint32)len; col++) { + if (od.object_name[col] != '\'') + continue; + + // Uh oh, 1337 h4x0r monkeying around! Strip that apostrophe! + memcpy(&od.object_name[col], &od.object_name[col + 1], len - col); + len--; + col--; + } + + strupr(od.object_name); // Model names are always upper-case. + + if ((od.object_name[0] < 'A') || (od.object_name[0] > 'Z')) { + c->Message(0, "ERROR: Model name must start with a letter."); + return; + } + + if (id == 0) { + // No ID specified. Get a best-guess next number from the database + // If there's a problem retrieving an ID from the database, it'll end up being object # 1. No biggie. + + query = "SELECT MAX(id) FROM object"; + results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + id = atoi(row[0]); + } + + id++; + } + + // Make sure not to overwrite already-spawned objects that haven't been saved yet. + while (o = entity_list.FindObject(id)) + id++; + + // Static object + if (od.object_type == 0) + od.object_type = staticType; // Temporary. We'll make it 0 when we Save + + od.zone_id = zone->GetZoneID(); + od.zone_instance = zone->GetInstanceVersion(); + + o = new Object(id, od.object_type, icon, od, nullptr); + + // Add to our zone entity list and spawn immediately for all clients + entity_list.AddObject(o, true); + + // Bump player back to avoid getting stuck inside new object + + // GetHeading() returns half of the actual heading, for some reason, so we'll double it here for computation + x2 = 10.0f * sin(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); + y2 = 10.0f * cos(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); + c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading() * 2); + + c->Message(0, "Spawning object with tentative id %u at location (%.1f, %.1f, %.1f heading %.1f). Use '#object Save' to save to database when satisfied with placement.", id, od.x, od.y, od.z, od.heading); + + // Temporary Static Object + if (od.object_type == staticType) + c->Message(0, "- Note: Static Object will act like a tradeskill container and will not reflect size, solidtype, or incline values until you commit with '#object Save', after which it will be unchangeable until you use '#object Edit' and zone back in."); + + return; + } + + if (strcasecmp(sep->arg[1], "edit") == 0) { + + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) < 1)) { + c->Message(0, "Usage: #object Edit (ObjectID) [PropertyName] [NewValue]"); + c->Message(0, "- Static Object (Type 0) Properties: model, type, size, solidtype, incline"); + c->Message(0, "- Tradeskill Object (Type 2+) Properties: model, type, icon"); + + return; + } + + o = entity_list.FindObject(id); + + // Object already available in-zone? + if (o) { + // Yep, looks like we can make real-time changes. + if (sep->argnum < 4) { + // Or not. '#object Edit (ObjectID)' called without PropertyName and NewValue + c->Message(0, "Note: Object %u already unlocked and ready for changes", id); + return; + } + } + else { + // Object not found in-zone in a modifiable form. Check for valid matching circumstances. + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(0, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + uint32 objectsFound = 1; + + // Object not in this zone? + if (od.zone_id != zone->GetZoneID()) { + c->Message(0, "ERROR: Object %u not in this zone.", id); + return; + } + + // Object not in this instance? + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(0, "ERROR: Object %u not part of this instance version.", id); + return; + } + + switch (od.object_type) + { + case 0: // Static object needing unlocking // Convert to tradeskill object temporarily for changes - - len = snprintf(query, sizeof(query), "UPDATE object SET type=%u WHERE id=%u", TempStaticType, id); - - database.RunQuery(query, len); - - c->Message(0, "Static Object %u unlocked for editing. You must zone out and back in to make your changes, then commit them with '#object Save'.", id); - - if (sep->argnum >= 4) - { - c->Message(0, "NOTE: The change you specified has not been applied, since the static object had not been unlocked for editing yet."); - } - - return; - break; - case OT_DROPPEDITEM: - c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); - - return; - break; - case TempStaticType: - c->Message(0, "ERROR: Object %u has been unlocked for editing, but you must zone out and back in for your client to refresh its object table before you can make changes to it.", id); - - return; - break; - default: - // Unknown error preventing us from seeing the object in the zone. - - c->Message(0, "ERROR: Unknown problem attempting to manipulate object %u", id); - - return; - break; - } - } - - // If we're here, we have a manipulable object ready for changes. - - strlwr(sep->arg[3]); // Case insensitive PropertyName - strupr(sep->arg[4]); // In case it's model name, which should always be upper-case - - // Read current object info for reference - icon = o->GetIcon(); - o->GetObjectData(&od); - - // We'll be a little more picky with property names, to prevent errors. Check against the whole word. - switch (sep->arg[3][0]) - { - case 'm': - if (strcmp(sep->arg[3], "model") == 0) - { - if ((sep->arg[4][0] < 'A') || (sep->arg[4][0] > 'Z')) - { - c->Message(0, "ERROR: Model names must begin with a letter."); - - return; - } - - strn0cpy(od.object_name, sep->arg[4], sizeof(od.object_name)); - - o->SetObjectData(&od); - - c->Message(0, "Object %u now being rendered with model '%s'", id, od.object_name); - } - else - { - id = 0; // Setting ID to 0 will signify invalid input - } - break; - case 't': - if (strcmp(sep->arg[3], "type") == 0) - { - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) - { - c->Message(0, "ERROR: Invalid type number"); - - return; - } - - od.object_type = atoi(sep->arg[4]); - - switch (od.object_type) - { - case 0: - // Convert Static Object to temporary changeable type - od.object_type = TempStaticType; - c->Message(0, "Note: Static Object will still act like tradeskill object and will not reflect size, solidtype, or incline settings until committed to the database with '#object Save', after which it will be unchangeable until it is unlocked again with '#object Edit'."); - break; - case OT_DROPPEDITEM: - c->Message(0, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped items, which are not supported with #object. See the 'ground_spawns' table in the database."); - - return; - break; - default: - c->Message(0, "Object %u changed to Tradeskill Object Type %u", id, od.object_type); - break; - } - - o->SetType(od.object_type); - } - else - { - id = 0; // Setting ID to 0 will signify invalid input - } - break; - case 's': - if (strcmp(sep->arg[3], "size") == 0) - { - if (od.object_type != TempStaticType) - { - c->Message(0, "ERROR: Object %u is not a Static Object and does not support the Size property", id); - - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) - { - c->Message(0, "ERROR: Invalid size specified. Please enter a number."); - - return; - } - - od.unknown008 = atoi(sep->arg[4]); - o->SetObjectData(&od); - - if (od.unknown008 == 0) // 0 == unspecified == 100% - { - od.unknown008 = 100; - } - - c->Message(0, "Static Object %u set to %u%% size. Size will take effect when you commit to the database with '#object Save', after which the object will be unchangeable until you unlock it again with '#object Edit' and zone out and back in.", id, od.unknown008); - } - else if (strcmp(sep->arg[3], "solidtype") == 0) - { - if (od.object_type != TempStaticType) - { - c->Message(0, "ERROR: Object %u is not a Static Object and does not support the SolidType property", id); - - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) - { - c->Message(0, "ERROR: Invalid solidtype specified. Please enter a number."); - - return; - } - - od.unknown010 = atoi(sep->arg[4]); - o->SetObjectData(&od); - - c->Message(0, "Static Object %u set to SolidType %u. Change will take effect when you commit to the database with '#object Save'. Support for this property is on a per-model basis, mostly seen in smaller objects such as chests and tables.", id, od.unknown010); - } - else - { - id = 0; // Setting ID to 0 will signify invalid input - } - break; - case 'i': - if (strcmp(sep->arg[3], "icon") == 0) - { - if ((od.object_type < 2) || (od.object_type == TempStaticType)) - { - c->Message(0, "ERROR: Object %u is not a Tradeskill Object and does not support the Icon property", id); - - return; - } - - if ((icon = atoi(sep->arg[4])) == 0) - { - c->Message(0, "ERROR: Invalid Icon specified. Please enter an icon number."); - - return; - } - - o->SetIcon(icon); - - c->Message(0, "Tradeskill Object %u icon set to %u", id, icon); - } - else if (strcmp(sep->arg[3], "incline") == 0) - { - if (od.object_type != TempStaticType) - { - c->Message(0, "ERROR: Object %u is not a Static Object and does not support the Incline property", id); - - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) - { - c->Message(0, "ERROR: Invalid Incline specified. Please enter a number. Normal range is 0-512."); - - return; - } - - od.unknown020 = atoi(sep->arg[4]); - o->SetObjectData(&od); - - c->Message(0, "Static Object %u set to %u incline. Incline will take effect when you commit to the database with '#object Save', after which the object will be unchangeable until you unlock it again with '#object Edit' and zone out and back in.", id, od.unknown020); - } - else - { - id = 0; // Setting ID to 0 will signify invalid input - } - break; - default: - id = 0; // Setting ID to 0 will signify invalid input - break; - } - - if (id == 0) - { - c->Message(0, "ERROR: Unrecognized property name: %s", sep->arg[3]); - - return; - } - - // Repop object to have it reflect the change. - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - break; - case 'm': // Move - if ((sep->argnum < 2) || // Not enough arguments - ((id = atoi(sep->arg[2])) == 0) || // ID not specified - (((sep->arg[3][0] < '0') || (sep->arg[3][0] > '9')) && - ((sep->arg[3][0] & 0xDF) != 'T') && - (sep->arg[3][0] != '-') && (sep->arg[3][0] != '.'))) // Location argument not specified correctly - { - c->Message(0, "Usage: #object Move (ObjectID) ToMe|(x y z [h])"); - - return; - } - - if (!(o = entity_list.FindObject(id))) - { - len = snprintf(query, sizeof(query), "SELECT zoneid, version, type FROM object WHERE id=%u", id); - - if ((!database.RunQuery(query, len, errbuf, &result)) || ((row = mysql_fetch_row(result)) == 0)) - { - if (result) - { - mysql_free_result(result); - } - - c->Message(0, "ERROR: Object %u not found", id); - - return; - } - - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - - mysql_free_result(result); - - if (od.zone_id != zone->GetZoneID()) - { - c->Message(0, "ERROR: Object %u is not in this zone", id); - - return; - } - - if (od.zone_instance != zone->GetInstanceVersion()) - { - c->Message(0, "ERROR: Object %u is not in this instance version", id); - - return; - } - - switch (od.object_type) - { - case 0: - c->Message(0, "ERROR: Object %u is not yet unlocked for editing. Use '#object Edit' then zone out and back in to move it.", id); - - return; - break; - case TempStaticType: - c->Message(0, "ERROR: Object %u has been unlocked for editing, but you must zone out and back in before your client sees the change and will allow you to move it.", id); - - return; - break; - case 1: - c->Message(0, "ERROR: Object %u is a temporary spawned object and cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); - - return; - break; - default: - c->Message(0, "ERROR: Object %u not located in zone.", id); - - return; - break; - } - } - - if ((sep->arg[3][0] & 0xDF) == 'T') // Move To Me - { - od.x = c->GetX(); - od.y = c->GetY(); - od.z = c->GetZ() - (c->GetSize() * 0.625f); // Compensate for #loc bumping up Z coordinate by 62.5% of character's size. - - o->SetHeading(c->GetHeading() * 2.0f); // Compensate for GetHeading() returning half of actual - - // Bump player back to avoid getting stuck inside object - - // GetHeading() returns half of the actual heading, for some reason - x2 = 10.0f * sin(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); - y2 = 10.0f * cos(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); - c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading() * 2.0f); - } - else // Move to x, y, z [h] - { - od.x = atof(sep->arg[3]); - if (sep->argnum > 3) - { - od.y = atof(sep->arg[4]); - } - else - { - o->GetLocation(nullptr, &od.y, nullptr); - } - - if (sep->argnum > 4) - { - od.z = atof(sep->arg[5]); - } - else - { - o->GetLocation(nullptr, nullptr, &od.z); - } - - if (sep->argnum > 5) - { - o->SetHeading(atof(sep->arg[6])); - } - } - - o->SetLocation(od.x, od.y, od.z); - - // Despawn and respawn object to reflect change - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - break; - case 'r': // Rotate - // Insufficient or invalid arguments - if ((sep->argnum < 3) || ((id = atoi(sep->arg[2])) == 0)) - { - c->Message(0, "Usage: #object Rotate (ObjectID) (Heading, 0-512)"); - - return; - } - - if ((o = entity_list.FindObject(id)) == nullptr) - { - c->Message(0, "ERROR: Object %u not found in zone, or is a static object not yet unlocked with '#object Edit' for editing.", id); - - return; - } - - o->SetHeading(atof(sep->arg[3])); - - // Despawn and respawn object to reflect change - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - break; - case 's': // Save - // Insufficient or invalid arguments - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) - { - c->Message(0, "Usage: #object Save (ObjectID)"); - - return; - } - - o = entity_list.FindObject(id); - - sprintf(query, "SELECT zoneid, version, type FROM object WHERE id=%u", id); - - od.zone_id = 0; - od.zone_instance = 0; - od.object_type = 0; - - // If this ID isn't in the database yet, it's a new object - bNewObject = true; - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - if (row = mysql_fetch_row(result)) - { - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - - // ID already in database. Not a new object. - bNewObject = false; - } - - mysql_free_result(result); - } - - if (!o) - { - // Object not found in zone. Can't save an object we can't see. - - if (bNewObject) - { - c->Message(0, "ERROR: Object %u not found", id); - - return; - } - - if (od.zone_id != zone->GetZoneID()) - { - c->Message(0, "ERROR: Wrong Object ID. %u is not part of this zone.", id); - - return; - } - - if (od.zone_instance != zone->GetInstanceVersion()) - { - c->Message(0, "ERROR: Wrong Object ID. %u is not part of this instance version.", id); - - return; - } - - if (od.object_type == 0) - { - c->Message(0, "ERROR: Static Object %u has already been committed. Use '#object Edit %u' and zone out and back in to make changes.", id, id); - - return; - } - - if (od.object_type == 1) - { - c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which is not supported with #object. See the 'ground_spawns' table in the database.", id); - - return; - } - - c->Message(0, "ERROR: Object %u not found.", id); - - return; - } - - if ((od.zone_id > 0) && (od.zone_id != zone->GetZoneID())) - { - // Oops! Another GM already saved an object with our id from another zone. - // We'll have to get a new one. - - id = 0; - } - - if ((id > 0) && (od.zone_instance != zone->GetInstanceVersion())) - { - // Oops! Another GM already saved an object with our id from another instance. - // We'll have to get a new one. - - id = 0; - } - - // If we're asking for a new ID, it's a new object. - bNewObject |= (id == 0); - - o->GetObjectData(&od); - od.object_type = o->GetType(); - icon = o->GetIcon(); - - // We're committing to the database now. Return temporary object type to actual. - if (od.object_type == TempStaticType) - { - od.object_type = 0; - } - - if (bNewObject) - { - if (id == 0) - { - len = snprintf(query, sizeof(query), - "INSERT INTO object (zoneid, version, xpos, ypos, zpos, heading, objectname, type, icon, unknown08, unknown10, unknown20)" - " VALUES (%u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", - zone->GetZoneID(), zone->GetInstanceVersion(), - od.x, od.y, od.z, od.heading, - od.object_name, od.object_type, icon, - od.unknown008, od.unknown010, od.unknown020); - } - else - { - len = snprintf(query, sizeof(query), - "INSERT INTO object (id, zoneid, version, xpos, ypos, zpos, heading, objectname, type, icon, unknown08, unknown10, unknown20)" - " VALUES (%u, %u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", - id, zone->GetZoneID(), zone->GetInstanceVersion(), - od.x, od.y, od.z, od.heading, - od.object_name, od.object_type, icon, - od.unknown008, od.unknown010, od.unknown020); - } - } - else - { - len = snprintf(query, sizeof(query), - "UPDATE object SET " - " zoneid=%u, version=%u," - " xpos=%.1f, ypos=%.1f, zpos=%.1f, heading=%.1f," - " objectname='%s', type=%u, icon=%u," - " unknown08=%u, unknown10=%u, unknown20=%u" - " WHERE ID=%u", - zone->GetZoneID(), zone->GetInstanceVersion(), - od.x, od.y, od.z, od.heading, - od.object_name, od.object_type, icon, - od.unknown008, od.unknown010, od.unknown020, - id); - } - - if (!database.RunQuery(query, len, errbuf, 0, &col, &newid)) - { - col = 0; - } - - if (col == 0) - { - if (errbuf[0] == '\0') - { - // No change made, but no error message given - c->Message(0, "Database Error: Could not save change to Object %u", id); - } - else - { - c->Message(0, "Database Error: %s", errbuf); - } - - return; - } - else - { - if (bNewObject) - { - if (newid == id) - { - c->Message(0, "Saved new Object %u to database", id); - } - else - { - c->Message(0, "Saved Object. NOTE: Database returned a new ID number for object: %u", newid); - id = newid; - } - } - else - { - c->Message(0, "Saved changes to Object %u", id); - - newid = id; - } - } - - if (od.object_type == 0) - { - // Static Object - Respawn as nonfunctional door - - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - entity_list.RemoveObject(o->GetID()); - - memset(&door, 0, sizeof(door)); - - strn0cpy(door.zone_name, zone->GetShortName(), sizeof(door.zone_name)); - - door.db_id = 1000000000 + id; // Out of range of normal use for doors.id - door.door_id = -1; // Client doesn't care if these are all the same door_id - door.pos_x = od.x; // xpos - door.pos_y = od.y; // ypos - door.pos_z = od.z; // zpos - door.heading = od.heading; // heading - - strn0cpy(door.door_name, od.object_name, sizeof(door.door_name)); // objectname - - // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. - len = strlen(door.door_name); - if ((len > 9) && (memcmp(&door.door_name[len - 9], "_ACTORDEF", 10) == 0)) - { - door.door_name[len - 9] = '\0'; - } - - memcpy(door.dest_zone, "NONE", 5); - - if ((door.size = od.unknown008) == 0) // unknown08 = optional size percentage - { - door.size = 100; - } - - switch (door.opentype = od.unknown010) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) - { - case 0: - door.opentype = 31; - break; - case 1: - door.opentype = 9; - break; - } - - door.incline = od.unknown020; // unknown20 = optional incline value - door.client_version_mask = 0xFFFFFFFF; - - doors = new Doors(&door); - entity_list.AddDoor(doors); - - app = new EQApplicationPacket(OP_SpawnDoor, sizeof(Door_Struct)); - ds = (Door_Struct*)app->pBuffer; - - memset(ds, 0, sizeof(Door_Struct)); - memcpy(ds->name, door.door_name, 32); - ds->xPos = door.pos_x; - ds->yPos = door.pos_y; - ds->zPos = door.pos_z; - ds->heading = door.heading; - ds->incline = door.incline; - ds->size = door.size; - ds->doorId = door.door_id; - ds->opentype = door.opentype; - ds->unknown0052[9] = 1; // *ptr-1 and *ptr-3 from EntityList::MakeDoorSpawnPacket() - ds->unknown0052[11] = 1; - - entity_list.QueueClients(0, app); - safe_delete(app); - - c->Message(0, "NOTE: Object %u is now a static object, and is unchangeable. To make future changes, use '#object Edit' to convert it to a changeable form, then zone out and back in.", id); - } - break; - case 'c': // Copy - // Insufficient or invalid arguments - if ((sep->argnum < 3) || (((sep->arg[2][0] & 0xDF) != 'A') && ((sep->arg[2][0] < '0') || (sep->arg[2][0] > '9')))) - { - c->Message(0, "Usage: #object Copy All|(ObjectID) (InstanceVersion)"); - c->Message(0, "- Note: Only objects saved in the database can be copied to another instance."); - - return; - } - - od.zone_instance = atoi(sep->arg[3]); - - if (od.zone_instance == zone->GetInstanceVersion()) - { - c->Message(0, "ERROR: Source and destination instance versions are the same."); - - return; - } - - if ((sep->arg[2][0] & 0xDF) == 'A') - { - // Copy All - - len = snprintf(query, sizeof(query), - "INSERT INTO object (zoneid, version, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20)" - " SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20" - " FROM object" - " WHERE (zoneid=%u) AND (version=%u)", - od.zone_instance, zone->GetZoneID(), zone->GetInstanceVersion()); - - if (database.RunQuery(query, len, errbuf, 0, &col)) - { - c->Message(0, "Copied %u object%s into instance version %u", col, (col == 1) ? "" : "s", od.zone_instance); - } - else - { - if (errbuf[0] == '\0') - { - c->Message(0, "Database Error: No objects were copied into instance version %u", od.zone_instance); - } - else - { - c->Message(0, "Database Error: %s", errbuf); - } - } - } - else - { - // Copy ObjectID - id = atoi(sep->arg[2]); - - len = snprintf(query, sizeof(query), - "INSERT INTO object (zoneid, version, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20)" - " SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20" - " FROM object" - " WHERE (id=%u) AND (zoneid=%u) AND (version=%u)", - od.zone_instance, id, zone->GetZoneID(), zone->GetInstanceVersion()); - - if ((database.RunQuery(query, len, errbuf, 0, &col)) && (col > 0)) - { - c->Message(0, "Copied Object %u into instance version %u", id, od.zone_instance); - } - else - { - // Couldn't copy the object. - - if (errbuf[0] == '\0') - { - // No database error returned. See if we can figure out why. - - len = snprintf(query, sizeof(query), "SELECT zoneid, version FROM object WHERE id=%u", id); - - if (database.RunQuery(query, len, errbuf, &result)) - { - if (row = mysql_fetch_row(result)) - { - // Wrong ZoneID? - if (atoi(row[0]) != zone->GetZoneID()) - { - mysql_free_result(result); - - c->Message(0, "ERROR: Object %u is not part of this zone.", id); - - return; - } - - // Wrong Instance Version? - if (atoi(row[1]) != zone->GetInstanceVersion()) - { - mysql_free_result(result); - - c->Message(0, "ERROR: Object %u is not part of this instance version.", id); - - return; - } - - // Well, NO clue at this point. Just let 'em know something screwed up. - mysql_free_result(result); - - c->Message(0, "ERROR: Unknown database error copying Object %u to instance version %u", id, od.zone_instance); - - return; - } - - mysql_free_result(result); - } - - // Typo? - c->Message(0, "ERROR: Object %u not found", id); - } - else - { - c->Message(0, "Database Error: %s", errbuf); - } - } - } - break; - case 'd': // Delete - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) <= 0)) - { - c->Message(0, "Usage: #object Delete (ObjectID) -- NOTE: Object deletions are permanent and cannot be undone!"); - - return; - } - - o = entity_list.FindObject(id); - - if (o) - { - // Object found in zone. - - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(nullptr, app); - - entity_list.RemoveObject(o->GetID()); - - // Verifying ZoneID and Version in case someone else ended up adding an object with our ID - // from a different zone/version. Don't want to delete someone else's work. - sprintf(query, "DELETE FROM object WHERE (id=%u) AND (zoneid=%u) AND (version=%u) LIMIT 1", id, zone->GetZoneID(), zone->GetInstanceVersion()); - database.RunQuery(query, strlen(query)); - - c->Message(0, "Object %u deleted", id); - } - else - { - // Object not found in zone. - - sprintf(query, "SELECT type FROM object WHERE (id=%u) AND (zoneid=%u) AND (version=%u) LIMIT 1", id, zone->GetZoneID(), zone->GetInstanceVersion()); - - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - if (row = mysql_fetch_row(result)) - { - switch (atoi(row[0])) - { - case 0: // Static Object - mysql_free_result(result); - - sprintf(query, "DELETE FROM object WHERE (id=%u) AND (zoneid=%u) AND (version=%u) LIMIT 1", id, zone->GetZoneID(), zone->GetInstanceVersion()); - database.RunQuery(query, strlen(query)); - - c->Message(0, "Object %u deleted. NOTE: This static object will remain for anyone currently in the zone until they next zone out and in.", id); - - mysql_free_result(result); - - return; - break; - case 1: // Temporary Spawn - c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which is not supported with #object. See the 'ground_spawns' table in the database.", id); - - mysql_free_result(result); - - return; - break; - } - } - - mysql_free_result(result); - } - - c->Message(0, "ERROR: Object %u not found in this zone or instance!", id); - } - break; - case 'u': // Undo - Reload object from database to undo changes - // Insufficient or invalid arguments - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) - { - c->Message(0, "Usage: #object Undo (ObjectID) -- Reload object from database, undoing any changes you have made"); - - return; - } - - o = entity_list.FindObject(id); - - if (!o) - { - c->Message(0, "ERROR: Object %u not found in zone in a manipulable form. No changes to undo.", id); - - return; - } - - if (o->GetType() == OT_DROPPEDITEM) - { - c->Message(0, "ERROR: Object %u is a temporary spawned item and cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); - - return; - } - - // Despawn current item for reloading from database - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - entity_list.RemoveObject(o->GetID()); - safe_delete(app); - - len = snprintf(query, sizeof(query), - "SELECT xpos, ypos, zpos, heading, objectname, type, icon, unknown08, unknown10, unknown20" - " FROM object WHERE id=%u", id); - - if ((!database.RunQuery(query, len, errbuf, &result)) || ((row = mysql_fetch_row(result)) == 0)) - { - if (result) - { - mysql_free_result(result); - } - - if (errbuf[0] == '\0') - { - c->Message(0, "Database Error: Could not retrieve Object %u from object table.", id); - - return; - } - - c->Message(0, "Database Error: %s", errbuf); - - return; - } - - memset(&od, 0, sizeof(od)); - - col = 0; - od.x = atof(row[col++]); - od.y = atof(row[col++]); - od.z = atof(row[col++]); - od.heading = atof(row[col++]); - strn0cpy(od.object_name, row[col++], sizeof(od.object_name)); - od.object_type = atoi(row[col++]); - icon = atoi(row[col++]); - od.unknown008 = atoi(row[col++]); - od.unknown010 = atoi(row[col++]); - od.unknown020 = atoi(row[col++]); - - if (od.object_type == 0) - { - od.object_type = TempStaticType; - } - - o = new Object(id, od.object_type, icon, od, nullptr); - entity_list.AddObject(o, true); - - c->Message(0, "Object %u reloaded from database.", id); - break; - default: // Unrecognized command - c->Message(0, usage_string); - break; - } + query = StringFormat("UPDATE object SET type = %u WHERE id = %u", staticType, id); + + database.QueryDatabase(query); + + c->Message(0, "Static Object %u unlocked for editing. You must zone out and back in to make your changes, then commit them with '#object Save'.", id); + if (sep->argnum >= 4) + c->Message(0, "NOTE: The change you specified has not been applied, since the static object had not been unlocked for editing yet."); + return; + + case OT_DROPPEDITEM: + c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); + return; + + case staticType: + c->Message(0, "ERROR: Object %u has been unlocked for editing, but you must zone out and back in for your client to refresh its object table before you can make changes to it.", id); + return; + + default: + // Unknown error preventing us from seeing the object in the zone. + c->Message(0, "ERROR: Unknown problem attempting to manipulate object %u", id); + return; + } + } + + // If we're here, we have a manipulable object ready for changes. + strlwr(sep->arg[3]); // Case insensitive PropertyName + strupr(sep->arg[4]); // In case it's model name, which should always be upper-case + + // Read current object info for reference + icon = o->GetIcon(); + o->GetObjectData(&od); + + // We'll be a little more picky with property names, to prevent errors. Check against the whole word. + if (strcmp(sep->arg[3], "model") == 0) { + + if ((sep->arg[4][0] < 'A') || (sep->arg[4][0] > 'Z')) { + c->Message(0, "ERROR: Model names must begin with a letter."); + return; + } + + strn0cpy(od.object_name, sep->arg[4], sizeof(od.object_name)); + + o->SetObjectData(&od); + + c->Message(0, "Object %u now being rendered with model '%s'", id, od.object_name); + } + else if (strcmp(sep->arg[3], "type") == 0) { + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(0, "ERROR: Invalid type number"); + return; + } + + od.object_type = atoi(sep->arg[4]); + + switch (od.object_type) { + case 0: + // Convert Static Object to temporary changeable type + od.object_type = staticType; + c->Message(0, "Note: Static Object will still act like tradeskill object and will not reflect size, solidtype, or incline settings until committed to the database with '#object Save', after which it will be unchangeable until it is unlocked again with '#object Edit'."); + break; + + case OT_DROPPEDITEM: + c->Message(0, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped items, which are not supported with #object. See the 'ground_spawns' table in the database."); + return; + + default: + c->Message(0, "Object %u changed to Tradeskill Object Type %u", id, od.object_type); + break; + } + + o->SetType(od.object_type); + } + else if (strcmp(sep->arg[3], "size") == 0) { + if (od.object_type != staticType) { + c->Message(0, "ERROR: Object %u is not a Static Object and does not support the Size property", id); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(0, "ERROR: Invalid size specified. Please enter a number."); + return; + } + + od.unknown008 = atoi(sep->arg[4]); + o->SetObjectData(&od); + + if (od.unknown008 == 0) // 0 == unspecified == 100% + od.unknown008 = 100; + + c->Message(0, "Static Object %u set to %u%% size. Size will take effect when you commit to the database with '#object Save', after which the object will be unchangeable until you unlock it again with '#object Edit' and zone out and back in.", id, od.unknown008); + } + else if (strcmp(sep->arg[3], "solidtype") == 0) { + + if (od.object_type != staticType) { + c->Message(0, "ERROR: Object %u is not a Static Object and does not support the SolidType property", id); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(0, "ERROR: Invalid solidtype specified. Please enter a number."); + return; + } + + od.unknown010 = atoi(sep->arg[4]); + o->SetObjectData(&od); + + c->Message(0, "Static Object %u set to SolidType %u. Change will take effect when you commit to the database with '#object Save'. Support for this property is on a per-model basis, mostly seen in smaller objects such as chests and tables.", id, od.unknown010); + } + else if (strcmp(sep->arg[3], "icon") == 0) { + + if ((od.object_type < 2) || (od.object_type == staticType)) { + c->Message(0, "ERROR: Object %u is not a Tradeskill Object and does not support the Icon property", id); + return; + } + + if ((icon = atoi(sep->arg[4])) == 0) { + c->Message(0, "ERROR: Invalid Icon specified. Please enter an icon number."); + return; + } + + o->SetIcon(icon); + c->Message(0, "Tradeskill Object %u icon set to %u", id, icon); + } + else if (strcmp(sep->arg[3], "incline") == 0) { + if (od.object_type != staticType) { + c->Message(0, "ERROR: Object %u is not a Static Object and does not support the Incline property", id); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(0, "ERROR: Invalid Incline specified. Please enter a number. Normal range is 0-512."); + return; + } + + od.unknown020 = atoi(sep->arg[4]); + o->SetObjectData(&od); + + c->Message(0, "Static Object %u set to %u incline. Incline will take effect when you commit to the database with '#object Save', after which the object will be unchangeable until you unlock it again with '#object Edit' and zone out and back in.", id, od.unknown020); + } + else { + c->Message(0, "ERROR: Unrecognized property name: %s", sep->arg[3]); + return; + } + + // Repop object to have it reflect the change. + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "move") == 0) { + + if ((sep->argnum < 2) || // Not enough arguments + ((id = atoi(sep->arg[2])) == 0) || // ID not specified + (((sep->arg[3][0] < '0') || (sep->arg[3][0] > '9')) && + ((sep->arg[3][0] & 0xDF) != 'T') && + (sep->arg[3][0] != '-') && (sep->arg[3][0] != '.'))) { // Location argument not specified correctly + c->Message(0, "Usage: #object Move (ObjectID) ToMe|(x y z [h])"); + return; + } + + if (!(o = entity_list.FindObject(id))) { + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(0, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + + + if (od.zone_id != zone->GetZoneID()) { + c->Message(0, "ERROR: Object %u is not in this zone", id); + return; + } + + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(0, "ERROR: Object %u is not in this instance version", id); + return; + } + + switch (od.object_type) { + case 0: + c->Message(0, "ERROR: Object %u is not yet unlocked for editing. Use '#object Edit' then zone out and back in to move it.", id); + return; + + case staticType: + c->Message(0, "ERROR: Object %u has been unlocked for editing, but you must zone out and back in before your client sees the change and will allow you to move it.", id); + return; + + case 1: + c->Message(0, "ERROR: Object %u is a temporary spawned object and cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); + return; + + default: + c->Message(0, "ERROR: Object %u not located in zone.", id); + return; + } + } + + // Move To Me + if ((sep->arg[3][0] & 0xDF) == 'T') { + od.x = c->GetX(); + od.y = c->GetY(); + od.z = c->GetZ() - (c->GetSize() * 0.625f); // Compensate for #loc bumping up Z coordinate by 62.5% of character's size. + + o->SetHeading(c->GetHeading() * 2.0f); // Compensate for GetHeading() returning half of actual + + // Bump player back to avoid getting stuck inside object + + // GetHeading() returns half of the actual heading, for some reason + x2 = 10.0f * sin(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); + y2 = 10.0f * cos(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); + c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading() * 2.0f); + } // Move to x, y, z [h] + else { + od.x = atof(sep->arg[3]); + if (sep->argnum > 3) + od.y = atof(sep->arg[4]); + else + o->GetLocation(nullptr, &od.y, nullptr); + + if (sep->argnum > 4) + od.z = atof(sep->arg[5]); + else + o->GetLocation(nullptr, nullptr, &od.z); + + if (sep->argnum > 5) + o->SetHeading(atof(sep->arg[6])); + } + + o->SetLocation(od.x, od.y, od.z); + + // Despawn and respawn object to reflect change + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "rotate") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message(0, "Usage: #object Rotate (ObjectID) (Heading, 0-512)"); + return; + } + + if ((o = entity_list.FindObject(id)) == nullptr) { + c->Message(0, "ERROR: Object %u not found in zone, or is a static object not yet unlocked with '#object Edit' for editing.", id); + return; + } + + o->SetHeading(atof(sep->arg[3])); + + // Despawn and respawn object to reflect change + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "save") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message(0, "Usage: #object Save (ObjectID)"); + return; + } + + o = entity_list.FindObject(id); + + od.zone_id = 0; + od.zone_instance = 0; + od.object_type = 0; + + // If this ID isn't in the database yet, it's a new object + bNewObject = true; + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + + // ID already in database. Not a new object. + bNewObject = false; + } + + if (!o) { + // Object not found in zone. Can't save an object we can't see. + + if (bNewObject) { + c->Message(0, "ERROR: Object %u not found", id); + return; + } + + if (od.zone_id != zone->GetZoneID()) { + c->Message(0, "ERROR: Wrong Object ID. %u is not part of this zone.", id); + return; + } + + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(0, "ERROR: Wrong Object ID. %u is not part of this instance version.", id); + return; + } + + if (od.object_type == 0) { + c->Message(0, "ERROR: Static Object %u has already been committed. Use '#object Edit %u' and zone out and back in to make changes.", id, id); + return; + } + + if (od.object_type == 1) { + c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which is not supported with #object. See the 'ground_spawns' table in the database.", id); + return; + } + + c->Message(0, "ERROR: Object %u not found.", id); + return; + } + + // Oops! Another GM already saved an object with our id from another zone. + // We'll have to get a new one. + if ((od.zone_id > 0) && (od.zone_id != zone->GetZoneID())) + id = 0; + + // Oops! Another GM already saved an object with our id from another instance. + // We'll have to get a new one. + if ((id > 0) && (od.zone_instance != zone->GetInstanceVersion())) + id = 0; + + // If we're asking for a new ID, it's a new object. + bNewObject |= (id == 0); + + o->GetObjectData(&od); + od.object_type = o->GetType(); + icon = o->GetIcon(); + + // We're committing to the database now. Return temporary object type to actual. + if (od.object_type == staticType) + od.object_type = 0; + + if (!bNewObject) + query = StringFormat("UPDATE object SET zoneid = %u, version = %u, " + "xpos = %.1f, ypos=%.1f, zpos=%.1f, heading=%.1f, " + "objectname = '%s', type = %u, icon = %u, " + "unknown08 = %u, unknown10 = %u, unknown20 = %u " + "WHERE ID = %u", + zone->GetZoneID(), zone->GetInstanceVersion(), + od.x, od.y, od.z, od.heading, + od.object_name, od.object_type, icon, + od.unknown008, od.unknown010, od.unknown020, id); + else if (id == 0) + query = StringFormat("INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, objectname, " + "type, icon, unknown08, unknown10, unknown20) " + "VALUES (%u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", + zone->GetZoneID(), zone->GetInstanceVersion(), + od.x, od.y, od.z, od.heading, + od.object_name, od.object_type, icon, + od.unknown008, od.unknown010, od.unknown020); + else + query = StringFormat("INSERT INTO object " + "(id, zoneid, version, xpos, ypos, zpos, heading, objectname, " + "type, icon, unknown08, unknown10, unknown20) " + "VALUES (%u, %u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", + id, zone->GetZoneID(), zone->GetInstanceVersion(), + od.x, od.y, od.z, od.heading, + od.object_name, od.object_type, icon, + od.unknown008, od.unknown010, od.unknown020); + + results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + if (results.RowsAffected() == 0) { + // No change made, but no error message given + c->Message(0, "Database Error: Could not save change to Object %u", id); + return; + } + + if (bNewObject) { + if (newid == results.LastInsertedID()) { + c->Message(0, "Saved new Object %u to database", id); + return; + } + + c->Message(0, "Saved Object. NOTE: Database returned a new ID number for object: %u", newid); + id = newid; + return; + } + + c->Message(0, "Saved changes to Object %u", id); + newid = id; + + if (od.object_type == 0) { + // Static Object - Respawn as nonfunctional door + + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + entity_list.RemoveObject(o->GetID()); + + memset(&door, 0, sizeof(door)); + + strn0cpy(door.zone_name, zone->GetShortName(), sizeof(door.zone_name)); + + door.db_id = 1000000000 + id; // Out of range of normal use for doors.id + door.door_id = -1; // Client doesn't care if these are all the same door_id + door.pos_x = od.x; // xpos + door.pos_y = od.y; // ypos + door.pos_z = od.z; // zpos + door.heading = od.heading; // heading + + strn0cpy(door.door_name, od.object_name, sizeof(door.door_name)); // objectname + + // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. + uint32 len = strlen(door.door_name); + if ((len > 9) && (memcmp(&door.door_name[len - 9], "_ACTORDEF", 10) == 0)) + door.door_name[len - 9] = '\0'; + + memcpy(door.dest_zone, "NONE", 5); + + if ((door.size = od.unknown008) == 0) // unknown08 = optional size percentage + door.size = 100; + + switch (door.opentype = od.unknown010) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) + { + case 0: + door.opentype = 31; + break; + + case 1: + door.opentype = 9; + break; + + } + + door.incline = od.unknown020; // unknown20 = optional incline value + door.client_version_mask = 0xFFFFFFFF; + + doors = new Doors(&door); + entity_list.AddDoor(doors); + + app = new EQApplicationPacket(OP_SpawnDoor, sizeof(Door_Struct)); + ds = (Door_Struct*)app->pBuffer; + + memset(ds, 0, sizeof(Door_Struct)); + memcpy(ds->name, door.door_name, 32); + ds->xPos = door.pos_x; + ds->yPos = door.pos_y; + ds->zPos = door.pos_z; + ds->heading = door.heading; + ds->incline = door.incline; + ds->size = door.size; + ds->doorId = door.door_id; + ds->opentype = door.opentype; + ds->unknown0052[9] = 1; // *ptr-1 and *ptr-3 from EntityList::MakeDoorSpawnPacket() + ds->unknown0052[11] = 1; + + entity_list.QueueClients(0, app); + safe_delete(app); + + c->Message(0, "NOTE: Object %u is now a static object, and is unchangeable. To make future changes, use '#object Edit' to convert it to a changeable form, then zone out and back in.", id); + } + return; + } + + if (strcasecmp(sep->arg[1], "copy") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || (((sep->arg[2][0] & 0xDF) != 'A') && ((sep->arg[2][0] < '0') || (sep->arg[2][0] > '9')))) { + c->Message(0, "Usage: #object Copy All|(ObjectID) (InstanceVersion)"); + c->Message(0, "- Note: Only objects saved in the database can be copied to another instance."); + return; + } + + od.zone_instance = atoi(sep->arg[3]); + + if (od.zone_instance == zone->GetInstanceVersion()) { + c->Message(0, "ERROR: Source and destination instance versions are the same."); + return; + } + + if ((sep->arg[2][0] & 0xDF) == 'A') { + // Copy All + + std::string query = StringFormat("INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20) " + "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u) AND version = %u", + od.zone_instance, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + c->Message(0, "Copied %u object%s into instance version %u", results.RowCount(), (results.RowCount() == 1) ? "" : "s", od.zone_instance); + return; + } + + id = atoi(sep->arg[2]); + + std::string query = StringFormat("INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20) " + "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE id = %u AND zoneid = %u AND version = %u", + od.zone_instance, id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowsAffected() > 0) { + c->Message(0, "Copied Object %u into instance version %u", id, od.zone_instance); + return; + } + + // Couldn't copy the object. + + if (results.ErrorMessage().c_str() != '\0') { + c->Message(0, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + // No database error returned. See if we can figure out why. + + query = StringFormat("SELECT zoneid, version FROM object WHERE id = %u", id); + results = database.QueryDatabase(query); + if (!results.Success()) + return; + + if (results.RowCount() == 0) { + c->Message(0, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + // Wrong ZoneID? + if (atoi(row[0]) != zone->GetZoneID()) { + c->Message(0, "ERROR: Object %u is not part of this zone.", id); + return; + } + + // Wrong Instance Version? + if (atoi(row[1]) != zone->GetInstanceVersion()) { + c->Message(0, "ERROR: Object %u is not part of this instance version.", id); + return; + } + + // Well, NO clue at this point. Just let 'em know something screwed up. + c->Message(0, "ERROR: Unknown database error copying Object %u to instance version %u", id, od.zone_instance); + return; + } + + if (strcasecmp(sep->arg[1], "delete") == 0) { + + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) <= 0)) { + c->Message(0, "Usage: #object Delete (ObjectID) -- NOTE: Object deletions are permanent and cannot be undone!"); + return; + } + + o = entity_list.FindObject(id); + + if (o) { + // Object found in zone. + + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(nullptr, app); + + entity_list.RemoveObject(o->GetID()); + + // Verifying ZoneID and Version in case someone else ended up adding an object with our ID + // from a different zone/version. Don't want to delete someone else's work. + std::string query = StringFormat("DELETE FROM object " + "WHERE id = %u AND zoneid = %u " + "AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + + c->Message(0, "Object %u deleted", id); + return; + } + + + // Object not found in zone. + std::string query = StringFormat("SELECT type FROM object " + "WHERE id = %u AND zoneid = %u " + "AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + if (results.RowCount() == 0) { + c->Message(0, "ERROR: Object %u not found in this zone or instance!", id); + return; + } + + auto row = results.begin(); + + switch (atoi(row[0])) { + case 0: // Static Object + query = StringFormat("DELETE FROM object WHERE id = %u " + "AND zoneid = %u AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + results = database.QueryDatabase(query); + + c->Message(0, "Object %u deleted. NOTE: This static object will remain for anyone currently in the zone until they next zone out and in.", id); + return; + + case 1: // Temporary Spawn + c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which is not supported with #object. See the 'ground_spawns' table in the database.", id); + return; + + } + + return; + } + + if (strcasecmp(sep->arg[1], "undo") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message(0, "Usage: #object Undo (ObjectID) -- Reload object from database, undoing any changes you have made"); + return; + } + + o = entity_list.FindObject(id); + + if (!o) { + c->Message(0, "ERROR: Object %u not found in zone in a manipulable form. No changes to undo.", id); + return; + } + + if (o->GetType() == OT_DROPPEDITEM) { + c->Message(0, "ERROR: Object %u is a temporary spawned item and cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); + return; + } + + // Despawn current item for reloading from database + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + entity_list.RemoveObject(o->GetID()); + safe_delete(app); + + std::string query = StringFormat("SELECT xpos, ypos, zpos, " + "heading, objectname, type, icon, " + "unknown08, unknown10, unknown20 " + "FROM object WHERE id = %u", id); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(0, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + memset(&od, 0, sizeof(od)); + + auto row = results.begin(); + + od.x = atof(row[0]); + od.y = atof(row[1]); + od.z = atof(row[2]); + od.heading = atof(row[3]); + strn0cpy(od.object_name, row[4], sizeof(od.object_name)); + od.object_type = atoi(row[5]); + icon = atoi(row[6]); + od.unknown008 = atoi(row[7]); + od.unknown010 = atoi(row[8]); + od.unknown020 = atoi(row[9]); + + if (od.object_type == 0) + od.object_type = staticType; + + o = new Object(id, od.object_type, icon, od, nullptr); + entity_list.AddObject(o, true); + + c->Message(0, "Object %u reloaded from database.", id); + return; + } + + c->Message(0, usage_string); } void command_showspellslist(Client *c, const Seperator *sep) From f4203d06212abd9451cf8e2a339f1ab1521c8353 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Sat, 4 Oct 2014 12:08:50 -0700 Subject: [PATCH 273/368] command_mysql converted to QueryDatabase --- zone/command.cpp | 113 ++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index b03cb3d93..937174d1a 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -10838,7 +10838,9 @@ void command_mysql(Client *c, const Seperator *sep) { if(!sep->arg[1][0] || !sep->arg[2][0]) { c->Message(0, "Usage: #mysql query \"Query here\""); + return; } + if ( strcasecmp( sep->arg[1], "help" ) == 0 ) { c->Message(0, "MYSQL In-Game CLI Interface:"); c->Message(0, "Example: #mysql query \"Query goes here quoted\" -s -h"); @@ -10846,81 +10848,70 @@ void command_mysql(Client *c, const Seperator *sep) c->Message(0, "Example: #mysql query \"select * from table where name like \"#something#\""); c->Message(0, "-s - Spaces select entries apart"); c->Message(0, "-h - Colors every other select result"); + return; } + if ( strcasecmp( sep->arg[1], "query" ) == 0 ) { ///Parse switches here - int argnum = 3; bool Options = false, Optionh = false; bool Fail = false; + int argnum = 3; + bool optionS = false; + bool optionH = false; while(sep->arg[argnum] && strlen(sep->arg[argnum]) > 1){ switch(sep->arg[argnum][1]){ - case 's': Options = true; break; - case 'h': Optionh = true; break; - default: c->Message(15, "%s, there is no option '%c'", c->GetName(), sep->arg[argnum][1]); Fail = true; + case 's': optionS = true; break; + case 'h': optionH = true; break; + default: + c->Message(15, "%s, there is no option '%c'", c->GetName(), sep->arg[argnum][1]); + return; } ++argnum; } - if(!Fail) { - char errbuf[MYSQL_ERRMSG_SIZE]; - int HText = 0; - MYSQL_RES *result; - std::stringstream MsgText; - std::string QueryText(sep->arg[2]); - //swap # for % so like queries can work - std::replace(QueryText.begin(), QueryText.end(), '#', '%'); + int highlightTextIndex = 0; + std::string query(sep->arg[2]); + //swap # for % so like queries can work + std::replace(query.begin(), query.end(), '#', '%'); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Invalid query: '%s', '%s'", sep->arg[2], results.ErrorMessage().c_str()); + return; + } - if (database.RunQuery(QueryText.c_str(), QueryText.length(), errbuf, &result)) { - //Using sep->arg[2] again, replace # with %% so it doesn't screw up when sent through vsnprintf in Message - QueryText = sep->arg[2]; - int pos = QueryText.find('#'); - while(pos != std::string::npos) - { - QueryText.erase(pos,1); - QueryText.insert(pos, "%%"); - pos = QueryText.find('#'); - } + //Using sep->arg[2] again, replace # with %% so it doesn't screw up when sent through vsnprintf in Message + query = sep->arg[2]; + int pos = query.find('#'); + while(pos != std::string::npos) { + query.erase(pos,1); + query.insert(pos, "%%"); + pos = query.find('#'); + } + c->Message(15, "---Running query: '%s'", query.c_str()); - MsgText << "---Running query: '" << QueryText << "'"; - c->Message (15, MsgText.str().c_str()); - MsgText.str(""); + for (auto row = results.begin(); row != results.end(); ++row) { + std::stringstream lineText; + std::vector lineVec; + for(int i = 0; i < results.RowCount(); i++) { + //split lines that could overflow the buffer in Client::Message and get cut off + //This will crash MQ2 @ 4000 since their internal buffer is only 2048. + //Reducing it to 2000 fixes that but splits more results from tables with a lot of columns. + if(lineText.str().length() > 4000) { + lineVec.push_back(lineText.str()); + lineText.str(""); + } + lineText << results.FieldName(i) << ":" << "[" << (row[i] ? row[i] : "nullptr") << "] "; + } - MYSQL_ROW row; - while ((row = mysql_fetch_row(result))) { + lineVec.push_back(lineText.str()); - MYSQL_FIELD *fields = mysql_fetch_fields(result); - unsigned int num_fields = mysql_num_fields(result); - std::stringstream LineText; - std::vector LineVec; - for(int i = 0; i < num_fields; i++) { - //split lines that could overflow the buffer in Client::Message and get cut off - //This will crash MQ2 @ 4000 since their internal buffer is only 2048. - //Reducing it to 2000 fixes that but splits more results from tables with a lot of columns. - if(LineText.str().length() > 4000) { - LineVec.push_back(LineText.str()); - LineText.str(""); - } - LineText << fields[i].name << ":" << "[" << (row[i] ? row[i] : "nullptr") << "] "; - } - LineVec.push_back(LineText.str()); + if(optionS) //This provides spacing for the space switch + c->Message(0, " "); + if(optionH) //This option will highlight every other row + highlightTextIndex = 1 - highlightTextIndex; - if(Options) { //This provides spacing for the space switch - c->Message(0, " "); - } - if(Optionh) { //This option will highlight every other row - HText = 1 - HText; - } - for(int lineNum = 0; lineNum < LineVec.size(); ++lineNum) - { - c->Message(HText, LineVec[lineNum].c_str()); - } - } - } - else { - MsgText << "Invalid query: ' " << sep->arg[2] << " ', ' " << errbuf << " '"; - c->Message(0, MsgText.str().c_str()); - MsgText.str(""); - } - } - } + for(int lineNum = 0; lineNum < lineVec.size(); ++lineNum) + c->Message(highlightTextIndex, lineVec[lineNum].c_str()); + } + } } void command_xtargets(Client *c, const Seperator *sep) From 806a9fbb99cafeddeaffc58f758067d65d8c8d27 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 4 Oct 2014 23:38:20 -0400 Subject: [PATCH 274/368] Identified recast timer field in Ti item packet header --- common/patches/client62.cpp | 1 + common/patches/titanium.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/patches/client62.cpp b/common/patches/client62.cpp index 2da492145..5afd76bcd 100644 --- a/common/patches/client62.cpp +++ b/common/patches/client62.cpp @@ -1234,6 +1234,7 @@ namespace Client62 int i; uint32 sub_length; + // not sure if 6.2 has a recast timer timestamp field..but, something seems amiss between this and Ti's ordering MakeAnyLenString(&instance, "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", stackable ? charges : 1, diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 7c990751d..6c2591efe 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1675,7 +1675,7 @@ namespace Titanium 0, //merchant_slot, //instance ID, bullshit for now (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot, - 0, + 0, // item recast timer timestamp field (aka..last_cast_time field in SoF+ clients) (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? 1 : 0) : charges), inst->IsInstNoDrop() ? 1 : 0, 0 From 7dc1da21f0a892dcdddb2880e425a4bba7657ec9 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 5 Oct 2014 02:18:30 -0400 Subject: [PATCH 275/368] Move OP_ItemRecastDelay to after cast I originally assumed it needed to be sent "while casting" but it looks like right at the end (where we set the reuse timer) also works. --- zone/spells.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 310bc35bd..9ca3965cc 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -484,19 +484,6 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, return false; } - // This needs a bit more work for saving timer to PP for zoning etc - if (IsClient() && item_slot != INVALID_INDEX && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT))) { - ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot); - if (itm && itm->GetItem()->RecastDelay) { - outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); - ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; - ird->recast_delay = itm->GetItem()->RecastDelay; - ird->recast_type = itm->GetItem()->RecastType; - CastToClient()->QueuePacket(outapp); - safe_delete(outapp); - } - } - return(true); } @@ -2228,6 +2215,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot); if(itm && itm->GetItem()->RecastDelay > 0){ CastToClient()->GetPTimers().Start((pTimerItemStart + itm->GetItem()->RecastType), itm->GetItem()->RecastDelay); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); + ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; + ird->recast_delay = itm->GetItem()->RecastDelay; + ird->recast_type = itm->GetItem()->RecastType; + CastToClient()->QueuePacket(outapp); + safe_delete(outapp); } } From 4f2dbb37d26f9b4ab418e63cd65381095cac928d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 5 Oct 2014 02:28:17 -0400 Subject: [PATCH 276/368] Confirmed OP_ItemRecastDelay for SoF --- utils/patches/patch_SoF.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index 5bffdfc93..2e039b404 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -615,6 +615,8 @@ OP_RAWOutOfSession=0x0000 # OP_Some3ByteHPUpdate=0x0000 #initial HP update for mobs OP_InitialHPUpdate=0x0000 # +OP_ItemRecastDelay=0x0ada + # Opcodes from the client that are currently Unknowns: # 0x3E85 - Sent when Guild Management window is opened From fa1fe55e01c68688785e9be9cbbc307400fd7d9f Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 5 Oct 2014 06:12:33 -0400 Subject: [PATCH 277/368] Added corpse encode and decode translators --- changelog.txt | 3 ++ common/shareddb.cpp | 7 +++ zone/corpse.cpp | 113 +++++++++++++++++++++++++++++++++++++++++--- zone/corpse.h | 4 +- 4 files changed, 118 insertions(+), 9 deletions(-) diff --git a/changelog.txt b/changelog.txt index 6272d4a69..2d82f375e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/05/2014 == +Uleat: Added Server<->Corpse slot translators needed for re-enumeration (inactive until phased in) + == 10/03/2014 == Uleat: Fixed Ti(6.2) OP_AugmentInfo translation that I broke (does not currently need and I mis-read a process) Uleat: Moved client patch OP_LootItem slot translation to external handlers diff --git a/common/shareddb.cpp b/common/shareddb.cpp index aecfa0488..702861f06 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -191,6 +191,13 @@ bool SharedDatabase::SaveInventory(uint32 char_id, const ItemInst* inst, int16 s bool ret = false; uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; + // If we never save tribute slots..how are we to ever benefit from them!!? The client + // object is destroyed upon zoning - including its inventory object..and if tributes + // don't exist in the database, then they will never be loaded when the new client + // object is created in the new zone object... Something to consider... -U + // + // (we could add them to the 'NoRent' checks and dispose of after 30 minutes offline) + //never save tribute slots: if(slot_id >= EmuConstants::TRIBUTE_BEGIN && slot_id <= EmuConstants::TRIBUTE_END) return(true); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 6b3b4ac82..5f1345541 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -97,6 +97,7 @@ Corpse* Corpse::LoadFromDBData(uint32 in_dbid, uint32 in_charid, char* in_charna for (unsigned int i=0; i < dbpcs->itemcount; i++) { tmp = new ServerLootItem_Struct; memcpy(tmp, &dbpcs->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); + tmp->lootslot = CorpseToServerSlot(tmp->lootslot); // temp hack until corpse blobs are removed itemlist.push_back(tmp); } @@ -147,6 +148,7 @@ Corpse* Corpse::LoadFromDBData(uint32 in_dbid, uint32 in_charid, char* in_charna for (unsigned int i=0; i < dbpc->itemcount; i++) { tmp = new ServerLootItem_Struct; memcpy(tmp, &dbpc->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); + tmp->lootslot = CorpseToServerSlot(tmp->lootslot); // temp hack until corpse blobs are removed itemlist.push_back(tmp); } @@ -600,6 +602,7 @@ bool Corpse::Save() { end = itemlist.end(); for(; cur != end; ++cur) { ServerLootItem_Struct* item = *cur; + item->lootslot = ServerToCorpseSlot(item->lootslot); // temp hack until corpse blobs are removed memcpy((char*) &dbpc->items[x++], (char*) item, sizeof(player_lootitem::ServerLootItem_Struct)); } @@ -2055,15 +2058,111 @@ void Corpse::LoadPlayerCorpseDecayTime(uint32 dbid){ } /* -uint32 Corpse::ServerToCorpseSlot(int16 server_slot) { - // reserved -} +** Corpse slot translations are needed until corpse database blobs are converted +** +** To account for the addition of MainPowerSource, MainGeneral9 and MainGeneral10 into +** the contiguous possessions slot enumeration, the following designations will be used: +** +** Designatiom Server Corpse Offset +** -------------------------------------------------- +** MainCharm 0 0 0 +** ... ... ... 0 +** MainWaist 20 20 0 +** MainPowerSource 21 9999 +9978 +** MainAmmo 22 21 -1 +** +** MainGeneral1 23 22 -1 +** ... ... ... -1 +** MainGeneral8 30 29 -1 +** MainGeneral9 31 9997 +9966 +** MainGeneral10 32 9998 +9966 +** +** MainCursor 33 30 -3 +** +** MainGeneral1_1 251 251 0 +** ... ... ... 0 +** MainGeneral8_10 330 330 0 +** MainGeneral9_1 331 341 +10 +** ... ... ... +10 +** MainGeneral10_10 350 360 +10 +** +** MainCursor_1 351 331 -20 +** ... ... ... -20 +** MainCursor_10 360 340 -20 +** +** (Not all slot designations are valid to all clients..see ##_constants.h files for valid slot enumerations) */ -/* -int16 Corpse::CorpseToServerSlot(uint32 corpse_slot) { - // reserved +uint16 Corpse::ServerToCorpseSlot(uint16 server_slot) +{ + return server_slot; // temporary return + + /* + switch (server_slot) + { + case MainPowerSource: + return 9999; + case MainGeneral9: + return 9997; + case MainGeneral10: + return 9998; + case MainCursor: + return 30; + case MainAmmo: + case MainGeneral1: + case MainGeneral2: + case MainGeneral3: + case MainGeneral4: + case MainGeneral5: + case MainGeneral6: + case MainGeneral7: + case MainGeneral8: + return server_slot - 1; + default: + if (server_slot >= EmuConstants::CURSOR_BAG_BEGIN && server_slot <= EmuConstants::CURSOR_BAG_END) + return server_slot - 20; + else if (server_slot >= EmuConstants::GENERAL_BAGS_END - 19 && server_slot <= EmuConstants::GENERAL_BAGS_END) + return server_slot + 10; + else + return server_slot; + } + */ +} + +uint16 Corpse::CorpseToServerSlot(uint16 corpse_slot) +{ + return corpse_slot; // temporary return + + /* + switch (corpse_slot) + { + case 9999: + return MainPowerSource; + case 9997: + return MainGeneral9; + case 9998: + return MainGeneral10; + case 30: + return MainCursor; + case 21: // old SLOT_AMMO + case 22: // old PERSONAL_BEGIN + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: // old PERSONAL_END + return corpse_slot + 1; + default: + if (corpse_slot >= 331 && corpse_slot <= 340) + return corpse_slot + 20; + else if (corpse_slot >= 341 && corpse_slot <= 360) + return corpse_slot - 10; + else + return corpse_slot; + } + */ } -*/ /* void Corpse::CastRezz(uint16 spellid, Mob* Caster){ diff --git a/zone/corpse.h b/zone/corpse.h index e76b3b4ce..773ca3782 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -112,8 +112,8 @@ public: inline int GetRezzExp() { return rezzexp; } // these are a temporary work-around until corpse inventory is removed from the database blob - //static uint32 ServerToCorpseSlot(int16 server_slot); // encode - //static int16 CorpseToServerSlot(uint32 corpse_slot); // decode + static uint16 ServerToCorpseSlot(uint16 server_slot); // encode + static uint16 CorpseToServerSlot(uint16 corpse_slot); // decode protected: std::list MoveItemToCorpse(Client *client, ItemInst *item, int16 equipslot); From 8ff4e59d205c33e32c821fb95495d2f580d17de2 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 5 Oct 2014 07:51:20 -0400 Subject: [PATCH 278/368] Fix to allow regular runes to absorn spell damage (except dots). --- zone/attack.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index feb10369f..a7a50a6cd 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3315,7 +3315,10 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi if(damage < 1) return 0; - + + //Regular runes absorb spell damage (except dots) - Confirmed on live. + if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0) + damage = RuneAbsorb(damage, SE_Rune); if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0) damage = RuneAbsorb(damage, SE_AbsorbMagicAtt); From 3ad7ab625d21de81cc1c7aa3d51b6646f99e2860 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 5 Oct 2014 14:45:26 -0400 Subject: [PATCH 279/368] Add temp hack for num hits and UF Not sure what I screwed up, could of sworn it was working right before, but that was also 3 AM or something Rewrote UF's OP_BuffCreate to use the Write* functions which are a bit more straight forward --- common/patches/underfoot.cpp | 28 ++++++++++------------------ zone/spell_effects.cpp | 3 +++ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index 6291d3077..34ef61f6e 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -345,14 +345,10 @@ namespace Underfoot __packet->pBuffer = new unsigned char[sz]; memset(__packet->pBuffer, 0, sz); - uchar *ptr = __packet->pBuffer; - *((uint32*)ptr) = emu->entity_id; - ptr += sizeof(uint32); - ptr += sizeof(uint32); - *((uint8*)ptr) = emu->all_buffs; - ptr += sizeof(uchar); - *((uint16*)ptr) = emu->count; - ptr += sizeof(uint16); + __packet->WriteUInt32(emu->entity_id); + __packet->WriteUInt32(0); + __packet->WriteUInt8(emu->all_buffs); // 1 = all buffs, 0 = 1 buff + __packet->WriteUInt16(emu->count); for (uint16 i = 0; i < emu->count; ++i) { @@ -366,17 +362,13 @@ namespace Underfoot buffslot += 14; } - *((uint32*)ptr) = buffslot; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].spell_id; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].tics_remaining; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].num_hits; - ptr += sizeof(uint32); - *((uint8*)ptr) = !emu->all_buffs; - ptr += 1; + __packet->WriteUInt32(buffslot); + __packet->WriteUInt32(emu->entries[i].spell_id); + __packet->WriteUInt32(emu->entries[i].tics_remaining); + __packet->WriteUInt32(emu->entries[i].num_hits); + __packet->WriteString(""); } + __packet->WriteUInt8(!emu->all_buffs); FINISH_ENCODE(); /* diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f86f43bf4..0ce0c8834 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3396,6 +3396,9 @@ void Mob::BuffProcess() if(buffs[buffs_i].UpdateClient == true) { CastToClient()->SendBuffDurationPacket(buffs[buffs_i]); + // Hack to get UF to play nicer, RoF seems fine without it + if (CastToClient()->GetClientVersion() == EQClientUnderfoot) + CastToClient()->SendBuffNumHitPacket(buffs[buffs_i], buffs_i); buffs[buffs_i].UpdateClient = false; } } From 5be8e710a91f0f4cb80f9c1851b1a7813b5985d2 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 5 Oct 2014 15:15:17 -0400 Subject: [PATCH 280/368] More restrictive on the temp hack for UF (only spells with numhits) --- zone/spell_effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 0ce0c8834..b634ca4a3 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3397,7 +3397,7 @@ void Mob::BuffProcess() { CastToClient()->SendBuffDurationPacket(buffs[buffs_i]); // Hack to get UF to play nicer, RoF seems fine without it - if (CastToClient()->GetClientVersion() == EQClientUnderfoot) + if (CastToClient()->GetClientVersion() == EQClientUnderfoot && buffs[buffs_i].numhits > 0) CastToClient()->SendBuffNumHitPacket(buffs[buffs_i], buffs_i); buffs[buffs_i].UpdateClient = false; } From 1261c228a32e1fe888da4f3c9d8885039b5dd31f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 5 Oct 2014 16:13:13 -0400 Subject: [PATCH 281/368] Call CalcBonuses for RoF+ augments --- zone/client_packet.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2ef62c3d3..7edd9202b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3111,6 +3111,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) DeleteItemInInventory(MainCursor, 0, true); if (PutItemInInventory(slot_id, *itemOneToPush, true)) { + CalcBonuses(); //Message(13, "Sucessfully added an augment to your item!"); return; } @@ -3185,6 +3186,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) if (PutItemInInventory(MainCursor, *itemTwoToPush, true)) { + CalcBonuses(); //Message(15, "Successfully removed an augmentation!"); } } From 66448feece908edc040ffc5e07f720761eaacdaa Mon Sep 17 00:00:00 2001 From: JJ Date: Sun, 5 Oct 2014 21:42:33 -0400 Subject: [PATCH 282/368] Some updates to #command information. --- zone/command.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 2844e9daf..2d7a13943 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -370,7 +370,7 @@ int command_init(void) { command_add("opcode","- opcode management",250,command_opcode) || command_add("logs","[status|normal|error|debug|quest|all] - Subscribe to a log type",250,command_logs) || command_add("nologs","[status|normal|error|debug|quest|all] - Unsubscribe to a log type",250,command_nologs) || - command_add("ban","[name] - Ban by character name",150,command_ban) || + command_add("ban","[name] [reason]- Ban by character name",150,command_ban) || command_add("suspend","[name][days] - Suspend by character name and for specificed number of days",150,command_suspend) || command_add("ipban","[IP address] - Ban IP by character name",200,command_ipban) || command_add("oocmute","[1/0] - Mutes OOC chat",200,command_oocmute) || @@ -382,7 +382,7 @@ int command_init(void) { command_add("npcshout","[message] - Make your NPC target shout a message.",150,command_npcshout) || command_add("timers","- Display persistent timers for target",200,command_timers) || command_add("hp","- Refresh your HP bar from the server.",0,command_hp) || - command_add("pf","- ",0,command_pf) || + command_add("pf","- Display additional mob coordinate and wandering data",0,command_pf) || command_add("logsql","- enable SQL logging",200,command_logsql) || command_add("bestz","- Ask map for a good Z coord for your x,y coords.",0,command_bestz) || command_add("ginfo","- get group info on target.",20,command_ginfo) || From cab41487d533e64d52b76a72158460c69fb87b67 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 5 Oct 2014 22:47:57 -0400 Subject: [PATCH 283/368] Stop the book showing up while inspecting charms with RoF Stat display still broken sadly --- common/patches/rof.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index a107a1246..5fb0f9eed 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -2628,7 +2628,7 @@ namespace RoF else eq->window = emu->window; OUT(type); - eq->invslot = 0; // Set to hard 0 since it's not required for the structure to work + OUT(invslot); strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); FINISH_ENCODE(); @@ -4497,7 +4497,7 @@ namespace RoF SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); IN(type); - emu->invslot = 0; // Set to hard 0 since it's not required for the structure to work + IN(invslot); emu->window = (uint8)eq->window; strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); From 2bee9067845c3245692bbf86f3f17efd0cebcc8c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 6 Oct 2014 01:30:01 -0400 Subject: [PATCH 284/368] Slay Undead to String IDs (and better filtering) --- zone/attack.cpp | 24 +++++++++++++----------- zone/string_ids.h | 2 ++ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index a7a50a6cd..0b8cf07f1 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4203,24 +4203,26 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack } #endif //BOTS - float critChance = 0.0f; bool IsBerskerSPA = false; //1: Try Slay Undead - if(defender && defender->GetBodyType() == BT_Undead || defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire){ - + if (defender && (defender->GetBodyType() == BT_Undead || + defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire)) { int16 SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0]; - if (SlayRateBonus) { - - critChance += (float(SlayRateBonus)/100.0f); - critChance /= 100.0f; - - if(MakeRandomFloat(0, 1) < critChance){ + float slayChance = static_cast(SlayRateBonus) / 10000.0f; + if (MakeRandomFloat(0, 1) < slayChance) { int16 SlayDmgBonus = aabonuses.SlayUndead[1] + itembonuses.SlayUndead[1] + spellbonuses.SlayUndead[1]; - damage = (damage*SlayDmgBonus*2.25)/100; - entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s cleanses %s target!(%d)", GetCleanName(), this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its", damage); + damage = (damage * SlayDmgBonus * 2.25) / 100; + if (GetGender() == 1) // female + entity_list.FilteredMessageClose_StringID(this, false, 200, + MT_CritMelee, FilterMeleeCrits, FEMALE_SLAYUNDEAD, + GetCleanName(), itoa(damage)); + else // males and neuter I guess + entity_list.FilteredMessageClose_StringID(this, false, 200, + MT_CritMelee, FilterMeleeCrits, MALE_SLAYUNDEAD, + GetCleanName(), itoa(damage)); return; } } diff --git a/zone/string_ids.h b/zone/string_ids.h index 56e1c4043..41e0960b0 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -163,6 +163,8 @@ #define CANNOT_WAKE 555 //%1 tells you, 'I am unable to wake %2, master.' #define GUILD_NAME_IN_USE 711 //You cannot create a guild with that name, that guild already exists on this server. #define GM_GAINXP 1002 //[GM] You have gained %1 AXP and %2 EXP (%3). +#define MALE_SLAYUNDEAD 1007 //%1's holy blade cleanses his target!(%2) +#define FEMALE_SLAYUNDEAD 1008 //%1's holy blade cleanses her target!(%2) #define FINISHING_BLOW 1009 //%1 scores a Finishing Blow!! #define ASSASSINATES 1016 //%1 ASSASSINATES their victim!! #define CRIPPLING_BLOW 1021 //%1 lands a Crippling Blow!(%2) From 0438042844709c429a50c0c69e3cce1cb9c035e4 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 09:02:13 -0700 Subject: [PATCH 285/368] GeneralQueryReceive converted to QueryDatabase --- queryserv/database.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/queryserv/database.cpp b/queryserv/database.cpp index f2dfebb56..4f7fdf10f 100644 --- a/queryserv/database.cpp +++ b/queryserv/database.cpp @@ -349,17 +349,16 @@ void Database::GeneralQueryReceive(ServerPacket *pack) { /* These are general queries passed from anywhere in zone instead of packing structures and breaking them down again and again */ - char *Query = nullptr; - Query = new char[pack->ReadUInt32() + 1]; - pack->ReadString(Query); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if (!RunQuery(query, MakeAnyLenString(&query, Query), errbuf, 0, 0, &lastid)) { - _log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); + char *queryBuffer = new char[pack->ReadUInt32() + 1]; + pack->ReadString(queryBuffer); + + std::string query(queryBuffer); + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } - safe_delete_array(query); + safe_delete(pack); - safe_delete(Query); + safe_delete(queryBuffer); } From 787008171649a3f4ed3538d1d42cbcb9de467a9a Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 09:21:39 -0700 Subject: [PATCH 286/368] SaveGroupLeaderAA converted to QueryDatabase --- zone/groups.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index 6c0200250..f0cdb71de 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -561,6 +561,18 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender) } } + /* This may seem pointless but the case above does not cover the following situation: + * Group has Leader a, member b, member c + * b and c are out of zone + * a disconnects/quits + * b or c zone back in and disconnects/quits + * a is still "leader" from GetLeader()'s perspective and will crash the zone when we DelMember(b) + * Ultimately we should think up a better solution to this. + */ + if(oldmember == GetLeader()) { + SetLeader(nullptr); + } + ServerPacket* pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct)); ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; gl->gid = GetID(); @@ -1880,22 +1892,17 @@ void Group::SaveGroupLeaderAA() { // Stores the Group Leaders Leadership AA data from the Player Profile as a blob in the group_leaders table. // This is done so that group members not in the same zone as the Leader still have access to this information. + char *queryBuffer = new char[sizeof(GroupLeadershipAA_Struct) * 2 + 1]; + database.DoEscapeString(queryBuffer, (char*)&LeaderAbilities, sizeof(GroupLeadershipAA_Struct)); - char *Query = new char[200 + sizeof(GroupLeadershipAA_Struct)*2]; + std::string query = "UPDATE group_leaders SET leadershipaa = '"; + query += queryBuffer; + query += StringFormat("' WHERE gid = %i LIMIT 1", GetID()); + safe_delete_array(queryBuffer); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str()); - char *End = Query; - - End += sprintf(End, "UPDATE group_leaders SET leadershipaa='"); - - End += database.DoEscapeString(End, (char*)&LeaderAbilities, sizeof(GroupLeadershipAA_Struct)); - - End += sprintf(End,"' WHERE gid=%i LIMIT 1", GetID()); - - char errbuff[MYSQL_ERRMSG_SIZE]; - if (!database.RunQuery(Query, End - Query, errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", errbuff); - - safe_delete_array(Query); } void Group::UnMarkNPC(uint16 ID) From 9183541825200061d1d0dd2d19b67d192049d881 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 09:58:21 -0700 Subject: [PATCH 287/368] AddMember converted to QueryDatabase --- zone/raids.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 84ebcee7d..84b6bba12 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -74,14 +74,14 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo if(!c) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO raid_members SET raidid=%lu, charid=%lu, groupid=%lu, _class=%d, level=%d, name='%s', isgroupleader=%d, israidleader=%d, islooter=%d", (unsigned long)GetID(), (unsigned long)c->CharacterID(), (unsigned long)group, c->GetClass(), c->GetLevel(), c->GetName(), groupleader, rleader, looter ),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("INSERT INTO raid_members SET raidid = %lu, charid = %lu, " + "groupid = %lu, _class = %d, level = %d, name = '%s', " + "isgroupleader = %d, israidleader = %d, islooter = %d", + (unsigned long)GetID(), (unsigned long)c->CharacterID(), + (unsigned long)group, c->GetClass(), c->GetLevel(), + c->GetName(), groupleader, rleader, looter); + auto results = database.QueryDatabase(query); - safe_delete_array(query); LearnMembers(); VerifyRaid(); if(group < 12) @@ -514,7 +514,7 @@ void Raid::BalanceHP(int32 penalty, uint32 gid, int32 range, Mob* caster, int32 int dmgtaken = 0, numMem = 0, dmgtaken_tmp = 0; int gi = 0; - + float distance; float range2 = range*range; @@ -567,7 +567,7 @@ void Raid::BalanceMana(int32 penalty, uint32 gid, int32 range, Mob* caster, int3 if (!range) range = 200; - + float distance; float range2 = range*range; From 5781821ab341185c4b5ab598a46cce062da42e7b Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 10:12:00 -0700 Subject: [PATCH 288/368] RemoveMember converted to QueryDatabase --- zone/raids.cpp | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 84b6bba12..d9dd20e96 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -100,32 +100,26 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo safe_delete(pack); } -void Raid::RemoveMember(const char *c) +void Raid::RemoveMember(const char *characterName) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "DELETE FROM raid_members where name='%s'", c ),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("DELETE FROM raid_members where name='%s'", characterName); + auto results = database.QueryDatabase(query); - Client *m = entity_list.GetClientByName(c); - safe_delete_array(query); + Client *client = entity_list.GetClientByName(characterName); disbandCheck = true; - SendRaidRemoveAll(c); - SendRaidDisband(m); + SendRaidRemoveAll(characterName); + SendRaidDisband(client); LearnMembers(); VerifyRaid(); - if(m){ - m->SetRaidGrouped(false); - } + if(client) + client->SetRaidGrouped(false); ServerPacket *pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; rga->rid = GetID(); rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, c, 64); + strn0cpy(rga->playername, characterName, 64); rga->zoneid = zone->GetZoneID(); worldserver.SendPacket(pack); safe_delete(pack); From 73b29877001d93e823846ba463e339f437d3f74f Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 10:47:16 -0700 Subject: [PATCH 289/368] DisbandRaid converted to QueryDatabase --- zone/raids.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index d9dd20e96..4d421f6b9 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -127,14 +127,9 @@ void Raid::RemoveMember(const char *characterName) void Raid::DisbandRaid() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "DELETE FROM raid_members WHERE raidid=%lu", (unsigned long)GetID()),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("DELETE FROM raid_members WHERE raidid = %lu", (unsigned long)GetID()); + auto results = database.QueryDatabase(query); - safe_delete_array(query); LearnMembers(); VerifyRaid(); SendRaidDisbandAll(); From dfcddac2ef3e553174dcff82e112fc34c2f7f067 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 10:53:20 -0700 Subject: [PATCH 290/368] MoveMember converted to QueryDatabase --- zone/raids.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 4d421f6b9..4dee9d094 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -148,14 +148,10 @@ void Raid::DisbandRaid() void Raid::MoveMember(const char *name, uint32 newGroup) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET groupid=%lu WHERE name='%s'", (unsigned long)newGroup, name),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("UPDATE raid_members SET groupid = %lu WHERE name = '%s'", + (unsigned long)newGroup, name); + auto results = database.QueryDatabase(query); - safe_delete_array(query); LearnMembers(); VerifyRaid(); SendRaidMoveAll(name); From 11e08c9b67f199868ddc47cbd967057fc63e98ad Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 10:55:04 -0700 Subject: [PATCH 291/368] SetGroupleader converted to QueryDatabase --- zone/raids.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 4dee9d094..9298f9b0b 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -168,21 +168,13 @@ void Raid::MoveMember(const char *name, uint32 newGroup) void Raid::SetGroupLeader(const char *who, bool glFlag) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET isgroupleader=%lu WHERE name='%s'", (unsigned long)glFlag, who),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("UPDATE raid_members SET isgroupleader = %lu WHERE name = '%s'", + (unsigned long)glFlag, who); + auto results = database.QueryDatabase(query); - safe_delete_array(query); LearnMembers(); VerifyRaid(); - //if(glFlag == true){ //we're setting the flag - //this->SendMakeGroupLeaderPacket(who); - //} - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupLeader, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; rga->rid = GetID(); From 7a3f7602a58a0048b8d3dfb7d3e7bb0abd181fdb Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 10:59:59 -0700 Subject: [PATCH 292/368] SetRaidLeader converted to QueryDatabase --- zone/raids.cpp | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 9298f9b0b..f29010642 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -187,25 +187,15 @@ void Raid::SetGroupLeader(const char *who, bool glFlag) void Raid::SetRaidLeader(const char *wasLead, const char *name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (!database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET israidleader=0 WHERE name='%s'", wasLead),errbuf,&result)){ - printf("Set Raid Leader error: %s\n", errbuf); - } - else - mysql_free_result(result); + std::string query = StringFormat("UPDATE raid_members SET israidleader = 0 WHERE name = '%s'", wasLead); + auto results = database.QueryDatabase(query); + if (!results.Success()) + printf("Set Raid Leader error: %s\n", results.ErrorMessage().c_str()); - safe_delete_array(query); - query = 0; - - if (!database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET israidleader=1 WHERE name='%s'", name),errbuf,&result)){ - printf("Set Raid Leader error: %s\n", errbuf); - } - else - mysql_free_result(result); - - safe_delete_array(query); + query = StringFormat("UPDATE raid_members SET israidleader = 1 WHERE name = '%s'", name); + results = database.QueryDatabase(query); + if (!results.Success()) + printf("Set Raid Leader error: %s\n", results.ErrorMessage().c_str()); strn0cpy(leadername, name, 64); From 8553278759a56633485a47a59130a0492be98ca7 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 11:02:17 -0700 Subject: [PATCH 293/368] UpdateLevel converted to QueryDatabase --- zone/raids.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index f29010642..33c1f1a06 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -231,14 +231,10 @@ bool Raid::IsGroupLeader(const char *who) void Raid::UpdateLevel(const char *name, int newLevel) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET level=%lu WHERE name='%s'", (unsigned long)newLevel, name),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("UPDATE raid_members SET level = %lu WHERE name = '%s'", + (unsigned long)newLevel, name); + auto results = database.QueryDatabase(query); - safe_delete_array(query); LearnMembers(); VerifyRaid(); } From b0d358cda81b050d37678ca71b09619f05798c88 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 11:06:43 -0700 Subject: [PATCH 294/368] AddRaidLooter converted to QueryDatabase --- zone/raids.cpp | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 33c1f1a06..c9d3fbb3c 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -738,27 +738,16 @@ void Raid::TeleportRaid(Mob* sender, uint32 zoneID, uint16 instance_id, float x, void Raid::ChangeLootType(uint32 type) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_details SET loottype=%lu WHERE raidid=%lu", (unsigned long)type, (unsigned long)GetID()),errbuf,&result)){ - mysql_free_result(result); - } - - safe_delete_array(query); + std::string query = StringFormat("UPDATE raid_details SET loottype = %lu WHERE raidid = %lu", + (unsigned long)type, (unsigned long)GetID()); + auto results = database.QueryDatabase(query); LootType = type; } void Raid::AddRaidLooter(const char* looter) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET islooter=1 WHERE name='%s'", looter),errbuf,&result)){ - mysql_free_result(result); - } - - safe_delete_array(query); + std::string query = StringFormat("UPDATE raid_members SET islooter = 1 WHERE name = '%s'", looter); + auto results = database.QueryDatabase(query); for(int x = 0; x < MAX_RAID_MEMBERS; x++) { @@ -775,15 +764,6 @@ void Raid::AddRaidLooter(const char* looter) rga->instance_id = zone->GetInstanceID(); worldserver.SendPacket(pack); safe_delete(pack); - - /* For reference only at this time. This code adds a looter to the Raid Options Window. - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rgs = (RaidGeneral_Struct*)outapp->pBuffer; - rgs->action = 33; - strcpy(rgs->leader_name, looter); - QueuePacket(outapp); - safe_delete(outapp); */ } void Raid::RemoveRaidLooter(const char* looter) From b66b7a5fd26608245e0c3895d0faf433beec12ec Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 11:10:27 -0700 Subject: [PATCH 295/368] RemoveTaidLooter converted to QueryDatabase --- zone/raids.cpp | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index c9d3fbb3c..3790898f8 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -768,23 +768,15 @@ void Raid::AddRaidLooter(const char* looter) void Raid::RemoveRaidLooter(const char* looter) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET islooter=0 WHERE name='%s'", looter),errbuf,&result)){ - mysql_free_result(result); - } - - safe_delete_array(query); + std::string query = StringFormat("UPDATE raid_members SET islooter = 0 WHERE name = '%s'", looter); + auto results = database.QueryDatabase(query); for(int x = 0; x < MAX_RAID_MEMBERS; x++) - { - if(strcmp(looter, members[x].membername) == 0) - { + if(strcmp(looter, members[x].membername) == 0) { members[x].IsLooter = 0; break; } - } + ServerPacket *pack = new ServerPacket(ServerOP_DetailsChange, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; rga->rid = GetID(); @@ -792,15 +784,6 @@ void Raid::RemoveRaidLooter(const char* looter) rga->instance_id = zone->GetInstanceID(); worldserver.SendPacket(pack); safe_delete(pack); - - /* For reference only at this time. This code removes a looter from the Raid Options Window. - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rgs = (RaidGeneral_Struct*)outapp->pBuffer; - rgs->action = 34; - strcpy(rgs->leader_name, looter); - QueuePacket(outapp); - safe_delete(outapp); */ } bool Raid::IsRaidMember(const char *name){ From 67c8949cd48db95287194dc4afd713d04cf122bf Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 11:14:02 -0700 Subject: [PATCH 296/368] LockRaid converted to QueryDatabase --- zone/raids.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 3790898f8..648b6b418 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -1207,14 +1207,10 @@ void Raid::SendRaidGroupRemove(const char *who, uint32 gid) void Raid::LockRaid(bool lockFlag) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_details SET locked=%d WHERE raidid=%lu", lockFlag, (unsigned long)GetID()),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("UPDATE raid_details SET locked = %d WHERE raidid = %lu", + lockFlag, (unsigned long)GetID()); + auto results = database.QueryDatabase(query); - safe_delete_array(query); locked = lockFlag; if(lockFlag) SendRaidLock(); From 77cfd116e0bf12ccdf2bfccd999e94b06e7b3723 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 11:15:45 -0700 Subject: [PATCH 297/368] SetRaidDetails converted to QueryDatabase --- zone/raids.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 648b6b418..35829cdf8 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -1229,14 +1229,9 @@ void Raid::LockRaid(bool lockFlag) void Raid::SetRaidDetails() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO raid_details SET raidid=%lu, loottype=4, locked=0", (unsigned long)GetID()),errbuf,&result)){ - mysql_free_result(result); - } - - safe_delete_array(query); + std::string query = StringFormat("INSERT INTO raid_details SET raidid = %lu, loottype = 4, locked = 0", + (unsigned long)GetID()); + auto results = database.QueryDatabase(query); } void Raid::GetRaidDetails() From 4b1d3592d489b39e7d6a7b0cc5c1899a4e4d47b7 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 11:21:20 -0700 Subject: [PATCH 298/368] GetRaidDetails converted to QueryDatabase --- zone/raids.cpp | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 35829cdf8..49e529ebe 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -1236,24 +1236,21 @@ void Raid::SetRaidDetails() void Raid::GetRaidDetails() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT locked, loottype FROM raid_details WHERE raidid=%lu", (unsigned long)GetID()),errbuf,&result)){ - safe_delete_array(query); - if(mysql_num_rows(result) < 1) { - mysql_free_result(result); - LogFile->write(EQEMuLog::Error, "Error getting raid details for raid %lu: %s", (unsigned long)GetID(), errbuf); - return; - } - row = mysql_fetch_row(result); - if(row){ - locked = atoi(row[0]); - LootType = atoi(row[1]); - } - mysql_free_result(result); - } + std::string query = StringFormat("SELECT locked, loottype FROM raid_details WHERE raidid = %lu", + (unsigned long)GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + if (results.RowCount() == 0) { + LogFile->write(EQEMuLog::Error, "Error getting raid details for raid %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str()); + return; + } + + auto row = results.begin(); + + locked = atoi(row[0]); + LootType = atoi(row[1]); } bool Raid::LearnMembers() From a90babbae1be3abb21f9fc00a96fa98a58fd1956 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 11:26:03 -0700 Subject: [PATCH 299/368] LearnMembers converted to QueryDatabase --- zone/raids.cpp | 70 ++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 49e529ebe..1e8e23f8d 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -1256,39 +1256,43 @@ void Raid::GetRaidDetails() bool Raid::LearnMembers() { memset(members, 0, (sizeof(RaidMember)*MAX_RAID_MEMBERS)); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT name, groupid, _class, level, isgroupleader, israidleader, islooter FROM raid_members WHERE raidid=%lu", (unsigned long)GetID()),errbuf,&result)){ - safe_delete_array(query); - if(mysql_num_rows(result) < 1) { - mysql_free_result(result); - LogFile->write(EQEMuLog::Error, "Error getting raid members for raid %lu: %s", (unsigned long)GetID(), errbuf); - disbandCheck = true; - return(false); - } - int i = 0; - while((row = mysql_fetch_row(result))) { - if(!row[0]) - continue; - members[i].member = nullptr; - strn0cpy(members[i].membername, row[0], 64); - int GroupNum = atoi(row[1]); - if(GroupNum > 11) - members[i].GroupNumber = 0xFFFFFFFF; - else - members[i].GroupNumber = GroupNum; - members[i]._class = atoi(row[2]); - members[i].level = atoi(row[3]); - members[i].IsGroupLeader = atoi(row[4]); - members[i].IsRaidLeader = atoi(row[5]); - members[i].IsLooter = atoi(row[6]); - i++; - } - mysql_free_result(result); - } - return(true); + + std::string query = StringFormat("SELECT name, groupid, _class, level, " + "isgroupleader, israidleader, islooter " + "FROM raid_members WHERE raidid = %lu", + (unsigned long)GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return false; + + if(results.RowCount() == 0) { + LogFile->write(EQEMuLog::Error, "Error getting raid members for raid %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str()); + disbandCheck = true; + return false; + } + + int index = 0; + for(auto row = results.begin(); row != results.end(); ++row) { + if(!row[0]) + continue; + + members[index].member = nullptr; + strn0cpy(members[index].membername, row[0], 64); + int groupNum = atoi(row[1]); + if(groupNum > 11) + members[index].GroupNumber = 0xFFFFFFFF; + else + members[index].GroupNumber = groupNum; + + members[index]._class = atoi(row[2]); + members[index].level = atoi(row[3]); + members[index].IsGroupLeader = atoi(row[4]); + members[index].IsRaidLeader = atoi(row[5]); + members[index].IsLooter = atoi(row[6]); + ++index; + } + + return true; } void Raid::VerifyRaid() From be7d2e9457ec56f999faa6c7259b4828cbd7328f Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 11:36:42 -0700 Subject: [PATCH 300/368] LoadTaskSets converted to QueryDatabase --- zone/tasks.cpp | 57 +++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 3da32c03c..ea7febd0b 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -60,41 +60,32 @@ TaskManager::~TaskManager() { } bool TaskManager::LoadTaskSets() { - const char *TaskSetQuery = "SELECT `id`, `taskid` from `tasksets` WHERE `id` > 0 AND `id` < %i " - "AND `taskid` >= 0 AND `taskid` < %i ORDER BY `id`, `taskid` ASC"; - const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTaskSets: %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; // Clear all task sets in memory. Done so we can reload them on the fly if required by just calling // this method again. - for(int i=0; iwrite(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `id`, `taskid` from `tasksets` " + "WHERE `id` > 0 AND `id` < %i " + "AND `taskid` >= 0 AND `taskid` < %i " + "ORDER BY `id`, `taskid` ASC", + MAXTASKSETS, MAXTASKS); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadTaskSets: %s", results.ErrorMessage().c_str()); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + int taskSet = atoi(row[0]); + int taskID = atoi(row[1]); + + TaskSets[taskSet].push_back(taskID); + _log(TASKS__GLOBALLOAD, "Adding TaskID %4i to TaskSet %4i", taskID, taskSet); + } return true; - } bool TaskManager::LoadSingleTask(int TaskID) { @@ -1152,7 +1143,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task if(Tasks[TaskList[i]] != nullptr) break; } - // FIXME: The 10 and 5 values in this calculation are to account for the string "ABCD" we are putting in 3 times. + // FIXME: The 10 and 5 values in this calculation are to account for the string "ABCD" we are putting in 3 times. // // Calculate how big the packet needs to be pased on the number of tasks and the // size of the variable length strings. @@ -1230,9 +1221,9 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task // FIXME: In live packets, these two strings appear to be the same as the Text1 and Text2 // strings from the first activity in the task, however the task chooser/selector // does not appear to make use of them. - sprintf(Ptr, "ABCD"); + sprintf(Ptr, "ABCD"); Ptr = Ptr + strlen(Ptr) + 1; - sprintf(Ptr, "ABCD"); + sprintf(Ptr, "ABCD"); Ptr = Ptr + strlen(Ptr) + 1; AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)Ptr; @@ -1247,7 +1238,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task // In some packets, this next string looks like a short task summary, however it doesn't // appear anywhere in the client window. - sprintf(Ptr, "ABCD"); + sprintf(Ptr, "ABCD"); Ptr = Ptr + strlen(Ptr) + 1; } @@ -1979,7 +1970,7 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T taskmanager->SendSingleActiveTaskToClient(c, TaskIndex, TaskComplete, false); // Inform the client the task has been updated, both by a chat message c->Message(0, "Your task '%s' has been updated.", Task->Title); - + if(Task->Activity[ActivityID].GoalMethod != METHODQUEST) { char buf[24]; snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID); @@ -1992,7 +1983,7 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T QServ->PlayerLogEvent(Player_Log_Task_Updates, c->CharacterID(), event_desc); } } - + // If this task is now complete, the Completed tasks will have been // updated in UnlockActivities. Send the completed task list to the // client. This is the same sequence the packets are sent on live. From 48299b6024901c43a32f3c492922938aaa14fbc5 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Tue, 7 Oct 2014 11:56:38 -0700 Subject: [PATCH 301/368] LoadTasks converted to QueryDatabase --- zone/tasks.cpp | 330 +++++++++++++++++++++++-------------------------- 1 file changed, 152 insertions(+), 178 deletions(-) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index ea7febd0b..00749a454 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -117,203 +117,177 @@ void TaskManager::ReloadGoalLists() { _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadLists failed"); } -bool TaskManager::LoadTasks(int SingleTask) { +bool TaskManager::LoadTasks(int singleTask) { // If TaskID !=0, then just load the task specified. - - const char *AllTaskQuery = "SELECT `id`, `duration`, `title`, `description`, `reward`, `rewardid`," - "`cashreward`, `xpreward`, `rewardmethod`, `startzone`, `minlevel`, `maxlevel`, `repeatable` " - "from `tasks` WHERE `id` < %i"; - - const char *SingleTaskQuery = "SELECT `id`, `duration`, `title`, `description`, `reward`, `rewardid`," - "`cashreward`, `xpreward`, `rewardmethod`, `startzone`, `minlevel`, `maxlevel`, `repeatable` " - "from `tasks` WHERE `id` = %i"; - - const char *AllActivityQuery = "SELECT `taskid`, `step`, `activityid`, `activitytype`, `text1`, `text2`," - "`text3`, `goalid`, `goalmethod`, `goalcount`, `delivertonpc`, " - "`zoneid`, `optional` from `activities` WHERE " - "`taskid` < %i AND `activityid` < %i ORDER BY taskid, activityid ASC"; - - const char *SingleTaskActivityQuery = "SELECT `taskid`, `step`, `activityid`, `activitytype`, `text1`, `text2`," - "`text3`, `goalid`, `goalmethod`, `goalcount`, `delivertonpc`, " - "`zoneid`, `optional` from `activities` WHERE " - "`taskid` = %i AND `activityid` < %i ORDER BY taskid, activityid ASC"; - - const char *ERR_TASK_OOR = "[TASKS]Task ID %i out of range while loading tasks from database"; - - const char *ERR_TASK_OR_ACTIVITY_OOR = "[TASKS]Task or Activity ID (%i, %i) out of range while loading" - "activities from database"; - - const char *ERR_NOTASK = "[TASKS]Activity for non-existent task (%i, %i) while loading activities from database"; - - const char *ERR_SEQERR = "[TASKS]Activities for Task %i are not sequential starting at 0. Not loading task."; - - const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTasks: %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - int QueryLength = 0; - MYSQL_RES *result; - MYSQL_ROW row; - _log(TASKS__GLOBALLOAD, "TaskManager::LoadTasks Called"); - if(SingleTask == 0) { + std::string query; + if(singleTask == 0) { if(!GoalListManager.LoadLists()) _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadLists failed"); if(!LoadTaskSets()) _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadTaskSets failed"); - QueryLength = MakeAnyLenString(&query,AllTaskQuery,MAXTASKS); + query = StringFormat("SELECT `id`, `duration`, `title`, `description`, `reward`, " + "`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, " + "`startzone`, `minlevel`, `maxlevel`, `repeatable` " + "FROM `tasks` WHERE `id` < %i", MAXTASKS); } else - QueryLength = MakeAnyLenString(&query,SingleTaskQuery,SingleTask); + query = StringFormat("SELECT `id`, `duration`, `title`, `description`, `reward`, " + "`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, " + "`startzone`, `minlevel`, `maxlevel`, `repeatable` " + "FROM `tasks` WHERE `id` = %i",singleTask); - if(database.RunQuery(query,QueryLength,errbuf,&result)) { + const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTasks: %s"; - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - if((TaskID <= 0) || (TaskID >= MAXTASKS)) { - // This shouldn't happen, as the SELECT is bounded by MAXTASKS - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR, TaskID); - continue; - } - Tasks[TaskID] = new TaskInformation; - Tasks[TaskID]->Duration = atoi(row[1]); - Tasks[TaskID]->Title = new char[strlen(row[2]) + 1]; - strcpy(Tasks[TaskID]->Title, row[2]); - Tasks[TaskID]->Description = new char[strlen(row[3]) + 1]; - strcpy(Tasks[TaskID]->Description, row[3]); - Tasks[TaskID]->Reward = new char[strlen(row[4]) + 1]; - strcpy(Tasks[TaskID]->Reward, row[4]); - Tasks[TaskID]->RewardID = atoi(row[5]); - Tasks[TaskID]->CashReward = atoi(row[6]); - Tasks[TaskID]->XPReward = atoi(row[7]); - Tasks[TaskID]->RewardMethod = (TaskMethodType)atoi(row[8]); - Tasks[TaskID]->StartZone = atoi(row[9]); - Tasks[TaskID]->MinLevel = atoi(row[10]); - Tasks[TaskID]->MaxLevel = atoi(row[11]); - Tasks[TaskID]->Repeatable = atoi(row[12]); - Tasks[TaskID]->ActivityCount = 0; - Tasks[TaskID]->SequenceMode = ActivitiesSequential; - Tasks[TaskID]->LastStep = 0; - - _log(TASKS__GLOBALLOAD,"TaskID: %5i, Duration: %8i, StartZone: %3i Reward: %s MinLevel %i MaxLevel %i Repeatable: %s", - TaskID, Tasks[TaskID]->Duration, Tasks[TaskID]->StartZone, Tasks[TaskID]->Reward, - Tasks[TaskID]->MinLevel, Tasks[TaskID]->MaxLevel, - Tasks[TaskID]->Repeatable ? "Yes" : "No"); - _log(TASKS__GLOBALLOAD,"Title: %s ", Tasks[TaskID]->Title); - //_log(TASKS__GLOBALLOAD,"Description: %s ", Tasks[TaskID]->Description); - - } - mysql_free_result(result); - safe_delete_array(query); - - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - safe_delete_array(query); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); return false; - } + } - if(SingleTask==0) - QueryLength = MakeAnyLenString(&query,AllActivityQuery,MAXTASKS, MAXACTIVITIESPERTASK); + for(auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + + if((taskID <= 0) || (taskID >= MAXTASKS)) { + // This shouldn't happen, as the SELECT is bounded by MAXTASKS + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading tasks from database", taskID); + continue; + } + + Tasks[taskID] = new TaskInformation; + Tasks[taskID]->Duration = atoi(row[1]); + Tasks[taskID]->Title = new char[strlen(row[2]) + 1]; + strcpy(Tasks[taskID]->Title, row[2]); + Tasks[taskID]->Description = new char[strlen(row[3]) + 1]; + strcpy(Tasks[taskID]->Description, row[3]); + Tasks[taskID]->Reward = new char[strlen(row[4]) + 1]; + strcpy(Tasks[taskID]->Reward, row[4]); + Tasks[taskID]->RewardID = atoi(row[5]); + Tasks[taskID]->CashReward = atoi(row[6]); + Tasks[taskID]->XPReward = atoi(row[7]); + Tasks[taskID]->RewardMethod = (TaskMethodType)atoi(row[8]); + Tasks[taskID]->StartZone = atoi(row[9]); + Tasks[taskID]->MinLevel = atoi(row[10]); + Tasks[taskID]->MaxLevel = atoi(row[11]); + Tasks[taskID]->Repeatable = atoi(row[12]); + Tasks[taskID]->ActivityCount = 0; + Tasks[taskID]->SequenceMode = ActivitiesSequential; + Tasks[taskID]->LastStep = 0; + + _log(TASKS__GLOBALLOAD,"TaskID: %5i, Duration: %8i, StartZone: %3i Reward: %s MinLevel %i MaxLevel %i Repeatable: %s", + taskID, Tasks[taskID]->Duration, Tasks[taskID]->StartZone, Tasks[taskID]->Reward, + Tasks[taskID]->MinLevel, Tasks[taskID]->MaxLevel, + Tasks[taskID]->Repeatable ? "Yes" : "No"); + _log(TASKS__GLOBALLOAD,"Title: %s ", Tasks[taskID]->Title); + } + + + if(singleTask==0) + query = StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, " + "`text1`, `text2`, `text3`, `goalid`, `goalmethod`, " + "`goalcount`, `delivertonpc`, `zoneid`, `optional` " + "FROM `activities` " + "WHERE `taskid` < %i AND `activityid` < %i " + "ORDER BY taskid, activityid ASC", MAXTASKS, MAXACTIVITIESPERTASK); else - QueryLength = MakeAnyLenString(&query,SingleTaskActivityQuery, SingleTask, MAXACTIVITIESPERTASK); - - if(database.RunQuery(query,QueryLength, errbuf, &result)) { - - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - int Step = atoi(row[1]); - - int ActivityID = atoi(row[2]); - - if((TaskID <= 0) || (TaskID >= MAXTASKS) || (ActivityID < 0) || (ActivityID >= MAXACTIVITIESPERTASK)) { - // This shouldn't happen, as the SELECT is bounded by MAXTASKS - LogFile->write(EQEMuLog::Error, ERR_TASK_OR_ACTIVITY_OOR, TaskID, ActivityID); - continue; - } - if(Tasks[TaskID]==nullptr) { - LogFile->write(EQEMuLog::Error, ERR_NOTASK, TaskID, ActivityID); - continue; - } - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].StepNumber = Step; - - if(Step != 0) - Tasks[TaskID]->SequenceMode = ActivitiesStepped; - - if(Step >Tasks[TaskID]->LastStep) Tasks[TaskID]->LastStep = Step; - - // Task Activities MUST be numbered sequentially from 0. If not, log an error - // and set the task to nullptr. Subsequent activities for this task will raise - // ERR_NOTASK errors. - // Change to (ActivityID != (Tasks[TaskID]->ActivityCount + 1)) to index from 1 - if(ActivityID != Tasks[TaskID]->ActivityCount) { - LogFile->write(EQEMuLog::Error, ERR_SEQERR, TaskID, ActivityID); - Tasks[TaskID] = nullptr; - continue; - } - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Type = atoi(row[3]); - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1 = new char[strlen(row[4]) + 1]; - - if(strlen(row[4])>0) - strcpy(Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1, row[4]); - else - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1[0]=0; - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2 = new char[strlen(row[5]) + 1]; - - if(strlen(row[5])>0) - strcpy(Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2, row[5]); - else - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2[0]=0; - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3 = new char[strlen(row[6]) + 1]; - - if(strlen(row[6])>0) - strcpy(Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3, row[6]); - else - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3[0]=0; - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalID = atoi(row[7]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalMethod = (TaskMethodType)atoi(row[8]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalCount = atoi(row[9]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].DeliverToNPC = atoi(row[10]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].ZoneID = atoi(row[11]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Optional = atoi(row[12]); - - _log(TASKS__GLOBALLOAD, "Activity Slot %2i: ID %i for Task %5i. Type: %3i, GoalID: %8i, " - "GoalMethod: %i, GoalCount: %3i, ZoneID:%3i", - Tasks[TaskID]->ActivityCount, ActivityID, TaskID, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Type, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalID, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalMethod, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalCount, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].ZoneID); - - _log(TASKS__GLOBALLOAD, " Text1: %s", - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1); - _log(TASKS__GLOBALLOAD, " Text2: %s", - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2); - _log(TASKS__GLOBALLOAD, " Text3: %s", - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3); - - Tasks[TaskID]->ActivityCount++; - - } - mysql_free_result(result); - safe_delete_array(query); - - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, " + "`text1`, `text2`, `text3`, `goalid`, `goalmethod`, " + "`goalcount`, `delivertonpc`, `zoneid`, `optional` " + "FROM `activities` " + "WHERE `taskid` = %i AND `activityid` < %i " + "ORDER BY taskid, activityid ASC", singleTask, MAXACTIVITIESPERTASK); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); return false; + } + + for(auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + int step = atoi(row[1]); + + int activityID = atoi(row[2]); + + if((taskID <= 0) || (taskID >= MAXTASKS) || (activityID < 0) || (activityID >= MAXACTIVITIESPERTASK)) { + // This shouldn't happen, as the SELECT is bounded by MAXTASKS + LogFile->write(EQEMuLog::Error, "[TASKS]Task or Activity ID (%i, %i) out of range while loading " + "activities from database", taskID, activityID); + continue; + } + + if(Tasks[taskID]==nullptr) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity for non-existent task (%i, %i) while loading activities from database", taskID, activityID); + continue; + } + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].StepNumber = step; + + if(step != 0) + Tasks[taskID]->SequenceMode = ActivitiesStepped; + + if(step >Tasks[taskID]->LastStep) + Tasks[taskID]->LastStep = step; + + // Task Activities MUST be numbered sequentially from 0. If not, log an error + // and set the task to nullptr. Subsequent activities for this task will raise + // ERR_NOTASK errors. + // Change to (activityID != (Tasks[taskID]->ActivityCount + 1)) to index from 1 + if(activityID != Tasks[taskID]->ActivityCount) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activities for Task %i are not sequential starting at 0. Not loading task.", taskID, activityID); + Tasks[taskID] = nullptr; + continue; + } + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Type = atoi(row[3]); + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1 = new char[strlen(row[4]) + 1]; + + if(strlen(row[4])>0) + strcpy(Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1, row[4]); + else + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1[0]=0; + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2 = new char[strlen(row[5]) + 1]; + + if(strlen(row[5])>0) + strcpy(Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2, row[5]); + else + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2[0]=0; + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3 = new char[strlen(row[6]) + 1]; + + if(strlen(row[6])>0) + strcpy(Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3, row[6]); + else + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3[0]=0; + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID = atoi(row[7]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod = (TaskMethodType)atoi(row[8]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount = atoi(row[9]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].DeliverToNPC = atoi(row[10]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID = atoi(row[11]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Optional = atoi(row[12]); + + _log(TASKS__GLOBALLOAD, "Activity Slot %2i: ID %i for Task %5i. Type: %3i, GoalID: %8i, " + "GoalMethod: %i, GoalCount: %3i, ZoneID:%3i", + Tasks[taskID]->ActivityCount, activityID, taskID, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Type, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID); + + _log(TASKS__GLOBALLOAD, " Text1: %s", Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1); + _log(TASKS__GLOBALLOAD, " Text2: %s", Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2); + _log(TASKS__GLOBALLOAD, " Text3: %s", Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3); + + Tasks[taskID]->ActivityCount++; } + return true; } From 45ef740244093b9682496b058d12080351a70fa1 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 7 Oct 2014 19:02:07 -0400 Subject: [PATCH 302/368] Fix issue with Return Home button always being available --- world/worlddb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 32c4ada48..97b2a74d5 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -103,7 +103,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* if (RuleB(World, EnableReturnHomeButton)) { int now = time(nullptr); - if ((now - atoi(row[8])) >= RuleI(World, MinOfflineTimeToReturnHome)) + if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) cs->gohome[char_num] = 1; } From 6474e1353f9e7ae12dfe3a80a36c91c530d65a4a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 7 Oct 2014 22:54:12 -0400 Subject: [PATCH 303/368] Fix for a few spell triggers using correct resist dif --- zone/mob.cpp | 4 ++-- zone/spell_effects.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 2441ca0bc..8d53d50ef 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3064,7 +3064,7 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) { // If we trigger an effect then its over. if (IsValidSpell(spells[spell_id].base2[i])){ - SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff); + SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff); return true; } } @@ -3083,7 +3083,7 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) if(MakeRandomInt(0, 100) <= spells[spell_id].base[effect]) { if (IsValidSpell(spells[spell_id].base2[effect])){ - SpellFinished(spells[spell_id].base2[effect], target, 10, 0, -1, spells[spell_id].ResistDiff); + SpellFinished(spells[spell_id].base2[effect], target, 10, 0, -1, spells[spells[spell_id].base2[effect]].ResistDiff); return true; //Only trigger once of these per spell effect. } } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index b634ca4a3..7496a07a5 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2738,7 +2738,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (caster && IsValidSpell(spells[spell_id].base2[i])){ if(MakeRandomInt(0, 100) <= spells[spell_id].base[i]) - caster->SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spell_id].ResistDiff); + caster->SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff); } break; } From 2dacb523fc29fec587865cb4aa4a6ed2c7928b70 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 7 Oct 2014 23:40:13 -0400 Subject: [PATCH 304/368] Rework OPCharCreate logic with tutorial flag Hopefully resolves issues with toons on Titanium spawning out of bounds when starting in tutorial. Resolves setting home bind to tutorial and being out of bounds. --- changelog.txt | 3 ++ common/eq_packet_structs.h | 2 +- common/patches/client62.cpp | 1 + common/patches/client62_structs.h | 2 +- common/patches/rof.cpp | 9 ++--- common/patches/sod.cpp | 8 ++--- common/patches/sof.cpp | 8 ++--- common/patches/titanium.cpp | 1 + common/patches/titanium_structs.h | 2 +- common/patches/underfoot.cpp | 8 ++--- world/client.cpp | 55 +++++++++++++++++++------------ 11 files changed, 50 insertions(+), 49 deletions(-) diff --git a/changelog.txt b/changelog.txt index ed32c03cd..d00864914 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/07/2014 == +demonstar55: Identified tutorial flag in all charcreate packets, reworked logic to correctly set homes binds + == 10/05/2014 == Uleat: Added Server<->Corpse slot translators needed for re-enumeration (inactive until phased in) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index a022626e1..f49c0106c 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -689,7 +689,7 @@ struct CharCreate_Struct /*0076*/ uint32 drakkin_heritage; // added for SoF /*0080*/ uint32 drakkin_tattoo; // added for SoF /*0084*/ uint32 drakkin_details; // added for SoF -/*0088*/ +/*0088*/ uint32 tutorial; }; /* diff --git a/common/patches/client62.cpp b/common/patches/client62.cpp index 5afd76bcd..d734a29ec 100644 --- a/common/patches/client62.cpp +++ b/common/patches/client62.cpp @@ -1038,6 +1038,7 @@ namespace Client62 IN(face); IN(eyecolor1); IN(eyecolor2); + IN(tutorial); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/client62_structs.h b/common/patches/client62_structs.h index 24e273682..8c80e34a5 100644 --- a/common/patches/client62_structs.h +++ b/common/patches/client62_structs.h @@ -558,7 +558,7 @@ struct CharCreate_Struct /*0128*/ uint32 face; /*0132*/ uint32 eyecolor1; //its possiable we could have these switched /*0136*/ uint32 eyecolor2; //since setting one sets the other we really can't check -/*0140*/ uint32 unknown140; +/*0140*/ uint32 tutorial; //assumptions are bad! But guessed }; /* diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 5fb0f9eed..db1c5487e 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -4036,12 +4036,7 @@ namespace RoF IN(race); IN(class_); IN(deity); - - if (RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - + IN(start_zone); IN(haircolor); IN(beard); IN(beardcolor); @@ -4059,7 +4054,7 @@ namespace RoF IN(WIS); IN(INT); IN(CHA); - //IN(tutorial); + IN(tutorial); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 3501e73f0..8c64c479f 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -2705,12 +2705,7 @@ namespace SoD IN(hairstyle); IN(gender); IN(race); - - if (RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - + IN(start_zone); IN(haircolor); IN(deity); IN(STR); @@ -2723,6 +2718,7 @@ namespace SoD IN(face); IN(eyecolor1); IN(eyecolor2); + IN(tutorial); IN(drakkin_heritage); IN(drakkin_tattoo); IN(drakkin_details); diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 80c0cb7cf..5a1d69bf4 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -2105,12 +2105,7 @@ namespace SoF IN(hairstyle); IN(gender); IN(race); - - if (RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - + IN(start_zone); IN(haircolor); IN(deity); IN(STR); @@ -2123,6 +2118,7 @@ namespace SoF IN(face); IN(eyecolor1); IN(eyecolor2); + IN(tutorial); IN(drakkin_heritage); IN(drakkin_tattoo); IN(drakkin_details); diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 6c2591efe..0366e73d8 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1395,6 +1395,7 @@ namespace Titanium IN(face); IN(eyecolor1); IN(eyecolor2); + IN(tutorial); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 0e74af75e..17beb6479 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -565,7 +565,7 @@ struct CharCreate_Struct /*0064*/ uint32 face; // Could be unknown0076 /*0068*/ uint32 eyecolor1; //its possiable we could have these switched /*0073*/ uint32 eyecolor2; //since setting one sets the other we really can't check -/*0076*/ uint32 unknown0076; // Could be face +/*0076*/ uint32 tutorial; /*0080*/ }; diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index 34ef61f6e..1caf65bf3 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -3007,12 +3007,7 @@ namespace Underfoot IN(hairstyle); IN(gender); IN(race); - - if (RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - + IN(start_zone); IN(haircolor); IN(deity); IN(STR); @@ -3025,6 +3020,7 @@ namespace Underfoot IN(face); IN(eyecolor1); IN(eyecolor2); + IN(tutorial); IN(drakkin_heritage); IN(drakkin_tattoo); IN(drakkin_details); diff --git a/world/client.cpp b/world/client.cpp index a373004e1..a8f269be1 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1345,22 +1345,23 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { char startzone[50]={0}; uint32 i; struct in_addr in; - + int stats_sum = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA; in.s_addr = GetIP(); - clog(WORLD__CLIENT,"Character creation request from %s LS#%d (%s:%d) : ", GetCLE()->LSName(), GetCLE()->LSID(), inet_ntoa(in), GetPort()); - clog(WORLD__CLIENT,"Name: %s", name); - clog(WORLD__CLIENT,"Race: %d Class: %d Gender: %d Deity: %d Start zone: %d", + clog(WORLD__CLIENT, "Character creation request from %s LS#%d (%s:%d) : ", GetCLE()->LSName(), GetCLE()->LSID(), inet_ntoa(in), GetPort()); + clog(WORLD__CLIENT, "Name: %s", name); + clog(WORLD__CLIENT, "Race: %d Class: %d Gender: %d Deity: %d Start zone: %d", cc->race, cc->class_, cc->gender, cc->deity, cc->start_zone); - clog(WORLD__CLIENT,"STR STA AGI DEX WIS INT CHA Total"); - clog(WORLD__CLIENT,"%3d %3d %3d %3d %3d %3d %3d %3d", + clog(WORLD__CLIENT, "STR STA AGI DEX WIS INT CHA Total"); + clog(WORLD__CLIENT, "%3d %3d %3d %3d %3d %3d %3d %3d", cc->STR, cc->STA, cc->AGI, cc->DEX, cc->WIS, cc->INT, cc->CHA, stats_sum); - clog(WORLD__CLIENT,"Face: %d Eye colors: %d %d", cc->face, cc->eyecolor1, cc->eyecolor2); - clog(WORLD__CLIENT,"Hairstyle: %d Haircolor: %d", cc->hairstyle, cc->haircolor); - clog(WORLD__CLIENT,"Beard: %d Beardcolor: %d", cc->beard, cc->beardcolor); + clog(WORLD__CLIENT, "Face: %d Eye colors: %d %d", cc->face, cc->eyecolor1, cc->eyecolor2); + clog(WORLD__CLIENT, "Hairstyle: %d Haircolor: %d", cc->hairstyle, cc->haircolor); + clog(WORLD__CLIENT, "Beard: %d Beardcolor: %d", cc->beard, cc->beardcolor); + clog(WORLD__CLIENT, "Home: %d Tutorial: %d", cc->start_zone, cc->tutorial); /* Validate the char creation struct */ if(ClientVersionBit & BIT_SoFAndLater) { @@ -1475,22 +1476,34 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { pp.x = pp.y = pp.z = -1; } - if(!pp.binds[0].zoneId) { - pp.binds[0].zoneId = pp.zone_id; - pp.binds[0].x = pp.x; - pp.binds[0].y = pp.y; - pp.binds[0].z = pp.z; - pp.binds[0].heading = pp.heading; + /* Set Home Binds */ + if(!pp.binds[4].zoneId) { + pp.binds[4].zoneId = pp.zone_id; + pp.binds[4].x = pp.x; + pp.binds[4].y = pp.y; + pp.binds[4].z = pp.z; + pp.binds[4].heading = pp.heading; } - /* Set Starting city */ - pp.binds[4] = pp.binds[0]; + /* Overrides if we have the tutorial flag set! */ + if (cc->tutorial && RuleB(World, EnableTutorialButton)) { + pp.zone_id = RuleI(World, TutorialZoneID); + database.GetSafePoints(pp.zone_id, 0, &pp.x, &pp.y, &pp.z); + } + /* Will either be the same as home or tutorial */ + pp.binds[0].zoneId = pp.zone_id; + pp.binds[0].x = pp.x; + pp.binds[0].y = pp.y; + pp.binds[0].z = pp.z; + pp.binds[0].heading = pp.heading; - clog(WORLD__CLIENT,"Current location: %s %0.2f, %0.2f, %0.2f, %0.2f", - database.GetZoneName(pp.zone_id), pp.x, pp.y, pp.z, pp.heading); - clog(WORLD__CLIENT,"Bind location: %s %0.2f, %0.2f, %0.2f", - database.GetZoneName(pp.binds[0].zoneId), pp.binds[0].x, pp.binds[0].y, pp.binds[0].z); + clog(WORLD__CLIENT,"Current location: %s (%d) %0.2f, %0.2f, %0.2f, %0.2f", + database.GetZoneName(pp.zone_id), pp.zone_id, pp.x, pp.y, pp.z, pp.heading); + clog(WORLD__CLIENT,"Bind location: %s (%d) %0.2f, %0.2f, %0.2f", + database.GetZoneName(pp.binds[0].zoneId), pp.binds[0].zoneId, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z); + clog(WORLD__CLIENT,"Home location: %s (%d) %0.2f, %0.2f, %0.2f", + database.GetZoneName(pp.binds[4].zoneId), pp.binds[4].zoneId, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z); /* Starting Items inventory */ database.SetStartingItems(&pp, &inv, pp.race, pp.class_, pp.deity, pp.zone_id, pp.name, GetAdmin()); From 69336d1e530c52be8586bf215f5bfe4a6d8a874d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 8 Oct 2014 01:18:38 -0400 Subject: [PATCH 305/368] Minor clean ups to Client::OPCharCreate --- world/client.cpp | 80 ++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 47 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index a8f269be1..61b0cf4d4 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1337,14 +1337,15 @@ void Client::SendApproveWorld() safe_delete(outapp); } -bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { +bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) +{ PlayerProfile_Struct pp; ExtendedProfile_Struct ext; Inventory inv; time_t bday = time(nullptr); char startzone[50]={0}; uint32 i; - struct in_addr in; + struct in_addr in; int stats_sum = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA; @@ -1352,8 +1353,8 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { clog(WORLD__CLIENT, "Character creation request from %s LS#%d (%s:%d) : ", GetCLE()->LSName(), GetCLE()->LSID(), inet_ntoa(in), GetPort()); clog(WORLD__CLIENT, "Name: %s", name); - clog(WORLD__CLIENT, "Race: %d Class: %d Gender: %d Deity: %d Start zone: %d", - cc->race, cc->class_, cc->gender, cc->deity, cc->start_zone); + clog(WORLD__CLIENT, "Race: %d Class: %d Gender: %d Deity: %d Start zone: %d Tutorial: %s", + cc->race, cc->class_, cc->gender, cc->deity, cc->start_zone, cc->tutorial ? "true" : "false"); clog(WORLD__CLIENT, "STR STA AGI DEX WIS INT CHA Total"); clog(WORLD__CLIENT, "%3d %3d %3d %3d %3d %3d %3d %3d", cc->STR, cc->STA, cc->AGI, cc->DEX, cc->WIS, cc->INT, cc->CHA, @@ -1361,16 +1362,15 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { clog(WORLD__CLIENT, "Face: %d Eye colors: %d %d", cc->face, cc->eyecolor1, cc->eyecolor2); clog(WORLD__CLIENT, "Hairstyle: %d Haircolor: %d", cc->hairstyle, cc->haircolor); clog(WORLD__CLIENT, "Beard: %d Beardcolor: %d", cc->beard, cc->beardcolor); - clog(WORLD__CLIENT, "Home: %d Tutorial: %d", cc->start_zone, cc->tutorial); /* Validate the char creation struct */ - if(ClientVersionBit & BIT_SoFAndLater) { - if(!CheckCharCreateInfoSoF(cc)) { + if (ClientVersionBit & BIT_SoFAndLater) { + if (!CheckCharCreateInfoSoF(cc)) { clog(WORLD__CLIENT_ERR,"CheckCharCreateInfo did not validate the request (bad race/class/stats)"); return false; } } else { - if(!CheckCharCreateInfoTitanium(cc)) { + if (!CheckCharCreateInfoTitanium(cc)) { clog(WORLD__CLIENT_ERR,"CheckCharCreateInfo did not validate the request (bad race/class/stats)"); return false; } @@ -1406,18 +1406,16 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { pp.lastlogin = bday; pp.level = 1; pp.points = 5; - pp.cur_hp = 1000; // 1k hp during dev only + pp.cur_hp = 1000; // 1k hp during dev only pp.hunger_level = 6000; pp.thirst_level = 6000; - /* Racial Languages */ - SetRacialLanguages( &pp ); - SetRaceStartingSkills( &pp ); - SetClassStartingSkills( &pp ); + /* Set Racial and Class specific language and skills */ + SetRacialLanguages(&pp); + SetRaceStartingSkills(&pp); + SetClassStartingSkills(&pp); SetClassLanguages(&pp); pp.skills[SkillSenseHeading] = 200; - // Some one fucking fix this to use a field name. -Doodman - //pp.unknown3596[28] = 15; // @bp: This is to enable disc usage // strcpy(pp.servername, WorldConfig::get()->ShortName.c_str()); @@ -1430,60 +1428,50 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { for(i = 0; i < BUFF_COUNT; i++) pp.buffs[i].spellid = 0xFFFF; - /* - Was memset(pp.unknown3704, 0xffffffff, 8); - but I dont think thats what you really wanted to do... - memset is byte based - */ - /* If server is PVP by default, make all character set to it. */ pp.pvp = database.GetServerType() == 1 ? 1 : 0; /* If it is an SoF Client and the SoF Start Zone rule is set, send new chars there */ - if((ClientVersionBit & BIT_SoFAndLater) && (RuleI(World, SoFStartZoneID) > 0)) { - clog(WORLD__CLIENT,"Found 'SoFStartZoneID' rule setting: %i", (RuleI(World, SoFStartZoneID))); - pp.zone_id = (RuleI(World, SoFStartZoneID)); - if(pp.zone_id) + if (ClientVersionBit & BIT_SoFAndLater && RuleI(World, SoFStartZoneID) > 0) { + clog(WORLD__CLIENT,"Found 'SoFStartZoneID' rule setting: %i", RuleI(World, SoFStartZoneID)); + pp.zone_id = RuleI(World, SoFStartZoneID); + if (pp.zone_id) database.GetSafePoints(pp.zone_id, 0, &pp.x, &pp.y, &pp.z); else - clog(WORLD__CLIENT_ERR,"Error getting zone id for Zone ID %i", (RuleI(World, SoFStartZoneID))); - } - else { + clog(WORLD__CLIENT_ERR,"Error getting zone id for Zone ID %i", RuleI(World, SoFStartZoneID)); + } else { /* if there's a startzone variable put them in there */ - if(database.GetVariable("startzone", startzone, 50)) { + if (database.GetVariable("startzone", startzone, 50)) { clog(WORLD__CLIENT,"Found 'startzone' variable setting: %s", startzone); pp.zone_id = database.GetZoneID(startzone); - if(pp.zone_id) + if (pp.zone_id) database.GetSafePoints(pp.zone_id, 0, &pp.x, &pp.y, &pp.z); else clog(WORLD__CLIENT_ERR,"Error getting zone id for '%s'", startzone); - } - else{ /* otherwise use normal starting zone logic */ + } else { /* otherwise use normal starting zone logic */ bool ValidStartZone = false; - - if(ClientVersionBit & BIT_TitaniumAndEarlier) + if (ClientVersionBit & BIT_TitaniumAndEarlier) ValidStartZone = database.GetStartZone(&pp, cc); else ValidStartZone = database.GetStartZoneSoF(&pp, cc); - if(!ValidStartZone) + if (!ValidStartZone) return false; } } - if(!pp.zone_id) { + /* just in case */ + if (!pp.zone_id) { pp.zone_id = 1; // qeynos pp.x = pp.y = pp.z = -1; } /* Set Home Binds */ - if(!pp.binds[4].zoneId) { - pp.binds[4].zoneId = pp.zone_id; - pp.binds[4].x = pp.x; - pp.binds[4].y = pp.y; - pp.binds[4].z = pp.z; - pp.binds[4].heading = pp.heading; - } + pp.binds[4].zoneId = pp.zone_id; + pp.binds[4].x = pp.x; + pp.binds[4].y = pp.y; + pp.binds[4].z = pp.z; + pp.binds[4].heading = pp.heading; /* Overrides if we have the tutorial flag set! */ if (cc->tutorial && RuleB(World, EnableTutorialButton)) { @@ -1514,10 +1502,8 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { clog(WORLD__CLIENT_ERR,"Character creation failed: %s", pp.name); return false; } - else { - clog(WORLD__CLIENT,"Character creation successful: %s", pp.name); - return true; - } + clog(WORLD__CLIENT,"Character creation successful: %s", pp.name); + return true; } // returns true if the request is ok, false if there's an error From b6294a28cbb56484188a5aa15e770bb0b5e6cb8b Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 8 Oct 2014 01:28:03 -0400 Subject: [PATCH 306/368] Minor clean ups to CheckCharCreateInfo --- world/client.cpp | 89 ++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 52 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index 61b0cf4d4..f3f1a78c8 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1507,33 +1507,28 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) } // returns true if the request is ok, false if there's an error -bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) { - if(!cc) return false; +bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) +{ + if (!cc) + return false; _log(WORLD__CLIENT, "Validating char creation info..."); RaceClassCombos class_combo; bool found = false; int combos = character_create_race_class_combos.size(); - for(int i = 0; i < combos; ++i) { - if(character_create_race_class_combos[i].Class == cc->class_ && - character_create_race_class_combos[i].Race == cc->race && - character_create_race_class_combos[i].Deity == cc->deity) { - if(RuleB(World, EnableTutorialButton) && - (RuleI(World, TutorialZoneID) == cc->start_zone || - (character_create_race_class_combos[i].Zone == cc->start_zone))) { - class_combo = character_create_race_class_combos[i]; - found = true; - break; - } else if(character_create_race_class_combos[i].Zone == cc->start_zone) { - class_combo = character_create_race_class_combos[i]; - found = true; - break; - } + for (int i = 0; i < combos; ++i) { + if (character_create_race_class_combos[i].Class == cc->class_ && + character_create_race_class_combos[i].Race == cc->race && + character_create_race_class_combos[i].Deity == cc->deity && + character_create_race_class_combos[i].Zone == cc->start_zone) { + class_combo = character_create_race_class_combos[i]; + found = true; + break; } } - if(!found) { + if (!found) { _log(WORLD__CLIENT_ERR, "Could not find class/race/deity/start_zone combination"); return false; } @@ -1542,15 +1537,15 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) { uint32 allocs = character_create_allocations.size(); RaceClassAllocation allocation = {0}; found = false; - for(int i = 0; i < combos; ++i) { - if(character_create_allocations[i].Index == class_combo.AllocationIndex) { + for (int i = 0; i < allocs; ++i) { + if (character_create_allocations[i].Index == class_combo.AllocationIndex) { allocation = character_create_allocations[i]; found = true; break; } } - if(!found) { + if (!found) { _log(WORLD__CLIENT_ERR, "Could not find starting stats for selected character combo, cannot verify stats"); return false; } @@ -1563,37 +1558,37 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) { allocation.DefaultPointAllocation[5] + allocation.DefaultPointAllocation[6]; - if(cc->STR > allocation.BaseStats[0] + max_stats || cc->STR < allocation.BaseStats[0]) { + if (cc->STR > allocation.BaseStats[0] + max_stats || cc->STR < allocation.BaseStats[0]) { _log(WORLD__CLIENT_ERR, "Strength out of range"); return false; } - if(cc->DEX > allocation.BaseStats[1] + max_stats || cc->DEX < allocation.BaseStats[1]) { + if (cc->DEX > allocation.BaseStats[1] + max_stats || cc->DEX < allocation.BaseStats[1]) { _log(WORLD__CLIENT_ERR, "Dexterity out of range"); return false; } - if(cc->AGI > allocation.BaseStats[2] + max_stats || cc->AGI < allocation.BaseStats[2]) { + if (cc->AGI > allocation.BaseStats[2] + max_stats || cc->AGI < allocation.BaseStats[2]) { _log(WORLD__CLIENT_ERR, "Agility out of range"); return false; } - if(cc->STA > allocation.BaseStats[3] + max_stats || cc->STA < allocation.BaseStats[3]) { + if (cc->STA > allocation.BaseStats[3] + max_stats || cc->STA < allocation.BaseStats[3]) { _log(WORLD__CLIENT_ERR, "Stamina out of range"); return false; } - if(cc->INT > allocation.BaseStats[4] + max_stats || cc->INT < allocation.BaseStats[4]) { + if (cc->INT > allocation.BaseStats[4] + max_stats || cc->INT < allocation.BaseStats[4]) { _log(WORLD__CLIENT_ERR, "Intelligence out of range"); return false; } - if(cc->WIS > allocation.BaseStats[5] + max_stats || cc->WIS < allocation.BaseStats[5]) { + if (cc->WIS > allocation.BaseStats[5] + max_stats || cc->WIS < allocation.BaseStats[5]) { _log(WORLD__CLIENT_ERR, "Wisdom out of range"); return false; } - if(cc->CHA > allocation.BaseStats[6] + max_stats || cc->CHA < allocation.BaseStats[6]) { + if (cc->CHA > allocation.BaseStats[6] + max_stats || cc->CHA < allocation.BaseStats[6]) { _log(WORLD__CLIENT_ERR, "Charisma out of range"); return false; } @@ -1606,7 +1601,7 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) { current_stats += cc->INT - allocation.BaseStats[4]; current_stats += cc->WIS - allocation.BaseStats[5]; current_stats += cc->CHA - allocation.BaseStats[6]; - if(current_stats > max_stats) { + if (current_stats > max_stats) { _log(WORLD__CLIENT_ERR, "Current Stats > Maximum Stats"); return false; } @@ -1685,7 +1680,8 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) { /*Berserker*/ false, true, false, false, false, false, false, true, true, true, false, false, false, true, false, false} };//Initial table by kathgar, editted by Wiz for accuracy, solar too - if(!cc) return false; + if (!cc) + return false; _log(WORLD__CLIENT,"Validating char creation info..."); @@ -1699,19 +1695,16 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) // if out of range looking it up in the table would crash stuff // so we return from these - if(classtemp >= PLAYER_CLASS_COUNT) - { + if (classtemp >= PLAYER_CLASS_COUNT) { _log(WORLD__CLIENT_ERR," class is out of range"); return false; } - if(racetemp >= _TABLE_RACES) - { + if (racetemp >= _TABLE_RACES) { _log(WORLD__CLIENT_ERR," race is out of range"); return false; } - if(!ClassRaceLookupTable[classtemp][racetemp]) //Lookup table better than a bunch of ifs? - { + if (!ClassRaceLookupTable[classtemp][racetemp]) { //Lookup table better than a bunch of ifs? _log(WORLD__CLIENT_ERR," invalid race/class combination"); // we return from this one, since if it's an invalid combination our table // doesn't have meaningful values for the stats @@ -1739,44 +1732,36 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) // NOTE: these could just be else if, but i want to see all the stats // that are messed up not just the first hit - if(bTOTAL + stat_points != cTOTAL) - { + if (bTOTAL + stat_points != cTOTAL) { _log(WORLD__CLIENT_ERR," stat points total doesn't match expected value: expecting %d got %d", bTOTAL + stat_points, cTOTAL); Charerrors++; } - if(cc->STR > bSTR + stat_points || cc->STR < bSTR) - { + if (cc->STR > bSTR + stat_points || cc->STR < bSTR) { _log(WORLD__CLIENT_ERR," stat STR is out of range"); Charerrors++; } - if(cc->STA > bSTA + stat_points || cc->STA < bSTA) - { + if (cc->STA > bSTA + stat_points || cc->STA < bSTA) { _log(WORLD__CLIENT_ERR," stat STA is out of range"); Charerrors++; } - if(cc->AGI > bAGI + stat_points || cc->AGI < bAGI) - { + if (cc->AGI > bAGI + stat_points || cc->AGI < bAGI) { _log(WORLD__CLIENT_ERR," stat AGI is out of range"); Charerrors++; } - if(cc->DEX > bDEX + stat_points || cc->DEX < bDEX) - { + if (cc->DEX > bDEX + stat_points || cc->DEX < bDEX) { _log(WORLD__CLIENT_ERR," stat DEX is out of range"); Charerrors++; } - if(cc->WIS > bWIS + stat_points || cc->WIS < bWIS) - { + if (cc->WIS > bWIS + stat_points || cc->WIS < bWIS) { _log(WORLD__CLIENT_ERR," stat WIS is out of range"); Charerrors++; } - if(cc->INT > bINT + stat_points || cc->INT < bINT) - { + if (cc->INT > bINT + stat_points || cc->INT < bINT) { _log(WORLD__CLIENT_ERR," stat INT is out of range"); Charerrors++; } - if(cc->CHA > bCHA + stat_points || cc->CHA < bCHA) - { + if (cc->CHA > bCHA + stat_points || cc->CHA < bCHA) { _log(WORLD__CLIENT_ERR," stat CHA is out of range"); Charerrors++; } From a325380884d6d8956608de1c4c44d22d1f4a140c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 8 Oct 2014 03:36:31 -0400 Subject: [PATCH 307/368] Implement mana/hp/end replenishments if logged off for long enough Default set to on with a default time of 6 hours (21600 seconds) --- common/ruletypes.h | 2 ++ .../optional/2014_10_08_LoggedOffReplenishments.sql | 2 ++ zone/client_packet.cpp | 10 ++++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 utils/sql/git/optional/2014_10_08_LoggedOffReplenishments.sql diff --git a/common/ruletypes.h b/common/ruletypes.h index c5004ffd1..528770ecb 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -201,6 +201,8 @@ RULE_INT ( Zone, EbonCrystalItemID, 40902) RULE_INT ( Zone, RadiantCrystalItemID, 40903) RULE_BOOL ( Zone, LevelBasedEXPMods, false) // Allows you to use the level_exp_mods table in consideration to your players EXP hits RULE_INT ( Zone, WeatherTimer, 600) // Weather timer when no duration is available +RULE_BOOL ( Zone, EnableLoggedOffReplenishments, true) +RULE_INT ( Zone, MinOfflineTimeToReplenishments, 21600) // 21600 seconds is 6 Hours RULE_CATEGORY_END() RULE_CATEGORY( Map ) diff --git a/utils/sql/git/optional/2014_10_08_LoggedOffReplenishments.sql b/utils/sql/git/optional/2014_10_08_LoggedOffReplenishments.sql new file mode 100644 index 000000000..cd553784c --- /dev/null +++ b/utils/sql/git/optional/2014_10_08_LoggedOffReplenishments.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Zone:EnableLoggedOffReplenishments', 'true', 'Replenish mana/hp/end if logged off for MinOfflineTimeToReplenishments'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Zone:MinOfflineTimeToReplenishments', '21600', 'Minimum logged off time to trigger replenish mana/hp/end'); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 7edd9202b..988a6d5f5 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1375,7 +1375,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) query = StringFormat("SELECT `lfp`, `lfg`, `xtargets`, `firstlogon`, `guild_id`, `rank` FROM `character_data` LEFT JOIN `guild_members` ON `id` = `char_id` WHERE `id` = %i", cid); results = database.QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { - m_pp.lastlogin = time(nullptr); if (row[4] && atoi(row[4]) > 0){ guild_id = atoi(row[4]); if (row[5] != nullptr){ guildrank = atoi(row[5]); } @@ -1661,11 +1660,18 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) #endif CalcBonuses(); + if (RuleB(Zone, EnableLoggedOffReplenishments) && + time(nullptr) - m_pp.lastlogin >= RuleI(Zone, MinOfflineTimeToReplenishments)) { + m_pp.cur_hp = GetMaxHP(); + m_pp.mana = GetMaxMana(); + m_pp.endurance = GetMaxEndurance(); + } + if (m_pp.cur_hp <= 0) m_pp.cur_hp = GetMaxHP(); SetHP(m_pp.cur_hp); - Mob::SetMana(m_pp.mana); + Mob::SetMana(m_pp.mana); // mob function doesn't send the packet SetEndurance(m_pp.endurance); /* Update LFP in case any (or all) of our group disbanded while we were zoning. */ From e2d52ec3e557d42e69d942803effb2a7d72989ac Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 8 Oct 2014 13:43:32 -0400 Subject: [PATCH 308/368] Allow /setstartcity to work if home is set to tutorialb --- zone/client_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 988a6d5f5..74399274a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11832,7 +11832,7 @@ void Client::Handle_OP_SetServerFilter(const EQApplicationPacket *app) void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) { // if the character has a start city, don't let them use the command - if (m_pp.binds[4].zoneId != 0) { + if (m_pp.binds[4].zoneId != 0 && m_pp.binds[4].zoneId != 189) { Message(15, "Your home city has already been set.", m_pp.binds[4].zoneId, database.GetZoneName(m_pp.binds[4].zoneId)); return; } From e3e2b266f20670f1c3d41d769136bf3dfce05213 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 11:11:33 -0700 Subject: [PATCH 309/368] SaveClientState converted to QueryDatabase --- zone/tasks.cpp | 211 ++++++++++++++++++++++--------------------------- 1 file changed, 96 insertions(+), 115 deletions(-) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 00749a454..f8470b824 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -298,146 +298,127 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) { // in that slot had more activities than the one now occupying it. Hopefully retaining the slot number for the // duration of a session will overcome this. // - const char *TaskQuery="REPLACE INTO character_tasks (charid, taskid, slot, acceptedtime) " - "VALUES (%i, %i, %i, %i)"; - - const char *ActivityQuery="REPLACE INTO character_activities (charid, taskid, activityid, donecount, completed) " - "VALUES "; - - const char *CompletedTaskQuery="REPLACE INTO completed_tasks (charid, completedtime, taskid, activityid) " - "VALUES (%i, %i, %i, %i)"; + if(!c || !state) + return false; const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::SaveClientState %s"; - if(!c || !state) return false; + int characterID = c->CharacterID(); - int CharacterID = c->CharacterID(); - - _log(TASKS__CLIENTSAVE,"TaskManager::SaveClientState for character ID %d", CharacterID); - - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + _log(TASKS__CLIENTSAVE,"TaskManager::SaveClientState for character ID %d", characterID); if(state->ActiveTaskCount > 0) { - for(int Task=0; TaskActiveTasks[Task].TaskID; - if(TaskID==TASKSLOTEMPTY) continue; - if(state->ActiveTasks[Task].Updated) { + for(int task=0; taskActiveTasks[task].TaskID; + if(taskID==TASKSLOTEMPTY) + continue; - _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState for character ID %d, Updating TaskIndex %i TaskID %i", - CharacterID, Task, TaskID); + if(state->ActiveTasks[task].Updated) { - if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, - CharacterID, - TaskID, - Task, - state->ActiveTasks[Task].AcceptedTime), errbuf)) { + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState for character ID %d, Updating TaskIndex %i TaskID %i", characterID, task, taskID); - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } + std::string query = StringFormat("REPLACE INTO character_tasks (charid, taskid, slot, acceptedtime) " + "VALUES (%i, %i, %i, %i)", + characterID, taskID, task, state->ActiveTasks[task].AcceptedTime); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); else - state->ActiveTasks[Task].Updated = false; + state->ActiveTasks[task].Updated = false; - safe_delete_array(query); } - int UpdatedActivityCount = 0; - std::string UpdateActivityQuery = ActivityQuery; - char *buf = 0; + std::string query = "REPLACE INTO character_activities (charid, taskid, activityid, donecount, completed) " + "VALUES "; - for(int Activity=0; ActivityActivityCount; Activity++) { + int updatedActivityCount = 0; + for(int activityIndex = 0; activityIndexActivityCount; ++activityIndex) { - if(state->ActiveTasks[Task].Activity[Activity].Updated) { + if(!state->ActiveTasks[task].Activity[activityIndex].Updated) + continue; - _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientSate for character ID %d, " - "Updating Activity %i, %i", - CharacterID, Task, Activity); + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientSate for character ID %d, Updating Activity %i, %i", + characterID, task, activityIndex); - if(UpdatedActivityCount==0) { - MakeAnyLenString(&buf, "(%i, %i, %i, %i, %i)", CharacterID, TaskID, - Activity, - state->ActiveTasks[Task].Activity[Activity].DoneCount, - state->ActiveTasks[Task].Activity[Activity].State == - ActivityCompleted); - } - else { - MakeAnyLenString(&buf, ", (%i, %i, %i, %i, %i)", CharacterID, TaskID, - Activity, - state->ActiveTasks[Task].Activity[Activity].DoneCount, - state->ActiveTasks[Task].Activity[Activity].State == - ActivityCompleted); - } - UpdateActivityQuery = UpdateActivityQuery + buf; - safe_delete_array(buf); - UpdatedActivityCount++; - } + if(updatedActivityCount==0) + query += StringFormat("(%i, %i, %i, %i, %i)", + characterID, taskID, activityIndex, + state->ActiveTasks[task].Activity[activityIndex].DoneCount, + state->ActiveTasks[task].Activity[activityIndex].State == ActivityCompleted); + else + query += StringFormat(", (%i, %i, %i, %i, %i)", + characterID, taskID, activityIndex, + state->ActiveTasks[task].Activity[activityIndex].DoneCount, + state->ActiveTasks[task].Activity[activityIndex].State == ActivityCompleted); + + updatedActivityCount++; } - if(UpdatedActivityCount > 0) { - _log(TASKS__CLIENTSAVE, "Executing query %s", UpdateActivityQuery.c_str()); - if(!database.RunQuery(query,MakeAnyLenString(&query, UpdateActivityQuery.c_str()), - errbuf)) { + if(updatedActivityCount == 0) + continue; - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } - else { - state->ActiveTasks[Task].Updated=false; - for(int Activity=0; ActivityActivityCount; Activity++) - state->ActiveTasks[Task].Activity[Activity].Updated=false; + _log(TASKS__CLIENTSAVE, "Executing query %s", query.c_str()); + auto results = database.QueryDatabase(query); - } + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + continue; + } + + state->ActiveTasks[task].Updated=false; + for(int activityIndex=0; activityIndexActivityCount; ++activityIndex) + state->ActiveTasks[task].Activity[activityIndex].Updated=false; - safe_delete_array(query); - } } - - } - if(RuleB(TaskSystem, RecordCompletedTasks) && - (state->CompletedTasks.size() > (unsigned int)state->LastCompletedTaskLoaded)) { - - for(unsigned int i=state->LastCompletedTaskLoaded; iCompletedTasks.size(); i++) { - - _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState Saving Completed Task at slot %i", i); - int TaskID = state->CompletedTasks[i].TaskID; - if((TaskID<=0) || (TaskID>=MAXTASKS) || (Tasks[TaskID]==nullptr)) continue; - - // First we save a record with an ActivityID of -1. - // This indicates this task was completed at the given time. We infer that all - // none optional activities were completed. - // - if(!database.RunQuery(query,MakeAnyLenString(&query, CompletedTaskQuery, - CharacterID, - state->CompletedTasks[i].CompletedTime, - TaskID, -1), errbuf)) { - - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - continue; - } - safe_delete_array(query); - - // If the Rule to record non-optional task completion is not enabled, don't save it - if(!RuleB(TaskSystem, RecordCompletedOptionalActivities)) continue; - - // Insert one record for each completed optional task. - - for(int j=0; jActivityCount; j++) { - if(Tasks[TaskID]->Activity[j].Optional && state->CompletedTasks[i].ActivityDone[j]) { - - if(!database.RunQuery(query,MakeAnyLenString(&query, CompletedTaskQuery, - CharacterID, - state->CompletedTasks[i].CompletedTime, - TaskID, j), errbuf)) { - - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } - safe_delete_array(query); - } - } - } - state->LastCompletedTaskLoaded = state->CompletedTasks.size(); } + if(!RuleB(TaskSystem, RecordCompletedTasks) || (state->CompletedTasks.size() <= (unsigned int)state->LastCompletedTaskLoaded)) { + state->LastCompletedTaskLoaded = state->CompletedTasks.size(); + return true; + } + + const char* completedTaskQuery = "REPLACE INTO completed_tasks (charid, completedtime, taskid, activityid) " + "VALUES (%i, %i, %i, %i)"; + + for(unsigned int i=state->LastCompletedTaskLoaded; iCompletedTasks.size(); i++) { + + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState Saving Completed Task at slot %i", i); + int taskID = state->CompletedTasks[i].TaskID; + + if((taskID <= 0) || (taskID >= MAXTASKS) || (Tasks[taskID] == nullptr)) + continue; + + // First we save a record with an ActivityID of -1. + // This indicates this task was completed at the given time. We infer that all + // none optional activities were completed. + // + std::string query = StringFormat(completedTaskQuery, characterID, state->CompletedTasks[i].CompletedTime, taskID, -1); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + continue; + } + + // If the Rule to record non-optional task completion is not enabled, don't save it + if(!RuleB(TaskSystem, RecordCompletedOptionalActivities)) + continue; + + // Insert one record for each completed optional task. + + for(int j=0; jActivityCount; j++) { + if(!Tasks[taskID]->Activity[j].Optional || !state->CompletedTasks[i].ActivityDone[j]) + continue; + + query = StringFormat(completedTaskQuery, characterID, state->CompletedTasks[i].CompletedTime, taskID, j); + results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + + } + + } + + state->LastCompletedTaskLoaded = state->CompletedTasks.size(); return true; } From ad921d01d83e3529e61b637ccb0bdee5d6a9b677 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 12:30:08 -0700 Subject: [PATCH 310/368] LoadClientState converted to QueryDatabase --- zone/tasks.cpp | 425 +++++++++++++++++++++---------------------------- 1 file changed, 178 insertions(+), 247 deletions(-) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index f8470b824..89c34b477 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -451,300 +451,231 @@ void Client::RemoveClientTaskState() { bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) { - const char *TaskQuery = "SELECT `taskid`, `slot`, `acceptedtime` from `character_tasks` " - "WHERE `charid` = %i ORDER BY acceptedtime"; + if(!c || !state) + return false; - const char *ERR_TASK_OOR1 = "[TASKS]Task ID %i out of range while loading character tasks from database"; - - const char *ERR_SLOT_OOR = "[TASKS] Slot %i out of range while loading character tasks from database"; - - const char *ERR_DUP_SLOT = "[TASKS] Slot %i for Task %is is already occupied."; - - const char *ERR_MYSQLERROR1 = "[TASKS]Error in TaskManager::LoadClientState load Tasks: %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(!c || !state) return false; - - int CharacterID = c->CharacterID(); + int characterID = c->CharacterID(); state->ActiveTaskCount = 0; - _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientSate for character ID %d", CharacterID); + _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState for character ID %d", characterID); - if(database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, CharacterID), errbuf, &result)) { - - while((row = mysql_fetch_row(result))) { - - int TaskID = atoi(row[0]); - int Slot = atoi(row[1]); - - if((TaskID<0) || (TaskID>=MAXTASKS)) { - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR1, TaskID); - continue; - } - - if((Slot<0) || (Slot>=MAXACTIVETASKS)) { - LogFile->write(EQEMuLog::Error, ERR_SLOT_OOR, Slot); - continue; - } - - if(state->ActiveTasks[Slot].TaskID != TASKSLOTEMPTY) { - LogFile->write(EQEMuLog::Error, ERR_DUP_SLOT, Slot, TaskID); - continue; - } - - int acceptedtime = atoi(row[2]); - - state->ActiveTasks[Slot].TaskID = TaskID; - - state->ActiveTasks[Slot].CurrentStep = -1; - - state->ActiveTasks[Slot].AcceptedTime = acceptedtime; - - state->ActiveTasks[Slot].Updated = false; - - for(int i=0; iActiveTasks[Slot].Activity[i].ActivityID = -1; - } - - //LoadClientActivitiesForTask(CharacterID, &state->ActiveTasks[state->ActiveTaskCount]); - // Calculate which activities are active based on those completed. - //state->UnlockActivities(state->ActiveTaskCount); - - state->ActiveTaskCount++; - - _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, " - "Accepted Time: %8X", - CharacterID, TaskID,acceptedtime); - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR1, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `taskid`, `slot`, `acceptedtime` " + "FROM `character_tasks` " + "WHERE `charid` = %i ORDER BY acceptedtime", characterID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load Tasks: %s", results.ErrorMessage().c_str()); return false; + } + + for(auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + int slot = atoi(row[1]); + + if((taskID<0) || (taskID>=MAXTASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading character tasks from database", taskID); + continue; + } + + if((slot<0) || (slot>=MAXACTIVETASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS] Slot %i out of range while loading character tasks from database", slot); + continue; + } + + if(state->ActiveTasks[slot].TaskID != TASKSLOTEMPTY) { + LogFile->write(EQEMuLog::Error, "[TASKS] Slot %i for Task %is is already occupied.", slot, taskID); + continue; + } + + int acceptedtime = atoi(row[2]); + + state->ActiveTasks[slot].TaskID = taskID; + state->ActiveTasks[slot].CurrentStep = -1; + state->ActiveTasks[slot].AcceptedTime = acceptedtime; + state->ActiveTasks[slot].Updated = false; + + for(int i=0; iActiveTasks[slot].Activity[i].ActivityID = -1; + + ++state->ActiveTaskCount; + + _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, Accepted Time: %8X", characterID, taskID, acceptedtime); } // Load Activities + _log(TASKS__CLIENTLOAD, "LoadClientState. Loading activities for character ID %d", characterID); - const char *ActivityQuery = "SELECT `taskid`, `activityid`, `donecount`, `completed` " - " from `character_activities` WHERE `charid` = %i " - "ORDER BY `taskid` ASC, `activityid` ASC"; - - const char *ERR_TASK_OOR2 = "[TASKS]Task ID %i out of range while loading character activities from database"; - - const char *ERR_ACTIVITY_OOR = "[TASKS]Activity ID %i out of range while loading character activities from database"; - - const char *ERR_NOTASK = "[TASKS]Activity %i found for task %i which client does not have."; - - const char *ERR_MYSQLERROR2 = "[TASKS]Error in TaskManager::LoadClientState load Activities: %s"; - - _log(TASKS__CLIENTLOAD, "LoadClientState. Loading activities for character ID %d", CharacterID); - - - - if(database.RunQuery(query,MakeAnyLenString(&query, ActivityQuery, - CharacterID), errbuf, &result)) { - - - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - if((TaskID<0) || (TaskID>=MAXTASKS)) { - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR2, TaskID); - continue; - } - int ActivityID = atoi(row[1]); - if((ActivityID<0) || (ActivityID>=MAXACTIVITIESPERTASK)) { - LogFile->write(EQEMuLog::Error, ERR_ACTIVITY_OOR, ActivityID); - continue; - } - - // Find Active Task Slot - int ActiveTaskIndex = -1; - - for(int i=0; iActiveTasks[i].TaskID == TaskID) { - ActiveTaskIndex = i; - break; - } - } - - if(ActiveTaskIndex == -1) { - LogFile->write(EQEMuLog::Error, ERR_NOTASK, ActivityID, TaskID); - continue; - } - - int DoneCount = atoi(row[2]); - bool Completed = atoi(row[3]); - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].ActivityID = ActivityID; - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].DoneCount = DoneCount; - if(Completed) state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].State = ActivityCompleted; - else - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].State = ActivityHidden; - - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].Updated = false; - - _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, ActivityID: %i, " - "DoneCount: %i, Completed: %i", - CharacterID, TaskID, ActivityID, DoneCount, Completed); - - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR2, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT `taskid`, `activityid`, `donecount`, `completed` " + "FROM `character_activities` " + "WHERE `charid` = %i " + "ORDER BY `taskid` ASC, `activityid` ASC", characterID); + results = database.QueryDatabase(query); + if (!results.Success()){ + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load Activities: %s", results.ErrorMessage().c_str()); return false; } - const char *CompletedTaskQuery = "SELECT `taskid`, `activityid`, `completedtime` from `completed_tasks` " - "WHERE `charid` = %i ORDER BY completedtime, taskid, activityid"; + for (auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + if((taskID<0) || (taskID>=MAXTASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading character activities from database", taskID); + continue; + } - const char *ERR_TASK_OOR3 = "[TASKS]Task ID %i out of range while loading completed tasks from database"; + int activityID = atoi(row[1]); + if((activityID<0) || (activityID>=MAXACTIVITIESPERTASK)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity ID %i out of range while loading character activities from database", activityID); + continue; + } - const char *ERR_ACTIVITY_OOR2 = "[TASKS]Activity ID %i out of range while loading completed tasks from database"; + // Find Active Task Slot + int activeTaskIndex = -1; - const char *ERR_MYSQLERROR3 = "[TASKS]Error in TaskManager::LoadClientState load completed tasks: %s"; + for(int i=0; iActiveTasks[i].TaskID == taskID) { + activeTaskIndex = i; + break; + } + if(activeTaskIndex == -1) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity %i found for task %i which client does not have.", activityID, taskID); + continue; + } + int doneCount = atoi(row[2]); + bool completed = atoi(row[3]); + state->ActiveTasks[activeTaskIndex].Activity[activityID].ActivityID = activityID; + state->ActiveTasks[activeTaskIndex].Activity[activityID].DoneCount = doneCount; + if(completed) + state->ActiveTasks[activeTaskIndex].Activity[activityID].State = ActivityCompleted; + else + state->ActiveTasks[activeTaskIndex].Activity[activityID].State = ActivityHidden; + + state->ActiveTasks[activeTaskIndex].Activity[activityID].Updated = false; + + _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, ActivityID: %i, DoneCount: %i, Completed: %i", characterID, taskID, activityID, doneCount, completed); + + } if(RuleB(TaskSystem, RecordCompletedTasks)) { - if(database.RunQuery(query,MakeAnyLenString(&query, CompletedTaskQuery, - CharacterID), errbuf, &result)) { - - CompletedTaskInformation cti; - - for(int i=0; i=MAXTASKS)) { - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR3, TaskID); - continue; - } - int ActivityID = atoi(row[1]); - - // An ActivityID of -1 means mark all the none optional activities in the - // task as complete. If the Rule to record optional activities is enabled, - // subsequent records for this task will flag any optional tasks that were - // completed. - if((ActivityID<-1) || (ActivityID>=MAXACTIVITIESPERTASK)) { - LogFile->write(EQEMuLog::Error, ERR_ACTIVITY_OOR2, ActivityID); - continue; - } - int CompletedTime = atoi(row[2]); - - if((PreviousTaskID != -1) && ((TaskID != PreviousTaskID) || - (CompletedTime != PreviousCompletedTime))) { - - state->CompletedTasks.push_back(cti); - for(int i=0; iActivityCount; i++) - if(!Task->Activity[i].Optional) - cti.ActivityDone[i] = true; - } - else - cti.ActivityDone[ActivityID] = true; - - } - if(PreviousTaskID != -1) - state->CompletedTasks.push_back(cti); - - state->LastCompletedTaskLoaded = state->CompletedTasks.size(); - - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR3, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT `taskid`, `activityid`, `completedtime` " + "FROM `completed_tasks` " + "WHERE `charid` = %i ORDER BY completedtime, taskid, activityid", + characterID); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load completed tasks: %s", results.ErrorMessage().c_str()); return false; + } + + CompletedTaskInformation cti; + + for(int i=0; i=MAXTASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading completed tasks from database", taskID); + continue; + } + + // An ActivityID of -1 means mark all the none optional activities in the + // task as complete. If the Rule to record optional activities is enabled, + // subsequent records for this task will flag any optional tasks that were + // completed. + int activityID = atoi(row[1]); + if((activityID<-1) || (activityID>=MAXACTIVITIESPERTASK)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity ID %i out of range while loading completed tasks from database", activityID); + continue; + } + + int completedTime = atoi(row[2]); + if((previousTaskID != -1) && ((taskID != previousTaskID) || (completedTime != previousCompletedTime))) { + state->CompletedTasks.push_back(cti); + for(int i=0; iActivityCount; i++) + if(!task->Activity[i].Optional) + cti.ActivityDone[i] = true; + } + else + cti.ActivityDone[activityID] = true; + + } + + if(previousTaskID != -1) + state->CompletedTasks.push_back(cti); + + state->LastCompletedTaskLoaded = state->CompletedTasks.size(); + + } + + query = StringFormat("SELECT `taskid` FROM character_enabledtasks " + "WHERE `charid` = %i AND `taskid` >0 AND `taskid` < %i " + "ORDER BY `taskid` ASC", + characterID, MAXTASKS); + results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load enabled tasks: %s", results.ErrorMessage().c_str()); + else + for (auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + state->EnabledTasks.push_back(taskID); + _log(TASKS__CLIENTLOAD, "Adding TaskID %i to enabled tasks", taskID); } - } - - const char *EnabledTaskQuery = "SELECT `taskid` FROM character_enabledtasks WHERE `charid` = %i " - "AND `taskid` >0 AND `taskid` < %i ORDER BY `taskid` ASC"; - - const char *ERR_MYSQLERROR4 = "[TASKS]Error in TaskManager::LoadClientState load enabled tasks: %s"; - - if(database.RunQuery(query,MakeAnyLenString(&query, EnabledTaskQuery, - CharacterID, MAXTASKS), errbuf, &result)) { - - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - state->EnabledTasks.push_back(TaskID); - _log(TASKS__CLIENTLOAD, "Adding TaskID %i to enabled tasks", TaskID); - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR4, errbuf); - safe_delete_array(query); - } // Check that there is an entry in the client task state for every activity in each task // This should only break if a ServerOP adds or deletes activites for a task that players already // have active, or due to a bug. - - - const char *ERR_NOTASK2 = "[TASKS]Character %i has task %i which does not exist."; - - const char *ERR_INCONSISTENT = "[TASKS]Fatal error in character %i task state. Activity %i for " - "Task %i either missing from client state or from task."; - for(int i=0; iActiveTasks[i].TaskID; - if(TaskID==TASKSLOTEMPTY) continue; - if(!Tasks[TaskID]) { + int taskID = state->ActiveTasks[i].TaskID; + if(taskID==TASKSLOTEMPTY) continue; + if(!Tasks[taskID]) { c->Message(13, "Active Task Slot %i, references a task (%i), that does not exist. " - "Removing from memory. Contact a GM to resolve this.",i, TaskID); + "Removing from memory. Contact a GM to resolve this.",i, taskID); - LogFile->write(EQEMuLog::Error, ERR_NOTASK2, CharacterID, TaskID); + LogFile->write(EQEMuLog::Error, "[TASKS]Character %i has task %i which does not exist.", characterID, taskID); state->ActiveTasks[i].TaskID=TASKSLOTEMPTY; continue; } - for(int j=0; jActivityCount; j++) { + for(int j=0; jActivityCount; j++) { if(state->ActiveTasks[i].Activity[j].ActivityID != j) { c->Message(13, "Active Task %i, %s. Activity count does not match expected value." "Removing from memory. Contact a GM to resolve this.", - TaskID, Tasks[TaskID]->Title); + taskID, Tasks[taskID]->Title); - LogFile->write(EQEMuLog::Error, ERR_INCONSISTENT, CharacterID, j, TaskID); + LogFile->write(EQEMuLog::Error, "[TASKS]Fatal error in character %i task state. Activity %i for " + "Task %i either missing from client state or from task.", characterID, j, taskID); state->ActiveTasks[i].TaskID=TASKSLOTEMPTY; break; } } } - for(int i=0; iActiveTasks[i].TaskID != TASKSLOTEMPTY) - state->UnlockActivities(CharacterID, i); + state->UnlockActivities(characterID, i); - _log(TASKS__CLIENTLOAD, "LoadClientState for Character ID %d DONE!", CharacterID); + _log(TASKS__CLIENTLOAD, "LoadClientState for Character ID %d DONE!", characterID); return true; } From 2c275f603aa8418bd8e21408bcb95d29b0484d78 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 13:07:03 -0700 Subject: [PATCH 311/368] EnableTask converted to QueryDatabase --- zone/tasks.cpp | 65 +++++++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 89c34b477..d9890045a 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -679,32 +679,33 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) { return true; } -void ClientTaskState::EnableTask(int CharID, int TaskCount, int *TaskList) { +void ClientTaskState::EnableTask(int characterID, int taskCount, int *tasks) { // Check if the Task is already enabled for this client // - std::vector TasksEnabled; - std::vector::iterator Iterator; + std::vector tasksEnabled; - for(int i=0; i TaskList[i]) break; - ++Iterator; + if((*iterator) > tasks[i]) + break; + ++iterator; } - if(AddTask) { - EnabledTasks.insert(Iterator, TaskList[i]); + + if(addTask) { + EnabledTasks.insert(iterator, tasks[i]); // Make a note of the task we enabled, for later SQL generation - TasksEnabled.push_back(TaskList[i]); + tasksEnabled.push_back(tasks[i]); } } @@ -712,35 +713,19 @@ void ClientTaskState::EnableTask(int CharID, int TaskCount, int *TaskList) { for(unsigned int i=0; iwrite(EQEMuLog::Error, "[TASKS]Error in ClientTaskState::EnableTask %s %s", query.c_str(), results.ErrorMessage().c_str()); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - char *buf = 0; - - for(unsigned int i=0; iwrite(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - } - - safe_delete_array(query); } void ClientTaskState::DisableTask(int CharID, int TaskCount, int *TaskList) { From 5f11e91da2cb636b72a0879fa74482b257768452 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 13:17:03 -0700 Subject: [PATCH 312/368] DisableTask converted to QueryDatabase --- zone/tasks.cpp | 73 +++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 45 deletions(-) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index d9890045a..881e34d80 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -728,28 +728,31 @@ void ClientTaskState::EnableTask(int characterID, int taskCount, int *tasks) { } -void ClientTaskState::DisableTask(int CharID, int TaskCount, int *TaskList) { +void ClientTaskState::DisableTask(int charID, int taskCount, int *taskList) { // Check if the Task is enabled for this client // - std::vector TasksDisabled; - std::vector::iterator Iterator; + std::vector tasksDisabled; - for(int i=0; i TaskList[i]) break; - ++Iterator; + + if((*iterator) > taskList[i]) + break; + + ++iterator; } - if(RemoveTask) { - EnabledTasks.erase(Iterator); - TasksDisabled.push_back(TaskList[i]); + + if(removeTask) { + EnabledTasks.erase(iterator); + tasksDisabled.push_back(taskList[i]); } } @@ -757,40 +760,20 @@ void ClientTaskState::DisableTask(int CharID, int TaskCount, int *TaskList) { for(unsigned int i=0; iwrite(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - } - - safe_delete_array(query); + queryStream << ")"; + std::string query = queryStream.str(); + _log(TASKS__UPDATE, "Executing query %s", query.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "[TASKS]Error in ClientTaskState::DisableTask %s %s", query.c_str(), results.ErrorMessage().c_str()); } bool ClientTaskState::IsTaskEnabled(int TaskID) { From fca359bf51b60d4d899b0002b06004efad666fe8 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 13:22:10 -0700 Subject: [PATCH 313/368] DeleteCompletedTaskFromDatabase --- zone/tasks.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 881e34d80..538080b28 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -1272,24 +1272,18 @@ int ClientTaskState::GetActiveTaskID(int index) { return ActiveTasks[index].TaskID; } -static void DeleteCompletedTaskFromDatabase(int CharID, int TaskID) { +static void DeleteCompletedTaskFromDatabase(int charID, int taskID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + _log(TASKS__UPDATE, "DeleteCompletedTasksFromDatabase. CharID = %i, TaskID = %i", charID, taskID); - const char *TaskQuery="DELETE FROM completed_tasks WHERE charid=%i AND taskid = %i"; - - _log(TASKS__UPDATE, "DeleteCompletedTasksFromDatabase. CharID = %i, TaskID = %i", - CharID, TaskID); - - if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, CharID, TaskID), errbuf)) { - - LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s, %s", query, errbuf); - safe_delete_array(query); + const std::string query = StringFormat("DELETE FROM completed_tasks WHERE charid=%i AND taskid = %i", charID, taskID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s, %s", query.c_str(), results.ErrorMessage().c_str()); return; } - _log(TASKS__UPDATE, "Delete query %s", query); - safe_delete_array(query); + + _log(TASKS__UPDATE, "Delete query %s", query.c_str()); } bool ClientTaskState::UnlockActivities(int CharID, int TaskIndex) { From 1a4b794ce4e86df19501aa4fd71894f02b3afa7a Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 13:33:22 -0700 Subject: [PATCH 314/368] RemoveTask converted to QueryDatabase --- zone/tasks.cpp | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 538080b28..76a789c92 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -2981,40 +2981,29 @@ void ClientTaskState::CancelTask(Client *c, int SequenceNumber, bool RemoveFromD RemoveTask(c, SequenceNumber); } -void ClientTaskState::RemoveTask(Client *c, int SequenceNumber) { +void ClientTaskState::RemoveTask(Client *c, int sequenceNumber) { - int CharacterID = c->CharacterID(); + int characterID = c->CharacterID(); + _log(TASKS__UPDATE, "ClientTaskState Cancel Task %i ", sequenceNumber); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - const char *TaskQuery="DELETE FROM character_tasks WHERE charid=%i AND taskid = %i"; - - const char *ActivityQuery="DELETE FROM character_activities WHERE charid=%i AND taskid = %i"; - - _log(TASKS__UPDATE, "ClientTaskState Cancel Task %i ", SequenceNumber); - - if(!database.RunQuery(query,MakeAnyLenString(&query, ActivityQuery, - CharacterID, - ActiveTasks[SequenceNumber].TaskID), errbuf)) { - - LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", errbuf); - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM character_activities WHERE charid=%i AND taskid = %i", + characterID, ActiveTasks[sequenceNumber].TaskID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", results.ErrorMessage().c_str()); return; } - _log(TASKS__UPDATE, "CancelTask: %s", query); - safe_delete_array(query); - if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, - CharacterID, - ActiveTasks[SequenceNumber].TaskID), errbuf)) { + _log(TASKS__UPDATE, "CancelTask: %s", query.c_str()); - LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", errbuf); - } + query = StringFormat("DELETE FROM character_tasks WHERE charid=%i AND taskid = %i", + characterID, ActiveTasks[sequenceNumber].TaskID); + results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", results.ErrorMessage().c_str()); - _log(TASKS__UPDATE, "CancelTask: %s", query); - safe_delete_array(query); + _log(TASKS__UPDATE, "CancelTask: %s", query.c_str()); - ActiveTasks[SequenceNumber].TaskID = TASKSLOTEMPTY; + ActiveTasks[sequenceNumber].TaskID = TASKSLOTEMPTY; ActiveTaskCount--; } From 0d8cbca016f3795a92bc014073a9c4339e235247 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 14:11:35 -0700 Subject: [PATCH 315/368] LoadLists converted to QueryDatabase --- zone/tasks.cpp | 149 ++++++++++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 82 deletions(-) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 76a789c92..a815c541f 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -3125,98 +3125,83 @@ TaskGoalListManager::~TaskGoalListManager() { bool TaskGoalListManager::LoadLists() { - - const char *CountQuery = "SELECT `listid`, COUNT(`entry`) FROM `goallists` GROUP by `listid` " - "ORDER BY `listid`"; - - const char *ListQuery = "SELECT `entry` from `goallists` WHERE `listid`=%i " - "ORDER BY `entry` ASC LIMIT %i"; - - const char *ERR_MYSQLERROR = "Error in TaskGoalListManager::LoadLists: %s %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - _log(TASKS__GLOBALLOAD, "TaskGoalListManager::LoadLists Called"); - for(int i=0; i< NumberOfLists; i++) { - + for(int i=0; i< NumberOfLists; i++) safe_delete_array(TaskGoalLists[i].GoalItemEntries); - - } safe_delete_array(TaskGoalLists); + const char *ERR_MYSQLERROR = "Error in TaskGoalListManager::LoadLists: %s %s"; + NumberOfLists = 0; - if(database.RunQuery(query,MakeAnyLenString(&query,CountQuery),errbuf,&result)) { - - NumberOfLists = mysql_num_rows(result); - _log(TASKS__GLOBALLOAD, "Database returned a count of %i lists", NumberOfLists); - - TaskGoalLists = new TaskGoalList_Struct[NumberOfLists]; - - int ListIndex = 0; - - while((row = mysql_fetch_row(result))) { - int ListID = atoi(row[0]); - int ListSize = atoi(row[1]); - - TaskGoalLists[ListIndex].ListID = ListID; - TaskGoalLists[ListIndex].Size = ListSize; - TaskGoalLists[ListIndex].Min = 0; - TaskGoalLists[ListIndex].Max = 0; - TaskGoalLists[ListIndex].GoalItemEntries = new int[ListSize]; - - ListIndex++; - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - safe_delete_array(query); + std::string query = "SELECT `listid`, COUNT(`entry`) " + "FROM `goallists` GROUP by `listid` " + "ORDER BY `listid`"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query.c_str(), results.ErrorMessage().c_str()); return false; + } + + NumberOfLists = results.RowCount(); + _log(TASKS__GLOBALLOAD, "Database returned a count of %i lists", NumberOfLists); + + TaskGoalLists = new TaskGoalList_Struct[NumberOfLists]; + + int listIndex = 0; + + for(auto row = results.begin(); row != results.end(); ++row) { + int listID = atoi(row[0]); + int listSize = atoi(row[1]); + + TaskGoalLists[listIndex].ListID = listID; + TaskGoalLists[listIndex].Size = listSize; + TaskGoalLists[listIndex].Min = 0; + TaskGoalLists[listIndex].Max = 0; + TaskGoalLists[listIndex].GoalItemEntries = new int[listSize]; + + listIndex++; + } + + for(int listIndex = 0; listIndex < NumberOfLists; listIndex++) { + + int listID = TaskGoalLists[listIndex].ListID; + unsigned int size = TaskGoalLists[listIndex].Size; + query = StringFormat("SELECT `entry` from `goallists` " + "WHERE `listid` = %i " + "ORDER BY `entry` ASC LIMIT %i", + listID, size); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query.c_str(), results.ErrorMessage().c_str()); + TaskGoalLists[listIndex].Size = 0; + continue; + } + + // This should only happen if a row is deleted in between us retrieving the counts + // at the start of this method and getting to here. It should not be possible for + // an INSERT to cause a problem, as the SELECT is used with a LIMIT + if(results.RowCount() < size) + TaskGoalLists[listIndex].Size = results.RowCount(); + + int entryIndex = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++entryIndex) { + + int entry = atoi(row[0]); + + if(entry < TaskGoalLists[listIndex].Min) + TaskGoalLists[listIndex].Min = entry; + + if(entry > TaskGoalLists[listIndex].Max) + TaskGoalLists[listIndex].Max = entry; + + TaskGoalLists[listIndex].GoalItemEntries[entryIndex] = entry; + + } + } - for(int ListIndex = 0; ListIndex < NumberOfLists; ListIndex++) { - - int ListID = TaskGoalLists[ListIndex].ListID; - unsigned int Size = TaskGoalLists[ListIndex].Size; - - if(database.RunQuery(query,MakeAnyLenString(&query,ListQuery,ListID,Size),errbuf,&result)) { - // This should only happen if a row is deleted in between us retrieving the counts - // at the start of this method and getting to here. It should not be possible for - // an INSERT to cause a problem, as the SELECT is used with a LIMIT - if(mysql_num_rows(result) < Size) - TaskGoalLists[ListIndex].Size = mysql_num_rows(result); - - int EntryIndex = 0; - - while((row = mysql_fetch_row(result))) { - - int Entry = atoi(row[0]); - - if(Entry < TaskGoalLists[ListIndex].Min) - TaskGoalLists[ListIndex].Min = Entry; - - if(Entry > TaskGoalLists[ListIndex].Max) - TaskGoalLists[ListIndex].Max = Entry; - - TaskGoalLists[ListIndex].GoalItemEntries[EntryIndex++] = Entry; - - } - - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - TaskGoalLists[ListIndex].Size = 0; - safe_delete_array(query); - } - } return true; } From 1da70ee6ed9ab95ea19948e17ab40199ca50bb34 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 14:15:43 -0700 Subject: [PATCH 316/368] LoadProximities converted to QueryDatabase --- zone/tasks.cpp | 60 +++++++++++++++++++------------------------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index a815c541f..924d3770b 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -3299,47 +3299,33 @@ TaskProximityManager::~TaskProximityManager() { } -bool TaskProximityManager::LoadProximities(int ZoneID) { +bool TaskProximityManager::LoadProximities(int zoneID) { + TaskProximity proximity; - const char *ProximityQuery = "SELECT `exploreid`, `minx`, `maxx`, `miny`, `maxy`, " - "`minz`, `maxz` from `proximities` WHERE `zoneid`=%i " - "ORDER BY `zoneid` ASC"; - - const char *ERR_MYSQLERROR = "Error in TaskProximityManager::LoadProximities %s %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - - TaskProximity Proximity; - - _log(TASKS__GLOBALLOAD, "TaskProximityManager::LoadProximities Called for zone %i", ZoneID); + _log(TASKS__GLOBALLOAD, "TaskProximityManager::LoadProximities Called for zone %i", zoneID); TaskProximities.clear(); - if(database.RunQuery(query,MakeAnyLenString(&query,ProximityQuery, ZoneID),errbuf,&result)) { - - while((row = mysql_fetch_row(result))) { - Proximity.ExploreID = atoi(row[0]); - Proximity.MinX = atof(row[1]); - Proximity.MaxX = atof(row[2]); - Proximity.MinY = atof(row[3]); - Proximity.MaxY = atof(row[4]); - Proximity.MinZ = atof(row[5]); - Proximity.MaxZ = atof(row[6]); - - TaskProximities.push_back(Proximity); - - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `exploreid`, `minx`, `maxx`, " + "`miny`, `maxy`, `minz`, `maxz` " + "FROM `proximities` WHERE `zoneid` = %i " + "ORDER BY `zoneid` ASC", zoneID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in TaskProximityManager::LoadProximities %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } - safe_delete_array(query); + } + + for( auto row = results.begin(); row != results.end(); ++row) { + proximity.ExploreID = atoi(row[0]); + proximity.MinX = atof(row[1]); + proximity.MaxX = atof(row[2]); + proximity.MinY = atof(row[3]); + proximity.MaxY = atof(row[4]); + proximity.MinZ = atof(row[5]); + proximity.MaxZ = atof(row[6]); + + TaskProximities.push_back(proximity); + } return true; From e2333e671be37e02c87c91452206fe2608116882 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 14:28:40 -0700 Subject: [PATCH 317/368] HandleautoCombine converted to QueryDatabase --- zone/tradeskills.cpp | 60 ++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 91cb458a0..348d0cfe7 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -142,7 +142,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme // Adding augment if (in_augment->augment_slot == -1) { - if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && + if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) { tobe_auged->PutAugment(slot, *auged_with); @@ -424,38 +424,28 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac return; } - - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - char *query = 0; - - uint32 qlen = 0; - uint8 qcount = 0; - - //pull the list of components - qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount " - " FROM tradeskill_recipe_entries AS tre " - " WHERE tre.componentcount > 0 AND tre.recipe_id=%u", rac->recipe_id); - - if (!database.RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query, errbuf); - safe_delete_array(query); + //pull the list of components + std::string query = StringFormat("SELECT tre.item_id,tre.componentcount " + "FROM tradeskill_recipe_entries AS tre " + "WHERE tre.componentcount > 0 AND tre.recipe_id = %u", + rac->recipe_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); user->QueuePacket(outapp); safe_delete(outapp); return; } - safe_delete_array(query); - qcount = mysql_num_rows(result); - if(qcount < 1) { + if(results.RowCount() < 1) { LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: no components returned"); user->QueuePacket(outapp); safe_delete(outapp); return; } - if(qcount > 10) { - LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: too many components returned (%u)", qcount); + + if(results.RowCount() > 10) { + LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: too many components returned (%u)", results.RowCount()); user->QueuePacket(outapp); safe_delete(outapp); return; @@ -466,17 +456,15 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac uint8 counts[10]; memset(counts, 0, sizeof(counts)); - //search for all the items in their inventory Inventory& user_inv = user->GetInv(); uint8 count = 0; uint8 needcount = 0; - uint8 r,k; std::list MissingItems; - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + uint8 needItemIndex = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++needItemIndex) { uint32 item = (uint32)atoi(row[0]); uint8 num = (uint8) atoi(row[1]); @@ -491,10 +479,9 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac MissingItems.push_back(item); //dont start deleting anything until we have found it all. - items[r] = item; - counts[r] = num; + items[needItemIndex] = item; + counts[needItemIndex] = num; } - mysql_free_result(result); //make sure we found it all... if(count != needcount) @@ -520,12 +507,12 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac //remove all the items from the players inventory, with updates... int16 slot; - for(r = 0; r < qcount; r++) { + for(uint8 r = 0; r < results.RowCount(); r++) { if(items[r] == 0 || counts[r] == 0) continue; //skip empties, could prolly break here //we have to loop here to delete 1 at a time in case its in multiple stacks. - for(k = 0; k < counts[r]; k++) { + for(uint8 k = 0; k < counts[r]; k++) { slot = user_inv.HasItem(items[r], 1, invWherePersonal); if (slot == INVALID_INDEX) { //WTF... I just checked this above, but just to be sure... @@ -539,19 +526,14 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac const ItemInst* inst = user_inv.GetItem(slot); if (inst && !inst->IsStackable()) - { user->DeleteItemInInventory(slot, 0, true); - } else - { user->DeleteItemInInventory(slot, 1, true); - } } } //otherwise, we found it all... outp->reply_code = 0x00000000; //success for finding it... - user->QueuePacket(outapp); safe_delete(outapp); @@ -1094,7 +1076,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { ++itr; } return(true); - } + } /* Tradeskill Fail */ else { success_modifier = 2; // Halves the chance @@ -1172,7 +1154,7 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float chance_stage2 = 12.5 - (.08 * (current_raw_skill - 175)); } } - + chance_stage2 = mod_tradeskill_skillup(chance_stage2); if (chance_stage2 > MakeRandomFloat(0, 99)) { From d5955da08c3697fef6d456359353f8390880e466 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 14:38:38 -0700 Subject: [PATCH 318/368] TradeskillSearchResults converted to QueryDatabase --- zone/tradeskills.cpp | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 348d0cfe7..18de50283 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -425,7 +425,7 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac } //pull the list of components - std::string query = StringFormat("SELECT tre.item_id,tre.componentcount " + std::string query = StringFormat("SELECT tre.item_id, tre.componentcount " "FROM tradeskill_recipe_entries AS tre " "WHERE tre.componentcount > 0 AND tre.recipe_id = %u", rac->recipe_id); @@ -638,32 +638,25 @@ SkillUseTypes Object::TypeToSkill(uint32 type) void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsigned long objtype, unsigned long someid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!database.RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query, errbuf); + std::string internalQuery(query); + auto results = database.QueryDatabase(internalQuery); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", internalQuery.c_str(), results.ErrorMessage().c_str()); return; } - uint8 qcount = 0; + if(results.RowCount() < 1) + return; //search gave no results... not an error - qcount = mysql_num_rows(result); - if(qcount < 1) { - //search gave no results... not an error - return; - } - if(mysql_num_fields(result) != 6) { - LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query); + if(results.ColumnCount() != 6) { + LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", internalQuery.c_str()); return; } - uint8 r; - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + for(auto row = results.begin(); row != results.end(); ++row) { if(row == nullptr || row[0] == nullptr || row[1] == nullptr || row[2] == nullptr || row[3] == nullptr || row[5] == nullptr) continue; + uint32 recipe = (uint32)atoi(row[0]); const char *name = row[1]; uint32 trivial = (uint32) atoi(row[2]); @@ -673,14 +666,10 @@ void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsi // Skip the recipes that exceed the threshold in skill difference // Recipes that have either been made before or were // explicitly learned are excempt from that limit - if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff)) { - if (((int32)trivial - (int32)GetSkill((SkillUseTypes)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff) - && row[4] == nullptr) - { + if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff) + && ((int32)trivial - (int32)GetSkill((SkillUseTypes)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff) + && row[4] == nullptr) continue; - } - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct)); RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer; @@ -693,7 +682,7 @@ void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsi strn0cpy(reply->recipe_name, name, sizeof(reply->recipe_name)); FastQueuePacket(&outapp); } - mysql_free_result(result); + } void Client::SendTradeskillDetails(uint32 recipe_id) { From 0fde0fbd23eb29ef4483769165ac11de40a4dc5e Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 14:41:34 -0700 Subject: [PATCH 319/368] removed unneeded querylength parameter from TradeskillSearchResults --- zone/client.h | 8 ++++---- zone/client_packet.cpp | 42 +++++++++++++++++++++--------------------- zone/tradeskills.cpp | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/zone/client.h b/zone/client.h index 03748849d..0fc5a3d1d 100644 --- a/zone/client.h +++ b/zone/client.h @@ -314,7 +314,7 @@ public: /* New PP Save Functions */ bool SaveCurrency(){ return database.SaveCharacterCurrency(this->CharacterID(), &m_pp); } bool SaveAA(); - + inline bool ClientDataLoaded() const { return client_data_loaded; } inline bool Connected() const { return (client_state == CLIENT_CONNECTED); } inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); } @@ -664,7 +664,7 @@ public: void IncreaseLanguageSkill(int skill_id, int value = 1); virtual uint16 GetSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return((itembonuses.skillmod[skill_id] > 0) ? m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100 : m_pp.skills[skill_id]); } return 0; } uint32 GetRawSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; } - bool HasSkill(SkillUseTypes skill_id) const; + bool HasSkill(SkillUseTypes skill_id) const; bool CanHaveSkill(SkillUseTypes skill_id) const; void SetSkill(SkillUseTypes skill_num, uint16 value); void AddSkill(SkillUseTypes skillid, uint16 value); @@ -681,7 +681,7 @@ public: inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } uint8 SkillTrainLevel(SkillUseTypes skillid, uint16 class_); - void TradeskillSearchResults(const char *query, unsigned long qlen, unsigned long objtype, unsigned long someid); + void TradeskillSearchResults(const char *query, unsigned long objtype, unsigned long someid); void SendTradeskillDetails(uint32 recipe_id); bool TradeskillExecute(DBTradeskillRecipe_Struct *spec); void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, SkillUseTypes tradeskill); @@ -1452,7 +1452,7 @@ private: unsigned int RestRegenHP; unsigned int RestRegenMana; unsigned int RestRegenEndurance; - + bool EngagedRaidTarget; uint32 SavedRaidRestTimer; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 74399274a..1202c2453 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -523,7 +523,7 @@ void Client::CompleteConnect() switch (rank) { case 0: { rank = 5; break; } // GUILD_MEMBER 0 case 1: { rank = 3; break; } // GUILD_OFFICER 1 - case 2: { rank = 1; break; } // GUILD_LEADER 2 + case 2: { rank = 1; break; } // GUILD_LEADER 2 default: { break; } // GUILD_NONE } } @@ -848,7 +848,7 @@ void Client::CompleteConnect() void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z) { //ToDo: Break warp down for special zones. Some zones have special teleportation pads or bad .map files which can trigger the detector without a legit zone request. - + switch (CheatType) { case MQWarp: //Some zones may still have issues. Database updates will eliminate most if not all problems. @@ -1304,7 +1304,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if(strlen(cze->char_name) > 63) return; - conn_state = ReceivedZoneEntry; + conn_state = ReceivedZoneEntry; ClientVersion = Connection()->ClientVersion(); if (ClientVersion != EQClientUnknown) @@ -1329,7 +1329,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) client_state = CLIENT_KICKED; return; } - + strcpy(name, cze->char_name); /* Check for Client Spoofing */ if (client != 0) { @@ -1342,7 +1342,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) client->Disconnect(); } - uint32 pplen = 0; + uint32 pplen = 0; EQApplicationPacket* outapp = 0; MYSQL_RES* result = 0; bool loaditems = 0; @@ -1366,8 +1366,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (lsaccountid && atoi(row[2]) > 0){ lsaccountid = atoi(row[2]); } else{ lsaccountid = 0; } gmspeed = atoi(row[3]); - revoked = atoi(row[4]); - gmhideme = atoi(row[5]); + revoked = atoi(row[4]); + gmhideme = atoi(row[5]); if (account_creation){ account_creation = atoul(row[6]); } } @@ -1375,17 +1375,17 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) query = StringFormat("SELECT `lfp`, `lfg`, `xtargets`, `firstlogon`, `guild_id`, `rank` FROM `character_data` LEFT JOIN `guild_members` ON `id` = `char_id` WHERE `id` = %i", cid); results = database.QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { - if (row[4] && atoi(row[4]) > 0){ - guild_id = atoi(row[4]); + if (row[4] && atoi(row[4]) > 0){ + guild_id = atoi(row[4]); if (row[5] != nullptr){ guildrank = atoi(row[5]); } else{ guildrank = GUILD_RANK_NONE; } } - + if (LFP){ LFP = atoi(row[0]); } if (LFG){ LFG = atoi(row[1]); } if (firstlogon){ firstlogon = atoi(row[3]); } } - + if (RuleB(Character, SharedBankPlat)) m_pp.platinum_shared = database.GetSharedPlatinum(this->AccountID()); @@ -1417,7 +1417,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /* Set Con State for Reporting */ conn_state = PlayerProfileLoaded; - m_pp.zone_id = zone->GetZoneID(); + m_pp.zone_id = zone->GetZoneID(); m_pp.zoneInstance = zone->GetInstanceID(); /* Set Total Seconds Played */ @@ -1426,7 +1426,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) max_AAXP = RuleI(AA, ExpPerPoint); /* If we can maintain intoxication across zones, check for it */ if (!RuleB(Character, MaintainIntoxicationAcrossZones)) - m_pp.intoxication = 0; + m_pp.intoxication = 0; strcpy(name, m_pp.name); strcpy(lastname, m_pp.last_name); @@ -1435,14 +1435,14 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_pp.x = zone->safe_x(); m_pp.y = zone->safe_y(); m_pp.z = zone->safe_z(); - } + } /* If too far below ground, then fix */ // float ground_z = GetGroundZ(m_pp.x, m_pp.y, m_pp.z); // if (m_pp.z < (ground_z - 500)) // m_pp.z = ground_z; /* Set Mob variables for spawn */ - class_ = m_pp.class_; + class_ = m_pp.class_; level = m_pp.level; x_pos = m_pp.x; y_pos = m_pp.y; @@ -1471,7 +1471,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /* Load Guild */ if (!IsInAGuild()) { m_pp.guild_id = GUILD_NONE; } else { - m_pp.guild_id = GuildID(); + m_pp.guild_id = GuildID(); if (zone->GetZoneID() == RuleI(World, GuildBankZoneID)) GuildBanker = (guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || guild_mgr.GetBankerFlag(CharacterID())); } @@ -1711,12 +1711,12 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) #endif /* Reset to max so they dont drown on zone in if its underwater */ - m_pp.air_remaining = 60; + m_pp.air_remaining = 60; /* Check for PVP Zone status*/ if (zone->IsPVPZone()) m_pp.pvp = 1; /* Time entitled on Account: Move to account */ - m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; + m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; /* Reset rest timer if the durations have been lowered in the database */ if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) m_pp.RestTimer = 0; @@ -1836,7 +1836,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) QueuePacket(outapp); safe_delete(outapp); - SetAttackTimer(); + SetAttackTimer(); conn_state = ZoneInfoSent; return; @@ -11358,7 +11358,7 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " " LIMIT 100 ", CharacterID(), buf, containers); - TradeskillSearchResults(query, qlen, tsf->object_type, tsf->some_id); + TradeskillSearchResults(query, tsf->object_type, tsf->some_id); safe_delete_array(query); return; @@ -11416,7 +11416,7 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) " LIMIT 200 " , CharacterID(), searchclause, rss->mintrivial, rss->maxtrivial, containers); - TradeskillSearchResults(query, qlen, rss->object_type, rss->some_id); + TradeskillSearchResults(query, rss->object_type, rss->some_id); safe_delete_array(query); return; diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 18de50283..70e687be6 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -636,7 +636,7 @@ SkillUseTypes Object::TypeToSkill(uint32 type) return TradeskillUnknown; } -void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsigned long objtype, unsigned long someid) { +void Client::TradeskillSearchResults(const char *query, unsigned long objtype, unsigned long someid) { std::string internalQuery(query); auto results = database.QueryDatabase(internalQuery); From 7656b9b928ae30cf31686d9eb0698aa5d1835757 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 15:00:34 -0700 Subject: [PATCH 320/368] TradeskillSearchResults converted to const std::string query instead of const *char query --- zone/client.h | 2 +- zone/client_packet.cpp | 99 ++++++++++++++++++------------------------ zone/tradeskills.cpp | 9 ++-- 3 files changed, 48 insertions(+), 62 deletions(-) diff --git a/zone/client.h b/zone/client.h index 0fc5a3d1d..3375d7066 100644 --- a/zone/client.h +++ b/zone/client.h @@ -681,7 +681,7 @@ public: inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } uint8 SkillTrainLevel(SkillUseTypes skillid, uint16 class_); - void TradeskillSearchResults(const char *query, unsigned long objtype, unsigned long someid); + void TradeskillSearchResults(const std::string query, unsigned long objtype, unsigned long someid); void SendTradeskillDetails(uint32 recipe_id); bool TradeskillExecute(DBTradeskillRecipe_Struct *spec); void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, SkillUseTypes tradeskill); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 1202c2453..6da4a265f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11311,56 +11311,46 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) // some_id = 0 if world combiner, item number otherwise // make where clause segment for container(s) - char containers[30]; - if (tsf->some_id == 0) { - // world combiner so no item number - snprintf(containers, 29, "= %u", tsf->object_type); - } - else { - // container in inventory - snprintf(containers, 29, "in (%u,%u)", tsf->object_type, tsf->some_id); - } - - char *query = 0; - char buf[5500]; //gotta be big enough for 500 IDs + std::string containers; + if (tsf->some_id == 0) + containers += StringFormat(" = %u ", tsf->object_type); // world combiner so no item number + else + containers += StringFormat(" in (%u, %u) ", tsf->object_type, tsf->some_id); // container in inventory + std::string favoriteIDs; //gotta be big enough for 500 IDs bool first = true; - uint16 r; - char *pos = buf; - //Assumes item IDs are <10 characters long - for (r = 0; r < 500; r++) { - if (tsf->favorite_recipes[r] == 0) + for (uint16 favoriteIndex = 0; favoriteIndex < 500; ++favoriteIndex) { + if (tsf->favorite_recipes[favoriteIndex] == 0) continue; if (first) { - pos += snprintf(pos, 10, "%u", tsf->favorite_recipes[r]); + favoriteIDs += StringFormat("%u", tsf->favorite_recipes[favoriteIndex]); first = false; } - else { - pos += snprintf(pos, 10, ",%u", tsf->favorite_recipes[r]); - } + else + favoriteIDs += StringFormat(",%u", tsf->favorite_recipes[favoriteIndex]); } if (first) //no favorites.... return; - //To be a good kid, I should move this SQL somewhere else... - //but im lazy right now, so it stays here - uint32 qlen = 0; - qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " - " FROM tradeskill_recipe AS tr " - " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " - " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " - " WHERE tr.enabled <> 0 AND tr.id IN (%s) " - " AND tr.must_learn & 0x20 <> 0x20 AND ((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " - " GROUP BY tr.id " - " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " - " LIMIT 100 ", CharacterID(), buf, containers); + const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, " + "SUM(tre.componentcount), crl.madecount,tr.tradeskill " + "FROM tradeskill_recipe AS tr " + "LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " + "LEFT JOIN (SELECT recipe_id, madecount " + "FROM char_recipe_list " + "WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " + "WHERE tr.enabled <> 0 AND tr.id IN (%s) " + "AND tr.must_learn & 0x20 <> 0x20 AND " + "((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) " + "OR (tr.must_learn & 0x3 = 0)) " + "GROUP BY tr.id " + "HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " + "LIMIT 100 ", CharacterID(), favoriteIDs.c_str(), containers.c_str()); TradeskillSearchResults(query, tsf->object_type, tsf->some_id); - - safe_delete_array(query); return; } @@ -11389,36 +11379,33 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) snprintf(containers, 29, "in (%u,%u)", rss->object_type, rss->some_id); } - char *query = 0; - char searchclause[140]; //2X rss->query + SQL crap + std::string searchClause; //omit the rlike clause if query is empty if (rss->query[0] != 0) { char buf[120]; //larger than 2X rss->query database.DoEscapeString(buf, rss->query, strlen(rss->query)); - - snprintf(searchclause, 139, "name rlike '%s' AND", buf); + searchClause = StringFormat("name rlike '%s' AND", buf); } - else { - searchclause[0] = '\0'; - } - uint32 qlen = 0; //arbitrary limit of 200 recipes, makes sense to me. - qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " - " FROM tradeskill_recipe AS tr " - " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " - " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " - " WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 " - " AND tr.must_learn & 0x20 <> 0x20 AND((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " - " GROUP BY tr.id " - " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " - " LIMIT 200 " - , CharacterID(), searchclause, rss->mintrivial, rss->maxtrivial, containers); - + const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, " + "SUM(tre.componentcount), crl.madecount,tr.tradeskill " + "FROM tradeskill_recipe AS tr " + "LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id " + "LEFT JOIN (SELECT recipe_id, madecount " + "FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " + "WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 " + "AND tr.must_learn & 0x20 <> 0x20 " + "AND ((tr.must_learn & 0x3 <> 0 " + "AND crl.madecount IS NOT NULL) " + "OR (tr.must_learn & 0x3 = 0)) " + "GROUP BY tr.id " + "HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " + "LIMIT 200 ", + CharacterID(), searchClause.c_str(), + rss->mintrivial, rss->maxtrivial, containers); TradeskillSearchResults(query, rss->object_type, rss->some_id); - - safe_delete_array(query); return; } diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 70e687be6..82b56dc18 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -636,12 +636,11 @@ SkillUseTypes Object::TypeToSkill(uint32 type) return TradeskillUnknown; } -void Client::TradeskillSearchResults(const char *query, unsigned long objtype, unsigned long someid) { +void Client::TradeskillSearchResults(const std::string query, unsigned long objtype, unsigned long someid) { - std::string internalQuery(query); - auto results = database.QueryDatabase(internalQuery); + auto results = database.QueryDatabase(query); if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", internalQuery.c_str(), results.ErrorMessage().c_str()); + LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } @@ -649,7 +648,7 @@ void Client::TradeskillSearchResults(const char *query, unsigned long objtype, u return; //search gave no results... not an error if(results.ColumnCount() != 6) { - LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", internalQuery.c_str()); + LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query.c_str()); return; } From e2894ef2aaf0b7492efaca9ff94eb5233bdaa97d Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 15:05:24 -0700 Subject: [PATCH 321/368] SendTradeskillDetails converted to QueryDatabase --- zone/tradeskills.cpp | 46 ++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 82b56dc18..004de366a 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -686,34 +686,25 @@ void Client::TradeskillSearchResults(const std::string query, unsigned long objt void Client::SendTradeskillDetails(uint32 recipe_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - char *query = 0; - - uint32 qlen = 0; - uint8 qcount = 0; - - //pull the list of components - qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount,i.icon,i.Name " - " FROM tradeskill_recipe_entries AS tre " - " LEFT JOIN items AS i ON tre.item_id = i.id " - " WHERE tre.componentcount > 0 AND tre.recipe_id=%u", recipe_id); - - if (!database.RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query, errbuf); - safe_delete_array(query); + //pull the list of components + std::string query = StringFormat("SELECT tre.item_id,tre.componentcount,i.icon,i.Name " + "FROM tradeskill_recipe_entries AS tre " + "LEFT JOIN items AS i ON tre.item_id = i.id " + "WHERE tre.componentcount > 0 AND tre.recipe_id = %u", + recipe_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } - safe_delete_array(query); - qcount = mysql_num_rows(result); - if(qcount < 1) { + if(results.RowCount() < 1) { LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: no components returned"); return; } - if(qcount > 10) { - LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: too many components returned (%u)", qcount); + + if(results.RowCount() > 10) { + LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: too many components returned (%u)", results.RowCount()); return; } @@ -743,20 +734,18 @@ void Client::SendTradeskillDetails(uint32 recipe_id) { uint32 len; uint32 datalen = 0; uint8 count = 0; - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + + for(auto row = results.begin(); row != results.end(); ++row) { //watch for references to items which are not in the //items table, which the left join will make nullptr... - if(row[2] == nullptr || row[3] == nullptr) { + if(row[2] == nullptr || row[3] == nullptr) continue; - } uint32 item = (uint32)atoi(row[0]); uint8 num = (uint8) atoi(row[1]); - - uint32 icon = (uint32) atoi(row[2]); + const char *name = row[3]; len = strlen(name); if(len > 63) @@ -786,7 +775,6 @@ void Client::SendTradeskillDetails(uint32 recipe_id) { } } - mysql_free_result(result); //now move the item data over top of the FFFFs uint8 dist = sizeof(uint32) * (10 - count); From 21d6865e8c714ff749054aa5657d1332c127ab3d Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 15:43:50 -0700 Subject: [PATCH 322/368] GetTradeRecipe converted to QueryDatabase --- zone/tradeskills.cpp | 285 ++++++++++++++++++++----------------------- 1 file changed, 129 insertions(+), 156 deletions(-) diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 004de366a..6e06800da 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -1149,194 +1149,167 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - char *query = 0; - char buf2[4096]; - uint32 sum = 0; - uint32 count = 0; - uint32 qcount = 0; - uint32 qlen = 0; - - // make where clause segment for container(s) - char containers[30]; - if (some_id == 0) { - // world combiner so no item number - snprintf(containers,29, "= %u", c_type); - } else { - // container in inventory - snprintf(containers,29, "in (%u,%u)", c_type, some_id); - } - - buf2[0] = '\0'; + std::string containers;// make where clause segment for container(s) + if (some_id == 0) + containers = StringFormat("= %u", c_type); // world combiner so no item number + else + containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory //Could prolly watch for stacks in this loop and handle them properly... //just increment sum and count accordingly bool first = true; - uint8 i; - char *pos = buf2; - for (i = 0; i < 10; i++) { // TODO: need to determine if this is bound to world/item container size + std::string buf2; + uint32 count = 0; + uint32 sum = 0; + for (uint8 i = 0; i < 10; i++) { // TODO: need to determine if this is bound to world/item container size const ItemInst* inst = container->GetItem(i); - if (inst) { - const Item_Struct* item = GetItem(inst->GetItem()->ID); - if (item) { - if(first) { - pos += snprintf(pos, 19, "%d", item->ID); - first = false; - } else { - pos += snprintf(pos, 19, ",%d", item->ID); - } - sum += item->ID; - count++; - } - } - } - *pos = '\0'; + if (!inst) + continue; - if(count < 1) { - return(false); //no items == no recipe + const Item_Struct* item = GetItem(inst->GetItem()->ID); + if (!item) + continue; + + if(first) { + buf2 += StringFormat("%d", item->ID); + first = false; + } else + buf2 += StringFormat(",%d", item->ID); + + sum += item->ID; + count++; } - qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id " - " FROM tradeskill_recipe_entries AS tre" - " INNER JOIN tradeskill_recipe AS tr ON (tre.recipe_id = tr.id) " - " WHERE tr.enabled AND (( tre.item_id IN(%s) AND tre.componentcount>0 )" - " OR ( tre.item_id %s AND tre.iscontainer=1 ))" - " GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u" - " AND sum(tre.item_id * tre.componentcount) = %u", buf2, containers, count, sum); + if(count == 0) + return false; //no items == no recipe - if (!RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query); - safe_delete_array(query); - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", errbuf); - return(false); - } - safe_delete_array(query); - - qcount = mysql_num_rows(result); - if(qcount > 1) { - //multiple recipes, partial match... do an extra query to get it exact. - //this happens when combining components for a smaller recipe - //which is completely contained within another recipe - - first = true; - pos = buf2; - for (i = 0; i < qcount; i++) { - row = mysql_fetch_row(result); - uint32 recipeid = (uint32)atoi(row[0]); - if(first) { - pos += snprintf(pos, 19, "%u", recipeid); - first = false; - } else { - pos += snprintf(pos, 19, ",%u", recipeid); - } - //length limit on buf2 - if(i == 214) { //Maximum number of recipe matches (19 * 215 = 4096) - LogFile->write(EQEMuLog::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", i + 1, qcount); - break; - } - } - - qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id" - " FROM tradeskill_recipe_entries AS tre" - " WHERE tre.recipe_id IN (%s)" - " GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u" - " AND sum(tre.item_id * tre.componentcount) = %u", buf2, count, sum); - - if (!RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query); - safe_delete_array(query); - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf); - return(false); - } - safe_delete_array(query); - - qcount = mysql_num_rows(result); + std::string query = StringFormat("SELECT tre.recipe_id " + "FROM tradeskill_recipe_entries AS tre " + "INNER JOIN tradeskill_recipe AS tr ON (tre.recipe_id = tr.id) " + "WHERE tr.enabled AND (( tre.item_id IN(%s) AND tre.componentcount > 0) " + "OR ( tre.item_id %s AND tre.iscontainer=1 ))" + "GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u " + "AND sum(tre.item_id * tre.componentcount) = %u", + buf2.c_str(), containers.c_str(), count, sum); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query.c_str()); + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", results.ErrorMessage().c_str()); + return false; } - if(qcount < 1) - return(false); + if (results.RowCount() > 1) { + //multiple recipes, partial match... do an extra query to get it exact. + //this happens when combining components for a smaller recipe + //which is completely contained within another recipe + first = true; + uint32 index = 0; + buf2 = ""; + for (auto row = results.begin(); row != results.end(); ++row, ++index) { + uint32 recipeid = (uint32)atoi(row[0]); + if(first) { + buf2 += StringFormat("%u", recipeid); + first = false; + } else + buf2 += StringFormat(",%u", recipeid); - if(qcount > 1) - { + //length limit on buf2 + if(index == 214) { //Maximum number of recipe matches (19 * 215 = 4096) + LogFile->write(EQEMuLog::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", index + 1, results.RowCount()); + break; + } + } + + query = StringFormat("SELECT tre.recipe_id " + "FROM tradeskill_recipe_entries AS tre " + "WHERE tre.recipe_id IN (%s) " + "GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u " + "AND sum(tre.item_id * tre.componentcount) = %u", buf2.c_str(), count, sum); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str()); + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str()); + return false; + } + } + + if (results.RowCount() < 1) + return false; + + if(results.RowCount() > 1) { //The recipe is not unique, so we need to compare the container were using. - uint32 containerId = 0; - if(some_id) { //Standard container + if(some_id) //Standard container containerId = some_id; - } - else if(c_type) { //World container + else if(c_type)//World container containerId = c_type; - } - else { //Invalid container - return(false); + else //Invalid container + return false; + + query = StringFormat("SELECT tre.recipe_id " + "FROM tradeskill_recipe_entries AS tre " + "WHERE tre.recipe_id IN (%s) " + "AND tre.item_id = %u;", buf2.c_str(), containerId); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str()); + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str()); + return false; } - qlen = MakeAnyLenString(&query,"SELECT tre.recipe_id FROM tradeskill_recipe_entries as tre WHERE tre.recipe_id IN (%s)" - " AND tre.item_id = %u;",buf2,containerId); - - if (!RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query); - safe_delete_array(query); - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf); - return(false); - } - safe_delete_array(query); - - uint32 resultRowTotal = mysql_num_rows(result); - - if(resultRowTotal == 0) { //Recipe contents matched more than 1 recipe, but not in this container + if(results.RowCount() == 0) { //Recipe contents matched more than 1 recipe, but not in this container LogFile->write(EQEMuLog::Error, "Combine error: Incorrect container is being used!"); - return(false); - } - if(resultRowTotal > 1) { //Recipe contents matched more than 1 recipe in this container - LogFile->write(EQEMuLog::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", resultRowTotal, containerId); + return false; } + + if (results.RowCount() > 1) //Recipe contents matched more than 1 recipe in this container + LogFile->write(EQEMuLog::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", results.RowCount(), containerId); + } - row = mysql_fetch_row(result); + auto row = results.begin(); uint32 recipe_id = (uint32)atoi(row[0]); - mysql_free_result(result); //Right here we verify that we actually have ALL of the tradeskill components.. //instead of part which is possible with experimentation. //This is here because something's up with the query above.. it needs to be rethought out bool has_components = true; - char TSerrbuf[MYSQL_ERRMSG_SIZE]; - char *TSquery = 0; - MYSQL_RES *TSresult; - MYSQL_ROW TSrow; - if (RunQuery(TSquery, MakeAnyLenString(&TSquery, "SELECT item_id, componentcount from tradeskill_recipe_entries where recipe_id=%i AND componentcount > 0", recipe_id), TSerrbuf, &TSresult)) { - while((TSrow = mysql_fetch_row(TSresult))!=nullptr) { - int ccnt = 0; - for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) { - const ItemInst* inst = container->GetItem(x); - if(inst){ - const Item_Struct* item = GetItem(inst->GetItem()->ID); - if (item) { - if(item->ID == atoi(TSrow[0])){ - ccnt++; - } - } - } - } - if(ccnt != atoi(TSrow[1])) - has_components = false; - } - mysql_free_result(TSresult); - } else { - LogFile->write(EQEMuLog::Error, "Error in tradeskill verify query: '%s': %s", TSquery, TSerrbuf); - } - safe_delete_array(TSquery); - if(has_components == false){ + query = StringFormat("SELECT item_id, componentcount " + "FROM tradeskill_recipe_entries " + "WHERE recipe_id = %i AND componentcount > 0", + recipe_id); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in tradeskill verify query: '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec); + } - return false; - } + if (results.RowCount() == 0) + return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec); - return(GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec)); + for (auto row = results.begin(); row != results.end(); ++row) { + int ccnt = 0; + + for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) { + const ItemInst* inst = container->GetItem(x); + if(!inst) + continue; + + const Item_Struct* item = GetItem(inst->GetItem()->ID); + if (!item) + continue; + + if(item->ID == atoi(row[0])) + ccnt++; + } + + if(ccnt != atoi(row[1])) + return false; + } + + return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec); } bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id, From 97c1c479f9ab41cda290a45a818385bb18032ad6 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 15:55:12 -0700 Subject: [PATCH 323/368] GetTradeRecipe converted to QueryDatabase --- zone/tradeskills.cpp | 143 ++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 83 deletions(-) diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 6e06800da..78b42fa73 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -1315,56 +1315,46 @@ bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint3 bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - char *query = 0; - - uint32 qcount = 0; - uint32 qlen; // make where clause segment for container(s) - char containers[30]; - if (some_id == 0) { - // world combiner so no item number - snprintf(containers,29, "= %u", c_type); - } else { - // container in inventory - snprintf(containers,29, "in (%u,%u)", c_type, some_id); + std::string containers; + if (some_id == 0) + containers = StringFormat("= %u", c_type); // world combiner so no item number + else + containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory + + std::string query = StringFormat("SELECT tr.id, tr.tradeskill, tr.skillneeded, " + "tr.trivial, tr.nofail, tr.replace_container, " + "tr.name, tr.must_learn, tr.quest, crl.madecount " + "FROM tradeskill_recipe AS tr " + "INNER JOIN tradeskill_recipe_entries AS tre " + "ON tr.id = tre.recipe_id " + "LEFT JOIN (SELECT recipe_id, madecount " + "FROM char_recipe_list WHERE char_id = %u) AS crl " + "ON tr.id = crl.recipe_id " + "WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled " + "GROUP BY tr.id", + char_id, (unsigned long)recipe_id, containers.c_str()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query.c_str()); + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str()); + return false; } - qlen = MakeAnyLenString(&query, "SELECT tr.id, tr.tradeskill, tr.skillneeded," - " tr.trivial, tr.nofail, tr.replace_container, tr.name, tr.must_learn, tr.quest, crl.madecount" - " FROM tradeskill_recipe AS tr inner join tradeskill_recipe_entries as tre" - " ON tr.id = tre.recipe_id" - " LEFT JOIN (SELECT recipe_id, madecount from char_recipe_list WHERE char_id = %u) AS crl " - " ON tr.id = crl.recipe_id " - " WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled " - " GROUP BY tr.id", char_id, (unsigned long)recipe_id, containers); + if(results.RowCount() != 1) + return false;//just not found i guess.. - if (!RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query); - safe_delete_array(query); - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf); - return(false); - } - safe_delete_array(query); - - qcount = mysql_num_rows(result); - if(qcount != 1) { - //just not found i guess.. - return(false); - } - - row = mysql_fetch_row(result); - spec->tradeskill = (SkillUseTypes)atoi(row[1]); - spec->skill_needed = (int16)atoi(row[2]); - spec->trivial = (uint16)atoi(row[3]); - spec->nofail = atoi(row[4]) ? true : false; + auto row = results.begin(); + spec->tradeskill = (SkillUseTypes)atoi(row[1]); + spec->skill_needed = (int16)atoi(row[2]); + spec->trivial = (uint16)atoi(row[3]); + spec->nofail = atoi(row[4]) ? true : false; spec->replace_container = atoi(row[5]) ? true : false; spec->name = row[6]; spec->must_learn = (uint8)atoi(row[7]); spec->quest = atoi(row[8]) ? true : false; + if (row[9] == nullptr) { spec->has_learnt = false; spec->madecount = 0; @@ -1373,72 +1363,59 @@ bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id spec->madecount = (uint32)atoul(row[9]); } spec->recipe_id = recipe_id; - mysql_free_result(result); //Pull the on-success items... - qlen = MakeAnyLenString(&query, "SELECT item_id,successcount FROM tradeskill_recipe_entries" - " WHERE successcount>0 AND recipe_id=%u", recipe_id); - - if (!RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query, errbuf); - safe_delete_array(query); - return(false); + query = StringFormat("SELECT item_id,successcount FROM tradeskill_recipe_entries " + "WHERE successcount > 0 AND recipe_id = %u", recipe_id); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); - qcount = mysql_num_rows(result); - if(qcount < 1) { + if(results.RowCount() < 1) { LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success: no success items returned"); - return(false); + return false; } - uint8 r; + spec->onsuccess.clear(); - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + for(auto row = results.begin(); row != results.end(); ++row) { uint32 item = (uint32)atoi(row[0]); uint8 num = (uint8) atoi(row[1]); spec->onsuccess.push_back(std::pair(item, num)); } - mysql_free_result(result); + spec->onfail.clear(); //Pull the on-fail items... - qlen = MakeAnyLenString(&query, "SELECT item_id,failcount FROM tradeskill_recipe_entries" - " WHERE failcount>0 AND recipe_id=%u", recipe_id); - - spec->onfail.clear(); - if (RunQuery(query, qlen, errbuf, &result)) { - - qcount = mysql_num_rows(result); - uint8 r; - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + query = StringFormat("SELECT item_id, failcount FROM tradeskill_recipe_entries " + "WHERE failcount > 0 AND recipe_id = %u", recipe_id); + results = QueryDatabase(query); + if (results.Success()) + for(auto row = results.begin(); row != results.end(); ++row) { uint32 item = (uint32)atoi(row[0]); uint8 num = (uint8) atoi(row[1]); spec->onfail.push_back(std::pair(item, num)); } - mysql_free_result(result); - } + + spec->salvage.clear(); + + // Don't bother with the query if TS is nofail + if (spec->nofail) + return true; // Pull the salvage list - qlen = MakeAnyLenString(&query, "SELECT item_id,salvagecount FROM tradeskill_recipe_entries WHERE salvagecount>0 AND recipe_id=%u", recipe_id); - - spec->salvage.clear(); - // Don't bother with the query if TS is nofail - if (!spec->nofail && RunQuery(query, qlen, errbuf, &result)) { - qcount = mysql_num_rows(result); - uint8 r; - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + query = StringFormat("SELECT item_id, salvagecount " + "FROM tradeskill_recipe_entries " + "WHERE salvagecount > 0 AND recipe_id = %u", recipe_id); + results = QueryDatabase(query); + if (results.Success()) + for(auto row = results.begin(); row != results.begin(); ++row) { uint32 item = (uint32)atoi(row[0]); uint8 num = (uint8)atoi(row[1]); spec->salvage.push_back(std::pair(item, num)); } - mysql_free_result(result); - } - safe_delete_array(query); - - return(true); + return true; } void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount) From 5dac9a944b4ddb65e1f4ee901bc70f4d69ddd3e1 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 16:02:30 -0700 Subject: [PATCH 324/368] LearnRecipe converted to QueryDatabase --- zone/tradeskills.cpp | 87 +++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 53 deletions(-) diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 78b42fa73..09cf70fc3 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -1418,73 +1418,54 @@ bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id return true; } -void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount) +void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madeCount) { - char *query = 0; - uint32 qlen; - char errbuf[MYSQL_ERRMSG_SIZE]; - - qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list " - " SET recipe_id = %u, char_id = %u, madecount = %u " - " ON DUPLICATE KEY UPDATE madecount = %u;" - , recipe_id, char_id, madecount, madecount); - - if (!RunQuery(query, qlen, errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query, errbuf); - } - safe_delete_array(query); + std::string query = StringFormat("INSERT INTO char_recipe_list " + "SET recipe_id = %u, char_id = %u, madecount = %u " + "ON DUPLICATE KEY UPDATE madecount = %u;", + recipe_id, char_id, madeCount, madeCount); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); } void Client::LearnRecipe(uint32 recipeID) { - char *query = 0; - uint32 qlen; - uint32 qcount = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - qlen = MakeAnyLenString(&query, "SELECT tr.name, crl.madecount " - " FROM tradeskill_recipe as tr " - " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl " - " ON tr.id = crl.recipe_id " - " WHERE tr.id = %u ;", CharacterID(), recipeID); - - if (!database.RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT tr.name, crl.madecount " + "FROM tradeskill_recipe AS tr " + "LEFT JOIN (SELECT recipe_id, madecount " + "FROM char_recipe_list WHERE char_id = %u) AS crl " + "ON tr.id = crl.recipe_id " + "WHERE tr.id = %u ;", CharacterID(), recipeID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } - qcount = mysql_num_rows(result); - if (qcount != 1) { - LogFile->write(EQEMuLog::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, qcount); - mysql_free_result(result); - safe_delete_array(query); + if (results.RowCount() != 1) { + LogFile->write(EQEMuLog::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, results.RowCount()); return; } - safe_delete_array(query); - row = mysql_fetch_row(result); + auto row = results.begin(); - if (row != nullptr && row[0] != nullptr) { - // Only give Learn message if character doesn't know the recipe - if (row[1] == nullptr) { - Message_StringID(4, TRADESKILL_LEARN_RECIPE, row[0]); - // Actually learn the recipe now - qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list " - " SET recipe_id = %u, char_id = %u, madecount = 0 " - " ON DUPLICATE KEY UPDATE madecount = madecount;" - , recipeID, CharacterID()); + if (row[0] == nullptr) + return; - if (!database.RunQuery(query, qlen, errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query, errbuf); - } - safe_delete_array(query); - } - } + // Only give Learn message if character doesn't know the recipe + if (row[1] != nullptr) + return; - mysql_free_result(result); + Message_StringID(4, TRADESKILL_LEARN_RECIPE, row[0]); + // Actually learn the recipe now + query = StringFormat("INSERT INTO char_recipe_list " + "SET recipe_id = %u, char_id = %u, madecount = 0 " + "ON DUPLICATE KEY UPDATE madecount = madecount;", + recipeID, CharacterID()); + results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); } From 6221c1f8ab39f712b67c40254ebb8c6a5ce8bbad Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 16:04:19 -0700 Subject: [PATCH 325/368] EnableRecipe converted to QueryDatabase --- zone/tradeskills.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 09cf70fc3..78480a7c1 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -1511,19 +1511,13 @@ bool Client::CanIncreaseTradeskill(SkillUseTypes tradeskill) { bool ZoneDatabase::EnableRecipe(uint32 recipe_id) { - char *query = 0; - uint32 qlen; - char errbuf[MYSQL_ERRMSG_SIZE]; - uint32 affected_rows = 0; + std::string query = StringFormat("UPDATE tradeskill_recipe SET enabled = 1 " + "WHERE id = %u;", recipe_id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in EnableRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - qlen = MakeAnyLenString(&query, "UPDATE tradeskill_recipe SET enabled = 1 WHERE id = %u;", recipe_id); - - if (!RunQuery(query, qlen, errbuf, 0, &affected_rows)) { - LogFile->write(EQEMuLog::Error, "Error in EnableRecipe query '%s': %s", query, errbuf); - } - safe_delete_array(query); - - return (affected_rows > 0); + return results.RowsAffected() > 0; } bool ZoneDatabase::DisableRecipe(uint32 recipe_id) From e338f801eaf7b12e966eede7f286361a0b911e84 Mon Sep 17 00:00:00 2001 From: Arthur Ice Date: Wed, 8 Oct 2014 16:05:52 -0700 Subject: [PATCH 326/368] DisableRecipe converted to QueryDatabase --- zone/tradeskills.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 78480a7c1..0fd130f89 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -1522,16 +1522,11 @@ bool ZoneDatabase::EnableRecipe(uint32 recipe_id) bool ZoneDatabase::DisableRecipe(uint32 recipe_id) { - char *query = 0; - uint32 qlen; - char errbuf[MYSQL_ERRMSG_SIZE]; - uint32 affected_rows = 0; + std::string query = StringFormat("UPDATE tradeskill_recipe SET enabled = 0 " + "WHERE id = %u;", recipe_id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in DisableRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - qlen = MakeAnyLenString(&query, "UPDATE tradeskill_recipe SET enabled = 0 WHERE id = %u;", recipe_id); - - if (!RunQuery(query, qlen, errbuf, 0, &affected_rows)) { - LogFile->write(EQEMuLog::Error, "Error in DisableRecipe query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return (affected_rows > 0); + return results.RowsAffected() > 0; } From 28ac586ed8e9e0ef1cd789ff5a3d678c2e8850d5 Mon Sep 17 00:00:00 2001 From: JJ Date: Thu, 9 Oct 2014 00:23:54 -0400 Subject: [PATCH 327/368] Update info for #suspend --- zone/command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/command.cpp b/zone/command.cpp index 2d7a13943..aa632be82 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -371,7 +371,7 @@ int command_init(void) { command_add("logs","[status|normal|error|debug|quest|all] - Subscribe to a log type",250,command_logs) || command_add("nologs","[status|normal|error|debug|quest|all] - Unsubscribe to a log type",250,command_nologs) || command_add("ban","[name] [reason]- Ban by character name",150,command_ban) || - command_add("suspend","[name][days] - Suspend by character name and for specificed number of days",150,command_suspend) || + command_add("suspend","[name] [days] [reason] - Suspend by character name and for specificed number of days",150,command_suspend) || command_add("ipban","[IP address] - Ban IP by character name",200,command_ipban) || command_add("oocmute","[1/0] - Mutes OOC chat",200,command_oocmute) || command_add("revoke","[charname] [1/0] - Makes charname unable to talk on OOC",200,command_revoke) || From c3288296109762807dffa766e1a4c0bfa5a210cb Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 9 Oct 2014 22:55:28 -0400 Subject: [PATCH 328/368] Fix out of bounds error in SaveCharacterLeadershipAAs --- zone/zonedb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 7815b5b96..1ba36c5fd 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1295,7 +1295,7 @@ bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, bool ZoneDatabase::SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp){ uint8 first_entry = 0; std::string query = ""; - for (int i = 0; i <= MAX_LEADERSHIP_AA_ARRAY; i++){ + for (int i = 0; i < MAX_LEADERSHIP_AA_ARRAY; i++){ if (pp->leader_abilities.ranks[i] > 0){ if (first_entry != 1){ query = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, rank) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); From ea4c23efcf378b00909b70a7352167dc01624f46 Mon Sep 17 00:00:00 2001 From: JJ Date: Thu, 9 Oct 2014 23:11:18 -0400 Subject: [PATCH 329/368] Establish default order for merchantlist items. Allow merchants to use 80th slot. --- zone/client_process.cpp | 91 +++++++++++++++++++++-------------------- zone/zone.cpp | 56 +++++++++++++------------ 2 files changed, 77 insertions(+), 70 deletions(-) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 544200910..d9b2d9030 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -967,93 +967,96 @@ void Client::BulkSendInventoryItems() void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { const Item_Struct* handyitem = nullptr; - uint32 numItemSlots=80; //The max number of items passed in the transaction. + uint32 numItemSlots = 80; //The max number of items passed in the transaction. const Item_Struct *item; std::list merlist = zone->merchanttable[merchant_id]; std::list::const_iterator itr; Mob* merch = entity_list.GetMobByNpcTypeID(npcid); - if(merlist.size()==0){ //Attempt to load the data, it might have been missed if someone spawned the merchant after the zone was loaded + if (merlist.size() == 0) { //Attempt to load the data, it might have been missed if someone spawned the merchant after the zone was loaded zone->LoadNewMerchantData(merchant_id); merlist = zone->merchanttable[merchant_id]; - if(merlist.size()==0) + if (merlist.size() == 0) return; } std::list tmp_merlist = zone->tmpmerchanttable[npcid]; std::list::iterator tmp_itr; - uint32 i=1; + uint32 i = 1; uint8 handychance = 0; - for (itr = merlist.begin(); itr != merlist.end() && i < numItemSlots; ++itr) { + for (itr = merlist.begin(); itr != merlist.end() && i <= numItemSlots; ++itr) { MerchantList ml = *itr; if (merch->CastToNPC()->GetMerchantProbability() > ml.probability) continue; - - if(GetLevel() < ml.level_required) + + if (GetLevel() < ml.level_required) continue; if (!(ml.classes_required & (1 << (GetClass() - 1)))) continue; int32 fac = merch ? merch->GetPrimaryFaction() : 0; - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) continue; - handychance = MakeRandomInt(0, merlist.size() + tmp_merlist.size() - 1 ); + handychance = MakeRandomInt(0, merlist.size() + tmp_merlist.size() - 1); item = database.GetItem(ml.item); - if(item) { - if(handychance==0) - handyitem=item; + if (item) { + if (handychance == 0) + handyitem = item; else handychance--; - int charges=1; - if(item->ItemClass==ItemClassCommon) - charges=item->MaxCharges; + int charges = 1; + if (item->ItemClass == ItemClassCommon) + charges = item->MaxCharges; ItemInst* inst = database.CreateItem(item, charges); if (inst) { - if (RuleB(Merchant, UsePriceMod)){ - inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(merch,false))); + if (RuleB(Merchant, UsePriceMod)) { + inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(merch, false))); } else - inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate)); + inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate)); inst->SetMerchantSlot(ml.slot); inst->SetMerchantCount(-1); //unlimited - if(charges > 0) + if (charges > 0) inst->SetCharges(charges); else inst->SetCharges(1); - SendItemPacket(ml.slot-1, inst, ItemPacketMerchant); + SendItemPacket(ml.slot - 1, inst, ItemPacketMerchant); safe_delete(inst); } } // Account for merchant lists with gaps. - if(ml.slot >= i) + if (ml.slot >= i) { + if (ml.slot > i) + LogFile->write(EQEMuLog::Debug, "(WARNING) Merchantlist contains gap at slot %d. Merchant: %d, NPC: %d", i, merchant_id, npcid); i = ml.slot + 1; + } } std::list origtmp_merlist = zone->tmpmerchanttable[npcid]; tmp_merlist.clear(); - for(tmp_itr = origtmp_merlist.begin();tmp_itr != origtmp_merlist.end() && iItemClass==ItemClassCommon && (int16)ml.charges <= item->MaxCharges) // charges=ml.charges; //else charges = item->MaxCharges; ItemInst* inst = database.CreateItem(item, charges); if (inst) { - if (RuleB(Merchant, UsePriceMod)){ - inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(merch,false))); + if (RuleB(Merchant, UsePriceMod)) { + inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(merch, false))); } else - inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate)); + inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate)); inst->SetMerchantSlot(ml.slot); inst->SetMerchantCount(ml.charges); if(charges > 0) @@ -1069,32 +1072,32 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { } //this resets the slot zone->tmpmerchanttable[npcid] = tmp_merlist; - if(merch != nullptr && handyitem){ - char handy_id[8]={0}; - int greeting=MakeRandomInt(0, 4); - int greet_id=0; - switch(greeting){ + if (merch != nullptr && handyitem) { + char handy_id[8] = { 0 }; + int greeting = MakeRandomInt(0, 4); + int greet_id = 0; + switch (greeting) { case 1: - greet_id=MERCHANT_GREETING; + greet_id = MERCHANT_GREETING; break; case 2: - greet_id=MERCHANT_HANDY_ITEM1; + greet_id = MERCHANT_HANDY_ITEM1; break; case 3: - greet_id=MERCHANT_HANDY_ITEM2; + greet_id = MERCHANT_HANDY_ITEM2; break; case 4: - greet_id=MERCHANT_HANDY_ITEM3; + greet_id = MERCHANT_HANDY_ITEM3; break; default: - greet_id=MERCHANT_HANDY_ITEM4; + greet_id = MERCHANT_HANDY_ITEM4; } - sprintf(handy_id,"%i",greet_id); + sprintf(handy_id, "%i", greet_id); - if(greet_id!=MERCHANT_GREETING) - Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName(),handyitem->Name); + if (greet_id != MERCHANT_GREETING) + Message_StringID(10, GENERIC_STRINGID_SAY, merch->GetCleanName(), handy_id, this->GetName(), handyitem->Name); else - Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName()); + Message_StringID(10, GENERIC_STRINGID_SAY, merch->GetCleanName(), handy_id, this->GetName()); merch->CastToNPC()->FaceTarget(this->CastToMob()); } diff --git a/zone/zone.cpp b/zone/zone.cpp index 446110a4a..23aa95f2c 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -307,41 +307,40 @@ bool Zone::LoadGroundSpawns() { return(true); } -int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold){ +int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold) { int freeslot = 0; std::list merlist = merchanttable[merchantid]; std::list::const_iterator itr; uint32 i = 1; for (itr = merlist.begin(); itr != merlist.end(); ++itr) { MerchantList ml = *itr; - if(ml.item == item) + if (ml.item == item) return 0; // Account for merchant lists with gaps in them. - if(ml.slot >= i) + if (ml.slot >= i) i = ml.slot + 1; - } std::list tmp_merlist = tmpmerchanttable[npcid]; std::list::const_iterator tmp_itr; bool update_charges = false; TempMerchantList ml; - while(freeslot == 0 && !update_charges){ + while (freeslot == 0 && !update_charges) { freeslot = i; for (tmp_itr = tmp_merlist.begin(); tmp_itr != tmp_merlist.end(); ++tmp_itr) { ml = *tmp_itr; - if(ml.item == item){ + if (ml.item == item) { update_charges = true; freeslot = 0; break; } - if((ml.slot == i) || (ml.origslot==i)) { - freeslot=0; + if ((ml.slot == i) || (ml.origslot == i)) { + freeslot = 0; } } i++; } - if(update_charges){ + if (update_charges) { tmp_merlist.clear(); std::list oldtmp_merlist = tmpmerchanttable[npcid]; for (tmp_itr = oldtmp_merlist.begin(); tmp_itr != oldtmp_merlist.end(); ++tmp_itr) { @@ -349,27 +348,27 @@ int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charg if(ml2.item != item) tmp_merlist.push_back(ml2); } - if(sold) + if (sold) ml.charges = ml.charges + charges; else ml.charges = charges; - if(!ml.origslot) + if (!ml.origslot) ml.origslot = ml.slot; - if(charges>0){ + if (charges > 0) { database.SaveMerchantTemp(npcid, ml.origslot, item, ml.charges); tmp_merlist.push_back(ml); } - else{ - database.DeleteMerchantTemp(npcid,ml.origslot); + else { + database.DeleteMerchantTemp(npcid, ml.origslot); } tmpmerchanttable[npcid] = tmp_merlist; - if(sold) + if (sold) return ml.slot; } - if(freeslot){ - if(charges<0) //sanity check only, shouldnt happen + if (freeslot) { + if (charges < 0) //sanity check only, shouldnt happen charges = 0x7FFF; database.SaveMerchantTemp(npcid, freeslot, item, charges); tmp_merlist = tmpmerchanttable[npcid]; @@ -391,13 +390,13 @@ uint32 Zone::GetTempMerchantQuantity(uint32 NPCID, uint32 Slot) { std::list::const_iterator Iterator; for (Iterator = TmpMerchantList.begin(); Iterator != TmpMerchantList.end(); ++Iterator) - if((*Iterator).slot == Slot) + if ((*Iterator).slot == Slot) return (*Iterator).charges; return 0; } -void Zone::LoadTempMerchantData(){ +void Zone::LoadTempMerchantData() { LogFile->write(EQEMuLog::Status, "Loading Temporary Merchant Lists..."); std::string query = StringFormat( "SELECT " @@ -412,7 +411,8 @@ void Zone::LoadTempMerchantData(){ "WHERE " "ml.npcid = se.npcid " "AND se.spawngroupid = s2.spawngroupid " - "AND s2.zone = '%s' AND s2.version = %i", GetShortName(), GetInstanceVersion()); + "AND s2.zone = '%s' AND s2.version = %i" + "ORDER BY ml.slot ", GetShortName(), GetInstanceVersion()); auto results = database.QueryDatabase(query); if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in LoadTempMerchantData query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); @@ -441,11 +441,11 @@ void Zone::LoadTempMerchantData(){ pQueuedMerchantsWorkID = 0; } -void Zone::LoadNewMerchantData(uint32 merchantid){ +void Zone::LoadNewMerchantData(uint32 merchantid) { std::list merlist; std::string query = StringFormat("SELECT item, slot, faction_required, level_required, alt_currency_cost, " - "classes_required FROM merchantlist WHERE merchantid=%d", merchantid); + "classes_required FROM merchantlist WHERE merchantid=%d ORDER BY slot", merchantid); auto results = database.QueryDatabase(query); if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in LoadNewMerchantData query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); @@ -467,7 +467,7 @@ void Zone::LoadNewMerchantData(uint32 merchantid){ merchanttable[merchantid] = merlist; } -void Zone::GetMerchantDataForZoneLoad(){ +void Zone::GetMerchantDataForZoneLoad() { LogFile->write(EQEMuLog::Status, "Loading Merchant Lists..."); std::string query = StringFormat( "SELECT " @@ -485,15 +485,19 @@ void Zone::GetMerchantDataForZoneLoad(){ "spawnentry AS se, " "spawn2 AS s2 " "WHERE nt.merchant_id = ml.merchantid AND nt.id = se.npcid " - "AND se.spawngroupid = s2.spawngroupid AND s2.zone = '%s' AND s2.version = %i ", GetShortName(), GetInstanceVersion()); + "AND se.spawngroupid = s2.spawngroupid AND s2.zone = '%s' AND s2.version = %i " + "ORDER BY ml.slot ", GetShortName(), GetInstanceVersion()); auto results = database.QueryDatabase(query); std::map >::iterator cur; uint32 npcid = 0; - if (results.RowCount() == 0){ LogFile->write(EQEMuLog::Error, "Error in loading Merchant Data for zone"); return; } + if (results.RowCount() == 0) { + LogFile->write(EQEMuLog::Error, "Error in loading Merchant Data for zone"); + return; + } for (auto row = results.begin(); row != results.end(); ++row) { MerchantList ml; ml.id = atoul(row[0]); - if (npcid != ml.id){ + if (npcid != ml.id) { cur = merchanttable.find(ml.id); if (cur == merchanttable.end()) { std::list empty; From 3e1c917f1176a684cee33d6426303303a77ec138 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 9 Oct 2014 23:30:08 -0400 Subject: [PATCH 330/368] Fix issues with purchasing raid AAs --- zone/client_packet.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6da4a265f..2eb67ced4 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10565,8 +10565,9 @@ void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) //sell them the ability. m_pp.raid_leadership_points -= cost; m_pp.leader_abilities.ranks[aaid]++; - } - else { + + database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); + } else { //it is a group ability. if (cost > m_pp.group_leadership_points) { Message(13, "You do not have enough points to purchase this ability."); @@ -10585,7 +10586,10 @@ void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) UpdateLeadershipAA_Struct *u = (UpdateLeadershipAA_Struct *)outapp->pBuffer; u->ability_id = aaid; u->new_rank = m_pp.leader_abilities.ranks[aaid]; - u->pointsleft = m_pp.group_leadership_points; // FIXME: Take into account raid abilities + if (aaid >= raidAAMarkNPC) // raid AA + u->pointsleft = m_pp.raid_leadership_points; + else // group AA + u->pointsleft = m_pp.group_leadership_points; FastQueuePacket(&outapp); Group *g = GetGroup(); From 66cfb2e32bcd30e372d568bb7f54c307f25b2534 Mon Sep 17 00:00:00 2001 From: Uleat Date: Thu, 9 Oct 2014 23:54:48 -0400 Subject: [PATCH 331/368] Added a 'BOTS' converter to supplement Akkadius's recent 'PP' blob converter - updates views/function to reference the proper tables and columns. --- changelog.txt | 9 +++ common/database.cpp | 166 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) diff --git a/changelog.txt b/changelog.txt index d00864914..76bef57e4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,14 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/09/2014 == +Uleat: Added 'BOTS' conversion code to supplement the database 'PlayerProfile' blob conversion that Akkadius recently implemented. +Note: This automatic conversion uses the view `vwbotcharactermobs` as an update vector. If you need/would like for the converter to run on +previously and/or manually changed code, or just have a need for it to re-run, change the following in the database view and save: + "c.`last_login`," to "c.`timelaston`," + "c.`zone_id`" to "c.`zoneid`" + "FROM `character_data` AS c" to "FROM `character_old` AS c" +** This will only work if you haven't deleted your `character_old` table ** + == 10/07/2014 == demonstar55: Identified tutorial flag in all charcreate packets, reworked logic to correctly set homes binds diff --git a/common/database.cpp b/common/database.cpp index 1af4d05a0..dedc00ff1 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1915,6 +1915,172 @@ bool Database::CheckDatabaseConversions() { printf("\n\nCharacter blob conversion complete, continuing world bootup...\n"); } + +#ifdef BOTS + + int runbotsconvert = 0; + + /* Check For Legacy Bot References */ + rquery = StringFormat("SHOW CREATE VIEW `vwbotcharactermobs`"); + results = QueryDatabase(rquery); + if (results.RowCount() == 1){ + auto row = results.begin(); + std::string table_check = row[1]; + + if (table_check.find("character_data") == -1){ + runbotsconvert = 1; + printf("\n\n::: Legacy Bot Views and Function Detected... \n"); + printf("----------------------------------------------------------\n\n"); + printf(" Database currently has bot view/function linkage to obselete \n"); + printf(" table references and will now be converted...\n\n"); + printf("----------------------------------------------------------\n\n"); + std::cout << "Press ENTER to continue....." << std::endl << std::endl; + std::cin.ignore(1); + } + } + else{ + ThrowDBError(results.ErrorMessage(), "Bot View Discovery", rquery); + } + + if (runbotsconvert == 1){ + + /* Update view `vwbotcharactermobs` */ + rquery = StringFormat("DROP VIEW `vwbotcharactermobs`;"); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwbotcharactermobs`", rquery); + + rquery = StringFormat( + "CREATE VIEW `vwbotcharactermobs` AS\n" + "SELECT _utf8'C' AS mobtype,\n" // Natedog: '_utf8' + "c.`id`,\n" + "c.`name`,\n" + "c.`class`,\n" + "c.`level`,\n" + "c.`last_login`,\n" + "c.`zone_id`\n" + "FROM `character_data` AS c\n" + "UNION ALL\n" + "SELECT _utf8'B' AS mobtype,\n" // Natedog: '_utf8' + "b.`BotID` AS id,\n" + "b.`Name` AS name,\n" + "b.`Class` AS class,\n" + "b.`BotLevel` AS level,\n" + "0 AS timelaston,\n" + "0 AS zoneid\n" + "FROM bots AS b;" + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwbotcharactermobs`", rquery); + + + /* Update function `GetMobType` */ + rquery = StringFormat("DROP FUNCTION IF EXISTS `GetMobType`;"); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Drop Function `GetMobType`", rquery); + + rquery = StringFormat( + "CREATE FUNCTION `GetMobType` (mobname VARCHAR(64)) RETURNS CHAR(1)\n" + "BEGIN\n" + " DECLARE Result CHAR(1);\n" + "\n" + " SET Result = NULL;\n" + "\n" + " IF (SELECT COUNT(*) FROM `character_data` WHERE `name` = mobname) > 0 THEN\n" + " SET Result = 'C';\n" + " ELSEIF (SELECT COUNT(*) FROM `bots` WHERE `Name` = mobname) > 0 THEN\n" + " SET Result = 'B';\n" + " END IF;\n " + "\n" + " RETURN Result;\n" + "END" + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Create Function `GetMobType`", rquery); + + + /* Update view `vwgroups` */ + rquery = StringFormat("DROP VIEW IF EXISTS `vwgroups`;"); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwgroups`", rquery); + + rquery = StringFormat( + "CREATE VIEW `vwgroups` AS\n" + "SELECT g.`groupid` AS groupid,\n" + "GetMobType(g.`name`) AS mobtype,\n" + "g.`name` AS name,\n" + "g.`charid` AS mobid,\n" + "IFNULL(c.`level`, b.`BotLevel`) AS level\n" + "FROM `group_id` AS g\n" + "LEFT JOIN `character_data` AS c ON g.`name` = c.`name`\n" + "LEFT JOIN `bots` AS b ON g.`name` = b.`Name`;" + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwgroups`", rquery); + + + /* Update view `vwbotgroups` */ + rquery = StringFormat("DROP VIEW IF EXISTS `vwbotgroups`;"); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwbotgroups`", rquery); + + rquery = StringFormat( + "CREATE VIEW `vwbotgroups` AS\n" + "SELECT g.`BotGroupId`,\n" + "g.`BotGroupName`,\n" + "g.`BotGroupLeaderBotId`,\n" + "b.`Name` AS BotGroupLeaderName,\n" + "b.`BotOwnerCharacterId`,\n" + "c.`name` AS BotOwnerCharacterName\n" + "FROM `botgroup` AS g\n" + "JOIN `bots` AS b ON g.`BotGroupLeaderBotId` = b.`BotID`\n" + "JOIN `character_data` AS c ON b.`BotOwnerCharacterID` = c.`id`\n" + "ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`;" + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwbotgroups`", rquery); + + + /* Update view `vwguildmembers` */ + rquery = StringFormat("DROP VIEW IF EXISTS `vwguildmembers`;"); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwguildmembers`", rquery); + + rquery = StringFormat( + "CREATE VIEW `vwguildmembers` AS\n" + "SELECT 'C' AS mobtype,\n" + "cm.`char_id`,\n" + "cm.`guild_id`,\n" + "cm.`rank`,\n" + "cm.`tribute_enable`,\n" + "cm.`total_tribute`,\n" + "cm.`last_tribute`,\n" + "cm.`banker`,\n" + "cm.`public_note`,\n" + "cm.`alt`\n" + "FROM `guild_members` AS cm\n" + "UNION ALL\n" + "SELECT 'B' AS mobtype,\n" + "bm.`char_id`,\n" + "bm.`guild_id`,\n" + "bm.`rank`,\n" + "bm.`tribute_enable`,\n" + "bm.`total_tribute`,\n" + "bm.`last_tribute`,\n" + "bm.`banker`,\n" + "bm.`public_note`,\n" + "bm.`alt`\n" + "FROM `botguildmembers` AS bm;" + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwguildmembers`", rquery); + } + + if (runbotsconvert == 1){ + printf("\n\nBot views/function conversion complete, continuing world bootup...\n"); + } + +#endif + return true; } From 35049d530e6fccdd5a0b5993cf90359a346b09db Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 11 Oct 2014 01:14:11 -0400 Subject: [PATCH 332/368] Implement Raid MOTD for UF Don't forget to source 2014_10_11_RaidMOTD.sql SoD and RoF implementations still to come --- changelog.txt | 4 ++ common/eq_packet_structs.h | 4 ++ common/patches/underfoot.cpp | 51 +++++++++++++++--- common/patches/underfoot_structs.h | 5 ++ common/servertalk.h | 6 +++ .../sql/git/required/2014_10_11_RaidMOTD.sql | 1 + world/zoneserver.cpp | 8 +++ zone/client_packet.cpp | 18 ++++++- zone/raids.cpp | 52 ++++++++++++++++++- zone/raids.h | 9 +++- zone/worldserver.cpp | 12 +++++ 11 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 utils/sql/git/required/2014_10_11_RaidMOTD.sql diff --git a/changelog.txt b/changelog.txt index 76bef57e4..2461b4a36 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/11/2014 == +demonstar55: Implement Raid MOTD for UF + Don't forget 2014_10_11_RaidMOTD.sql! + == 10/09/2014 == Uleat: Added 'BOTS' conversion code to supplement the database 'PlayerProfile' blob conversion that Akkadius recently implemented. Note: This automatic conversion uses the view `vwbotcharactermobs` as an update vector. If you need/would like for the converter to run on diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index f49c0106c..eda2a5ce8 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3929,6 +3929,10 @@ struct RaidAddMember_Struct { /*139*/ uint8 flags[5]; //no idea if these are needed... }; +struct RaidMOTD_Struct { +/*000*/ RaidGeneral_Struct general; // leader_name and action only used +/*136*/ char motd[0]; // max size is 1024, but reply is variable +}; struct RaidAdd_Struct { /*000*/ uint32 action; //=0 diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index 1caf65bf3..03ef16a2a 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -1929,6 +1929,17 @@ namespace Underfoot add_member->flags[4] = in_add_member->flags[4]; dest->FastQueuePacket(&outapp); } + else if (raid_gen->action == 35) + { + RaidMOTD_Struct *inmotd = (RaidMOTD_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidMOTD_Struct) + strlen(inmotd->motd) + 1); + structs::RaidMOTD_Struct *outmotd = (structs::RaidMOTD_Struct *)outapp->pBuffer; + + outmotd->general.action = inmotd->general.action; + strn0cpy(outmotd->general.player_name, inmotd->general.player_name, 64); + strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); + dest->FastQueuePacket(&outapp); + } else { RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; @@ -3367,15 +3378,41 @@ namespace Underfoot DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct); - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); + // This is a switch on the RaidGeneral action + switch (*(uint32 *)__packet->pBuffer) { + case 35: { // raidMOTD + // we don't have a nice macro for this + structs::RaidMOTD_Struct *__eq_buffer = (structs::RaidMOTD_Struct *)__packet->pBuffer; + __eq_buffer->motd[1023] = '\0'; + size_t motd_size = strlen(__eq_buffer->motd) + 1; + __packet->size = sizeof(RaidMOTD_Struct) + motd_size; + __packet->pBuffer = new unsigned char[__packet->size]; + RaidMOTD_Struct *emu = (RaidMOTD_Struct *)__packet->pBuffer; + structs::RaidMOTD_Struct *eq = (structs::RaidMOTD_Struct *)__eq_buffer; + strn0cpy(emu->general.player_name, eq->general.player_name, 64); + strn0cpy(emu->motd, eq->motd, motd_size); + IN(general.action); + IN(general.parameter); + FINISH_DIRECT_DECODE(); + break; + } + case 36: { // raidPlayerNote unhandled + break; + } + default: { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + FINISH_DIRECT_DECODE(); + break; + } + } - FINISH_DIRECT_DECODE(); } DECODE(OP_ReadBook) diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index 89101ca2e..f331d5dbc 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -3623,6 +3623,11 @@ struct RaidAddMember_Struct { /*139*/ uint8 flags[5]; //no idea if these are needed... }; +struct RaidMOTD_Struct { +/*000*/ RaidGeneral_Struct general; // leader_name and action only used +/*140*/ char motd[0]; // max size 1024, but reply is variable +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name diff --git a/common/servertalk.h b/common/servertalk.h index 8c30715d9..f458c758e 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -104,6 +104,7 @@ #define ServerOP_GroupFollow 0x0110 #define ServerOP_GroupFollowAck 0x0111 #define ServerOP_GroupCancelInvite 0x0112 +#define ServerOP_RaidMOTD 0x0113 #define ServerOP_InstanceUpdateTime 0x014F #define ServerOP_AdventureRequest 0x0150 @@ -854,6 +855,11 @@ struct ServerRaidMessage_Struct { char message[0]; }; +struct ServerRaidMOTD_Struct { + uint32 rid; + char motd[0]; +}; + struct ServerLFGMatchesRequest_Struct { uint32 FromID; uint8 QuerierLevel; diff --git a/utils/sql/git/required/2014_10_11_RaidMOTD.sql b/utils/sql/git/required/2014_10_11_RaidMOTD.sql new file mode 100644 index 000000000..39fccd311 --- /dev/null +++ b/utils/sql/git/required/2014_10_11_RaidMOTD.sql @@ -0,0 +1 @@ +ALTER TABLE `raid_details` ADD `motd` varchar(1024); diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 1023dbe69..b94bda20a 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -409,6 +409,14 @@ bool ZoneServer::Process() { break; } + case ServerOP_RaidMOTD: { + if (pack->size < sizeof(ServerRaidMOTD_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_SpawnCondition: { if(pack->size != sizeof(ServerSpawnCondition_Struct)) break; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2eb67ced4..03d44c903 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -565,6 +565,7 @@ void Client::CompleteConnect() raid->SendRaidAdd(GetName(), this); raid->SendBulkRaid(this); raid->SendGroupUpdate(this); + raid->SendRaidMOTD(this); uint32 grpID = raid->GetGroup(GetName()); if (grpID < 12){ raid->SendRaidGroupRemove(GetName(), grpID); @@ -10656,8 +10657,8 @@ void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) { - if (app->size != sizeof(RaidGeneral_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_RaidCommand, size=%i, expected %i", app->size, sizeof(RaidGeneral_Struct)); + if (app->size < sizeof(RaidGeneral_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_RaidCommand, size=%i, expected at least %i", app->size, sizeof(RaidGeneral_Struct)); DumpPacket(app); return; } @@ -11219,6 +11220,19 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) break; } + case RaidCommandSetMotd: + { + Raid *r = entity_list.GetRaidByClient(this); + if (!r) + break; + // we don't use the RaidGeneral here! + RaidMOTD_Struct *motd = (RaidMOTD_Struct *)app->pBuffer; + r->SetRaidMOTD(std::string(motd->motd)); + r->SaveRaidMOTD(); + r->SendRaidMOTDToWorld(); + break; + } + default: { Message(13, "Raid command (%d) NYI", ri->action); break; diff --git a/zone/raids.cpp b/zone/raids.cpp index 1e8e23f8d..f22267a2f 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -89,6 +89,7 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo SendRaidAddAll(c->GetName()); c->SetRaidGrouped(true); + SendRaidMOTD(c); ServerPacket *pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; @@ -1205,6 +1206,44 @@ void Raid::SendRaidGroupRemove(const char *who, uint32 gid) safe_delete(pack); } +void Raid::SendRaidMOTD(Client *c) +{ + if (!c || motd.empty()) + return; + + size_t size = motd.size() + 1; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidMOTD_Struct) + size); + RaidMOTD_Struct *rmotd = (RaidMOTD_Struct *)outapp->pBuffer; + rmotd->general.action = raidSetMotd; + strn0cpy(rmotd->general.player_name, c->GetName(), 64); + strn0cpy(rmotd->motd, motd.c_str(), size); + c->FastQueuePacket(&outapp); +} + +void Raid::SendRaidMOTD() +{ + if (motd.empty()) + return; + + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) + if (members[i].member) + SendRaidMOTD(members[i].member); +} + +void Raid::SendRaidMOTDToWorld() +{ + if (motd.empty()) + return; + + size_t size = motd.size() + 1; + ServerPacket *pack = new ServerPacket(ServerOP_RaidMOTD, sizeof(ServerRaidMOTD_Struct) + size); + ServerRaidMOTD_Struct *smotd = (ServerRaidMOTD_Struct *)pack->pBuffer; + smotd->rid = GetID(); + strn0cpy(smotd->motd, motd.c_str(), size); + worldserver.SendPacket(pack); + safe_delete(pack); +} + void Raid::LockRaid(bool lockFlag) { std::string query = StringFormat("UPDATE raid_details SET locked = %d WHERE raidid = %lu", @@ -1229,14 +1268,14 @@ void Raid::LockRaid(bool lockFlag) void Raid::SetRaidDetails() { - std::string query = StringFormat("INSERT INTO raid_details SET raidid = %lu, loottype = 4, locked = 0", + std::string query = StringFormat("INSERT INTO raid_details SET raidid = %lu, loottype = 4, locked = 0, motd = ''", (unsigned long)GetID()); auto results = database.QueryDatabase(query); } void Raid::GetRaidDetails() { - std::string query = StringFormat("SELECT locked, loottype FROM raid_details WHERE raidid = %lu", + std::string query = StringFormat("SELECT locked, loottype, motd FROM raid_details WHERE raidid = %lu", (unsigned long)GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) @@ -1251,6 +1290,15 @@ void Raid::GetRaidDetails() locked = atoi(row[0]); LootType = atoi(row[1]); + motd = std::string(row[2]); +} + +void Raid::SaveRaidMOTD() +{ + std::string query = StringFormat("UPDATE raid_details SET motd = '%s' WHERE raidid = %lu", + EscapeString(motd).c_str(), (unsigned long)GetID()); + + auto results = database.QueryDatabase(query); } bool Raid::LearnMembers() diff --git a/zone/raids.h b/zone/raids.h index be12788e6..44952e5da 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -38,7 +38,7 @@ enum { //raid packet types: raidMembers = 6, //len 395+, details + members list raidNoAssignLeadership = 7, raidCreate = 8, //len 72 - raidUnknown = 9, + raidUnknown = 9, // unused? raidNoRaid = 10, //parameter=0 raidChangeLootType = 11, raidStringID = 12, @@ -130,6 +130,8 @@ public: void AddRaidLooter(const char* looter); void RemoveRaidLooter(const char* looter); + inline void SetRaidMOTD(std::string in_motd) { motd = in_motd; }; + //util func //keeps me from having to keep iterating through the list //when I want lots of data from the same entry @@ -160,6 +162,7 @@ public: //also learns raid structure based on db. void SetRaidDetails(); void GetRaidDetails(); + void SaveRaidMOTD(); bool LearnMembers(); void VerifyRaid(); void MemberZoned(Client *c); @@ -194,6 +197,9 @@ public: void SendMakeGroupLeaderPacketAll(); void SendMakeGroupLeaderPacket(const char *who); //13 void SendMakeGroupLeaderPacketTo(const char *who, Client *to); + void SendRaidMOTD(Client *c); + void SendRaidMOTD(); + void SendRaidMOTDToWorld(); void QueuePacket(const EQApplicationPacket *app, bool ack_req = true); @@ -206,6 +212,7 @@ protected: uint32 LootType; bool disbandCheck; bool forceDisband; + std::string motd; }; diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index a1100b7cb..1ff42cd02 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1372,6 +1372,18 @@ void WorldServer::Process() { break; } + case ServerOP_RaidMOTD: { + ServerRaidMOTD_Struct *rmotd = (ServerRaidMOTD_Struct *)pack->pBuffer; + if (!zone) + break; + Raid *r = entity_list.GetRaidByID(rmotd->rid); + if (!r) + break; + r->SetRaidMOTD(std::string(rmotd->motd)); + r->SendRaidMOTD(); + break; + } + case ServerOP_SpawnPlayerCorpse: { SpawnPlayerCorpse_Struct* s = (SpawnPlayerCorpse_Struct*)pack->pBuffer; Corpse* NewCorpse = database.LoadPlayerCorpse(s->player_corpse_id); From f097eaf3cd5a750a0c37ccbfde22812c3c4ab5dd Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 11 Oct 2014 01:54:32 -0400 Subject: [PATCH 333/368] Raid MOTD for RoF --- common/patches/rof.cpp | 52 ++++++++++++++++++++++++++++++------ common/patches/rof_structs.h | 5 ++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index db1c5487e..49f6c51f1 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -2602,6 +2602,17 @@ namespace RoF add_member->flags[4] = in_add_member->flags[4]; dest->FastQueuePacket(&outapp); } + else if (raid_gen->action == 35) + { + RaidMOTD_Struct *inmotd = (RaidMOTD_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidMOTD_Struct) + strlen(inmotd->motd) + 1); + structs::RaidMOTD_Struct *outmotd = (structs::RaidMOTD_Struct *)outapp->pBuffer; + + outmotd->general.action = inmotd->general.action; + strn0cpy(outmotd->general.player_name, inmotd->general.player_name, 64); + strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); + dest->FastQueuePacket(&outapp); + } else { RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; @@ -4475,15 +4486,40 @@ namespace RoF DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct); - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); + // This is a switch on the RaidGeneral action + switch (*(uint32 *)__packet->pBuffer) { + case 35: { // raidMOTD + // we don't have a nice macro for this + structs::RaidMOTD_Struct *__eq_buffer = (structs::RaidMOTD_Struct *)__packet->pBuffer; + __eq_buffer->motd[1023] = '\0'; + size_t motd_size = strlen(__eq_buffer->motd) + 1; + __packet->size = sizeof(RaidMOTD_Struct) + motd_size; + __packet->pBuffer = new unsigned char[__packet->size]; + RaidMOTD_Struct *emu = (RaidMOTD_Struct *)__packet->pBuffer; + structs::RaidMOTD_Struct *eq = (structs::RaidMOTD_Struct *)__eq_buffer; + strn0cpy(emu->general.player_name, eq->general.player_name, 64); + strn0cpy(emu->motd, eq->motd, motd_size); + IN(general.action); + IN(general.parameter); + FINISH_DIRECT_DECODE(); + break; + } + case 36: { // raidPlayerNote unhandled + break; + } + default: { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + FINISH_DIRECT_DECODE(); + break; + } + } } DECODE(OP_ReadBook) diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 5f1d2aac4..99dff46cc 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -3994,6 +3994,11 @@ struct RaidAddMember_Struct { /*139*/ uint8 flags[5]; //no idea if these are needed... }; +struct RaidMOTD_Struct { +/*000*/ RaidGeneral_Struct general; // leader_name and action only used +/*140*/ char motd[0]; // max size 1024, but reply is variable +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name From e5f3f4c5523c5eb92c869351887a380f106a02b0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 11 Oct 2014 02:03:49 -0400 Subject: [PATCH 334/368] Raid MOTD for SoD --- common/patches/sod.cpp | 52 ++++++++++++++++++++++++++++++------ common/patches/sod_structs.h | 5 ++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 8c64c479f..edfddabca 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1657,6 +1657,17 @@ namespace SoD add_member->flags[4] = in_add_member->flags[4]; dest->FastQueuePacket(&outapp); } + else if (raid_gen->action == 35) + { + RaidMOTD_Struct *inmotd = (RaidMOTD_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidMOTD_Struct) + strlen(inmotd->motd) + 1); + structs::RaidMOTD_Struct *outmotd = (structs::RaidMOTD_Struct *)outapp->pBuffer; + + outmotd->general.action = inmotd->general.action; + strn0cpy(outmotd->general.player_name, inmotd->general.player_name, 64); + strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); + dest->FastQueuePacket(&outapp); + } else { RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; @@ -2974,15 +2985,40 @@ namespace SoD DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct); - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); + // This is a switch on the RaidGeneral action + switch (*(uint32 *)__packet->pBuffer) { + case 35: { // raidMOTD + // we don't have a nice macro for this + structs::RaidMOTD_Struct *__eq_buffer = (structs::RaidMOTD_Struct *)__packet->pBuffer; + __eq_buffer->motd[1023] = '\0'; + size_t motd_size = strlen(__eq_buffer->motd) + 1; + __packet->size = sizeof(RaidMOTD_Struct) + motd_size; + __packet->pBuffer = new unsigned char[__packet->size]; + RaidMOTD_Struct *emu = (RaidMOTD_Struct *)__packet->pBuffer; + structs::RaidMOTD_Struct *eq = (structs::RaidMOTD_Struct *)__eq_buffer; + strn0cpy(emu->general.player_name, eq->general.player_name, 64); + strn0cpy(emu->motd, eq->motd, motd_size); + IN(general.action); + IN(general.parameter); + FINISH_DIRECT_DECODE(); + break; + } + case 36: { // raidPlayerNote unhandled + break; + } + default: { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + FINISH_DIRECT_DECODE(); + break; + } + } } DECODE(OP_ReadBook) diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 64d907c5b..53cc148b9 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -3562,6 +3562,11 @@ struct RaidAddMember_Struct { /*139*/ uint8 flags[5]; //no idea if these are needed... }; +struct RaidMOTD_Struct { +/*000*/ RaidGeneral_Struct general; // leader_name and action only used +/*140*/ char motd[0]; // max size 1024, but reply is variable +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name From 6c5b569c29f7addc2e80bdb1408dd0d502cbe355 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 11 Oct 2014 02:07:49 -0400 Subject: [PATCH 335/368] Raid MOTD for SoF --- common/patches/sof.cpp | 52 ++++++++++++++++++++++++++++++------ common/patches/sof_structs.h | 5 ++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 5a1d69bf4..bf5046c91 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1315,6 +1315,17 @@ namespace SoF add_member->flags[4] = in_add_member->flags[4]; dest->FastQueuePacket(&outapp); } + else if (raid_gen->action == 35) + { + RaidMOTD_Struct *inmotd = (RaidMOTD_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidMOTD_Struct) + strlen(inmotd->motd) + 1); + structs::RaidMOTD_Struct *outmotd = (structs::RaidMOTD_Struct *)outapp->pBuffer; + + outmotd->general.action = inmotd->general.action; + strn0cpy(outmotd->general.player_name, inmotd->general.player_name, 64); + strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); + dest->FastQueuePacket(&outapp); + } else { RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; @@ -2312,15 +2323,40 @@ namespace SoF DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct); - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); + // This is a switch on the RaidGeneral action + switch (*(uint32 *)__packet->pBuffer) { + case 35: { // raidMOTD + // we don't have a nice macro for this + structs::RaidMOTD_Struct *__eq_buffer = (structs::RaidMOTD_Struct *)__packet->pBuffer; + __eq_buffer->motd[1023] = '\0'; + size_t motd_size = strlen(__eq_buffer->motd) + 1; + __packet->size = sizeof(RaidMOTD_Struct) + motd_size; + __packet->pBuffer = new unsigned char[__packet->size]; + RaidMOTD_Struct *emu = (RaidMOTD_Struct *)__packet->pBuffer; + structs::RaidMOTD_Struct *eq = (structs::RaidMOTD_Struct *)__eq_buffer; + strn0cpy(emu->general.player_name, eq->general.player_name, 64); + strn0cpy(emu->motd, eq->motd, motd_size); + IN(general.action); + IN(general.parameter); + FINISH_DIRECT_DECODE(); + break; + } + case 36: { // raidPlayerNote unhandled + break; + } + default: { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + FINISH_DIRECT_DECODE(); + break; + } + } } DECODE(OP_ReadBook) diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 5164af3eb..c943b5108 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -3425,6 +3425,11 @@ struct RaidAddMember_Struct { /*139*/ uint8 flags[5]; //no idea if these are needed... }; +struct RaidMOTD_Struct { +/*000*/ RaidGeneral_Struct general; // leader_name and action only used +/*140*/ char motd[0]; // max size 1024, but reply is variable +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name From ea734c90b5de9bbda696a9f88ce6031243e89531 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 12 Oct 2014 22:57:49 -0500 Subject: [PATCH 336/368] Fix for LDON Character Stat load --- changelog.txt | 3 +++ world/adventure.cpp | 1 + zone/client_packet.cpp | 1 + zone/zonedb.cpp | 28 ++++++++++++++++++++++++++++ zone/zonedb.h | 1 + 5 files changed, 34 insertions(+) diff --git a/changelog.txt b/changelog.txt index 2461b4a36..18e1a738b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/12/2014 == +Akkadius: Fix for LDON Character Stat load + == 10/11/2014 == demonstar55: Implement Raid MOTD for UF Don't forget 2014_10_11_RaidMOTD.sql! diff --git a/world/adventure.cpp b/world/adventure.cpp index 45dd206e5..5f61aa5e9 100644 --- a/world/adventure.cpp +++ b/world/adventure.cpp @@ -358,6 +358,7 @@ void Adventure::Finished(AdventureWinStatus ws) afe.points = 0; } adventure_manager.AddFinishedEvent(afe); + database.UpdateAdventureStatsEntry(database.GetCharacterID((*iter).c_str()), GetTemplate()->theme, (ws != AWS_Lose) ? true : false); } ++iter; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 03d44c903..f7864df02 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1405,6 +1405,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ database.LoadCharacterLeadershipAA(cid, &m_pp); /* Load Character Leadership AA's */ database.LoadCharacterTribute(cid, &m_pp); /* Load CharacterTribute */ + database.LoadCharacterLDONStats(cid, &m_pp); /* Load Character LDON Stats */ /* Set item material tint */ for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 1ba36c5fd..ca6576ef9 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1028,6 +1028,34 @@ bool ZoneDatabase::LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_ return true; } +bool ZoneDatabase::LoadCharacterLDONStats(uint32 character_id, PlayerProfile_Struct* pp) { + std::string query = StringFormat("SELECT `guk_wins`, `mir_wins`, `mmc_wins`, `ruj_wins`, `tak_wins`, `guk_losses`, " + "`mir_losses`, `mmc_losses`, `ruj_losses`, `tak_losses` FROM `adventure_stats` WHERE player_id=%u", character_id); + + auto results = QueryDatabase(query); + + if (!results.Success()) + return false; + + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + + pp->ldon_wins_guk = atoi(row[0]); + pp->ldon_wins_mir = atoi(row[1]); + pp->ldon_wins_mmc = atoi(row[2]); + pp->ldon_wins_ruj = atoi(row[3]); + pp->ldon_wins_tak = atoi(row[4]); + pp->ldon_losses_guk = atoi(row[5]); + pp->ldon_losses_mir = atoi(row[6]); + pp->ldon_losses_mmc = atoi(row[7]); + pp->ldon_losses_ruj = atoi(row[8]); + pp->ldon_losses_tak = atoi(row[9]); + + return true; +} + bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat( "SELECT " diff --git a/zone/zonedb.h b/zone/zonedb.h index 30721de9a..d13e86c30 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -264,6 +264,7 @@ public: bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterLDONStats(uint32 character_id, PlayerProfile_Struct* pp); /* Character Data Saves */ bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home); From 15cec40ed586d05c7c35ffe5d584533230e4aac3 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 13 Oct 2014 15:59:11 -0700 Subject: [PATCH 337/368] Fix for issue #270, fix for temp merchant lists not loading correctly, fix for ah redundant getadventurestats code. --- common/database.cpp | 42 ++++++++++++++++++++++++++---------------- common/database.h | 3 +-- zone/client.cpp | 3 ++- zone/client_packet.cpp | 22 +++++++++++++++++----- zone/zone.cpp | 2 +- zone/zonedb.cpp | 28 ---------------------------- zone/zonedb.h | 1 - 7 files changed, 47 insertions(+), 54 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index dedc00ff1..76231c92a 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2949,11 +2949,20 @@ char* Database::GetGroupLeaderForLogin(const char* name, char* leaderbuf){ } void Database::SetGroupLeaderName(uint32 gid, const char* name) { - std::string query = StringFormat("REPLACE INTO `group_leaders` SET `gid` = %lu, `leadername` = '%s'",(unsigned long)gid,name); - auto results = QueryDatabase(query); + std::string query = StringFormat("UPDATE group_leaders SET leadername = '%s' WHERE gid = %u", EscapeString(name).c_str(), gid); + auto result = QueryDatabase(query); - if (!results.Success()) - std::cout << "Unable to set group leader: " << results.ErrorMessage() << std::endl; + if(result.RowsAffected() != 0) { + return; + } + + query = StringFormat("INSERT INTO group_leaders(gid, leadername, marknpc, leadershipaa, maintank, assist, puller) VALUES(%u, '%s', '', '', '', '', '')", + gid, EscapeString(name).c_str()); + result = QueryDatabase(query); + + if(!result.Success()) { + LogFile->write(EQEMuLog::Debug, "Error in Database::SetGroupLeaderName: %s", result.ErrorMessage().c_str()); + } } char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, GroupLeadershipAA_Struct* GLAA){ @@ -3696,8 +3705,7 @@ void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win) QueryDatabase(query); } -bool Database::GetAdventureStats(uint32 char_id, uint32 &guk_w, uint32 &mir_w, uint32 &mmc_w, uint32 &ruj_w, - uint32 &tak_w, uint32 &guk_l, uint32 &mir_l, uint32 &mmc_l, uint32 &ruj_l, uint32 &tak_l) +bool Database::GetAdventureStats(uint32 char_id, AdventureStats_Struct *as) { std::string query = StringFormat("SELECT `guk_wins`, `mir_wins`, `mmc_wins`, `ruj_wins`, `tak_wins`, `guk_losses`, " @@ -3712,16 +3720,18 @@ bool Database::GetAdventureStats(uint32 char_id, uint32 &guk_w, uint32 &mir_w, u auto row = results.begin(); - guk_w = atoi(row[0]); - mir_w = atoi(row[1]); - mmc_w = atoi(row[2]); - ruj_w = atoi(row[3]); - tak_w = atoi(row[4]); - guk_l = atoi(row[5]); - mir_l = atoi(row[6]); - mmc_l = atoi(row[7]); - ruj_l = atoi(row[8]); - tak_l = atoi(row[9]); + as->success.guk = atoi(row[0]); + as->success.mir = atoi(row[1]); + as->success.mmc = atoi(row[2]); + as->success.ruj = atoi(row[3]); + as->success.tak = atoi(row[4]); + as->failure.guk = atoi(row[5]); + as->failure.mir = atoi(row[6]); + as->failure.mmc = atoi(row[7]); + as->failure.ruj = atoi(row[8]); + as->failure.tak = atoi(row[9]); + as->failure.total = as->failure.guk + as->failure.mir + as->failure.mmc + as->failure.ruj + as->failure.tak; + as->success.total = as->success.guk + as->success.mir + as->success.mmc + as->success.ruj + as->success.tak; return true; } diff --git a/common/database.h b/common/database.h index a24198ca4..68ccf003a 100644 --- a/common/database.h +++ b/common/database.h @@ -174,8 +174,7 @@ public: * Adventure related. */ void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win); - bool GetAdventureStats(uint32 char_id, uint32 &guk_w, uint32 &mir_w, uint32 &mmc_w, uint32 &ruj_w, uint32 &tak_w, - uint32 &guk_l, uint32 &mir_l, uint32 &mmc_l, uint32 &ruj_l, uint32 &tak_l); + bool GetAdventureStats(uint32 char_id, AdventureStats_Struct *as); /* * Account Related diff --git a/zone/client.cpp b/zone/client.cpp index a277b9858..ade40b860 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1287,7 +1287,7 @@ bool Client::UpdateLDoNPoints(int32 points, uint32 theme) m_pp.ldon_points_ruj += rujpts; m_pp.ldon_points_tak += takpts; points-=splitpts; - // if anything left, recursively loop thru again + // if anything left, recursively loop thru again if (splitpts !=0) UpdateLDoNPoints(splitpts,0); break; @@ -1344,6 +1344,7 @@ bool Client::UpdateLDoNPoints(int32 points, uint32 theme) } } m_pp.ldon_points_available += points; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventurePointsUpdate, sizeof(AdventurePoints_Update_Struct)); AdventurePoints_Update_Struct* apus = (AdventurePoints_Update_Struct*)outapp->pBuffer; apus->ldon_available_points = m_pp.ldon_points_available; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f7864df02..e0f2b3f01 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1405,7 +1405,22 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ database.LoadCharacterLeadershipAA(cid, &m_pp); /* Load Character Leadership AA's */ database.LoadCharacterTribute(cid, &m_pp); /* Load CharacterTribute */ - database.LoadCharacterLDONStats(cid, &m_pp); /* Load Character LDON Stats */ + + /* Load AdventureStats */ + AdventureStats_Struct as; + if(database.GetAdventureStats(cid, &as)) + { + m_pp.ldon_wins_guk = as.success.guk; + m_pp.ldon_wins_mir = as.success.mir; + m_pp.ldon_wins_mmc = as.success.mmc; + m_pp.ldon_wins_ruj = as.success.ruj; + m_pp.ldon_wins_tak = as.success.tak; + m_pp.ldon_losses_guk = as.failure.guk; + m_pp.ldon_losses_mir = as.failure.mir; + m_pp.ldon_losses_mmc = as.failure.mmc; + m_pp.ldon_losses_ruj = as.failure.ruj; + m_pp.ldon_losses_tak = as.failure.tak; + } /* Set item material tint */ for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) @@ -2489,11 +2504,8 @@ void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app) EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureStatsReply, sizeof(AdventureStats_Struct)); AdventureStats_Struct *as = (AdventureStats_Struct*)outapp->pBuffer; - if (database.GetAdventureStats(CharacterID(), as->success.guk, as->success.mir, as->success.mmc, as->success.ruj, - as->success.tak, as->failure.guk, as->failure.mir, as->failure.mmc, as->failure.ruj, as->failure.tak)) + if (database.GetAdventureStats(CharacterID(), as)) { - as->failure.total = as->failure.guk + as->failure.mir + as->failure.mmc + as->failure.ruj + as->failure.tak; - as->success.total = as->success.guk + as->success.mir + as->success.mmc + as->success.ruj + as->success.tak; m_pp.ldon_wins_guk = as->success.guk; m_pp.ldon_wins_mir = as->success.mir; m_pp.ldon_wins_mmc = as->success.mmc; diff --git a/zone/zone.cpp b/zone/zone.cpp index 23aa95f2c..0207b28d4 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -411,7 +411,7 @@ void Zone::LoadTempMerchantData() { "WHERE " "ml.npcid = se.npcid " "AND se.spawngroupid = s2.spawngroupid " - "AND s2.zone = '%s' AND s2.version = %i" + "AND s2.zone = '%s' AND s2.version = %i " "ORDER BY ml.slot ", GetShortName(), GetInstanceVersion()); auto results = database.QueryDatabase(query); if (!results.Success()) { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index ca6576ef9..1ba36c5fd 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1028,34 +1028,6 @@ bool ZoneDatabase::LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_ return true; } -bool ZoneDatabase::LoadCharacterLDONStats(uint32 character_id, PlayerProfile_Struct* pp) { - std::string query = StringFormat("SELECT `guk_wins`, `mir_wins`, `mmc_wins`, `ruj_wins`, `tak_wins`, `guk_losses`, " - "`mir_losses`, `mmc_losses`, `ruj_losses`, `tak_losses` FROM `adventure_stats` WHERE player_id=%u", character_id); - - auto results = QueryDatabase(query); - - if (!results.Success()) - return false; - - if (results.RowCount() == 0) - return false; - - auto row = results.begin(); - - pp->ldon_wins_guk = atoi(row[0]); - pp->ldon_wins_mir = atoi(row[1]); - pp->ldon_wins_mmc = atoi(row[2]); - pp->ldon_wins_ruj = atoi(row[3]); - pp->ldon_wins_tak = atoi(row[4]); - pp->ldon_losses_guk = atoi(row[5]); - pp->ldon_losses_mir = atoi(row[6]); - pp->ldon_losses_mmc = atoi(row[7]); - pp->ldon_losses_ruj = atoi(row[8]); - pp->ldon_losses_tak = atoi(row[9]); - - return true; -} - bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat( "SELECT " diff --git a/zone/zonedb.h b/zone/zonedb.h index d13e86c30..30721de9a 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -264,7 +264,6 @@ public: bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterLDONStats(uint32 character_id, PlayerProfile_Struct* pp); /* Character Data Saves */ bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home); From be0621de42574fe3ee208b98af12c39eeb0d3f8c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 13 Oct 2014 21:36:59 -0400 Subject: [PATCH 338/368] Partial implementation of leadership in raids Currently working: stat bonuses and client side only effects Currently not working: Mark NPC and others that need more server side work Currently only tested on UF, Ti and 62 may work, but not tested SoF, SoD, and RoF need packet translators, which are most likely the same as UF --- changelog.txt | 6 + common/database.cpp | 136 ++++++++++++++++ common/database.h | 7 + common/eq_packet_structs.h | 67 +++++++- common/patches/client62_structs.h | 58 ++++++- common/patches/rof_structs.h | 58 ++++++- common/patches/sod_structs.h | 58 ++++++- common/patches/sof_structs.h | 58 ++++++- common/patches/titanium_structs.h | 58 ++++++- common/patches/underfoot.cpp | 12 ++ common/patches/underfoot_structs.h | 68 +++++++- .../required/2014_10_13_RaidLeadership.sql | 9 ++ world/net.cpp | 1 + zone/aa.cpp | 152 ++++++++++++++++++ zone/client.cpp | 6 +- zone/client.h | 4 + zone/client_packet.cpp | 31 +++- zone/raids.cpp | 120 +++++++++++++- zone/raids.h | 24 ++- 19 files changed, 891 insertions(+), 42 deletions(-) create mode 100644 utils/sql/git/required/2014_10_13_RaidLeadership.sql diff --git a/changelog.txt b/changelog.txt index 18e1a738b..efa724859 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/13/2014 == +demonstar55: Partially implement leadership and raids + Currently working: client side only effects and stat bonuses. + Not working: Mark NPC, and other stuff that need extra server side support + Currently only UF tested (Tit and 62 may just work, others need packet work) + == 10/12/2014 == Akkadius: Fix for LDON Character Stat load diff --git a/common/database.cpp b/common/database.cpp index 76231c92a..5ed156ae5 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -3169,6 +3169,142 @@ const char* Database::GetRaidLeaderName(uint32 rid) return name; } +// maintank, assist, puller, marknpc currently unused +void Database::GetGroupLeadershipInfo(uint32 gid, uint32 rid, char *maintank, + char *assist, char *puller, char *marknpc, GroupLeadershipAA_Struct *GLAA) +{ + std::string query = StringFormat( + "SELECT maintank, assist, puller, marknpc, leadershipaa FROM raid_leaders WHERE gid = %lu AND rid = %lu", + (unsigned long)gid, (unsigned long)rid); + auto results = QueryDatabase(query); + + if (!results.Success() || results.RowCount() == 0) { + if (maintank) + maintank[0] = '\0'; + + if (assist) + assist[0] = '\0'; + + if (puller) + puller[0] = '\0'; + + if (marknpc) + marknpc[0] = '\0'; + + return; + } + + auto row = results.begin(); + + if (maintank) + strcpy(maintank, row[0]); + + if (assist) + strcpy(assist, row[1]); + + if (puller) + strcpy(puller, row[2]); + + if (marknpc) + strcpy(marknpc, row[3]); + + if (GLAA && results.LengthOfColumn(4) == sizeof(GroupLeadershipAA_Struct)) + memcpy(GLAA, row[4], sizeof(GroupLeadershipAA_Struct)); + + return; +} + +// maintank, assist, puller, marknpc currently unused +void Database::GetRaidLeadershipInfo(uint32 rid, char *maintank, + char *assist, char *puller, char *marknpc, RaidLeadershipAA_Struct *RLAA) +{ + std::string query = StringFormat( + "SELECT maintank, assist, puller, marknpc, leadershipaa FROM raid_leaders WHERE gid = %lu AND rid = %lu", + (unsigned long)0xFFFFFFFF, (unsigned long)rid); + auto results = QueryDatabase(query); + + if (!results.Success() || results.RowCount() == 0) { + if (maintank) + maintank[0] = '\0'; + + if (assist) + assist[0] = '\0'; + + if (puller) + puller[0] = '\0'; + + if (marknpc) + marknpc[0] = '\0'; + + return; + } + + auto row = results.begin(); + + if (maintank) + strcpy(maintank, row[0]); + + if (assist) + strcpy(assist, row[1]); + + if (puller) + strcpy(puller, row[2]); + + if (marknpc) + strcpy(marknpc, row[3]); + + if (RLAA && results.LengthOfColumn(4) == sizeof(RaidLeadershipAA_Struct)) + memcpy(RLAA, row[4], sizeof(RaidLeadershipAA_Struct)); + + return; +} + +void Database::SetRaidGroupLeaderInfo(uint32 gid, uint32 rid) +{ + std::string query = StringFormat("UPDATE raid_leaders SET leadershipaa = '', WHERE gid = %lu AND rid = %lu", + (unsigned long)gid, (unsigned long)rid); + auto results = QueryDatabase(query); + + if (results.RowsAffected() != 0) + return; + + query = StringFormat("INSERT INTO raid_leaders(gid, rid, marknpc, leadershipaa, maintank, assist, puller) VALUES(%lu, %lu, '', '', '', '', '')", + (unsigned long)gid, (unsigned long)rid); + results = QueryDatabase(query); + + if (!results.Success()) + std::cout << "Unable to set raid/group leader: " << results.ErrorMessage() << std::endl; + + return; +} + +// Clearing all raid leaders +void Database::ClearAllRaidLeaders(void) +{ + std::string query("DELETE from raid_leaders"); + auto results = QueryDatabase(query); + + if (!results.Success()) + std::cout << "Unable to clear raid leaders: " << results.ErrorMessage() << std::endl; + + return; +} + +void Database::ClearRaidLeader(uint32 gid, uint32 rid) +{ + if (rid == 0) { + ClearAllRaidLeaders(); + return; + } + + std::string query = StringFormat("DELETE from raid_leaders where gid = %lu and rid = %lu", + (unsigned long)gid, (unsigned long)rid); + auto results = QueryDatabase(query); + + if (!results.Success()) + std::cout << "Unable to clear raid leader: " << results.ErrorMessage() << std::endl; +} + bool Database::VerifyInstanceAlive(uint16 instance_id, uint32 char_id) { //we are not saved to this instance so set our instance to 0 diff --git a/common/database.h b/common/database.h index 68ccf003a..6fa9ef60a 100644 --- a/common/database.h +++ b/common/database.h @@ -214,6 +214,12 @@ public: void ClearRaidDetails(uint32 rid = 0); uint32 GetRaidID(const char* name); const char *GetRaidLeaderName(uint32 rid); + void GetGroupLeadershipInfo(uint32 gid, uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, + GroupLeadershipAA_Struct* GLAA = nullptr); + void GetRaidLeadershipInfo(uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, + RaidLeadershipAA_Struct* RLAA = nullptr); + void SetRaidGroupLeaderInfo(uint32 gid, uint32 rid); + void ClearRaidLeader(uint32 gid = 0xFFFFFFFF, uint32 rid = 0); bool CheckDatabaseConversions(); @@ -273,6 +279,7 @@ private: */ void ClearAllRaids(); void ClearAllRaidDetails(); + void ClearAllRaidLeaders(); }; #endif diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index eda2a5ce8..a78b2afa1 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -759,14 +759,62 @@ struct MovePotionToBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** @@ -3934,6 +3982,15 @@ struct RaidMOTD_Struct { /*136*/ char motd[0]; // max size is 1024, but reply is variable }; +struct RaidLeadershipUpdate_Struct { +/*000*/ uint32 action; +/*004*/ char player_name[64]; +/*068*/ char leader_name[64]; +/*132*/ GroupLeadershipAA_Struct group; //unneeded +/*196*/ RaidLeadershipAA_Struct raid; +/*260*/ char Unknown260[128]; //unverified +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name diff --git a/common/patches/client62_structs.h b/common/patches/client62_structs.h index 8c80e34a5..aae4c7919 100644 --- a/common/patches/client62_structs.h +++ b/common/patches/client62_structs.h @@ -611,14 +611,62 @@ struct PotionBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /* diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 99dff46cc..da07840c6 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -915,14 +915,62 @@ struct PotionBelt_Struct { BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; }; -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 53cc148b9..c32359f76 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -711,14 +711,62 @@ struct PotionBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index c943b5108..32a1e9d10 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -689,14 +689,62 @@ struct PotionBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 17beb6479..e8e5a9f23 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -619,14 +619,62 @@ struct PotionBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index 03ef16a2a..dd31d749d 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -1940,6 +1940,18 @@ namespace Underfoot strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); dest->FastQueuePacket(&outapp); } + else if (raid_gen->action == 14) + { + RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); + structs::RaidLeadershipUpdate_Struct *outlaa = (structs::RaidLeadershipUpdate_Struct *)outapp->pBuffer; + + outlaa->action = inlaa->action; + strn0cpy(outlaa->player_name, inlaa->player_name, 64); + strn0cpy(outlaa->leader_name, inlaa->leader_name, 64); + memcpy(&outlaa->raid, &inlaa->raid, sizeof(RaidLeadershipAA_Struct)); + dest->FastQueuePacket(&outapp); + } else { RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index f331d5dbc..2b7163bcf 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -754,14 +754,62 @@ struct PotionBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** @@ -3628,6 +3676,16 @@ struct RaidMOTD_Struct { /*140*/ char motd[0]; // max size 1024, but reply is variable }; +struct RaidLeadershipUpdate_Struct { +/*000*/ uint32 action; +/*004*/ char player_name[64]; +/*068*/ uint32 Unknown068; +/*072*/ char leader_name[64]; +/*136*/ GroupLeadershipAA_Struct group; //unneeded +/*200*/ RaidLeadershipAA_Struct raid; +/*264*/ char Unknown264[128]; +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name diff --git a/utils/sql/git/required/2014_10_13_RaidLeadership.sql b/utils/sql/git/required/2014_10_13_RaidLeadership.sql new file mode 100644 index 000000000..972e09ed5 --- /dev/null +++ b/utils/sql/git/required/2014_10_13_RaidLeadership.sql @@ -0,0 +1,9 @@ +CREATE TABLE `raid_leaders` ( + `gid` int(4) unsigned NOT NULL, + `rid` int(4) unsigned NOT NULL, + `marknpc` varchar(64) NOT NULL, + `maintank` varchar(64) NOT NULL, + `assist` varchar(64) NOT NULL, + `puller` varchar(64) NOT NULL, + `leadershipaa` tinyblob NOT NULL +); diff --git a/world/net.cpp b/world/net.cpp index c7340333e..7973b5872 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -283,6 +283,7 @@ int main(int argc, char** argv) { _log(WORLD__INIT, "Clearing raids.."); database.ClearRaid(); database.ClearRaidDetails(); + database.ClearRaidLeader(); _log(WORLD__INIT, "Loading items.."); if (!database.LoadItems()) _log(WORLD__INIT_ERR, "Error: Could not load item data. But ignoring"); diff --git a/zone/aa.cpp b/zone/aa.cpp index 8af3896d3..d07ed72cf 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1550,6 +1550,41 @@ void Client::ResetAA(){ int Client::GroupLeadershipAAHealthEnhancement() { + if (IsRaidGrouped()) { + int bonus = 0; + Raid *raid = GetRaid(); + if (!raid) + return 0; + uint32 group_id = raid->GetGroup(this); + if (group_id < 12 && raid->GroupCount(group_id) >= 3) { + switch (raid->GetLeadershipAA(groupAAHealthEnhancement, group_id)) { + case 1: + bonus = 30; + break; + case 2: + bonus = 60; + break; + case 3: + bonus = 100; + break; + } + } + if (raid->RaidCount() >= 18) { + switch (raid->GetLeadershipAA(raidAAHealthEnhancement)) { + case 1: + bonus += 30; + break; + case 2: + bonus += 60; + break; + case 3: + bonus += 100; + break; + } + } + return bonus; + } + Group *g = GetGroup(); if(!g || (g->GroupCount() < 3)) @@ -1572,6 +1607,41 @@ int Client::GroupLeadershipAAHealthEnhancement() int Client::GroupLeadershipAAManaEnhancement() { + if (IsRaidGrouped()) { + int bonus = 0; + Raid *raid = GetRaid(); + if (!raid) + return 0; + uint32 group_id = raid->GetGroup(this); + if (group_id < 12 && raid->GroupCount(group_id) >= 3) { + switch (raid->GetLeadershipAA(groupAAManaEnhancement, group_id)) { + case 1: + bonus = 30; + break; + case 2: + bonus = 60; + break; + case 3: + bonus = 100; + break; + } + } + if (raid->RaidCount() >= 18) { + switch (raid->GetLeadershipAA(raidAAManaEnhancement)) { + case 1: + bonus += 30; + break; + case 2: + bonus += 60; + break; + case 3: + bonus += 100; + break; + } + } + return bonus; + } + Group *g = GetGroup(); if(!g || (g->GroupCount() < 3)) @@ -1594,6 +1664,41 @@ int Client::GroupLeadershipAAManaEnhancement() int Client::GroupLeadershipAAHealthRegeneration() { + if (IsRaidGrouped()) { + int bonus = 0; + Raid *raid = GetRaid(); + if (!raid) + return 0; + uint32 group_id = raid->GetGroup(this); + if (group_id < 12 && raid->GroupCount(group_id) >= 3) { + switch (raid->GetLeadershipAA(groupAAHealthRegeneration, group_id)) { + case 1: + bonus = 4; + break; + case 2: + bonus = 6; + break; + case 3: + bonus = 8; + break; + } + } + if (raid->RaidCount() >= 18) { + switch (raid->GetLeadershipAA(raidAAHealthRegeneration)) { + case 1: + bonus += 4; + break; + case 2: + bonus += 6; + break; + case 3: + bonus += 8; + break; + } + } + return bonus; + } + Group *g = GetGroup(); if(!g || (g->GroupCount() < 3)) @@ -1616,6 +1721,53 @@ int Client::GroupLeadershipAAHealthRegeneration() int Client::GroupLeadershipAAOffenseEnhancement() { + if (IsRaidGrouped()) { + int bonus = 0; + Raid *raid = GetRaid(); + if (!raid) + return 0; + uint32 group_id = raid->GetGroup(this); + if (group_id < 12 && raid->GroupCount(group_id) >= 3) { + switch (raid->GetLeadershipAA(groupAAOffenseEnhancement, group_id)) { + case 1: + bonus = 10; + break; + case 2: + bonus = 19; + break; + case 3: + bonus = 28; + break; + case 4: + bonus = 34; + break; + case 5: + bonus = 40; + break; + } + } + if (raid->RaidCount() >= 18) { + switch (raid->GetLeadershipAA(raidAAOffenseEnhancement)) { + case 1: + bonus += 10; + break; + case 2: + bonus += 19; + break; + case 3: + bonus += 28; + break; + case 4: + bonus += 34; + break; + case 5: + bonus += 40; + break; + } + } + return bonus; + } + Group *g = GetGroup(); if(!g || (g->GroupCount() < 3)) diff --git a/zone/client.cpp b/zone/client.cpp index ade40b860..3b95c8d77 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3670,7 +3670,11 @@ void Client::LogSQL(const char *fmt, ...) { } void Client::GetGroupAAs(GroupLeadershipAA_Struct *into) const { - memcpy(into, &m_pp.leader_abilities, sizeof(GroupLeadershipAA_Struct)); + memcpy(into, &m_pp.leader_abilities.group, sizeof(GroupLeadershipAA_Struct)); +} + +void Client::GetRaidAAs(RaidLeadershipAA_Struct *into) const { + memcpy(into, &m_pp.leader_abilities.raid, sizeof(RaidLeadershipAA_Struct)); } void Client::EnteringMessages(Client* client) diff --git a/zone/client.h b/zone/client.h index 3375d7066..e31c15598 100644 --- a/zone/client.h +++ b/zone/client.h @@ -559,6 +559,9 @@ public: void SendLeadershipEXPUpdate(); bool IsLeadershipEXPOn(); inline int GetLeadershipAA(int AAID) { return m_pp.leader_abilities.ranks[AAID]; } + inline LeadershipAA_Struct &GetLeadershipAA() { return m_pp.leader_abilities; } + inline GroupLeadershipAA_Struct &GetGroupLeadershipAA() { return m_pp.leader_abilities.group; } + inline RaidLeadershipAA_Struct &GetRaidLeadershipAA() { return m_pp.leader_abilities.raid; } int GroupLeadershipAAHealthEnhancement(); int GroupLeadershipAAManaEnhancement(); int GroupLeadershipAAHealthRegeneration(); @@ -585,6 +588,7 @@ public: bool CheckLoreConflict(const Item_Struct* item); void ChangeLastName(const char* in_lastname); void GetGroupAAs(GroupLeadershipAA_Struct *into) const; + void GetRaidAAs(RaidLeadershipAA_Struct *into) const; void ClearGroupAAs(); void UpdateGroupAAs(int32 points, uint32 type); void SacrificeConfirm(Client* caster); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index e0f2b3f01..e74651300 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -546,6 +546,7 @@ void Client::CompleteConnect() raid = new Raid(raidid); if (raid->GetID() != 0){ entity_list.AddRaid(raid, raidid); + raid->LoadLeadership(); // Recreating raid in new zone, get leadership from DB } else raid = nullptr; @@ -566,11 +567,20 @@ void Client::CompleteConnect() raid->SendBulkRaid(this); raid->SendGroupUpdate(this); raid->SendRaidMOTD(this); + if (raid->IsLeader(this)) { // We're a raid leader, lets update just in case! + raid->UpdateRaidAAs(); + raid->SendAllRaidLeadershipAA(); + } uint32 grpID = raid->GetGroup(GetName()); if (grpID < 12){ raid->SendRaidGroupRemove(GetName(), grpID); raid->SendRaidGroupAdd(GetName(), grpID); + if (raid->IsGroupLeader(GetName())) { // group leader same thing! + raid->UpdateGroupAAs(raid->GetGroup(this)); + raid->GroupUpdate(grpID, false); + } } + raid->SendGroupLeadershipAA(this, grpID); // this may get sent an extra time ... if (raid->IsLocked()) raid->SendRaidLockTo(this); } @@ -10606,10 +10616,23 @@ void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) u->pointsleft = m_pp.group_leadership_points; FastQueuePacket(&outapp); - Group *g = GetGroup(); - // Update all group members with the new AA the leader has purchased. - if (g) { + if (IsRaidGrouped()) { + Raid *r = GetRaid(); + if (!r) + return; + if (aaid >= raidAAMarkNPC) { + r->UpdateRaidAAs(); + r->SendAllRaidLeadershipAA(); + } else { + uint32 gid = r->GetGroup(this); + r->UpdateGroupAAs(gid); + r->GroupUpdate(gid, false); + } + } else if (IsGrouped()) { + Group *g = GetGroup(); + if (!g) + return; g->UpdateGroupAAs(); g->SendLeadershipAAUpdate(); } @@ -11228,6 +11251,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) { if (strcmp(r->leadername, GetName()) == 0){ r->SetRaidLeader(GetName(), ri->leader_name); + r->UpdateRaidAAs(); + r->SendAllRaidLeadershipAA(); } } break; diff --git a/zone/raids.cpp b/zone/raids.cpp index f22267a2f..0adbcdef1 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -29,6 +29,8 @@ Raid::Raid(uint32 raidID) : GroupIDConsumer(raidID) { memset(members ,0, (sizeof(RaidMember)*MAX_RAID_MEMBERS)); + memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct)); + memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS); leader = nullptr; memset(leadername, 0, 64); locked = false; @@ -39,6 +41,8 @@ Raid::Raid(Client* nLeader) : GroupIDConsumer() { memset(members ,0, (sizeof(RaidMember)*MAX_RAID_MEMBERS)); + memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct)); + memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS); leader = nLeader; memset(leadername, 0, 64); strn0cpy(leadername, nLeader->GetName(), 64); @@ -84,8 +88,18 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo LearnMembers(); VerifyRaid(); + if (rleader) { + database.SetRaidGroupLeaderInfo(RAID_GROUPLESS, GetID()); + UpdateRaidAAs(); + } + if (group != RAID_GROUPLESS && groupleader) { + database.SetRaidGroupLeaderInfo(group, GetID()); + UpdateGroupAAs(group); + } if(group < 12) GroupUpdate(group); + else // get raid AAs, GroupUpdate will handles it otherwise + SendGroupLeadershipAA(c, RAID_GROUPLESS); SendRaidAddAll(c->GetName()); c->SetRaidGrouped(true); @@ -186,6 +200,18 @@ void Raid::SetGroupLeader(const char *who, bool glFlag) safe_delete(pack); } +Client *Raid::GetGroupLeader(uint32 group_id) +{ + if (group_id == RAID_GROUPLESS) + return nullptr; + + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) + if (members[i].member && members[i].IsGroupLeader && members[i].GroupNumber == group_id) + return members[i].member; + + return nullptr; +} + void Raid::SetRaidLeader(const char *wasLead, const char *name) { std::string query = StringFormat("UPDATE raid_members SET israidleader = 0 WHERE name = '%s'", wasLead); @@ -218,6 +244,59 @@ void Raid::SetRaidLeader(const char *wasLead, const char *name) safe_delete(pack); } +void Raid::SaveGroupLeaderAA(uint32 gid) +{ + char *queryBuffer = new char[sizeof(GroupLeadershipAA_Struct) * 2 + 1]; + database.DoEscapeString(queryBuffer, (char*)&group_aa[gid], sizeof(GroupLeadershipAA_Struct)); + + std::string query = "UPDATE raid_leaders SET leadershipaa = '"; + query += queryBuffer; + query += StringFormat("' WHERE gid = %lu AND rid = %lu LIMIT 1", gid, GetID()); + safe_delete_array(queryBuffer); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str()); +} + +void Raid::SaveRaidLeaderAA() +{ + char *queryBuffer = new char[sizeof(RaidLeadershipAA_Struct) * 2 + 1]; + database.DoEscapeString(queryBuffer, (char*)&raid_aa, sizeof(RaidLeadershipAA_Struct)); + _hex(NET__ERROR, queryBuffer, sizeof(RaidLeadershipAA_Struct)); + + std::string query = "UPDATE raid_leaders SET leadershipaa = '"; + query += queryBuffer; + query += StringFormat("' WHERE gid = %lu AND rid = %lu LIMIT 1", RAID_GROUPLESS, GetID()); + safe_delete_array(queryBuffer); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str()); +} + +void Raid::UpdateGroupAAs(uint32 gid) +{ + Client *gl = GetGroupLeader(gid); + + if (gl) + gl->GetGroupAAs(&group_aa[gid]); + else + memset(&group_aa[gid], 0, sizeof(GroupLeadershipAA_Struct)); + + SaveGroupLeaderAA(gid); +} + +void Raid::UpdateRaidAAs() +{ + Client *rl = GetLeader(); + + if (rl) + rl->GetRaidAAs(&raid_aa); + else + memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct)); + + SaveRaidLeaderAA(); +} + bool Raid::IsGroupLeader(const char *who) { for(int x = 0; x < MAX_RAID_MEMBERS; x++) @@ -1094,6 +1173,7 @@ void Raid::SendGroupUpdate(Client *to) strn0cpy(gu->leadersname, to->GetName(), 64); } strn0cpy(gu->yourname, to->GetName(), 64); + memcpy(&gu->leader_aas, &group_aa[grp], sizeof(GroupLeadershipAA_Struct)); to->FastQueuePacket(&outapp); } @@ -1106,8 +1186,10 @@ void Raid::GroupUpdate(uint32 gid, bool initial) { if(strlen(members[x].membername) > 0){ if(members[x].GroupNumber == gid){ - if(members[x].member) + if(members[x].member) { SendGroupUpdate(members[x].member); + SendGroupLeadershipAA(members[x].member, gid); + } } } } @@ -1244,6 +1326,34 @@ void Raid::SendRaidMOTDToWorld() safe_delete(pack); } +void Raid::SendGroupLeadershipAA(Client *c, uint32 gid) +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct)); + RaidLeadershipUpdate_Struct *rlaa = (RaidLeadershipUpdate_Struct *)outapp->pBuffer; + rlaa->action = raidSetLeaderAbilities; + strn0cpy(rlaa->leader_name, c->GetName(), 64); + strn0cpy(rlaa->player_name, c->GetName(), 64); + if (gid != RAID_GROUPLESS) + memcpy(&rlaa->group, &group_aa[gid], sizeof(GroupLeadershipAA_Struct)); + memcpy(&rlaa->raid, &raid_aa, sizeof(RaidLeadershipAA_Struct)); + c->QueuePacket(outapp); + safe_delete(outapp); +} + +void Raid::SendGroupLeadershipAA(uint32 gid) +{ + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) + if (members[i].member && members[i].GroupNumber == gid) + SendGroupLeadershipAA(members[i].member, gid); +} + +void Raid::SendAllRaidLeadershipAA() +{ + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) + if (members[i].member) + SendGroupLeadershipAA(members[i].member, members[i].GroupNumber); +} + void Raid::LockRaid(bool lockFlag) { std::string query = StringFormat("UPDATE raid_details SET locked = %d WHERE raidid = %lu", @@ -1484,3 +1594,11 @@ void Raid::RaidMessage_StringID(Mob* sender, uint32 type, uint32 string_id, cons } } +void Raid::LoadLeadership() +{ + database.GetRaidLeadershipInfo(GetID(), nullptr, nullptr, nullptr, nullptr, &raid_aa); + + for (uint32 group_id = 0; group_id < MAX_RAID_GROUPS; group_id++) + database.GetGroupLeadershipInfo(group_id, GetID(), nullptr, nullptr, nullptr, nullptr, &group_aa[group_id]); +} + diff --git a/zone/raids.h b/zone/raids.h index 44952e5da..6e2579367 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -43,8 +43,8 @@ enum { //raid packet types: raidChangeLootType = 11, raidStringID = 12, raidChangeGroupLeader = 13, //136 raid leader, new group leader, group_id? - raidBecomeGroupLeader = 14, //472 - raidUnknown2 = 15, + raidSetLeaderAbilities = 14, //472 + raidSetLeaderData = 15, // 14,15 SoE names, not sure on difference, 14 packet has 0x100 bytes 15 0x214 in addition to raid general raidChangeGroup = 16, //?? len 136 old leader, new leader, 0 (preceeded with a remove2) raidLock = 17, //len 136 leader?, leader, 0 raidUnlock = 18, //len 136 leader?, leader, 0 @@ -79,6 +79,7 @@ enum { //raid command types #define MAX_RAID_GROUPS 12 #define MAX_RAID_MEMBERS 72 +const uint32 RAID_GROUPLESS = 0xFFFFFFFF; struct RaidMember{ char membername[64]; @@ -111,6 +112,7 @@ public: void DisbandRaid(); void MoveMember(const char *name, uint32 newGroup); void SetGroupLeader(const char *who, bool glFlag = true); + Client *GetGroupLeader(uint32 group_id); void RemoveGroupLeader(const char *who); bool IsGroupLeader(const char *who); bool IsRaidMember(const char *name); @@ -203,6 +205,22 @@ public: void QueuePacket(const EQApplicationPacket *app, bool ack_req = true); + // Leadership + void UpdateGroupAAs(uint32 gid); + void SaveGroupLeaderAA(uint32 gid); + void UpdateRaidAAs(); + void SaveRaidLeaderAA(); + void SendGroupLeadershipAA(Client *c, uint32 gid); + void SendGroupLeadershipAA(uint32 gid); + void SendAllRaidLeadershipAA(); + void LoadLeadership(); + inline int GetLeadershipAA(int AAID, uint32 gid = 0) + { if (AAID >= 16) return raid_aa.ranks[AAID - 16]; else return group_aa[gid].ranks[AAID]; } + inline void SetGroupAAs(uint32 gid, GroupLeadershipAA_Struct *glaa) + { memcpy(&group_aa[gid], glaa, sizeof(GroupLeadershipAA_Struct)); } + inline void SetRaidAAs(RaidLeadershipAA_Struct *rlaa) + { memcpy(&raid_aa, rlaa, sizeof(RaidLeadershipAA_Struct)); } + RaidMember members[MAX_RAID_MEMBERS]; char leadername[64]; protected: @@ -213,6 +231,8 @@ protected: bool disbandCheck; bool forceDisband; std::string motd; + RaidLeadershipAA_Struct raid_aa; + GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS]; }; From d1561c28d9eabbe5162a5e9cc7a6bac3bbefc7a0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 13 Oct 2014 22:53:41 -0400 Subject: [PATCH 339/368] Raid Leadership SoF --- common/patches/sof.cpp | 12 ++++++++++++ common/patches/sof_structs.h | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index bf5046c91..2b4640f29 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1326,6 +1326,18 @@ namespace SoF strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); dest->FastQueuePacket(&outapp); } + else if (raid_gen->action == 14) + { + RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); + structs::RaidLeadershipUpdate_Struct *outlaa = (structs::RaidLeadershipUpdate_Struct *)outapp->pBuffer; + + outlaa->action = inlaa->action; + strn0cpy(outlaa->player_name, inlaa->player_name, 64); + strn0cpy(outlaa->leader_name, inlaa->leader_name, 64); + memcpy(&outlaa->raid, &inlaa->raid, sizeof(RaidLeadershipAA_Struct)); + dest->FastQueuePacket(&outapp); + } else { RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 32a1e9d10..a101d3cba 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -3478,6 +3478,16 @@ struct RaidMOTD_Struct { /*140*/ char motd[0]; // max size 1024, but reply is variable }; +struct RaidLeadershipUpdate_Struct { +/*000*/ uint32 action; +/*004*/ char player_name[64]; +/*068*/ uint32 Unknown068; +/*072*/ char leader_name[64]; +/*136*/ GroupLeadershipAA_Struct group; //unneeded +/*200*/ RaidLeadershipAA_Struct raid; +/*264*/ char Unknown264[128]; +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name From 41938fbe723bd7b15e7c8ddbbe4748c7e7f91b29 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 13 Oct 2014 22:54:57 -0400 Subject: [PATCH 340/368] Raid leadership SoD --- common/patches/sod.cpp | 12 ++++++++++++ common/patches/sod_structs.h | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index edfddabca..134be8008 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1668,6 +1668,18 @@ namespace SoD strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); dest->FastQueuePacket(&outapp); } + else if (raid_gen->action == 14) + { + RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); + structs::RaidLeadershipUpdate_Struct *outlaa = (structs::RaidLeadershipUpdate_Struct *)outapp->pBuffer; + + outlaa->action = inlaa->action; + strn0cpy(outlaa->player_name, inlaa->player_name, 64); + strn0cpy(outlaa->leader_name, inlaa->leader_name, 64); + memcpy(&outlaa->raid, &inlaa->raid, sizeof(RaidLeadershipAA_Struct)); + dest->FastQueuePacket(&outapp); + } else { RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index c32359f76..88c0ed91a 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -3615,6 +3615,16 @@ struct RaidMOTD_Struct { /*140*/ char motd[0]; // max size 1024, but reply is variable }; +struct RaidLeadershipUpdate_Struct { +/*000*/ uint32 action; +/*004*/ char player_name[64]; +/*068*/ uint32 Unknown068; +/*072*/ char leader_name[64]; +/*136*/ GroupLeadershipAA_Struct group; //unneeded +/*200*/ RaidLeadershipAA_Struct raid; +/*264*/ char Unknown264[128]; +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name From 6f00a69850db017853ae0c2cfa825942bbe8e3bd Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 13 Oct 2014 22:55:57 -0400 Subject: [PATCH 341/368] Raid Leadership RoF --- common/patches/rof.cpp | 12 ++++++++++++ common/patches/rof_structs.h | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 49f6c51f1..96ebcaf63 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -2613,6 +2613,18 @@ namespace RoF strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); dest->FastQueuePacket(&outapp); } + else if (raid_gen->action == 14) + { + RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); + structs::RaidLeadershipUpdate_Struct *outlaa = (structs::RaidLeadershipUpdate_Struct *)outapp->pBuffer; + + outlaa->action = inlaa->action; + strn0cpy(outlaa->player_name, inlaa->player_name, 64); + strn0cpy(outlaa->leader_name, inlaa->leader_name, 64); + memcpy(&outlaa->raid, &inlaa->raid, sizeof(RaidLeadershipAA_Struct)); + dest->FastQueuePacket(&outapp); + } else { RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index da07840c6..e65f2b74d 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4047,6 +4047,16 @@ struct RaidMOTD_Struct { /*140*/ char motd[0]; // max size 1024, but reply is variable }; +struct RaidLeadershipUpdate_Struct { +/*000*/ uint32 action; +/*004*/ char player_name[64]; +/*068*/ uint32 Unknown068; +/*072*/ char leader_name[64]; +/*136*/ GroupLeadershipAA_Struct group; //unneeded +/*200*/ RaidLeadershipAA_Struct raid; +/*264*/ char Unknown264[128]; +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name From ef5e93b71ba9e17fd847c1fe3340509dcb0a9be9 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 13 Oct 2014 23:24:25 -0400 Subject: [PATCH 342/368] Add debug log to Client::Handle_OP_DoGroupLeadershipAbility --- zone/client_packet.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index e74651300..fdc5e5021 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5345,6 +5345,8 @@ void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) } default: + LogFile->write(EQEMuLog::Debug, "Got unhandled OP_DoGroupLeadershipAbility Ability: %d Parameter: %d", + dglas->Ability, dglas->Parameter); break; } } From e53a91f5c3e18dbc29c76b91ce07c0e6a598dbb1 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 14 Oct 2014 00:01:51 -0400 Subject: [PATCH 343/368] Added some previous unidentified (still unhandled) UF opcodes Didn't want them to get lost ;) --- utils/patches/patch_Underfoot.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index 1b65a6481..e62b24b67 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -656,3 +656,7 @@ OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs OP_InitialHPUpdate=0x0000 # OP_ItemRecastDelay=0x82d7 + +# unhandled +OP_ClearRaidNPCMarks=0x2af4 +OP_ShieldGroup=0x23a1 From c002f834d47ce459dcdcbfe044c939f7da61f8ac Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 14 Oct 2014 17:20:19 -0400 Subject: [PATCH 344/368] Correct misidentified OP_MarkNPC (RoF,SoD,SoF,UF) [skip ci] --- utils/patches/patch_RoF.conf | 4 +++- utils/patches/patch_SoD.conf | 4 +++- utils/patches/patch_SoF.conf | 4 +++- utils/patches/patch_Underfoot.conf | 5 +++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 16197c0da..e4eb9ba14 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -289,8 +289,10 @@ OP_LeadershipExpToggle=0x3ea6 OP_LeadershipExpUpdate=0x6922 OP_PurchaseLeadershipAA=0x1962 OP_UpdateLeadershipAA=0x56aa -OP_MarkNPC=0x2d9f +OP_MarkNPC=0x1a6c +OP_MarkRaidNPC=0x2d9f #unimplemented OP_ClearNPCMarks=0x0d2d +OP_ClearRaidNPCMarks=0x433a #unimplemented OP_DelegateAbility=0x7820 OP_SetGroupTarget=0x118a OP_Charm=0x7118 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index f0d223edb..472d60e7a 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -288,8 +288,10 @@ OP_LeadershipExpToggle=0x34c5 # C OP_LeadershipExpUpdate=0x69d0 # C OP_PurchaseLeadershipAA=0x07b3 # C OP_UpdateLeadershipAA=0x6948 # C -OP_MarkNPC=0x0d4b # C +OP_MarkNPC=0x695c # C +OP_MarkRaidNPC=0x0d4b # C OP_ClearNPCMarks=0x5033 # C +OP_ClearRaidNPCMarks=0x5f55 # C OP_DoGroupLeadershipAbility=0x540b # C OP_GroupLeadershipAAUpdate=0x0c33 OP_DelegateAbility=0x0322 # C diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index 2e039b404..a50c1faf6 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -281,8 +281,10 @@ OP_LeadershipExpToggle=0x24D4 #Xinu 02/20/09 OP_LeadershipExpUpdate=0x58b6 #Derision 2009 OP_PurchaseLeadershipAA=0x1408 #Derision 2009 OP_UpdateLeadershipAA=0x7abf #Derision 2009 -OP_MarkNPC=0x00c6 #Derision 2009 +OP_MarkNPC=0x4697 #Derision 2009 +OP_MarkRaidNPC=0x00c6 OP_ClearNPCMarks=0x2ff2 # +OP_ClearRaidNPCMarks=0x56a9 # OP_DoGroupLeadershipAbility=0x5a64 #Derision 2009 OP_DelegateAbility=0x57e3 #Derision 2009 OP_SetGroupTarget=0x1651 #Derision 2009 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index e62b24b67..b7bdbc7e0 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -292,8 +292,10 @@ OP_LeadershipExpToggle=0x5033 # C OP_LeadershipExpUpdate=0x074f # C OP_PurchaseLeadershipAA=0x5f55 # C OP_UpdateLeadershipAA=0x77ed # C -OP_MarkNPC=0x3ec7 # C +OP_MarkNPC=0x66bf +OP_MarkRaidNPC=0x3ec7 # C OP_ClearNPCMarks=0x5c29 # C +OP_ClearRaidNPCMarks=0x2af4 OP_DoGroupLeadershipAbility=0x0068 # C OP_GroupLeadershipAAUpdate=0x167b # C OP_DelegateAbility=0x6e58 # C @@ -658,5 +660,4 @@ OP_InitialHPUpdate=0x0000 # OP_ItemRecastDelay=0x82d7 # unhandled -OP_ClearRaidNPCMarks=0x2af4 OP_ShieldGroup=0x23a1 From e38abaa32ac36f99ffe85cd059827acd059c9e71 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 14 Oct 2014 22:50:09 -0400 Subject: [PATCH 345/368] Optimize EntityList::UnMarkNPC Going over the group_list is much better than hitting the same group up to 5 unnecessary times --- zone/entity.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index fd031d618..4a8329639 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4120,15 +4120,10 @@ void EntityList::UnMarkNPC(uint16 ID) // each group to remove the dead mobs entity ID from the groups list of NPCs marked via the // Group Leadership AA Mark NPC ability. // - auto it = client_list.begin(); - while (it != client_list.end()) { - if (it->second) { - Group *g = nullptr; - g = it->second->GetGroup(); - - if (g) - g->UnMarkNPC(ID); - } + auto it = group_list.begin(); + while (it != group_list.end()) { + if (*it) + (*it)->UnMarkNPC(ID); ++it; } } From 69c0405004a62701f48b981081570d3f2a28a5e8 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 15 Oct 2014 07:49:39 -0400 Subject: [PATCH 346/368] Updated both load and drop sqls for bots..added a 'deprecated' load for pre-pp blob conversion databases..the auto-conversion code will catch this if the server is updated to a newer version. (Had a really nice load script coded..but, the supported version of MySQL doesn't support 'TEMPORARY' procedures, nor commit commands from 'PREPARE(d)' statements... :( ) --- changelog.txt | 6 + common/database.cpp | 3 + .../sql/git/bots/deprecated/load_bots_old.sql | 280 +++++++++ utils/sql/git/bots/drop_bots.sql | 46 +- utils/sql/git/bots/load_bots.sql | 535 +++++++++--------- 5 files changed, 574 insertions(+), 296 deletions(-) create mode 100644 utils/sql/git/bots/deprecated/load_bots_old.sql diff --git a/changelog.txt b/changelog.txt index 2461b4a36..6971c932d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/15/2014 == +Uleat: Cleaned up load/drop bots sqls, added '../utils/sql/git/bots/deprecated' and '../deprecated/load_bots_old.sql' (use this file on pre-player blob conversion databases.) +Notes: I modifed the behavior of both load and drop bots to fail on the first operation if their modifications have been performed already. + 'load_bots.sql' will explicitly add bot schema, while 'drop_bots.sql' will explicitly drop it. I also added a few lines to change + a few altered tables back to their original state - as of the date in the file. + == 10/11/2014 == demonstar55: Implement Raid MOTD for UF Don't forget 2014_10_11_RaidMOTD.sql! diff --git a/common/database.cpp b/common/database.cpp index dedc00ff1..b6453d3cb 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1933,6 +1933,8 @@ bool Database::CheckDatabaseConversions() { printf("----------------------------------------------------------\n\n"); printf(" Database currently has bot view/function linkage to obselete \n"); printf(" table references and will now be converted...\n\n"); + printf(" It is recommended that you backup your database \n"); + printf(" before continuing the automatic conversion process...\n\n"); printf("----------------------------------------------------------\n\n"); std::cout << "Press ENTER to continue....." << std::endl << std::endl; std::cin.ignore(1); @@ -1943,6 +1945,7 @@ bool Database::CheckDatabaseConversions() { } if (runbotsconvert == 1){ + printf("Running bot views/function database conversion... \n"); /* Update view `vwbotcharactermobs` */ rquery = StringFormat("DROP VIEW `vwbotcharactermobs`;"); diff --git a/utils/sql/git/bots/deprecated/load_bots_old.sql b/utils/sql/git/bots/deprecated/load_bots_old.sql new file mode 100644 index 000000000..9f57191ab --- /dev/null +++ b/utils/sql/git/bots/deprecated/load_bots_old.sql @@ -0,0 +1,280 @@ +-- 'load_bots_old' sql script file +-- current as of 10/15/2014 +-- +-- Use this file on databases where the player profile blob has not been converted +-- +-- Note: This file assumes a database free of bot remnants. If you have a prior +-- bot installation and wish to reload the default schema and entries, then +-- source 'drop_bots.sql' before sourcing this file. + + +ALTER TABLE `guild_members` DROP PRIMARY KEY; +ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`, `ismerc`); + +UPDATE `spawn2` SET `enabled` = 1 WHERE `id` IN (59297,59298); + +-- old command kept for reference (`commands` now only has 2 columns - `command` and `access`) +-- INSERT INTO `commands` VALUES ('bot', '0', 'Type \"#bot help\" to the see the list of available commands for bots.'); +INSERT INTO `commands` VALUES ('bot', '0'); + +INSERT INTO `rule_values` VALUES + ('1', 'Bots:BotAAExpansion', '8', 'The expansion through which bots will obtain AAs'), + ('1', 'Bots:BotFinishBuffing', 'false', 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.'), + ('1', 'Bots:BotGroupBuffing', 'false', 'Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB.'), + ('1', 'Bots:BotManaRegen', '3.0', 'Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players.'), + ('1', 'Bots:BotQuest', 'false', 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl'), + ('1', 'Bots:BotSpellQuest', 'false', 'Anita Thrall\'s (Anita_Thrall.pl) Bot Spell Scriber quests.'), + ('1', 'Bots:CreateBotCount', '150', 'Number of bots that each account can create'), + ('1', 'Bots:SpawnBotCount', '71', 'Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid'); + +CREATE TABLE `bots` ( + `BotID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotOwnerCharacterID` INT(10) UNSIGNED NOT NULL, + `BotSpellsID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Name` VARCHAR(64) NOT NULL, + `LastName` VARCHAR(32) DEFAULT NULL, + `BotLevel` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0', + `Race` SMALLINT(5) NOT NULL DEFAULT '0', + `Class` TINYINT(2) NOT NULL DEFAULT '0', + `Gender` TINYINT(2) NOT NULL DEFAULT '0', + `Size` FLOAT NOT NULL DEFAULT '0', + `Face` INT(10) NOT NULL DEFAULT '1', + `LuclinHairStyle` INT(10) NOT NULL DEFAULT '1', + `LuclinHairColor` INT(10) NOT NULL DEFAULT '1', + `LuclinEyeColor` INT(10) NOT NULL DEFAULT '1', + `LuclinEyeColor2` INT(10) NOT NULL DEFAULT '1', + `LuclinBeardColor` INT(10) NOT NULL DEFAULT '1', + `LuclinBeard` INT(10) NOT NULL DEFAULT '0', + `DrakkinHeritage` INT(10) NOT NULL DEFAULT '0', + `DrakkinTattoo` INT(10) NOT NULL DEFAULT '0', + `DrakkinDetails` INT(10) NOT NULL DEFAULT '0', + `HP` INTEGER NOT NULL DEFAULT '0', + `Mana` INTEGER NOT NULL DEFAULT '0', + `MR` SMALLINT(5) NOT NULL DEFAULT '0', + `CR` SMALLINT(5) NOT NULL DEFAULT '0', + `DR` SMALLINT(5) NOT NULL DEFAULT '0', + `FR` SMALLINT(5) NOT NULL DEFAULT '0', + `PR` SMALLINT(5) NOT NULL DEFAULT '0', + `Corrup` SMALLINT(5) NOT NULL DEFAULT '0', + `AC` SMALLINT(5) NOT NULL DEFAULT '0', + `STR` MEDIUMINT(8) NOT NULL DEFAULT '75', + `STA` MEDIUMINT(8) NOT NULL DEFAULT '75', + `DEX` MEDIUMINT(8) NOT NULL DEFAULT '75', + `AGI` MEDIUMINT(8) NOT NULL DEFAULT '75', + `_INT` MEDIUMINT(8) NOT NULL DEFAULT '80', + `WIS` MEDIUMINT(8) NOT NULL DEFAULT '75', + `CHA` MEDIUMINT(8) NOT NULL DEFAULT '75', + `ATK` MEDIUMINT(9) NOT NULL DEFAULT '0', + `BotCreateDate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `LastSpawnDate` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `TotalPlayTime` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `LastZoneId` SMALLINT(6) NOT NULL DEFAULT '0', + `BotInspectMessage` VARCHAR(256) NOT NULL DEFAULT '', + PRIMARY KEY (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botstances` ( + `BotID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `StanceID` TINYINT UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotID`), + CONSTRAINT `FK_botstances_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) +); + +CREATE TABLE `bottimers` ( + `BotID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `TimerID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Value` INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotID`), + CONSTRAINT `FK_bottimers_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) +); + +CREATE TABLE `botbuffs` ( + `BotBuffId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `SpellId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterLevel` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `DurationFormula` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `TicsRemaining` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `PoisonCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `DiseaseCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `CurseCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `CorruptionCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `HitCount` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `MeleeRune` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `MagicRune` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `DeathSaveSuccessChance` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterAARank` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Persistent` TINYINT(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`BotBuffId`), + KEY `FK_botbuff_1` (`BotId`), + CONSTRAINT `FK_botbuff_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botinventory` ( + `BotInventoryID` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotID` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `SlotID` INTEGER SIGNED NOT NULL DEFAULT '0', + `ItemID` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `charges` TINYINT(3) UNSIGNED DEFAULT 0, + `color` INTEGER UNSIGNED NOT NULL DEFAULT 0, + `augslot1` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot2` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot3` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot4` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot5` MEDIUMINT(7) UNSIGNED DEFAULT 0, + `instnodrop` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`BotInventoryID`), + KEY `FK_botinventory_1` (`BotID`), + CONSTRAINT `FK_botinventory_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botpets` ( + `BotPetsId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `PetId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `Name` VARCHAR(64) NULL, + `Mana` INTEGER NOT NULL DEFAULT '0', + `HitPoints` INTEGER NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetsId`), + KEY `FK_botpets_1` (`BotId`), + CONSTRAINT `FK_botpets_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`), + CONSTRAINT `U_botpets_1` UNIQUE (`BotId`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botpetbuffs` ( + `BotPetBuffId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotPetsId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `SpellId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterLevel` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Duration` INT(11) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetBuffId`), + KEY `FK_botpetbuffs_1` (`BotPetsId`), + CONSTRAINT `FK_botpetbuffs_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botpetinventory` ( + `BotPetInventoryId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotPetsId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `ItemId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetInventoryId`), + KEY `FK_botpetinventory_1` (`BotPetsId`), + CONSTRAINT `FK_botpetinventory_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botgroup` ( + `BotGroupId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotGroupLeaderBotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotGroupName` VARCHAR(64) NOT NULL, + PRIMARY KEY (`BotGroupId`), + KEY `FK_botgroup_1` (`BotGroupLeaderBotId`), + CONSTRAINT `FK_botgroup_1` FOREIGN KEY (`BotGroupLeaderBotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botgroupmembers` ( + `BotGroupMemberId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotGroupId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotGroupMemberId`), + KEY `FK_botgroupmembers_1` (`BotGroupId`), + CONSTRAINT `FK_botgroupmembers_1` FOREIGN KEY (`BotGroupId`) REFERENCES `botgroup` (`BotGroupId`), + KEY `FK_botgroupmembers_2` (`BotId`), + CONSTRAINT `FK_botgroupmembers_2` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botguildmembers` ( + `char_id` INT(11) NOT NULL DEFAULT '0', + `guild_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', + `rank` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `tribute_enable` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `total_tribute` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `last_tribute` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `banker` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `public_note` TEXT NULL, + `alt` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`char_id`) +) ENGINE=InnoDB; + +DELIMITER // + +CREATE FUNCTION `GetMobType` (mobname VARCHAR(64)) RETURNS CHAR(1) +BEGIN + DECLARE Result CHAR(1); + + SET Result = NULL; + + IF (SELECT COUNT(*) FROM `character_` WHERE `name` = mobname) > 0 THEN + SET Result = 'C'; + ELSEIF (SELECT COUNT(*) FROM `bots` WHERE `Name` = mobname) > 0 THEN + SET Result = 'B'; + END IF; + +RETURN Result; +END// + +DELIMITER ; + +CREATE VIEW `vwbotcharactermobs` AS +SELECT _utf8'C' AS mobtype, +c.`id`, +c.`name`, +c.`class`, +c.`level`, +c.`timelaston`, +c.`zoneid` +FROM `character_` AS c +UNION ALL +SELECT _utf8'B' AS mobtype, +b.`BotID` AS id, +b.`Name` AS name, +b.`Class` AS class, +b.`BotLevel` AS level, +0 AS timelaston, +0 AS zoneid +FROM bots AS b; + +CREATE VIEW `vwgroups` AS +SELECT g.`groupid` AS groupid, +GetMobType(g.`name`) AS mobtype, +g.`name` AS name, +g.`charid` AS mobid, +IFNULL(c.`level`, b.`BotLevel`) AS level +FROM `group_id` AS g +LEFT JOIN `character_` AS c ON g.`name` = c.`name` +LEFT JOIN `bots` AS b ON g.`name` = b.`Name`; + +CREATE VIEW `vwbotgroups` AS +SELECT g.`BotGroupId`, +g.`BotGroupName`, +g.`BotGroupLeaderBotId`, +b.`Name` AS BotGroupLeaderName, +b.`BotOwnerCharacterId`, +c.`name` AS BotOwnerCharacterName +FROM `botgroup` AS g +JOIN `bots` AS b ON g.`BotGroupLeaderBotId` = b.`BotID` +JOIN `character_` AS c ON b.`BotOwnerCharacterID` = c.`id` +ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`; + +CREATE VIEW `vwguildmembers` AS +SELECT 'C' AS mobtype, +cm.`char_id`, +cm.`guild_id`, +cm.`rank`, +cm.`tribute_enable`, +cm.`total_tribute`, +cm.`last_tribute`, +cm.`banker`, +cm.`public_note`, +cm.`alt` +FROM `guild_members` AS cm +UNION ALL +SELECT 'B' AS mobtype, +bm.`char_id`, +bm.`guild_id`, +bm.`rank`, +bm.`tribute_enable`, +bm.`total_tribute`, +bm.`last_tribute`, +bm.`banker`, +bm.`public_note`, +bm.`alt` +FROM `botguildmembers` AS bm; diff --git a/utils/sql/git/bots/drop_bots.sql b/utils/sql/git/bots/drop_bots.sql index d02425d73..63ecc7b91 100644 --- a/utils/sql/git/bots/drop_bots.sql +++ b/utils/sql/git/bots/drop_bots.sql @@ -1,21 +1,37 @@ -DROP TABLE IF EXISTS `botbuffs`; +-- 'drop_bots' sql script file +-- current as of 10/15/2014 +-- +-- Note: This file will revert all changes made by either 'load_bots' sql file. +-- There may still be remnants of bot activity in tables `guild_members` and +-- `group_id`. If these entries are causing issues, you may need to manually +-- remove them. + + +ALTER TABLE `guild_members` ADD PRIMARY KEY (`char_id`); +ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY (`groupid`, `charid`, `ismerc`); + +UPDATE `spawn2` SET `enabled` = 0 WHERE `id` IN (59297,59298); + +DELETE FROM `commands` WHERE `command` = 'bot'; +DELETE FROM `rule_values` WHERE `rule_name` LIKE 'Bots%'; + +DROP VIEW IF EXISTS `vwbotcharactermobs`; +DROP VIEW IF EXISTS `vwbotgroups`; +DROP VIEW IF EXISTS `vwgroups`; +DROP VIEW IF EXISTS `vwguildmembers`; + +DROP FUNCTION IF EXISTS `GetMobType`; + +DROP TABLE IF EXISTS `botguildmembers`; +DROP TABLE IF EXISTS `botgroupmembers`; +-- this table is not a part of 'load_bots.sql' +DROP TABLE IF EXISTS `botgroups`; +DROP TABLE IF EXISTS `botgroup`; DROP TABLE IF EXISTS `botpetinventory`; DROP TABLE IF EXISTS `botpetbuffs`; DROP TABLE IF EXISTS `botpets`; -DROP TABLE IF EXISTS `botgroupmembers`; -DROP TABLE IF EXISTS `botgroup`; -DROP TABLE IF EXISTS `botgroups`; DROP TABLE IF EXISTS `botinventory`; -DROP TABLE IF EXISTS `botguildmembers`; -DROP TABLE IF EXISTS `botstances`; +DROP TABLE IF EXISTS `botbuffs`; DROP TABLE IF EXISTS `bottimers`; +DROP TABLE IF EXISTS `botstances`; DROP TABLE IF EXISTS `bots`; -DROP VIEW IF EXISTS `vwGuildMembers`; -DROP VIEW IF EXISTS `vwBotCharacterMobs`; -DROP VIEW IF EXISTS `vwBotGroups`; - -delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1; - -delete from commands where command = 'bot'; - -update spawn2 set enabled = 0 where id in (59297,59298); \ No newline at end of file diff --git a/utils/sql/git/bots/load_bots.sql b/utils/sql/git/bots/load_bots.sql index d5a358d69..7fe35041e 100644 --- a/utils/sql/git/bots/load_bots.sql +++ b/utils/sql/git/bots/load_bots.sql @@ -1,307 +1,280 @@ --- This is pretty much a straight copy of the original files with fixes applied. --- HeidiSQL does not like sub-directory references, so this should now run from there. --- The 'headers' are left in place for reference only. +-- 'load_bots' sql script file +-- current as of 10/15/2014 +-- +-- Use this file on databases where the player profile blob has been converted. +-- +-- Note: This file assumes a database free of bot remnants. If you have a prior +-- bot installation and wish to reload the default schema and entries, then +-- source 'drop_bots.sql' before sourcing this file. --- FILE: --- source player_tables/botguildmembers.sql; -CREATE TABLE IF NOT EXISTS `botguildmembers` ( - `char_id` int(11) NOT NULL default '0', - `guild_id` mediumint(8) unsigned NOT NULL default '0', - `rank` tinyint(3) unsigned NOT NULL default '0', - `tribute_enable` tinyint(3) unsigned NOT NULL default '0', - `total_tribute` int(10) unsigned NOT NULL default '0', - `last_tribute` int(10) unsigned NOT NULL default '0', - `banker` tinyint(3) unsigned NOT NULL default '0', - `public_note` text NULL, - `alt` tinyint(3) unsigned NOT NULL default '0', - PRIMARY KEY (`char_id`) -) ENGINE=InnoDB; --- FILE: --- source player_tables/bots.sql; -update spawn2 set enabled = 1 where id in (59297,59298); +ALTER TABLE `guild_members` DROP PRIMARY KEY; +ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`, `ismerc`); -INSERT INTO rule_values VALUES ('1', 'Bots:BotManaRegen', '3.0', 'Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players.'); -INSERT INTO rule_values VALUES ('1', 'Bots:BotFinishBuffing', 'false', 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.'); -INSERT INTO rule_values VALUES ('1', 'Bots:CreateBotCount', '150', 'Number of bots that each account can create'); -INSERT INTO rule_values VALUES ('1', 'Bots:SpawnBotCount', '71', 'Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid'); -INSERT INTO rule_values VALUES ('1', 'Bots:BotQuest', 'false', 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl'); -INSERT INTO rule_values VALUES ('1', 'Bots:BotGroupBuffing', 'false', 'Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB.'); -INSERT INTO rule_values VALUES ('1', 'Bots:BotSpellQuest', 'false', 'Anita Thrall\'s (Anita_Thrall.pl) Bot Spell Scriber quests.'); +UPDATE `spawn2` SET `enabled` = 1 WHERE `id` IN (59297,59298); --- this is a hack fix to maintain the original file process -delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1; -INSERT INTO rule_values VALUES ('1', 'Bots:BotAAExpansion', '8', 'The expansion through which bots will obtain AAs'); - --- field count has changed +-- old command kept for reference (`commands` now only has 2 columns - `command` and `access`) -- INSERT INTO `commands` VALUES ('bot', '0', 'Type \"#bot help\" to the see the list of available commands for bots.'); INSERT INTO `commands` VALUES ('bot', '0'); -CREATE TABLE bots ( - `BotID` int(10) unsigned NOT NULL AUTO_INCREMENT, - `BotOwnerCharacterID` int(10) unsigned NOT NULL, - `BotSpellsID` int(10) unsigned NOT NULL DEFAULT '0', - `Name` varchar(64) NOT NULL, - `LastName` varchar(32) DEFAULT NULL, - `BotLevel` tinyint(2) unsigned NOT NULL DEFAULT '0', - `Race` smallint(5) NOT NULL DEFAULT '0', - `Class` tinyint(2) NOT NULL DEFAULT '0', - `Gender` tinyint(2) NOT NULL DEFAULT '0', - `Size` float NOT NULL DEFAULT '0', - `Face` int(10) NOT NULL DEFAULT '1', - `LuclinHairStyle` int(10) NOT NULL DEFAULT '1', - `LuclinHairColor` int(10) NOT NULL DEFAULT '1', - `LuclinEyeColor` int(10) NOT NULL DEFAULT '1', - `LuclinEyeColor2` int(10) NOT NULL DEFAULT '1', - `LuclinBeardColor` int(10) NOT NULL DEFAULT '1', - `LuclinBeard` int(10) NOT NULL DEFAULT '0', - `DrakkinHeritage` int(10) NOT NULL DEFAULT '0', - `DrakkinTattoo` int(10) NOT NULL DEFAULT '0', - `DrakkinDetails` int(10) NOT NULL DEFAULT '0', - `HP` INTEGER NOT NULL DEFAULT '0', - `Mana` INTEGER NOT NULL DEFAULT '0', - `MR` smallint(5) NOT NULL DEFAULT '0', - `CR` smallint(5) NOT NULL DEFAULT '0', - `DR` smallint(5) NOT NULL DEFAULT '0', - `FR` smallint(5) NOT NULL DEFAULT '0', - `PR` smallint(5) NOT NULL DEFAULT '0', - `Corrup` SMALLINT(5) NOT NULL DEFAULT '0', - `AC` smallint(5) NOT NULL DEFAULT '0', - `STR` mediumint(8) NOT NULL DEFAULT '75', - `STA` mediumint(8) NOT NULL DEFAULT '75', - `DEX` mediumint(8) NOT NULL DEFAULT '75', - `AGI` mediumint(8) NOT NULL DEFAULT '75', - `_INT` mediumint(8) NOT NULL DEFAULT '80', - `WIS` mediumint(8) NOT NULL DEFAULT '75', - `CHA` mediumint(8) NOT NULL DEFAULT '75', - `ATK` mediumint(9) NOT NULL DEFAULT '0', - `BotCreateDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `LastSpawnDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `TotalPlayTime` int(10) unsigned NOT NULL DEFAULT '0', - `LastZoneId` smallint(6) NOT NULL DEFAULT '0', - `BotInspectMessage` VARCHAR(256) NOT NULL DEFAULT '', - PRIMARY KEY (`BotID`) +INSERT INTO `rule_values` VALUES + ('1', 'Bots:BotAAExpansion', '8', 'The expansion through which bots will obtain AAs'), + ('1', 'Bots:BotFinishBuffing', 'false', 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.'), + ('1', 'Bots:BotGroupBuffing', 'false', 'Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB.'), + ('1', 'Bots:BotManaRegen', '3.0', 'Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players.'), + ('1', 'Bots:BotQuest', 'false', 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl'), + ('1', 'Bots:BotSpellQuest', 'false', 'Anita Thrall\'s (Anita_Thrall.pl) Bot Spell Scriber quests.'), + ('1', 'Bots:CreateBotCount', '150', 'Number of bots that each account can create'), + ('1', 'Bots:SpawnBotCount', '71', 'Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid'); + +CREATE TABLE `bots` ( + `BotID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotOwnerCharacterID` INT(10) UNSIGNED NOT NULL, + `BotSpellsID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Name` VARCHAR(64) NOT NULL, + `LastName` VARCHAR(32) DEFAULT NULL, + `BotLevel` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0', + `Race` SMALLINT(5) NOT NULL DEFAULT '0', + `Class` TINYINT(2) NOT NULL DEFAULT '0', + `Gender` TINYINT(2) NOT NULL DEFAULT '0', + `Size` FLOAT NOT NULL DEFAULT '0', + `Face` INT(10) NOT NULL DEFAULT '1', + `LuclinHairStyle` INT(10) NOT NULL DEFAULT '1', + `LuclinHairColor` INT(10) NOT NULL DEFAULT '1', + `LuclinEyeColor` INT(10) NOT NULL DEFAULT '1', + `LuclinEyeColor2` INT(10) NOT NULL DEFAULT '1', + `LuclinBeardColor` INT(10) NOT NULL DEFAULT '1', + `LuclinBeard` INT(10) NOT NULL DEFAULT '0', + `DrakkinHeritage` INT(10) NOT NULL DEFAULT '0', + `DrakkinTattoo` INT(10) NOT NULL DEFAULT '0', + `DrakkinDetails` INT(10) NOT NULL DEFAULT '0', + `HP` INTEGER NOT NULL DEFAULT '0', + `Mana` INTEGER NOT NULL DEFAULT '0', + `MR` SMALLINT(5) NOT NULL DEFAULT '0', + `CR` SMALLINT(5) NOT NULL DEFAULT '0', + `DR` SMALLINT(5) NOT NULL DEFAULT '0', + `FR` SMALLINT(5) NOT NULL DEFAULT '0', + `PR` SMALLINT(5) NOT NULL DEFAULT '0', + `Corrup` SMALLINT(5) NOT NULL DEFAULT '0', + `AC` SMALLINT(5) NOT NULL DEFAULT '0', + `STR` MEDIUMINT(8) NOT NULL DEFAULT '75', + `STA` MEDIUMINT(8) NOT NULL DEFAULT '75', + `DEX` MEDIUMINT(8) NOT NULL DEFAULT '75', + `AGI` MEDIUMINT(8) NOT NULL DEFAULT '75', + `_INT` MEDIUMINT(8) NOT NULL DEFAULT '80', + `WIS` MEDIUMINT(8) NOT NULL DEFAULT '75', + `CHA` MEDIUMINT(8) NOT NULL DEFAULT '75', + `ATK` MEDIUMINT(9) NOT NULL DEFAULT '0', + `BotCreateDate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `LastSpawnDate` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `TotalPlayTime` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `LastZoneId` SMALLINT(6) NOT NULL DEFAULT '0', + `BotInspectMessage` VARCHAR(256) NOT NULL DEFAULT '', + PRIMARY KEY (`BotID`) ) ENGINE=InnoDB; -ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`); -ALTER TABLE `guild_members` DROP PRIMARY KEY; +CREATE TABLE `botstances` ( + `BotID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `StanceID` TINYINT UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotID`), + CONSTRAINT `FK_botstances_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) +); -DROP VIEW IF EXISTS `vwGuildMembers`; -CREATE VIEW `vwGuildMembers` AS - select 'C' as mobtype, -cm.char_id, -cm.guild_id, -cm.rank, -cm.tribute_enable, -cm.total_tribute, -cm.last_tribute, -cm.banker, -cm.public_note, -cm.alt -from guild_members as cm -union all -select 'B' as mobtype, -bm.char_id, -bm.guild_id, -bm.rank, -bm.tribute_enable, -bm.total_tribute, -bm.last_tribute, -bm.banker, -bm.public_note, -bm.alt -from botguildmembers as bm; +CREATE TABLE `bottimers` ( + `BotID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `TimerID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Value` INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotID`), + CONSTRAINT `FK_bottimers_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) +); -DROP VIEW IF EXISTS `vwBotCharacterMobs`; -CREATE VIEW `vwBotCharacterMobs` AS - select 'C' as mobtype, -c.id, -c.name, -c.class, -c.level, -c.timelaston, -c.zoneid -from character_ as c -union all -select 'B' as mobtype, -b.BotID as id, -b.Name as name, -b.Class as class, -b.BotLevel as level, -0 as timelaston, -0 as zoneid -from bots as b; - --- FILE: --- source player_tables/botpetstatepersists.sql; -DROP TABLE IF EXISTS `botpetinventory`; -DROP TABLE IF EXISTS `botpetbuffs`; -DROP TABLE IF EXISTS `botpets`; - -CREATE TABLE IF NOT EXISTS `botpets` ( - `BotPetsId` integer unsigned NOT NULL AUTO_INCREMENT, - `PetId` integer unsigned NOT NULL DEFAULT '0', - `BotId` integer unsigned NOT NULL DEFAULT '0', - `Name` varchar(64) NULL, - `Mana` integer NOT NULL DEFAULT '0', - `HitPoints` integer NOT NULL DEFAULT '0', - PRIMARY KEY (`BotPetsId`), - KEY `FK_botpets_1` (`BotId`), - CONSTRAINT `FK_botpets_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`), - CONSTRAINT `U_botpets_1` UNIQUE (`BotId`) +CREATE TABLE `botbuffs` ( + `BotBuffId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `SpellId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterLevel` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `DurationFormula` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `TicsRemaining` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `PoisonCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `DiseaseCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `CurseCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `CorruptionCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `HitCount` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `MeleeRune` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `MagicRune` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `DeathSaveSuccessChance` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterAARank` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Persistent` TINYINT(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`BotBuffId`), + KEY `FK_botbuff_1` (`BotId`), + CONSTRAINT `FK_botbuff_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; -CREATE TABLE IF NOT EXISTS `botpetbuffs` ( - `BotPetBuffId` int(10) unsigned NOT NULL AUTO_INCREMENT, - `BotPetsId` int(10) unsigned NOT NULL DEFAULT '0', - `SpellId` int(10) unsigned NOT NULL DEFAULT '0', - `CasterLevel` int(10) unsigned NOT NULL DEFAULT '0', - `Duration` int(11) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`BotPetBuffId`), - KEY `FK_botpetbuffs_1` (`BotPetsId`), - CONSTRAINT `FK_botpetbuffs_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; - -CREATE TABLE IF NOT EXISTS `botpetinventory` ( - `BotPetInventoryId` integer unsigned NOT NULL AUTO_INCREMENT, - `BotPetsId` integer unsigned NOT NULL DEFAULT '0', - `ItemId` integer unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`BotPetInventoryId`), - KEY `FK_botpetinventory_1` (`BotPetsId`), - CONSTRAINT `FK_botpetinventory_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; - --- FILE: --- source player_tables/botinventory.sql; -CREATE TABLE IF NOT EXISTS botinventory ( - BotInventoryID integer unsigned NOT NULL auto_increment, - BotID integer unsigned NOT NULL DEFAULT '0', - SlotID integer signed NOT NULL DEFAULT '0', - ItemID integer unsigned NOT NULL DEFAULT '0', - charges tinyint(3) unsigned DEFAULT 0, - color integer unsigned NOT NULL DEFAULT 0, - augslot1 mediumint(7) unsigned NOT NULL DEFAULT 0, - augslot2 mediumint(7) unsigned NOT NULL DEFAULT 0, - augslot3 mediumint(7) unsigned NOT NULL DEFAULT 0, - augslot4 mediumint(7) unsigned NOT NULL DEFAULT 0, - augslot5 mediumint(7) unsigned DEFAULT 0, - instnodrop tinyint(1) unsigned NOT NULL DEFAULT 0, - PRIMARY KEY (BotInventoryID), - KEY FK_botinventory_1 (BotID), - CONSTRAINT FK_botinventory_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) +CREATE TABLE `botinventory` ( + `BotInventoryID` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotID` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `SlotID` INTEGER SIGNED NOT NULL DEFAULT '0', + `ItemID` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `charges` TINYINT(3) UNSIGNED DEFAULT 0, + `color` INTEGER UNSIGNED NOT NULL DEFAULT 0, + `augslot1` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot2` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot3` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot4` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot5` MEDIUMINT(7) UNSIGNED DEFAULT 0, + `instnodrop` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`BotInventoryID`), + KEY `FK_botinventory_1` (`BotID`), + CONSTRAINT `FK_botinventory_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) ) ENGINE=InnoDB; --- FILE: --- source player_tables/botbuffs.sql; -DROP TABLE IF EXISTS `botbuffs`; -CREATE TABLE `botbuffs` ( - `BotBuffId` int(10) unsigned NOT NULL AUTO_INCREMENT, - `BotId` int(10) unsigned NOT NULL DEFAULT '0', - `SpellId` int(10) unsigned NOT NULL DEFAULT '0', - `CasterLevel` int(10) unsigned NOT NULL DEFAULT '0', - `DurationFormula` int(10) unsigned NOT NULL DEFAULT '0', - `TicsRemaining` int(11) unsigned NOT NULL DEFAULT '0', - `PoisonCounters` int(11) unsigned NOT NULL DEFAULT '0', - `DiseaseCounters` int(11) unsigned NOT NULL DEFAULT '0', - `CurseCounters` int(11) unsigned NOT NULL DEFAULT '0', - `CorruptionCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', - `HitCount` int(10) unsigned NOT NULL DEFAULT '0', - `MeleeRune` int(10) unsigned NOT NULL DEFAULT '0', - `MagicRune` int(10) unsigned NOT NULL DEFAULT '0', - `DeathSaveSuccessChance` int(10) unsigned NOT NULL DEFAULT '0', - `CasterAARank` int(10) unsigned NOT NULL DEFAULT '0', - `Persistent` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`BotBuffId`), - KEY `FK_botbuff_1` (`BotId`), - CONSTRAINT `FK_botbuff_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) +CREATE TABLE `botpets` ( + `BotPetsId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `PetId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `Name` VARCHAR(64) NULL, + `Mana` INTEGER NOT NULL DEFAULT '0', + `HitPoints` INTEGER NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetsId`), + KEY `FK_botpets_1` (`BotId`), + CONSTRAINT `FK_botpets_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`), + CONSTRAINT `U_botpets_1` UNIQUE (`BotId`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; --- FILE: --- source player_tables/botadventuring.sql; -DELIMITER $$ +CREATE TABLE `botpetbuffs` ( + `BotPetBuffId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotPetsId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `SpellId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterLevel` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Duration` INT(11) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetBuffId`), + KEY `FK_botpetbuffs_1` (`BotPetsId`), + CONSTRAINT `FK_botpetbuffs_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botpetinventory` ( + `BotPetInventoryId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotPetsId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `ItemId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetInventoryId`), + KEY `FK_botpetinventory_1` (`BotPetsId`), + CONSTRAINT `FK_botpetinventory_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botgroup` ( + `BotGroupId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotGroupLeaderBotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotGroupName` VARCHAR(64) NOT NULL, + PRIMARY KEY (`BotGroupId`), + KEY `FK_botgroup_1` (`BotGroupLeaderBotId`), + CONSTRAINT `FK_botgroup_1` FOREIGN KEY (`BotGroupLeaderBotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botgroupmembers` ( + `BotGroupMemberId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotGroupId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotGroupMemberId`), + KEY `FK_botgroupmembers_1` (`BotGroupId`), + CONSTRAINT `FK_botgroupmembers_1` FOREIGN KEY (`BotGroupId`) REFERENCES `botgroup` (`BotGroupId`), + KEY `FK_botgroupmembers_2` (`BotId`), + CONSTRAINT `FK_botgroupmembers_2` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botguildmembers` ( + `char_id` INT(11) NOT NULL DEFAULT '0', + `guild_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', + `rank` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `tribute_enable` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `total_tribute` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `last_tribute` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `banker` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `public_note` TEXT NULL, + `alt` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`char_id`) +) ENGINE=InnoDB; + +DELIMITER \\ -DROP FUNCTION IF EXISTS `GetMobType` $$ CREATE FUNCTION `GetMobType` (mobname VARCHAR(64)) RETURNS CHAR(1) BEGIN - DECLARE Result CHAR(1); - - SET Result = NULL; - - IF (select count(*) from character_ where name = mobname) > 0 THEN - SET Result = 'C'; - ELSEIF (select count(*) from bots where Name = mobname) > 0 THEN - SET Result = 'B'; - END IF; - - RETURN Result; -END $$ + DECLARE Result CHAR(1); + + SET Result = NULL; + + IF (SELECT COUNT(*) FROM `character_data` WHERE `name` = mobname) > 0 THEN + SET Result = 'C'; + ELSEIF (SELECT COUNT(*) FROM `bots` WHERE `Name` = mobname) > 0 THEN + SET Result = 'B'; + END IF; + + RETURN Result; +END\\ DELIMITER ; -DROP VIEW IF EXISTS `vwGroups`; -CREATE VIEW `vwGroups` AS - select g.groupid as groupid, -GetMobType(g.name) as mobtype, -g.name as name, -g.charid as mobid, -ifnull(c.level, b.BotLevel) as level -from group_id as g -left join character_ as c on g.name = c.name -left join bots as b on g.name = b.Name; +CREATE VIEW `vwbotcharactermobs` AS +SELECT _utf8'C' AS mobtype, +c.`id`, +c.`name`, +c.`class`, +c.`level`, +c.`last_login`, +c.`zone_id` +FROM `character_data` AS c +UNION ALL +SELECT _utf8'B' AS mobtype, +b.`BotID` AS id, +b.`Name` AS name, +b.`Class` AS class, +b.`BotLevel` AS level, +0 AS timelaston, +0 AS zoneid +FROM bots AS b; --- FILE: --- source player_tables/botgroups.sql; -DROP TABLE IF EXISTS `botgroupmembers`; -DROP TABLE IF EXISTS `botgroup`; +CREATE VIEW `vwgroups` AS +SELECT g.`groupid` AS groupid, +GetMobType(g.`name`) AS mobtype, +g.`name` AS name, +g.`charid` AS mobid, +IFNULL(c.`level`, b.`BotLevel`) AS level +FROM `group_id` AS g +LEFT JOIN `character_data` AS c ON g.`name` = c.`name` +LEFT JOIN `bots` AS b ON g.`name` = b.`Name`; -CREATE TABLE IF NOT EXISTS `botgroup` ( - `BotGroupId` integer unsigned NOT NULL AUTO_INCREMENT, - `BotGroupLeaderBotId` integer unsigned NOT NULL DEFAULT '0', - `BotGroupName` varchar(64) NOT NULL, - PRIMARY KEY (`BotGroupId`), - KEY FK_botgroup_1 (BotGroupLeaderBotId), - CONSTRAINT FK_botgroup_1 FOREIGN KEY (BotGroupLeaderBotId) REFERENCES bots (BotID) -) ENGINE=InnoDB; +CREATE VIEW `vwbotgroups` AS +SELECT g.`BotGroupId`, +g.`BotGroupName`, +g.`BotGroupLeaderBotId`, +b.`Name` AS BotGroupLeaderName, +b.`BotOwnerCharacterId`, +c.`name` AS BotOwnerCharacterName +FROM `botgroup` AS g +JOIN `bots` AS b ON g.`BotGroupLeaderBotId` = b.`BotID` +JOIN `character_data` AS c ON b.`BotOwnerCharacterID` = c.`id` +ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`; -CREATE TABLE IF NOT EXISTS `botgroupmembers` ( - `BotGroupMemberId` integer unsigned NOT NULL AUTO_INCREMENT, - `BotGroupId` integer unsigned NOT NULL DEFAULT '0', - `BotId` integer unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`BotGroupMemberId`), - KEY FK_botgroupmembers_1 (BotGroupId), - CONSTRAINT FK_botgroupmembers_1 FOREIGN KEY (BotGroupId) REFERENCES botgroup (BotGroupId), - KEY FK_botgroupmembers_2 (BotId), - CONSTRAINT FK_botgroupmembers_2 FOREIGN KEY (BotId) REFERENCES bots (BotID) -) ENGINE=InnoDB; - -DROP VIEW IF EXISTS `vwBotGroups`; -CREATE VIEW `vwBotGroups` AS -select g.BotGroupId, -g.BotGroupName, -g.BotGroupLeaderBotId, -b.Name as BotGroupLeaderName, -b.BotOwnerCharacterId, -c.name as BotOwnerCharacterName -from botgroup as g -join bots as b on g.BotGroupLeaderBotId = b.BotID -join character_ as c on b.BotOwnerCharacterID = c.id -order by b.BotOwnerCharacterId, g.BotGroupName; - --- FILE: --- source player_tables/botstances.sql; -CREATE TABLE botstances ( - BotID int(10) unsigned NOT NULL default '0', - StanceID tinyint unsigned NOT NULL default '0', - PRIMARY KEY (BotID), - CONSTRAINT FK_botstances_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) -); - --- FILE: --- source player_tables/bottimers.sql; -CREATE TABLE bottimers ( -BotID int(10) unsigned NOT NULL default '0', -TimerID int(10) unsigned NOT NULL default '0', -Value int(10) unsigned NOT NULL default '0', -PRIMARY KEY (BotID), -CONSTRAINT FK_bottimers_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) -) +CREATE VIEW `vwguildmembers` AS +SELECT 'C' AS mobtype, +cm.`char_id`, +cm.`guild_id`, +cm.`rank`, +cm.`tribute_enable`, +cm.`total_tribute`, +cm.`last_tribute`, +cm.`banker`, +cm.`public_note`, +cm.`alt` +FROM `guild_members` AS cm +UNION ALL +SELECT 'B' AS mobtype, +bm.`char_id`, +bm.`guild_id`, +bm.`rank`, +bm.`tribute_enable`, +bm.`total_tribute`, +bm.`last_tribute`, +bm.`banker`, +bm.`public_note`, +bm.`alt` +FROM `botguildmembers` AS bm; From 594657f2c738b188a226b8698bbff81f14651a59 Mon Sep 17 00:00:00 2001 From: JJ Date: Wed, 15 Oct 2014 22:08:27 -0400 Subject: [PATCH 347/368] Manual merge for 0c3e51963b5803d66443e71554d3e06a296805b3 in pull request #269. --- common/database.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 91a659d5b..f4a22d823 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2931,22 +2931,26 @@ uint32 Database::GetGroupID(const char* name){ } /* Is this really getting used properly... A half implementation ? Akkadius */ -char* Database::GetGroupLeaderForLogin(const char* name, char* leaderbuf){ - leaderbuf = ""; +char* Database::GetGroupLeaderForLogin(const char* name, char* leaderbuf) { + strcpy(leaderbuf, ""); + uint32 group_id = 0; + std::string query = StringFormat("SELECT `groupid` FROM `group_id` WHERE `name = '%s'", name); auto results = QueryDatabase(query); - auto row = results.begin(); uint32 group_id = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - if (row[0]){ group_id = atoi(row[0]); } - } - if (group_id > 0){ - query = StringFormat("SELECT `leadername` FROM `group_leader` WHERE `gid` = '%u' AND `groupid` = %u LIMIT 1", group_id); - results = QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { - if (row[0]){ strcpy(leaderbuf, row[0]); } - } - } + for (auto row = results.begin(); row != results.end(); ++row) + if (row[0]) + group_id = atoi(row[0]); + + if (group_id == 0) + return leaderbuf; + + query = StringFormat("SELECT `leadername` FROM `group_leader` WHERE `gid` = '%u' AND `groupid` = %u LIMIT 1", group_id); + results = QueryDatabase(query); + + for (auto row = results.begin(); row != results.end(); ++row) + if (row[0]) + strcpy(leaderbuf, row[0]); return leaderbuf; } From fc8cb2b5b724eb5179d19c92fac21b8e7c695b23 Mon Sep 17 00:00:00 2001 From: JJ Date: Wed, 15 Oct 2014 22:12:57 -0400 Subject: [PATCH 348/368] Manual merge of 4e3826228d43321abec7805f65b6d43d0f04ab06 in #269. --- common/database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/database.cpp b/common/database.cpp index f4a22d823..24f171cc9 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -943,7 +943,7 @@ bool Database::CheckDatabaseConversions() { // printppdebug = 1; if (runconvert == 1){ - printf("Running character binary blob to database conversion... \n", number_of_characters); + printf("Running character binary blob to database conversion... \n"); /* Get the number of characters */ rquery = StringFormat("SELECT COUNT(`id`) FROM `character_`"); results = QueryDatabase(rquery); From 6028dbb4e52f8b51ab32075091502578620ae45f Mon Sep 17 00:00:00 2001 From: JJ Date: Wed, 15 Oct 2014 22:31:44 -0400 Subject: [PATCH 349/368] hateborne: grid assign match fix. --- zone/waypoints.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index a36bcec8b..cfefe73f3 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -1084,14 +1084,14 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid) // how much it's allowed to be off by #define _GASSIGN_TOLERANCE 1.0 - if(results.RowCount() == 0) // try a fuzzy match if that didn't find it + if (results.RowCount() == 0) // try a fuzzy match if that didn't find it { query = StringFormat("SELECT id,x,y FROM spawn2 WHERE zone='%s' AND " "ABS( ABS(x) - ABS(%f) ) < %f AND " "ABS( ABS(y) - ABS(%f) ) < %f", zone->GetShortName(), x, _GASSIGN_TOLERANCE, y, _GASSIGN_TOLERANCE); results = QueryDatabase(query); - if(!results.Success()) { + if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error querying fuzzy spawn2 '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); return; } @@ -1100,12 +1100,13 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid) matches = results.RowCount(); } - if (matches == 0) { + if (matches == 0) + { client->Message(0, "ERROR: Unable to assign grid - can't find it in spawn2"); return; } - if(matches == 1) + if(matches > 1) { client->Message(0, "ERROR: Unable to assign grid - multiple spawn2 rows match"); return; @@ -1125,7 +1126,8 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid) return; } - if (results.RowsAffected() != 1) { + if (results.RowsAffected() != 1) + { client->Message(0, "ERROR: found spawn2 id %d but the update query failed", spawn2id); return; } @@ -1133,7 +1135,8 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid) if(client) client->LogSQL(query.c_str()); - if(!fuzzy) { + if (!fuzzy) + { client->Message(0, "Grid assign: spawn2 id = %d updated - exact match", spawn2id); return; } From 0f9cfc06150af1aa7b87c84077e48d05dc54c956 Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 15 Oct 2014 22:15:24 -0700 Subject: [PATCH 350/368] Very new versions of gcc appear to have now cleaned up yet another header leaking out --- luabind/luabind/detail/object_rep.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/luabind/luabind/detail/object_rep.hpp b/luabind/luabind/detail/object_rep.hpp index dafa6532a..93b3e39d6 100644 --- a/luabind/luabind/detail/object_rep.hpp +++ b/luabind/luabind/detail/object_rep.hpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace luabind { namespace detail { From 9c6ab056596e5ea79b0e073f883b2d24d3c47c57 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 16 Oct 2014 01:05:43 -0700 Subject: [PATCH 351/368] Removed PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP ifdef, we dont use it anyway and it was failing on FreeBSD gcc49 --- common/unix.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/common/unix.h b/common/unix.h index c78d05cb2..d4155f946 100644 --- a/common/unix.h +++ b/common/unix.h @@ -18,9 +18,6 @@ #ifndef _WINDOWS #ifndef __UNIX_H__ #define __UNIX_H__ - #ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP - #define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP {0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, __LOCK_INITIALIZER} - #endif #include typedef int SOCKET; From 4cf845e6fff3c748505a48e3673e6997afe2ddb6 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 16 Oct 2014 01:16:38 -0700 Subject: [PATCH 352/368] Workaround for FreeBSD not having std::to_string --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a975ef533..4e2f5f9e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,7 @@ ENDIF(MSVC) IF(UNIX) IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") ADD_DEFINITIONS(-DFREEBSD) + ADD_DEFINITIONS(-D_GLIBCXX_USE_C99) SET(FREEBSD TRUE) ENDIF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") IF(CMAKE_SYSTEM_NAME MATCHES "Darwin") From 7ca23d14c7f1ecd9a8ac53d33d478f9874c50619 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Oct 2014 17:01:06 -0400 Subject: [PATCH 353/368] Remove debug statement --- zone/raids.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index 0adbcdef1..c0879eca6 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -262,7 +262,6 @@ void Raid::SaveRaidLeaderAA() { char *queryBuffer = new char[sizeof(RaidLeadershipAA_Struct) * 2 + 1]; database.DoEscapeString(queryBuffer, (char*)&raid_aa, sizeof(RaidLeadershipAA_Struct)); - _hex(NET__ERROR, queryBuffer, sizeof(RaidLeadershipAA_Struct)); std::string query = "UPDATE raid_leaders SET leadershipaa = '"; query += queryBuffer; From 2267881d52645f329893efe2a2112dc5529b8079 Mon Sep 17 00:00:00 2001 From: Uleat Date: Thu, 16 Oct 2014 19:16:52 -0400 Subject: [PATCH 354/368] Fixed BOTS auto-convert code and load/drop scripts. Added a sql for fixing an existing database that was auto-converted incorrectly (located in ../bots/deprecated) --- changelog.txt | 4 + common/database.cpp | 34 ++++----- .../2014_10_16_Lower_Case_View_Fix.sql | 74 +++++++++++++++++++ .../sql/git/bots/deprecated/load_bots_old.sql | 8 +- utils/sql/git/bots/drop_bots.sql | 8 +- utils/sql/git/bots/load_bots.sql | 8 +- 6 files changed, 107 insertions(+), 29 deletions(-) create mode 100644 utils/sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql diff --git a/changelog.txt b/changelog.txt index ecdc1c62d..c1b68b304 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/16/2014 == +Uleat: Fixed the auto-conversion view naming error and renamed the views in the script files. Added a fix sql for databases that auto-converted. +Fix SQL: ../sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql + == 10/15/2014 == Uleat: Cleaned up load/drop bots sqls, added '../utils/sql/git/bots/deprecated' and '../deprecated/load_bots_old.sql' (use this file on pre-player blob conversion databases.) Notes: I modifed the behavior of both load and drop bots to fail on the first operation if their modifications have been performed already. diff --git a/common/database.cpp b/common/database.cpp index 24f171cc9..f08cf06b4 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1921,7 +1921,7 @@ bool Database::CheckDatabaseConversions() { int runbotsconvert = 0; /* Check For Legacy Bot References */ - rquery = StringFormat("SHOW CREATE VIEW `vwbotcharactermobs`"); + rquery = StringFormat("SHOW CREATE VIEW `vwBotCharacterMobs`"); results = QueryDatabase(rquery); if (results.RowCount() == 1){ auto row = results.begin(); @@ -1948,12 +1948,12 @@ bool Database::CheckDatabaseConversions() { printf("Running bot views/function database conversion... \n"); /* Update view `vwbotcharactermobs` */ - rquery = StringFormat("DROP VIEW `vwbotcharactermobs`;"); + rquery = StringFormat("DROP VIEW `vwBotCharacterMobs`;"); results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Drop View `vwbotcharactermobs`", rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwBotCharacterMobs`", rquery); rquery = StringFormat( - "CREATE VIEW `vwbotcharactermobs` AS\n" + "CREATE VIEW `vwBotCharacterMobs` AS\n" "SELECT _utf8'C' AS mobtype,\n" // Natedog: '_utf8' "c.`id`,\n" "c.`name`,\n" @@ -1973,7 +1973,7 @@ bool Database::CheckDatabaseConversions() { "FROM bots AS b;" ); results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Create View `vwbotcharactermobs`", rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwBotCharacterMobs`", rquery); /* Update function `GetMobType` */ @@ -2002,12 +2002,12 @@ bool Database::CheckDatabaseConversions() { /* Update view `vwgroups` */ - rquery = StringFormat("DROP VIEW IF EXISTS `vwgroups`;"); + rquery = StringFormat("DROP VIEW IF EXISTS `vwGroups`;"); results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Drop View `vwgroups`", rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwGroups`", rquery); rquery = StringFormat( - "CREATE VIEW `vwgroups` AS\n" + "CREATE VIEW `vwGroups` AS\n" "SELECT g.`groupid` AS groupid,\n" "GetMobType(g.`name`) AS mobtype,\n" "g.`name` AS name,\n" @@ -2018,16 +2018,16 @@ bool Database::CheckDatabaseConversions() { "LEFT JOIN `bots` AS b ON g.`name` = b.`Name`;" ); results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Create View `vwgroups`", rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwGroups`", rquery); /* Update view `vwbotgroups` */ - rquery = StringFormat("DROP VIEW IF EXISTS `vwbotgroups`;"); + rquery = StringFormat("DROP VIEW IF EXISTS `vwBotGroups`;"); results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Drop View `vwbotgroups`", rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwBotGroups`", rquery); rquery = StringFormat( - "CREATE VIEW `vwbotgroups` AS\n" + "CREATE VIEW `vwBotGroups` AS\n" "SELECT g.`BotGroupId`,\n" "g.`BotGroupName`,\n" "g.`BotGroupLeaderBotId`,\n" @@ -2040,16 +2040,16 @@ bool Database::CheckDatabaseConversions() { "ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`;" ); results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Create View `vwbotgroups`", rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwBotGroups`", rquery); /* Update view `vwguildmembers` */ - rquery = StringFormat("DROP VIEW IF EXISTS `vwguildmembers`;"); + rquery = StringFormat("DROP VIEW IF EXISTS `vwGuildMembers`;"); results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Drop View `vwguildmembers`", rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwGuildMembers`", rquery); rquery = StringFormat( - "CREATE VIEW `vwguildmembers` AS\n" + "CREATE VIEW `vwGuildMembers` AS\n" "SELECT 'C' AS mobtype,\n" "cm.`char_id`,\n" "cm.`guild_id`,\n" @@ -2075,7 +2075,7 @@ bool Database::CheckDatabaseConversions() { "FROM `botguildmembers` AS bm;" ); results = QueryDatabase(rquery); - ThrowDBError(results.ErrorMessage(), "Create View `vwguildmembers`", rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwGuildMembers`", rquery); } if (runbotsconvert == 1){ diff --git a/utils/sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql b/utils/sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql new file mode 100644 index 000000000..eb53c9ade --- /dev/null +++ b/utils/sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql @@ -0,0 +1,74 @@ +-- A fix for the auto-conversion view renaming issue + + +DROP VIEW IF EXISTS `vwbotcharactermobs`; +DROP VIEW IF EXISTS `vwbotgroups`; +DROP VIEW IF EXISTS `vwgroups`; +DROP VIEW IF EXISTS `vwguildmembers`; + + +CREATE VIEW `vwBotCharacterMobs` AS +SELECT _utf8'C' AS mobtype, +c.`id`, +c.`name`, +c.`class`, +c.`level`, +c.`last_login`, +c.`zone_id` +FROM `character_data` AS c +UNION ALL +SELECT _utf8'B' AS mobtype, +b.`BotID` AS id, +b.`Name` AS name, +b.`Class` AS class, +b.`BotLevel` AS level, +0 AS timelaston, +0 AS zoneid +FROM bots AS b; + +CREATE VIEW `vwGroups` AS +SELECT g.`groupid` AS groupid, +GetMobType(g.`name`) AS mobtype, +g.`name` AS name, +g.`charid` AS mobid, +IFNULL(c.`level`, b.`BotLevel`) AS level +FROM `group_id` AS g +LEFT JOIN `character_data` AS c ON g.`name` = c.`name` +LEFT JOIN `bots` AS b ON g.`name` = b.`Name`; + +CREATE VIEW `vwBotGroups` AS +SELECT g.`BotGroupId`, +g.`BotGroupName`, +g.`BotGroupLeaderBotId`, +b.`Name` AS BotGroupLeaderName, +b.`BotOwnerCharacterId`, +c.`name` AS BotOwnerCharacterName +FROM `botgroup` AS g +JOIN `bots` AS b ON g.`BotGroupLeaderBotId` = b.`BotID` +JOIN `character_data` AS c ON b.`BotOwnerCharacterID` = c.`id` +ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`; + +CREATE VIEW `vwGuildMembers` AS +SELECT 'C' AS mobtype, +cm.`char_id`, +cm.`guild_id`, +cm.`rank`, +cm.`tribute_enable`, +cm.`total_tribute`, +cm.`last_tribute`, +cm.`banker`, +cm.`public_note`, +cm.`alt` +FROM `guild_members` AS cm +UNION ALL +SELECT 'B' AS mobtype, +bm.`char_id`, +bm.`guild_id`, +bm.`rank`, +bm.`tribute_enable`, +bm.`total_tribute`, +bm.`last_tribute`, +bm.`banker`, +bm.`public_note`, +bm.`alt` +FROM `botguildmembers` AS bm; diff --git a/utils/sql/git/bots/deprecated/load_bots_old.sql b/utils/sql/git/bots/deprecated/load_bots_old.sql index 9f57191ab..44d1c1b90 100644 --- a/utils/sql/git/bots/deprecated/load_bots_old.sql +++ b/utils/sql/git/bots/deprecated/load_bots_old.sql @@ -213,7 +213,7 @@ END// DELIMITER ; -CREATE VIEW `vwbotcharactermobs` AS +CREATE VIEW `vwBotCharacterMobs` AS SELECT _utf8'C' AS mobtype, c.`id`, c.`name`, @@ -232,7 +232,7 @@ b.`BotLevel` AS level, 0 AS zoneid FROM bots AS b; -CREATE VIEW `vwgroups` AS +CREATE VIEW `vwGroups` AS SELECT g.`groupid` AS groupid, GetMobType(g.`name`) AS mobtype, g.`name` AS name, @@ -242,7 +242,7 @@ FROM `group_id` AS g LEFT JOIN `character_` AS c ON g.`name` = c.`name` LEFT JOIN `bots` AS b ON g.`name` = b.`Name`; -CREATE VIEW `vwbotgroups` AS +CREATE VIEW `vwBotGroups` AS SELECT g.`BotGroupId`, g.`BotGroupName`, g.`BotGroupLeaderBotId`, @@ -254,7 +254,7 @@ JOIN `bots` AS b ON g.`BotGroupLeaderBotId` = b.`BotID` JOIN `character_` AS c ON b.`BotOwnerCharacterID` = c.`id` ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`; -CREATE VIEW `vwguildmembers` AS +CREATE VIEW `vwGuildMembers` AS SELECT 'C' AS mobtype, cm.`char_id`, cm.`guild_id`, diff --git a/utils/sql/git/bots/drop_bots.sql b/utils/sql/git/bots/drop_bots.sql index 63ecc7b91..326194ad6 100644 --- a/utils/sql/git/bots/drop_bots.sql +++ b/utils/sql/git/bots/drop_bots.sql @@ -15,10 +15,10 @@ UPDATE `spawn2` SET `enabled` = 0 WHERE `id` IN (59297,59298); DELETE FROM `commands` WHERE `command` = 'bot'; DELETE FROM `rule_values` WHERE `rule_name` LIKE 'Bots%'; -DROP VIEW IF EXISTS `vwbotcharactermobs`; -DROP VIEW IF EXISTS `vwbotgroups`; -DROP VIEW IF EXISTS `vwgroups`; -DROP VIEW IF EXISTS `vwguildmembers`; +DROP VIEW IF EXISTS `vwBotCharacterMobs`; +DROP VIEW IF EXISTS `vwBotGroups`; +DROP VIEW IF EXISTS `vwGroups`; +DROP VIEW IF EXISTS `vwGuildMembers`; DROP FUNCTION IF EXISTS `GetMobType`; diff --git a/utils/sql/git/bots/load_bots.sql b/utils/sql/git/bots/load_bots.sql index 7fe35041e..9957b882a 100644 --- a/utils/sql/git/bots/load_bots.sql +++ b/utils/sql/git/bots/load_bots.sql @@ -213,7 +213,7 @@ END\\ DELIMITER ; -CREATE VIEW `vwbotcharactermobs` AS +CREATE VIEW `vwBotCharacterMobs` AS SELECT _utf8'C' AS mobtype, c.`id`, c.`name`, @@ -232,7 +232,7 @@ b.`BotLevel` AS level, 0 AS zoneid FROM bots AS b; -CREATE VIEW `vwgroups` AS +CREATE VIEW `vwGroups` AS SELECT g.`groupid` AS groupid, GetMobType(g.`name`) AS mobtype, g.`name` AS name, @@ -242,7 +242,7 @@ FROM `group_id` AS g LEFT JOIN `character_data` AS c ON g.`name` = c.`name` LEFT JOIN `bots` AS b ON g.`name` = b.`Name`; -CREATE VIEW `vwbotgroups` AS +CREATE VIEW `vwBotGroups` AS SELECT g.`BotGroupId`, g.`BotGroupName`, g.`BotGroupLeaderBotId`, @@ -254,7 +254,7 @@ JOIN `bots` AS b ON g.`BotGroupLeaderBotId` = b.`BotID` JOIN `character_data` AS c ON b.`BotOwnerCharacterID` = c.`id` ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`; -CREATE VIEW `vwguildmembers` AS +CREATE VIEW `vwGuildMembers` AS SELECT 'C' AS mobtype, cm.`char_id`, cm.`guild_id`, From 9c725332645d8f4b832abf7c4715cbeae1073cd1 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Oct 2014 19:20:53 -0400 Subject: [PATCH 355/368] Fix issue with raidMakeLeader packets --- common/patches/rof.cpp | 2 +- common/patches/sod.cpp | 2 +- common/patches/sof.cpp | 2 +- common/patches/underfoot.cpp | 2 +- zone/raids.cpp | 12 ++++++------ 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 96ebcaf63..1fcb74078 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -2613,7 +2613,7 @@ namespace RoF strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); dest->FastQueuePacket(&outapp); } - else if (raid_gen->action == 14) + else if (raid_gen->action == 14 || raid_gen->action == 30) { RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 134be8008..8a023ce42 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1668,7 +1668,7 @@ namespace SoD strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); dest->FastQueuePacket(&outapp); } - else if (raid_gen->action == 14) + else if (raid_gen->action == 14 || raid_gen->action == 30) { RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 2b4640f29..2075fd142 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1326,7 +1326,7 @@ namespace SoF strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); dest->FastQueuePacket(&outapp); } - else if (raid_gen->action == 14) + else if (raid_gen->action == 14 || raid_gen->action == 30) { RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index dd31d749d..aa342a412 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -1940,7 +1940,7 @@ namespace Underfoot strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); dest->FastQueuePacket(&outapp); } - else if (raid_gen->action == 14) + else if (raid_gen->action == 14 || raid_gen->action == 30) { RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); diff --git a/zone/raids.cpp b/zone/raids.cpp index c0879eca6..e456d0beb 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -1094,12 +1094,12 @@ void Raid::QueuePacket(const EQApplicationPacket *app, bool ack_req) void Raid::SendMakeLeaderPacket(const char *who) //30 { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct)); + RaidLeadershipUpdate_Struct *rg = (RaidLeadershipUpdate_Struct*)outapp->pBuffer; rg->action = raidMakeLeader; strn0cpy(rg->leader_name, who, 64); strn0cpy(rg->player_name, who, 64); - rg->parameter = 0; + memcpy(&rg->raid, &raid_aa, sizeof(RaidLeadershipAA_Struct)); QueuePacket(outapp); safe_delete(outapp); } @@ -1109,12 +1109,12 @@ void Raid::SendMakeLeaderPacketTo(const char *who, Client *to) if(!to) return; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct)); + RaidLeadershipUpdate_Struct *rg = (RaidLeadershipUpdate_Struct*)outapp->pBuffer; rg->action = raidMakeLeader; strn0cpy(rg->leader_name, who, 64); strn0cpy(rg->player_name, who, 64); - rg->parameter = 0; + memcpy(&rg->raid, &raid_aa, sizeof(RaidLeadershipAA_Struct)); to->QueuePacket(outapp); safe_delete(outapp); } From b10062452acac1dbf2abdf4e18ab53834cae4967 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 17 Oct 2014 01:06:27 -0400 Subject: [PATCH 356/368] Fix issue with moving a group leader in a raid Moving a group leader to the ungrouped section was not correctly finding an appropriate new group leader. --- zone/client_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index fdc5e5021..f1d534d71 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11137,7 +11137,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) r->SetGroupLeader(ri->leader_name, false); for (int x = 0; x < MAX_RAID_MEMBERS; x++) { - if (strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, ri->leader_name) != 0) + if (r->members[x].GroupNumber == oldgrp && strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, ri->leader_name) != 0) { r->SetGroupLeader(r->members[x].membername); Client *cgl = entity_list.GetClientByName(r->members[x].membername); From 6d95fc2547561d25f909c63504364529e820fd17 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 17 Oct 2014 15:05:47 -0400 Subject: [PATCH 357/368] Handle Leadership with leader change in RaidCommandDisband --- zone/client_packet.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f1d534d71..a7f839d65 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11010,6 +11010,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) if (r->members[x].GroupNumber == grp){ r->SetGroupLeader(ri->leader_name, false); r->SetGroupLeader(r->members[x].membername); + r->UpdateGroupAAs(grp); break; } } @@ -11021,6 +11022,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) if (strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, r->members[i].membername) != 0) { r->SetRaidLeader(r->members[i].membername, r->members[x].membername); + r->UpdateRaidAAs(); + r->SendAllRaidLeadershipAA(); break; } } From 2db5dc2ed708eee6db5ab5cbcd00e5c3ce84923d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 17 Oct 2014 15:10:43 -0400 Subject: [PATCH 358/368] ndle Leadership with leader change in RaidCommandMoveGroup --- zone/client_packet.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a7f839d65..2d7df70ce 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11077,6 +11077,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) if (strcmp(ri->leader_name, r->members[x].membername) != 0 && strlen(ri->leader_name) > 0) { r->SetGroupLeader(r->members[x].membername); + r->UpdateGroupAAs(oldgrp); Client *cgl = entity_list.GetClientByName(r->members[x].membername); if (cgl){ r->SendRaidRemove(r->members[x].membername, cgl); @@ -11104,8 +11105,10 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } } } - if (grpcount == 0) + if (grpcount == 0) { r->SetGroupLeader(ri->leader_name); + r->UpdateGroupAAs(ri->parameter); + } r->MoveMember(ri->leader_name, ri->parameter); if (c){ @@ -11143,6 +11146,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) if (r->members[x].GroupNumber == oldgrp && strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, ri->leader_name) != 0) { r->SetGroupLeader(r->members[x].membername); + r->UpdateGroupAAs(oldgrp); Client *cgl = entity_list.GetClientByName(r->members[x].membername); if (cgl){ r->SendRaidRemove(r->members[x].membername, cgl); From 223d06645d111955164f77cdd6d34f0671a0040e Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 17 Oct 2014 21:04:29 -0400 Subject: [PATCH 359/368] Added clarification to 'drop_bots.sql' notes for cases of failed script --- utils/sql/git/bots/drop_bots.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utils/sql/git/bots/drop_bots.sql b/utils/sql/git/bots/drop_bots.sql index 326194ad6..df8ca4fa8 100644 --- a/utils/sql/git/bots/drop_bots.sql +++ b/utils/sql/git/bots/drop_bots.sql @@ -5,6 +5,10 @@ -- There may still be remnants of bot activity in tables `guild_members` and -- `group_id`. If these entries are causing issues, you may need to manually -- remove them. +-- +-- If this script fails due to a 'SQL Error (1068): Multiple primary key defined' +-- error, run this query: ALTER TABLE `guild_members` DROP PRIMARY KEY; +-- and it should remove the key so this script will process in its entirety. ALTER TABLE `guild_members` ADD PRIMARY KEY (`char_id`); From e8eb774458780019f59df34aa2ae8076c797d9d0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 18 Oct 2014 00:17:46 -0400 Subject: [PATCH 360/368] Implement group mentor, leadership exp sharing (SoF+ only) Currently only works in normal groups Some decisions: the EXP will be rounded in the favor of the group leader No idea how live actually handles it. --- changelog.txt | 3 ++ common/database.cpp | 23 ++++++++--- common/database.h | 2 +- common/emu_oplist.h | 1 + common/eq_packet_structs.h | 6 +++ utils/patches/patch_RoF.conf | 1 + utils/patches/patch_SoD.conf | 1 + utils/patches/patch_SoF.conf | 1 + utils/patches/patch_Underfoot.conf | 1 + .../git/required/2014_10_18_group_mentor.sql | 2 + zone/client_packet.cpp | 32 +++++++++++++-- zone/client_packet.h | 1 + zone/exp.cpp | 17 ++++++-- zone/groups.cpp | 40 +++++++++++++++++++ zone/groups.h | 8 ++++ zone/worldserver.cpp | 5 ++- zone/zonedb.cpp | 2 +- 17 files changed, 131 insertions(+), 15 deletions(-) create mode 100644 utils/sql/git/required/2014_10_18_group_mentor.sql diff --git a/changelog.txt b/changelog.txt index c1b68b304..df7f664f4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/18/2014== +demonstar55: Implement group mentor, sharing leadership exp (SoF+ only) + == 10/16/2014 == Uleat: Fixed the auto-conversion view naming error and renamed the views in the script files. Added a fix sql for databases that auto-converted. Fix SQL: ../sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql diff --git a/common/database.cpp b/common/database.cpp index f08cf06b4..f36ab1713 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2963,7 +2963,7 @@ void Database::SetGroupLeaderName(uint32 gid, const char* name) { return; } - query = StringFormat("INSERT INTO group_leaders(gid, leadername, marknpc, leadershipaa, maintank, assist, puller) VALUES(%u, '%s', '', '', '', '', '')", + query = StringFormat("INSERT INTO group_leaders(gid, leadername, marknpc, leadershipaa, maintank, assist, puller, mentoree, mentor_percent) VALUES(%u, '%s', '', '', '', '', '', '', '0')", gid, EscapeString(name).c_str()); result = QueryDatabase(query); @@ -2972,8 +2972,9 @@ void Database::SetGroupLeaderName(uint32 gid, const char* name) { } } -char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, GroupLeadershipAA_Struct* GLAA){ - std::string query = StringFormat("SELECT `leadername`, `maintank`, `assist`, `puller`, `marknpc`, `leadershipaa` FROM `group_leaders` WHERE `gid` = %lu",(unsigned long)gid); +char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, char *mentoree, int *mentor_percent, GroupLeadershipAA_Struct* GLAA) +{ + std::string query = StringFormat("SELECT `leadername`, `maintank`, `assist`, `puller`, `marknpc`, `mentoree`, `mentor_percent`, `leadershipaa` FROM `group_leaders` WHERE `gid` = %lu",(unsigned long)gid); auto results = QueryDatabase(query); if (!results.Success() || results.RowCount() == 0) { @@ -2992,6 +2993,12 @@ char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* mainta if(marknpc) marknpc[0] = '\0'; + if (mentoree) + mentoree[0] = '\0'; + + if (mentor_percent) + *mentor_percent = 0; + return leaderbuf; } @@ -3012,8 +3019,14 @@ char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* mainta if(marknpc) strcpy(marknpc, row[4]); - if(GLAA && results.LengthOfColumn(5) == sizeof(GroupLeadershipAA_Struct)) - memcpy(GLAA, row[5], sizeof(GroupLeadershipAA_Struct)); + if (mentoree) + strcpy(mentoree, row[5]); + + if (mentor_percent) + *mentor_percent = atoi(row[6]); + + if(GLAA && results.LengthOfColumn(7) == sizeof(GroupLeadershipAA_Struct)) + memcpy(GLAA, row[7], sizeof(GroupLeadershipAA_Struct)); return leaderbuf; } diff --git a/common/database.h b/common/database.h index 6fa9ef60a..71a6de9fb 100644 --- a/common/database.h +++ b/common/database.h @@ -203,7 +203,7 @@ public: void SetGroupLeaderName(uint32 gid, const char* name); char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, - GroupLeadershipAA_Struct* GLAA = nullptr); + char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr); void ClearGroupLeader(uint32 gid = 0); diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 6ddd59123..c0ca0ca4e 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -207,6 +207,7 @@ N(OP_GroupInvite2), N(OP_GroupLeaderChange), N(OP_GroupLeadershipAAUpdate), N(OP_GroupMakeLeader), +N(OP_GroupMentor), N(OP_GroupRoles), N(OP_GroupUpdate), N(OP_GroupUpdateB), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index a78b2afa1..d0792fd37 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -2231,6 +2231,12 @@ struct GroupLeaderChange_Struct /*128*/ char Unknown128[20]; }; +struct GroupMentor_Struct { +/*000*/ int percent; +/*004*/ char name[64]; +/*068*/ +}; + struct FaceChange_Struct { /*000*/ uint8 haircolor; /*001*/ uint8 beardcolor; diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index e4eb9ba14..6bd486bfa 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -500,6 +500,7 @@ OP_GroupRoles=0x047c OP_GroupMakeLeader=0x4129 OP_DoGroupLeadershipAbility=0x17d7 OP_GroupLeadershipAAUpdate=0x6567 +OP_GroupMentor=0x56DB # LFG/LFP Opcodes OP_LFGCommand=0x4463 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 472d60e7a..0effd1b3c 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -486,6 +486,7 @@ OP_GroupDisbandOther=0x162d OP_GroupLeaderChange=0x7545 OP_GroupRoles=0x6b67 OP_GroupMakeLeader=0x6087 +OP_GroupMentor=0x1224 # LFG/LFP Opcodes OP_LFGCommand=0x3288 # C OP_LFGGetMatchesRequest=0x5613 # C diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index a50c1faf6..0b53daefd 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -452,6 +452,7 @@ OP_GroupDelete=0x0000 # OP_CancelInvite=0x596C #Trevius 03/02/09 OP_GroupFollow2=0x59D4 #Xinu 02/20/09 OP_GroupInvite2=0x07F6 #Xinu 02/20/09 +OP_GroupMentor=0x9EF3 #LFG/LFP Opcodes OP_LFGCommand=0x5D81 #Trevius 01/16/09 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index b7bdbc7e0..01cc1ad47 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -489,6 +489,7 @@ OP_GroupDisbandOther=0x49f6 # C OP_GroupLeaderChange=0x0c33 # C OP_GroupRoles=0x116d # C OP_GroupMakeLeader=0x5851 +OP_GroupMentor=0x292f # LFG/LFP Opcodes OP_LFGCommand=0x2c38 # C diff --git a/utils/sql/git/required/2014_10_18_group_mentor.sql b/utils/sql/git/required/2014_10_18_group_mentor.sql new file mode 100644 index 000000000..bd139f076 --- /dev/null +++ b/utils/sql/git/required/2014_10_18_group_mentor.sql @@ -0,0 +1,2 @@ +ALTER TABLE `group_leaders` ADD `mentoree` VARCHAR(64) NOT NULL; +ALTER TABLE `group_leaders` ADD `mentor_percent` INT(4) DEFAULT 0 NOT NULL; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2d7df70ce..f759c9321 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -234,6 +234,7 @@ void MapOpcodes() ConnectedOpcodes[OP_GroupInvite] = &Client::Handle_OP_GroupInvite; ConnectedOpcodes[OP_GroupInvite2] = &Client::Handle_OP_GroupInvite2; ConnectedOpcodes[OP_GroupMakeLeader] = &Client::Handle_OP_GroupMakeLeader; + ConnectedOpcodes[OP_GroupMentor] = &Client::Handle_OP_GroupMentor; ConnectedOpcodes[OP_GroupRoles] = &Client::Handle_OP_GroupRoles; ConnectedOpcodes[OP_GroupUpdate] = &Client::Handle_OP_GroupUpdate; ConnectedOpcodes[OP_GuildBank] = &Client::Handle_OP_GuildBank; @@ -1635,9 +1636,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) } } //else, somebody from our group is already here... - if (group) - group->UpdatePlayer(this); - else + if (!group) database.SetGroupID(GetName(), 0, CharacterID()); //cannot re-establish group, kill it } @@ -1656,9 +1655,11 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) char AssistName[64]; char PullerName[64]; char NPCMarkerName[64]; + char mentoree_name[64]; + int mentor_percent; GroupLeadershipAA_Struct GLAA; memset(ln, 0, 64); - strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, &GLAA)); + strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, mentoree_name, &mentor_percent, &GLAA)); Client *c = entity_list.GetClientByName(ln); if (c) group->SetLeader(c); @@ -1668,6 +1669,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) group->SetPuller(PullerName); group->SetNPCMarker(NPCMarkerName); group->SetGroupAAs(&GLAA); + group->SetGroupMentor(mentor_percent, mentoree_name); //group->NotifyMainTank(this, 1); //group->NotifyMainAssist(this, 1); @@ -1679,6 +1681,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) group->SendLeadershipAAUpdate(); } + group->UpdatePlayer(this); LFG = false; } @@ -6858,6 +6861,27 @@ void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app) } } +void Client::Handle_OP_GroupMentor(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupMentor_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupMentor, size=%i, expected %i", app->size, sizeof(GroupMentor_Struct)); + DumpPacket(app); + return; + } + GroupMentor_Struct *gms = (GroupMentor_Struct *)app->pBuffer; + Group *group = GetGroup(); + if (!group) + return; + gms->name[63] = '\0'; + + if (strlen(gms->name)) + group->SetGroupMentor(gms->percent, gms->name); + else + group->ClearGroupMentor(); + + return; +} + void Client::Handle_OP_GroupRoles(const EQApplicationPacket *app) { if (app->size != sizeof(GroupRole_Struct)) { diff --git a/zone/client_packet.h b/zone/client_packet.h index 7cf7ce06b..0868b93ba 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -138,6 +138,7 @@ void Handle_OP_GroupInvite(const EQApplicationPacket *app); void Handle_OP_GroupInvite2(const EQApplicationPacket *app); void Handle_OP_GroupMakeLeader(const EQApplicationPacket *app); + void Handle_OP_GroupMentor(const EQApplicationPacket *app); void Handle_OP_GroupRoles(const EQApplicationPacket *app); void Handle_OP_GroupUpdate(const EQApplicationPacket *app); void Handle_OP_GuildBank(const EQApplicationPacket *app); diff --git a/zone/exp.cpp b/zone/exp.cpp index a82aa25d9..c5c5cf69d 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -133,7 +133,7 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { } } - if(IsLeadershipEXPOn() && ((conlevel == CON_BLUE) || (conlevel == CON_WHITE) || (conlevel == CON_YELLOW) || (conlevel == CON_RED))) { + if(IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED)) { add_exp = static_cast(static_cast(add_exp) * 0.8f); if(GetGroup()) @@ -141,8 +141,19 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { if((m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())) && (RuleI(Character, KillsPerGroupLeadershipAA) > 0)) { - AddLeadershipEXP(GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA), 0); - Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA); + Client *mentoree = GetGroup()->GetMentoree(); + if (GetGroup()->GetMentorPercent() && mentoree && + mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) { + uint32 mentor_exp = exp * (GetGroup()->GetMentorPercent() / 100.0f); + exp -= mentor_exp; + mentoree->AddLeadershipEXP(mentor_exp, 0); // ends up rounded down + mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + } + if (exp > 0) { // possible if you mentor 100% to the other client + AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it + Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + } } else Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS); diff --git a/zone/groups.cpp b/zone/groups.cpp index f0cdb71de..b9eda9836 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -46,6 +46,7 @@ Group::Group(uint32 gid) : GroupIDConsumer(gid) { leader = nullptr; + mentoree = nullptr; memset(members,0,sizeof(Mob*) * MAX_GROUP_MEMBERS); AssistTargetID = 0; TankTargetID = 0; @@ -81,6 +82,7 @@ Group::Group(Mob* leader) TankTargetID = 0; PullerTargetID = 0; memset(&LeaderAbilities, 0, sizeof(GroupLeadershipAA_Struct)); + mentoree = nullptr; uint32 i; for(i=0;iIsClient() && !mentoree && mentoree_name.length() && !mentoree_name.compare(update->GetName())) + mentoree = update->CastToClient(); + return false; } @@ -500,6 +507,9 @@ void Group::MemberZoned(Mob* removemob) { if(removemob->IsClient() && HasRole(removemob, RolePuller)) SetGroupPullerTarget(0); + + if (removemob->IsClient() && removemob == mentoree) + mentoree = nullptr; } bool Group::DelMemberOOZ(const char *Name) { @@ -528,6 +538,8 @@ bool Group::DelMemberOOZ(const char *Name) { } ClearAllNPCMarks(); } + if (Name == mentoree_name) + ClearGroupMentor(); return true; } } @@ -642,6 +654,9 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender) UnDelegatePuller(oldmember->GetName()); } + if (oldmember->GetName() == mentoree_name) + ClearGroupMentor(); + if(oldmember->IsClient()) SendMarkedNPCsToMember(oldmember->CastToClient(), true); @@ -1736,6 +1751,31 @@ void Group::SetGroupPullerTarget(Mob *m) } } +void Group::SetGroupMentor(int percent, char *name) +{ + mentoree_name = name; + mentor_percent = percent; + Client *client = entity_list.GetClientByName(name); + + mentoree = client ? client : nullptr; + std::string query = StringFormat("UPDATE group_leaders SET mentoree = '%s', mentor_percent = %i WHERE gid = %i LIMIT 1", + mentoree_name.c_str(), mentor_percent, GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set group mentor: %s\n", results.ErrorMessage().c_str()); +} + +void Group::ClearGroupMentor() +{ + mentoree_name.clear(); + mentor_percent = 0; + mentoree = nullptr; + std::string query = StringFormat("UPDATE group_leaders SET mentoree = '', mentor_percent = 0 WHERE gid = %i LIMIT 1", GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear group mentor: %s\n", results.ErrorMessage().c_str()); +} + void Group::NotifyAssistTarget(Client *c) { // Send a packet to the specified client notifying them of the group target selected by the Main Assist. diff --git a/zone/groups.h b/zone/groups.h index fe21ff3a4..250b86946 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -132,6 +132,11 @@ public: const char *GetClientNameByIndex(uint8 index); void UpdateXTargetMarkedNPC(uint32 Number, Mob *m); + void SetGroupMentor(int percent, char *name); + void ClearGroupMentor(); + inline int GetMentorPercent() { return mentor_percent; } + inline Client *GetMentoree() { return mentoree; } + Mob* members[MAX_GROUP_MEMBERS]; char membername[MAX_GROUP_MEMBERS][64]; uint8 MemberRoles[MAX_GROUP_MEMBERS]; @@ -151,6 +156,9 @@ private: uint16 PullerTargetID; uint16 MarkedNPCs[MAX_MARKED_NPCS]; + std::string mentoree_name; + Client *mentoree; + int mentor_percent; }; #endif diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 1ff42cd02..be0a1a193 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1001,9 +1001,11 @@ void WorldServer::Process() { char AssistName[64]; char PullerName[64]; char NPCMarkerName[64]; + char mentoree_name[64]; + int mentor_percent; GroupLeadershipAA_Struct GLAA; memset(ln, 0, 64); - strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, &GLAA)); + strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, mentoree_name, &mentor_percent, &GLAA)); Client *lc = entity_list.GetClientByName(ln); if(lc) group->SetLeader(lc); @@ -1013,6 +1015,7 @@ void WorldServer::Process() { group->SetPuller(PullerName); group->SetNPCMarker(NPCMarkerName); group->SetGroupAAs(&GLAA); + group->SetGroupMentor(mentor_percent, mentoree_name); } } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 1ba36c5fd..e83edf2b9 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2534,7 +2534,7 @@ void ZoneDatabase::RefreshGroupFromDB(Client *client){ gu->action = groupActUpdate; strcpy(gu->yourname, client->GetName()); - GetGroupLeadershipInfo(group->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas); + GetGroupLeadershipInfo(group->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas); gu->NPCMarkerID = group->GetNPCMarkerID(); int index = 0; From 3f056462d6562bb3b76b0ea6618c1e3dfa60ee11 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 18 Oct 2014 13:27:33 -0400 Subject: [PATCH 361/368] Enable gaining of group leadership while in raids Note: raid leaders can only gain raid leadership, so they won't ever gain group leadership while leading a raid, even if they don't end up gaining group due to the restrictions. From what I can tell, this should be in line with live --- changelog.txt | 1 + zone/client.cpp | 20 ++++++++++++++++++-- zone/exp.cpp | 44 ++++++++++++++++++++++++++------------------ 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/changelog.txt b/changelog.txt index df7f664f4..1b3a75404 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 10/18/2014== demonstar55: Implement group mentor, sharing leadership exp (SoF+ only) +demonstar55: Add gaining of group leadership while in raids == 10/16/2014 == Uleat: Fixed the auto-conversion view naming error and renamed the views in the script files. Added a fix sql for databases that auto-converted. diff --git a/zone/client.cpp b/zone/client.cpp index 3b95c8d77..b8527204f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -4256,12 +4256,28 @@ bool Client::IsLeadershipEXPOn() { Group *g = GetGroup(); - if(g && g->IsLeader(this) && (g->GroupCount() > 2)) + if (g && g->IsLeader(this) && g->GroupCount() > 2) return true; Raid *r = GetRaid(); - if(r && r->IsLeader(this) && (r->RaidCount() > 17)) + if (!r) + return false; + + // raid leaders can only gain raid AA XP + if (r->IsLeader(this)) { + if (r->RaidCount() > 17) + return true; + else + return false; + } + + uint32 gid = r->GetGroup(this); + + if (gid > 11) // not in a group + return false; + + if (r->IsGroupLeader(GetName()) && r->GroupCount(gid) > 2) return true; return false; diff --git a/zone/exp.cpp b/zone/exp.cpp index c5c5cf69d..a06fdc017 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -133,14 +133,12 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { } } - if(IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED)) { + if (IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED)) { add_exp = static_cast(static_cast(add_exp) * 0.8f); - if(GetGroup()) - { - if((m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())) - && (RuleI(Character, KillsPerGroupLeadershipAA) > 0)) - { + if (GetGroup()) { + if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel()) + && RuleI(Character, KillsPerGroupLeadershipAA) > 0) { uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA); Client *mentoree = GetGroup()->GetMentoree(); if (GetGroup()->GetMentorPercent() && mentoree && @@ -154,20 +152,30 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); } - } - else + } else { Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS); - } - else - { - if((m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel())) - && (RuleI(Character, KillsPerRaidLeadershipAA) > 0)) - { - AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA)); - Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP); } - else - Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS); + } else { + Raid *raid = GetRaid(); + // Raid leaders CAN NOT gain group AA XP, other group leaders can though! + if (raid->IsLeader(this)) { + if (m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel()) + && RuleI(Character, KillsPerRaidLeadershipAA) > 0) { + AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA)); + Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP); + } else { + Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS); + } + } else { + if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel()) + && RuleI(Character, KillsPerGroupLeadershipAA) > 0) { + // mentoring stuff needs to be added here when raids are have support for it + AddLeadershipEXP(0, GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA)); + Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + } else { + Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS); + } + } } } From 69a124e11f56460a06e633d16c1a83eb0ab64ae4 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 19 Oct 2014 11:39:35 -0400 Subject: [PATCH 362/368] Updated #peekinv to display item links properly in RoF clients --- changelog.txt | 3 + zone/command.cpp | 266 +++++++++++++---------------------------------- 2 files changed, 75 insertions(+), 194 deletions(-) diff --git a/changelog.txt b/changelog.txt index 1b3a75404..1a1c7397a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/19/2014 == +Uleat: Updated command #peekinv to display item links properly in RoF clients + == 10/18/2014== demonstar55: Implement group mentor, sharing leadership exp (SoF+ only) demonstar55: Add gaining of group leadership while in raids diff --git a/zone/command.cpp b/zone/command.cpp index 7a1c7fafa..93c718bce 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2780,6 +2780,21 @@ void command_peekinv(Client *c, const Seperator *sep) return; } + // linkcore_# indicates the number of consecutive 0's..not the actual length + const std::string linkcore_49 = "%c%06X0000000000000000000000000000000000000000000000000%s%c"; // RoF+ + const std::string linkcore_44 = "%c%06X00000000000000000000000000000000000000000000%s%c"; // SoF->UF + const std::string linkcore_39 = "%c%06X000000000000000000000000000000000000000%s%c"; // 6.2->Ti + const char* linkcore = nullptr; + std::string linkbase = ""; + + // consider pushing 'linkcores' to EQLimits + if (c->GetClientVersion() >= EQClientRoF) + linkcore = linkcore_49.c_str(); + else if (c->GetClientVersion() >= EQClientSoF) + linkcore = linkcore_44.c_str(); + else + linkcore = linkcore_39.c_str(); + bool bAll = (strcasecmp(sep->arg[1], "all") == 0); bool bFound = false; Client* client = c->GetTarget()->CastToClient(); @@ -2792,64 +2807,32 @@ void command_peekinv(Client *c, const Seperator *sep) for (int16 i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - // this kind of stuff needs to be pushed to the client translators - c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "WornSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } + if (bAll || (strcasecmp(sep->arg[1], "inv")==0)) { // Personal inventory items bFound = true; for (int16 i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "InvSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer)) { for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { const ItemInst* instbag = client->GetInv().GetItem(i, j); item = (instbag) ? instbag->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:instbag->GetCharges())); - } - else - { - c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:instbag->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(i, j), i, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } @@ -2857,10 +2840,10 @@ void command_peekinv(Client *c, const Seperator *sep) { const ItemInst* inst = client->GetInv().GetItem(MainPowerSource); item = (inst) ? inst->GetItem() : nullptr; - c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", MainPowerSource, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "WornSlot: %i, Item: %i (%s), Charges: %i", + MainPowerSource, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } @@ -2870,59 +2853,30 @@ void command_peekinv(Client *c, const Seperator *sep) // Personal inventory items bFound = true; iter_queue it; - int i=0; + int i = 0; if(client->GetInv().CursorEmpty()) { // Display 'front' cursor slot even if 'empty' (item(30[0]) == null) - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", MainCursor, i, - 0, 0x12, 0, "null", 0x12, 0); - } - else - { - c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", MainCursor, i, - 0, 0x12, 0, "null", 0x12, 0); - } + linkbase = StringFormat(linkcore, 0x12, 0, "null", 0x12); + c->Message((item == 0), "CursorSlot: %i, Item: %i (%s), Charges: %i", + MainCursor, 0, linkbase.c_str(), 0); } else { for(it=client->GetInv().cursor_begin();it!=client->GetInv().cursor_end();++it,i++) { const ItemInst* inst = *it; item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", MainCursor, i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", MainCursor, i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i", + MainPowerSource, i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer) && i==0) { // 'CSD 1' - only display contents of slot 30[0] container..higher ones don't exist for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { const ItemInst* instbag = client->GetInv().GetItem(MainCursor, j); item = (instbag) ? instbag->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(MainCursor, j), - MainCursor, j, ((item == 0) ? 0 : item->ID), 0x12, ((item == 0) ? 0 : item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:instbag->GetCharges())); - } - else - { - c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(MainCursor, j), - MainCursor, j, ((item == 0) ? 0 : item->ID), 0x12, ((item == 0) ? 0 : item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:instbag->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(i, j), i, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } @@ -2935,20 +2889,10 @@ void command_peekinv(Client *c, const Seperator *sep) for (int16 i = EmuConstants::TRIBUTE_BEGIN; i <= EmuConstants::TRIBUTE_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "TributeSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } @@ -2959,128 +2903,62 @@ void command_peekinv(Client *c, const Seperator *sep) for (i = EmuConstants::BANK_BEGIN; i <= EmuConstants::BANK_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "BankSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer)) { for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { const ItemInst* instbag = client->GetInv().GetItem(i, j); item = (instbag) ? instbag->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(i, j), i, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } for (i = EmuConstants::SHARED_BANK_BEGIN; i <= EmuConstants::SHARED_BANK_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "SharedBankSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer)) { for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { const ItemInst* instbag = client->GetInv().GetItem(i, j); item = (instbag) ? instbag->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), " SharedBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(i, j), i, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } } + if (bAll || (strcasecmp(sep->arg[1], "trade")==0)) { // Items in trade window (current trader only, not the other trader) bFound = true; for (int16 i = EmuConstants::TRADE_BEGIN; i <= EmuConstants::TRADE_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "TradeSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer)) { for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { const ItemInst* instbag = client->GetInv().GetItem(i, j); item = (instbag) ? instbag->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(i, j), i, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } From cef2aa64d923b7b25d48cf28a10b9ac8375cf9ed Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 19 Oct 2014 15:03:11 -0400 Subject: [PATCH 363/368] Fixed server<->corpse translators to reflect what actually needs to be translated (should not have affected anything in the old configuration) --- zone/corpse.cpp | 10 +++++----- zone/corpse.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 5f1345541..c72f96372 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -97,7 +97,7 @@ Corpse* Corpse::LoadFromDBData(uint32 in_dbid, uint32 in_charid, char* in_charna for (unsigned int i=0; i < dbpcs->itemcount; i++) { tmp = new ServerLootItem_Struct; memcpy(tmp, &dbpcs->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); - tmp->lootslot = CorpseToServerSlot(tmp->lootslot); // temp hack until corpse blobs are removed + tmp->equipSlot = CorpseToServerSlot(tmp->equipSlot); // temp hack until corpse blobs are removed itemlist.push_back(tmp); } @@ -148,7 +148,7 @@ Corpse* Corpse::LoadFromDBData(uint32 in_dbid, uint32 in_charid, char* in_charna for (unsigned int i=0; i < dbpc->itemcount; i++) { tmp = new ServerLootItem_Struct; memcpy(tmp, &dbpc->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); - tmp->lootslot = CorpseToServerSlot(tmp->lootslot); // temp hack until corpse blobs are removed + tmp->equipSlot = CorpseToServerSlot(tmp->equipSlot); // temp hack until corpse blobs are removed itemlist.push_back(tmp); } @@ -602,7 +602,7 @@ bool Corpse::Save() { end = itemlist.end(); for(; cur != end; ++cur) { ServerLootItem_Struct* item = *cur; - item->lootslot = ServerToCorpseSlot(item->lootslot); // temp hack until corpse blobs are removed + item->equipSlot = ServerToCorpseSlot(item->equipSlot); // temp hack until corpse blobs are removed memcpy((char*) &dbpc->items[x++], (char*) item, sizeof(player_lootitem::ServerLootItem_Struct)); } @@ -2092,7 +2092,7 @@ void Corpse::LoadPlayerCorpseDecayTime(uint32 dbid){ ** ** (Not all slot designations are valid to all clients..see ##_constants.h files for valid slot enumerations) */ -uint16 Corpse::ServerToCorpseSlot(uint16 server_slot) +int16 Corpse::ServerToCorpseSlot(int16 server_slot) { return server_slot; // temporary return @@ -2128,7 +2128,7 @@ uint16 Corpse::ServerToCorpseSlot(uint16 server_slot) */ } -uint16 Corpse::CorpseToServerSlot(uint16 corpse_slot) +int16 Corpse::CorpseToServerSlot(int16 corpse_slot) { return corpse_slot; // temporary return diff --git a/zone/corpse.h b/zone/corpse.h index 773ca3782..2d1eecc98 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -112,8 +112,8 @@ public: inline int GetRezzExp() { return rezzexp; } // these are a temporary work-around until corpse inventory is removed from the database blob - static uint16 ServerToCorpseSlot(uint16 server_slot); // encode - static uint16 CorpseToServerSlot(uint16 corpse_slot); // decode + static int16 ServerToCorpseSlot(int16 server_slot); // encode + static int16 CorpseToServerSlot(int16 corpse_slot); // decode protected: std::list MoveItemToCorpse(Client *client, ItemInst *item, int16 equipslot); From a327c91bacd2add6a5c2e3dda71d62733692366a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 19 Oct 2014 17:20:07 -0400 Subject: [PATCH 364/368] Implement group mentoring while in raids (SoF+ only) Note: This does not allow the raid leader to share raid leader XP and since the raid leader doesn't gain group leadership, they can't share that either. --- common/database.cpp | 22 ++++-- common/database.h | 2 +- .../required/2014_10_19_raid_group_mentor.sql | 2 + zone/client_packet.cpp | 18 ++++- zone/exp.cpp | 17 ++++- zone/raids.cpp | 67 ++++++++++++++++++- zone/raids.h | 14 ++++ 7 files changed, 130 insertions(+), 12 deletions(-) create mode 100644 utils/sql/git/required/2014_10_19_raid_group_mentor.sql diff --git a/common/database.cpp b/common/database.cpp index f36ab1713..599d615a7 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -3191,10 +3191,10 @@ const char* Database::GetRaidLeaderName(uint32 rid) // maintank, assist, puller, marknpc currently unused void Database::GetGroupLeadershipInfo(uint32 gid, uint32 rid, char *maintank, - char *assist, char *puller, char *marknpc, GroupLeadershipAA_Struct *GLAA) + char *assist, char *puller, char *marknpc, char *mentoree, int *mentor_percent, GroupLeadershipAA_Struct *GLAA) { std::string query = StringFormat( - "SELECT maintank, assist, puller, marknpc, leadershipaa FROM raid_leaders WHERE gid = %lu AND rid = %lu", + "SELECT maintank, assist, puller, marknpc, mentoree, mentor_percent, leadershipaa FROM raid_leaders WHERE gid = %lu AND rid = %lu", (unsigned long)gid, (unsigned long)rid); auto results = QueryDatabase(query); @@ -3211,6 +3211,12 @@ void Database::GetGroupLeadershipInfo(uint32 gid, uint32 rid, char *maintank, if (marknpc) marknpc[0] = '\0'; + if (mentoree) + mentoree[0] = '\0'; + + if (mentor_percent) + *mentor_percent = 0; + return; } @@ -3228,8 +3234,14 @@ void Database::GetGroupLeadershipInfo(uint32 gid, uint32 rid, char *maintank, if (marknpc) strcpy(marknpc, row[3]); - if (GLAA && results.LengthOfColumn(4) == sizeof(GroupLeadershipAA_Struct)) - memcpy(GLAA, row[4], sizeof(GroupLeadershipAA_Struct)); + if (mentoree) + strcpy(mentoree, row[4]); + + if (mentor_percent) + *mentor_percent = atoi(row[5]); + + if (GLAA && results.LengthOfColumn(6) == sizeof(GroupLeadershipAA_Struct)) + memcpy(GLAA, row[6], sizeof(GroupLeadershipAA_Struct)); return; } @@ -3288,7 +3300,7 @@ void Database::SetRaidGroupLeaderInfo(uint32 gid, uint32 rid) if (results.RowsAffected() != 0) return; - query = StringFormat("INSERT INTO raid_leaders(gid, rid, marknpc, leadershipaa, maintank, assist, puller) VALUES(%lu, %lu, '', '', '', '', '')", + query = StringFormat("INSERT INTO raid_leaders(gid, rid, marknpc, leadershipaa, maintank, assist, puller, mentoree, mentor_percent) VALUES(%lu, %lu, '', '', '', '', '', '', 0)", (unsigned long)gid, (unsigned long)rid); results = QueryDatabase(query); diff --git a/common/database.h b/common/database.h index 71a6de9fb..9b388c84a 100644 --- a/common/database.h +++ b/common/database.h @@ -215,7 +215,7 @@ public: uint32 GetRaidID(const char* name); const char *GetRaidLeaderName(uint32 rid); void GetGroupLeadershipInfo(uint32 gid, uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, - GroupLeadershipAA_Struct* GLAA = nullptr); + char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr); void GetRaidLeadershipInfo(uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, RaidLeadershipAA_Struct* RLAA = nullptr); void SetRaidGroupLeaderInfo(uint32 gid, uint32 rid); diff --git a/utils/sql/git/required/2014_10_19_raid_group_mentor.sql b/utils/sql/git/required/2014_10_19_raid_group_mentor.sql new file mode 100644 index 000000000..4cb8d5613 --- /dev/null +++ b/utils/sql/git/required/2014_10_19_raid_group_mentor.sql @@ -0,0 +1,2 @@ +ALTER TABLE `raid_leaders` ADD `mentoree` VARCHAR(64) NOT NULL; +ALTER TABLE `raid_leaders` ADD `mentor_percent` INT(4) DEFAULT 0 NOT NULL; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f759c9321..0ec740507 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -576,6 +576,7 @@ void Client::CompleteConnect() if (grpID < 12){ raid->SendRaidGroupRemove(GetName(), grpID); raid->SendRaidGroupAdd(GetName(), grpID); + raid->CheckGroupMentor(grpID, this); if (raid->IsGroupLeader(GetName())) { // group leader same thing! raid->UpdateGroupAAs(raid->GetGroup(this)); raid->GroupUpdate(grpID, false); @@ -6869,10 +6870,25 @@ void Client::Handle_OP_GroupMentor(const EQApplicationPacket *app) return; } GroupMentor_Struct *gms = (GroupMentor_Struct *)app->pBuffer; + gms->name[63] = '\0'; + + if (IsRaidGrouped()) { + Raid *raid = GetRaid(); + if (!raid) + return; + uint32 group_id = raid->GetGroup(this); + if (group_id > 11) + return; + if (strlen(gms->name)) + raid->SetGroupMentor(group_id, gms->percent, gms->name); + else + raid->ClearGroupMentor(group_id); + return; + } + Group *group = GetGroup(); if (!group) return; - gms->name[63] = '\0'; if (strlen(gms->name)) group->SetGroupMentor(gms->percent, gms->name); diff --git a/zone/exp.cpp b/zone/exp.cpp index a06fdc017..0e01a4c0e 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -169,9 +169,20 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { } else { if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel()) && RuleI(Character, KillsPerGroupLeadershipAA) > 0) { - // mentoring stuff needs to be added here when raids are have support for it - AddLeadershipEXP(0, GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA)); - Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + uint32 group_id = raid->GetGroup(this); + uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA); + Client *mentoree = raid->GetMentoree(group_id); + if (raid->GetMentorPercent(group_id) && mentoree && + mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) { + uint32 mentor_exp = exp * (raid->GetMentorPercent(group_id) / 100.0f); + exp -= mentor_exp; + mentoree->AddLeadershipEXP(mentor_exp, 0); + mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + } + if (exp > 0) { + AddLeadershipEXP(exp, 0); + Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + } } else { Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS); } diff --git a/zone/raids.cpp b/zone/raids.cpp index e456d0beb..699bc6b1d 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -31,6 +31,10 @@ Raid::Raid(uint32 raidID) memset(members ,0, (sizeof(RaidMember)*MAX_RAID_MEMBERS)); memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct)); memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS); + for (int i = 0; i < MAX_RAID_GROUPS; i++) { + group_mentor[i].mentor_percent = 0; + group_mentor[i].mentoree = nullptr; + } leader = nullptr; memset(leadername, 0, 64); locked = false; @@ -43,6 +47,10 @@ Raid::Raid(Client* nLeader) memset(members ,0, (sizeof(RaidMember)*MAX_RAID_MEMBERS)); memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct)); memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS); + for (int i = 0; i < MAX_RAID_GROUPS; i++) { + group_mentor[i].mentor_percent = 0; + group_mentor[i].mentoree = nullptr; + } leader = nLeader; memset(leadername, 0, 64); strn0cpy(leadername, nLeader->GetName(), 64); @@ -1482,13 +1490,19 @@ void Raid::MemberZoned(Client *c) if(!c) return; + // Raid::GetGroup() goes over the members as well, this way we go over once + uint32 gid = RAID_GROUPLESS; for(int x = 0; x < MAX_RAID_MEMBERS; x++) { if(members[x].member == c) { members[x].member = nullptr; + gid = members[x].GroupNumber; } } + + if (gid < 12 && group_mentor[gid].mentoree == c) + group_mentor[gid].mentoree = nullptr; } void Raid::SendHPPacketsTo(Client *c) @@ -1597,7 +1611,56 @@ void Raid::LoadLeadership() { database.GetRaidLeadershipInfo(GetID(), nullptr, nullptr, nullptr, nullptr, &raid_aa); - for (uint32 group_id = 0; group_id < MAX_RAID_GROUPS; group_id++) - database.GetGroupLeadershipInfo(group_id, GetID(), nullptr, nullptr, nullptr, nullptr, &group_aa[group_id]); + char mentor_name[64]; + for (uint32 group_id = 0; group_id < MAX_RAID_GROUPS; group_id++) { + database.GetGroupLeadershipInfo(group_id, GetID(), nullptr, nullptr, nullptr, nullptr, + mentor_name, &group_mentor[group_id].mentor_percent, &group_aa[group_id]); + if (strlen(mentor_name)) { + group_mentor[group_id].name = mentor_name; + mentor_name[0] = '\0'; + } + } +} + +void Raid::SetGroupMentor(uint32 group_id, int percent, char *name) +{ + if (group_id > 11) + return; + group_mentor[group_id].name = name; + group_mentor[group_id].mentor_percent = percent; + Client *client = entity_list.GetClientByName(name); + group_mentor[group_id].mentoree = client ? client : nullptr; + + std::string query = StringFormat("UPDATE raid_leaders SET mentoree = '%s', mentor_percent = %i WHERE gid = %i AND rid = %i LIMIT 1", + name, percent, group_id, GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set raid group mentor: %s\n", results.ErrorMessage().c_str()); +} + +void Raid::ClearGroupMentor(uint32 group_id) +{ + if (group_id > 11) + return; + group_mentor[group_id].name.clear(); + group_mentor[group_id].mentor_percent = 0; + group_mentor[group_id].mentoree = nullptr; + + std::string query = StringFormat("UPDATE raid_leaders SET mentoree = '', mentor_percent = 0 WHERE gid = %i AND rid = %i LIMIT 1", + group_id, GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear raid group mentor: %s\n", results.ErrorMessage().c_str()); +} + +// there isn't a nice place to add this in another function, unlike groups +// so we do it here instead +void Raid::CheckGroupMentor(uint32 group_id, Client *c) +{ + if (!c || group_id > 11) + return; + + if (group_mentor[group_id].name == c->GetName()) + group_mentor[group_id].mentoree = c; } diff --git a/zone/raids.h b/zone/raids.h index 6e2579367..eb963ad32 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -92,6 +92,12 @@ struct RaidMember{ bool IsLooter; }; +struct GroupMentor { + std::string name; + Client *mentoree; + int mentor_percent; +}; + class Raid : public GroupIDConsumer { public: Raid(Client *nLeader); @@ -221,6 +227,12 @@ public: inline void SetRaidAAs(RaidLeadershipAA_Struct *rlaa) { memcpy(&raid_aa, rlaa, sizeof(RaidLeadershipAA_Struct)); } + void SetGroupMentor(uint32 group_id, int percent, char *name); + void ClearGroupMentor(uint32 group_id); + void CheckGroupMentor(uint32 group_id, Client *c); // this just checks if we should be fixing the pointer in group mentor struct on zone + inline int GetMentorPercent(uint32 group_id) { return group_mentor[group_id].mentor_percent; } + inline Client *GetMentoree(uint32 group_id) { return group_mentor[group_id].mentoree; } + RaidMember members[MAX_RAID_MEMBERS]; char leadername[64]; protected: @@ -233,6 +245,8 @@ protected: std::string motd; RaidLeadershipAA_Struct raid_aa; GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS]; + + GroupMentor group_mentor[MAX_RAID_GROUPS]; }; From 466eecacc4b97474a21197927ca96715d986bea7 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 19 Oct 2014 18:34:43 -0400 Subject: [PATCH 365/368] Implement Group Inspect Buffs for raids --- changelog.txt | 2 ++ zone/client_packet.cpp | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/changelog.txt b/changelog.txt index 1a1c7397a..b6f5f2ad6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 10/19/2014 == Uleat: Updated command #peekinv to display item links properly in RoF clients +demonstar55: Group Mentoring in raids +demonstar55: Inspect Buffs (text only version) works in raid groups == 10/18/2014== demonstar55: Implement group mentor, sharing leadership exp (SoF+ only) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0ec740507..6d9291685 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5338,6 +5338,17 @@ void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) if (!Target || !Target->IsClient()) return; + if (IsRaidGrouped()) { + Raid *raid = GetRaid(); + if (!raid) + return; + uint32 group_id = raid->GetGroup(this); + if (group_id > 11 || raid->GroupCount(group_id) < 3) + return; + Target->CastToClient()->InspectBuffs(this, raid->GetLeadershipAA(groupAAInspectBuffs, group_id)); + return; + } + Group *g = GetGroup(); if (!g || (g->GroupCount() < 3)) From a6ae2ca635508fd40af053d9f957a89fcbec3ec0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 19 Oct 2014 22:30:11 -0400 Subject: [PATCH 366/368] Switch Inspect Buffs to use the OP code Client 6.2 is SOL until someone find the op for that client --- changelog.txt | 1 + common/emu_oplist.h | 1 + common/eq_packet_structs.h | 5 ++++ common/patches/rof.cpp | 14 +++++++++++ common/patches/rof_ops.h | 1 + common/patches/rof_structs.h | 5 ++++ common/patches/underfoot.cpp | 14 +++++++++++ common/patches/underfoot_ops.h | 1 + common/patches/underfoot_structs.h | 8 +++++++ utils/patches/patch_RoF.conf | 1 + utils/patches/patch_SoD.conf | 1 + utils/patches/patch_SoF.conf | 1 + utils/patches/patch_Titanium.conf | 2 ++ utils/patches/patch_Underfoot.conf | 1 + zone/aa.cpp | 37 ++++++++++++++---------------- 15 files changed, 73 insertions(+), 20 deletions(-) diff --git a/changelog.txt b/changelog.txt index b6f5f2ad6..ed10c7173 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) Uleat: Updated command #peekinv to display item links properly in RoF clients demonstar55: Group Mentoring in raids demonstar55: Inspect Buffs (text only version) works in raid groups +demonstar55: Make use of the Inspect Buffs op/packet. 62 SOL until someone finds its op == 10/18/2014== demonstar55: Implement group mentor, sharing leadership exp (SoF+ only) diff --git a/common/emu_oplist.h b/common/emu_oplist.h index c0ca0ca4e..e8ce3b986 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -245,6 +245,7 @@ N(OP_IncreaseStats), N(OP_InitialHPUpdate), N(OP_InitialMobHealth), N(OP_InspectAnswer), +N(OP_InspectBuffs), N(OP_InspectMessageUpdate), N(OP_InspectRequest), N(OP_InstillDoubt), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index d0792fd37..a5e22e44e 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3968,6 +3968,11 @@ struct MarkNPC_Struct /*08**/ char Name[64]; }; +struct InspectBuffs_Struct { +/*000*/ uint32 spell_id[BUFF_COUNT]; +/*100*/ uint32 tics_remaining[BUFF_COUNT]; +}; + struct RaidGeneral_Struct { /*00*/ uint32 action; //=10 /*04*/ char player_name[64]; //should both be the player's name diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 1fcb74078..3a03b3a62 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -1309,6 +1309,20 @@ namespace RoF FINISH_ENCODE(); }*/ + ENCODE(OP_InspectBuffs) + { + ENCODE_LENGTH_EXACT(InspectBuffs_Struct); + SETUP_DIRECT_ENCODE(InspectBuffs_Struct, structs::InspectBuffs_Struct); + + // we go over the internal 25 instead of the packet's since no entry is 0, which it will be already + for (int i = 0; i < BUFF_COUNT; i++) { + OUT(spell_id[i]); + OUT(tics_remaining[i]); + } + + FINISH_ENCODE(); + } + ENCODE(OP_InspectRequest) { ENCODE_LENGTH_EXACT(Inspect_Struct); diff --git a/common/patches/rof_ops.h b/common/patches/rof_ops.h index 030c331d6..57ff1f91d 100644 --- a/common/patches/rof_ops.h +++ b/common/patches/rof_ops.h @@ -44,6 +44,7 @@ E(OP_GuildMemberUpdate) E(OP_GuildsList) E(OP_HPUpdate) E(OP_Illusion) +E(OP_InspectBuffs) E(OP_InspectRequest) E(OP_InterruptCast) E(OP_ItemLinkResponse) diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index e65f2b74d..1393b026e 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -2473,6 +2473,11 @@ struct GroupFollow_Struct { // Live Follow Struct /*0152*/ }; +struct InspectBuffs_Struct { +/*000*/ uint32 spell_id[BUFF_COUNT]; +/*168*/ uint32 tics_remaining[BUFF_COUNT]; +}; + struct LFG_Struct { /*000*/ uint32 unknown000; /*004*/ uint32 value; // 0x00 = off 0x01 = on diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index aa342a412..ad850be36 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -1125,6 +1125,20 @@ namespace Underfoot FINISH_ENCODE(); } + ENCODE(OP_InspectBuffs) + { + ENCODE_LENGTH_EXACT(InspectBuffs_Struct); + SETUP_DIRECT_ENCODE(InspectBuffs_Struct, structs::InspectBuffs_Struct); + + // we go over the internal 25 instead of the packet's since no entry is 0, which it will be already + for (int i = 0; i < BUFF_COUNT; i++) { + OUT(spell_id[i]); + OUT(tics_remaining[i]); + } + + FINISH_ENCODE(); + } + ENCODE(OP_InspectRequest) { ENCODE_LENGTH_EXACT(Inspect_Struct); diff --git a/common/patches/underfoot_ops.h b/common/patches/underfoot_ops.h index 28864301d..38cfb79e3 100644 --- a/common/patches/underfoot_ops.h +++ b/common/patches/underfoot_ops.h @@ -34,6 +34,7 @@ E(OP_GroupUpdate) E(OP_GuildMemberList) E(OP_GuildsList) E(OP_Illusion) +E(OP_InspectBuffs) E(OP_InspectRequest) E(OP_ItemLinkResponse) E(OP_ItemPacket) diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index 2b7163bcf..eec3270f5 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -2166,6 +2166,14 @@ struct GroupFollow_Struct { // Underfoot Follow Struct /*0152*/ }; +struct InspectBuffs_Struct { +/*000*/ uint32 spell_id[BUFF_COUNT]; +/*100*/ uint32 filler100[5]; // BUFF_COUNT is really 30... +/*120*/ uint32 tics_remaining[BUFF_COUNT]; +/*220*/ uint32 filler220[5]; // BUFF_COUNT is really 30... +}; + + struct LFG_Struct { /*000*/ uint32 unknown000; /*004*/ uint32 value; // 0x00 = off 0x01 = on diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 6bd486bfa..97098946d 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -501,6 +501,7 @@ OP_GroupMakeLeader=0x4129 OP_DoGroupLeadershipAbility=0x17d7 OP_GroupLeadershipAAUpdate=0x6567 OP_GroupMentor=0x56DB +OP_InspectBuffs=0x01f3 # LFG/LFP Opcodes OP_LFGCommand=0x4463 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 0effd1b3c..338ee82e7 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -487,6 +487,7 @@ OP_GroupLeaderChange=0x7545 OP_GroupRoles=0x6b67 OP_GroupMakeLeader=0x6087 OP_GroupMentor=0x1224 +OP_InspectBuffs=0x66bf # LFG/LFP Opcodes OP_LFGCommand=0x3288 # C OP_LFGGetMatchesRequest=0x5613 # C diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index 0b53daefd..fc0c716e1 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -453,6 +453,7 @@ OP_CancelInvite=0x596C #Trevius 03/02/09 OP_GroupFollow2=0x59D4 #Xinu 02/20/09 OP_GroupInvite2=0x07F6 #Xinu 02/20/09 OP_GroupMentor=0x9EF3 +OP_InspectBuffs=0x3547 #LFG/LFP Opcodes OP_LFGCommand=0x5D81 #Trevius 01/16/09 diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index 50e426874..841f77bf5 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -420,6 +420,8 @@ OP_RaidJoin=0x1f21 # ShowEQ 10/27/05 OP_RaidInvite=0x5891 # ShowEQ 10/27/05 OP_RaidUpdate=0x1f21 # EQEmu 06/29/05 +OP_InspectBuffs=0x4FB6 + OP_ZoneComplete=0x0000 OP_ItemLinkText=0x0000 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index 01cc1ad47..56838da65 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -490,6 +490,7 @@ OP_GroupLeaderChange=0x0c33 # C OP_GroupRoles=0x116d # C OP_GroupMakeLeader=0x5851 OP_GroupMentor=0x292f +OP_InspectBuffs=0x105b # LFG/LFP Opcodes OP_LFGCommand=0x2c38 # C diff --git a/zone/aa.cpp b/zone/aa.cpp index d07ed72cf..83a6b7967 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1793,29 +1793,26 @@ int Client::GroupLeadershipAAOffenseEnhancement() void Client::InspectBuffs(Client* Inspector, int Rank) { - if(!Inspector || (Rank == 0)) return; + // At some point the removed the restriction of being a group member for this to work + // not sure when, but the way it's coded now, it wouldn't work with mobs. + if (!Inspector || Rank == 0) + return; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_InspectBuffs, sizeof(InspectBuffs_Struct)); + InspectBuffs_Struct *ib = (InspectBuffs_Struct *)outapp->pBuffer; - Inspector->Message_StringID(0, CURRENT_SPELL_EFFECTS, GetName()); uint32 buff_count = GetMaxTotalSlots(); - for (uint32 i = 0; i < buff_count; ++i) - { - if (buffs[i].spellid != SPELL_UNKNOWN) - { - if(Rank == 1) - Inspector->Message(0, "%s", spells[buffs[i].spellid].name); - else - { - if (spells[buffs[i].spellid].buffdurationformula == DF_Permanent) - Inspector->Message(0, "%s (Permanent)", spells[buffs[i].spellid].name); - else { - char *TempString = nullptr; - MakeAnyLenString(&TempString, "%.1f", static_cast(buffs[i].ticsremaining) / 10.0f); - Inspector->Message_StringID(0, BUFF_MINUTES_REMAINING, spells[buffs[i].spellid].name, TempString); - safe_delete_array(TempString); - } - } - } + uint32 packet_index = 0; + for (uint32 i = 0; i < buff_count; i++) { + if (buffs[i].spellid == SPELL_UNKNOWN) + continue; + ib->spell_id[packet_index] = buffs[i].spellid; + if (Rank > 1) + ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buffdurationformula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining; + packet_index++; } + + Inspector->FastQueuePacket(&outapp); } //this really need to be renamed to LoadAAActions() From 3db23328218969fdb10fae5eaf2ed3166aae6fa4 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 19 Oct 2014 22:30:45 -0400 Subject: [PATCH 367/368] Missed one! --- zone/command.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 93c718bce..33f7c2068 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2812,6 +2812,16 @@ void command_peekinv(Client *c, const Seperator *sep) c->Message((item == 0), "WornSlot: %i, Item: %i (%s), Charges: %i", i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } + if (c->GetClientVersion() >= EQClientSoF) + { + const ItemInst* inst = client->GetInv().GetItem(MainPowerSource); + item = (inst) ? inst->GetItem() : nullptr; + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "WornSlot: %i, Item: %i (%s), Charges: %i", + MainPowerSource, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); + } + } if (bAll || (strcasecmp(sep->arg[1], "inv")==0)) { @@ -2836,15 +2846,6 @@ void command_peekinv(Client *c, const Seperator *sep) } } } - if(c->GetClientVersion() >= EQClientSoF) - { - const ItemInst* inst = client->GetInv().GetItem(MainPowerSource); - item = (inst) ? inst->GetItem() : nullptr; - - linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); - c->Message((item == 0), "WornSlot: %i, Item: %i (%s), Charges: %i", - MainPowerSource, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); - } } // Changed to show 'empty' cursors and not to show bag slots on 'queued' cursor slots (cursor bag slots 331 to 340 are not arrayed...) @@ -2867,7 +2868,7 @@ void command_peekinv(Client *c, const Seperator *sep) linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i", - MainPowerSource, i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); + MainCursor, i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer) && i==0) { // 'CSD 1' - only display contents of slot 30[0] container..higher ones don't exist for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { @@ -2876,7 +2877,7 @@ void command_peekinv(Client *c, const Seperator *sep) linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); c->Message((item == 0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - Inventory::CalcSlotId(i, j), i, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); + Inventory::CalcSlotId(MainCursor, j), MainCursor, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } From c81491f97ebebf988b5ee96a9c505d690bc59870 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 20 Oct 2014 01:05:08 -0400 Subject: [PATCH 368/368] Inspect buffs rank 1 will now show NPC buffs in target window (SoD+) --- changelog.txt | 3 +++ zone/client_packet.cpp | 22 +++++++++++++++++++--- zone/entity.cpp | 32 +++++++++++++++++++++++++------- zone/entity.h | 2 +- zone/spell_effects.cpp | 6 ++++++ zone/spells.cpp | 6 ++++++ 6 files changed, 60 insertions(+), 11 deletions(-) diff --git a/changelog.txt b/changelog.txt index ed10c7173..915f7a812 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/20/2014 == +demonstar55: Inspect Buffs rank 1 will now show NPC buffs in target window (SoD+) + == 10/19/2014 == Uleat: Updated command #peekinv to display item links properly in RoF clients demonstar55: Group Mentoring in raids diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6d9291685..efc8a5d8a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12951,9 +12951,25 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) if (nt) { SetTarget(nt); - if ((nt->IsClient() && !nt->CastToClient()->GetPVP()) || - (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || - (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) + bool inspect_buffs = false; + // rank 1 gives you ability to see NPC buffs in target window (SoD+) + if (nt->IsNPC()) { + if (IsRaidGrouped()) { + Raid *raid = GetRaid(); + if (raid) { + uint32 gid = raid->GetGroup(this); + if (gid < 12 && raid->GroupCount(gid) > 2) + inspect_buffs = raid->GetLeadershipAA(groupAAInspectBuffs, gid); + } + } else { + Group *group = GetGroup(); + if (group && group->GroupCount() > 2) + inspect_buffs = group->GetLeadershipAA(groupAAInspectBuffs); + } + } + if (nt == this || inspect_buffs || (nt->IsClient() && !nt->CastToClient()->GetPVP()) || + (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || + (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) nt->SendBuffsToClient(this); } else diff --git a/zone/entity.cpp b/zone/entity.cpp index 4a8329639..0ce530419 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1351,7 +1351,7 @@ void EntityList::RefreshClientXTargets(Client *c) } void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *app, - bool iSendToSender, Mob *SkipThisMob, bool ackreq, bool HoTT, uint32 ClientVersionBits) + bool iSendToSender, Mob *SkipThisMob, bool ackreq, bool HoTT, uint32 ClientVersionBits, bool inspect_buffs) { auto it = client_list.begin(); while (it != client_list.end()) { @@ -1365,8 +1365,7 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap Mob *TargetsTarget = nullptr; - if (Target) - TargetsTarget = Target->GetTarget(); + TargetsTarget = Target->GetTarget(); bool Send = false; @@ -1378,11 +1377,30 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap Send = true; if (c != sender) { - if (Target == sender) - Send = true; - else if (HoTT) - if (TargetsTarget == sender) + if (Target == sender) { + if (inspect_buffs) { // if inspect_buffs is true we're sending a mob's buffs to those with the LAA + if (c->IsRaidGrouped()) { + Raid *raid = c->GetRaid(); + if (!raid) + continue; + uint32 gid = raid->GetGroup(c); + if (gid > 11 || raid->GroupCount(gid) < 3) + continue; + if (raid->GetLeadershipAA(groupAAInspectBuffs, gid)) + Send = true; + } else { + Group *group = c->GetGroup(); + if (!group || group->GroupCount() < 3) + continue; + if (group->GetLeadershipAA(groupAAInspectBuffs)) + Send = true; + } + } else { Send = true; + } + } else if (HoTT && TargetsTarget == sender) { + Send = true; + } } if (Send && (c->GetClientVersionBit() & ClientVersionBits)) diff --git a/zone/entity.h b/zone/entity.h index 9599aa7fc..5776ed936 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -297,7 +297,7 @@ public: void QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint32 guildeqid = 0); void QueueClientsGuildBankItemUpdate(const GuildBankItemUpdate_Struct *gbius, uint32 GuildID); void QueueClientsByTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true, Mob* SkipThisMob = 0, bool ackreq = true, - bool HoTT = true, uint32 ClientVersionBits = 0xFFFFFFFF); + bool HoTT = true, uint32 ClientVersionBits = 0xFFFFFFFF, bool inspect_buffs = false); void QueueClientsByXTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true); void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 7496a07a5..41e8f1e50 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4168,6 +4168,12 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) safe_delete(outapp); } + if (IsNPC()) { + EQApplicationPacket *outapp = MakeBuffsPacket(); + entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, BIT_SoDAndLater, true); + safe_delete(outapp); + } + if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { EQApplicationPacket *outapp = MakeBuffsPacket(false); diff --git a/zone/spells.cpp b/zone/spells.cpp index c6fb43b24..50eb9479f 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3128,6 +3128,12 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid safe_delete(outapp); } + if (IsNPC()) { + EQApplicationPacket *outapp = MakeBuffsPacket(); + entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, BIT_SoDAndLater, true); + safe_delete(outapp); + } + // recalculate bonuses since we stripped/added buffs CalcBonuses();