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/changelog.txt b/changelog.txt index a006c9a50..b002c0524 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,124 @@ 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 + - 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/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 +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) +demonstar55: Tell queues (and offline) messages now show correctly +demonstar55: Fix starting with capital check + +== 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/CMakeLists.txt b/common/CMakeLists.txt index a6a25f612..6047e2712 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 @@ -104,7 +103,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 88f01a676..ed90b9a79 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -18,6 +18,8 @@ #include "../common/debug.h" #include "../common/rulesys.h" #include +#include +#include #include #include #include @@ -26,6 +28,7 @@ #include #include #include +#include #include // Disgrace: for windows compile @@ -70,55 +73,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; + 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 @@ -193,24 +166,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()) @@ -223,32 +190,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; } @@ -256,10 +215,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 @@ -283,12 +240,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; } @@ -303,8 +258,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); @@ -344,279 +298,450 @@ 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); - +/* 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); - - if (!results.Success()) - { - std::cerr << "Error in ReserveName query '" << query << "' " << results.ErrorMessage() << std::endl; - return false; - } - + 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; + + /* Write to log file */ + std::ofstream log("eqemu_query_error_log.txt", std::ios_base::app | std::ios_base::out); + log << "ERROR " << query_title << ": " << ErrorMessage << "\n" << query << "\n"; + log.close(); + + 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; + 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 + query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", charid); +#endif + QueryDatabase(query); + + return true; +} + +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, " + EscapeString(pp->name).c_str(), // " `name`, " + EscapeString(pp->last_name).c_str(), // " 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, " + EscapeString(pp->title).c_str(), // " title, " + EscapeString(pp->suffix).c_str(), // " 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); + 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); - 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); - 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; + /* 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]); + } + } } - -#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; + results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate Starting Languages", query); return true; } -// Store new character information into the character_ and inventory tables -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; - +/* 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 */ + 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()); - 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()); @@ -626,40 +751,38 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inven #endif } - if (i == MainCursor) { - i = EmuConstants::GENERAL_BAGS_BEGIN; + 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::CURSOR_BAG_END) { + i = EmuConstants::BANK_BEGIN; + continue; } - else if (i == EmuConstants::BANK_END) { - i = EmuConstants::BANK_BAGS_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); + std::string query = StringFormat("SELECT `account_id`, `id` FROM `character_data` WHERE name='%s'", EscapeString(charname).c_str()); auto results = QueryDatabase(query); @@ -670,7 +793,7 @@ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { } if (results.RowCount() != 1) - return 0; + return 0; auto row = results.begin(); @@ -684,12 +807,9 @@ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { // 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_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()); return 0; } @@ -697,8 +817,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]); } @@ -706,11 +825,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; } @@ -725,8 +843,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 @@ -737,12 +854,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; } @@ -759,15 +874,13 @@ 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); +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); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in GetCharName query '" << query << "' " << results.ErrorMessage() << std::endl; - return; + return; } auto row = results.begin(); @@ -776,6 +889,1024 @@ 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; + float ratio = x / (float)n; + int c = ratio * w; + std::cout << std::setw(3) << (int)(ratio * 100) << "% ["; + for (int x = 0; xexpended_aa > 4000000){ e_pp->expended_aa = 0; } + + /* 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); + ThrowDBError(results.ErrorMessage(), "Character Inspect Message Convert", rquery); + } + + /* 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); + ThrowDBError(results.ErrorMessage(), "Character Currency Convert", 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 == 0 ? 0 : 1, + 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); + 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 + 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); 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)) { + 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 */ + 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++){ + 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); ThrowDBError(results.ErrorMessage(), "Character Language Convert", rquery); } + /* 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); ThrowDBError(results.ErrorMessage(), "Character Skills Convert Convert", rquery); } + /* 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); 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++){ + 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); ThrowDBError(results.ErrorMessage(), "Character Memmed Spells Convert", rquery); } + /* Run Discipline Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_DISCIPLINES; i++){ + 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; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); + } + } + 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++){ + 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); ThrowDBError(results.ErrorMessage(), "Character Material Convert", rquery); } + /* 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); ThrowDBError(results.ErrorMessage(), "Character Tribute Convert", rquery); } + /* Run Bandolier Convert */ + first_entry = 0; rquery = ""; + 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){ + 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); ThrowDBError(results.ErrorMessage(), "Character Bandolier Convert", rquery); } + /* 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); 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++){ + 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; + } + rquery = rquery + StringFormat(", (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Leadership AA Convert", rquery); } + } + } + 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; +} + bool Database::LoadVariables() { char *query = nullptr; @@ -1140,7 +2271,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; } @@ -1221,7 +2352,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); @@ -1234,14 +2365,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; } @@ -1277,11 +2408,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()) @@ -1294,13 +2423,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; } @@ -1311,13 +2437,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; } @@ -1329,15 +2452,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; } @@ -1352,13 +2474,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; } @@ -1366,57 +2486,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; } @@ -1424,8 +2498,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); @@ -1507,13 +2580,11 @@ 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()) - { + if (!results.Success()) { std::cerr << "Error in GetCharacterInfo query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -1522,20 +2593,13 @@ uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZo return 0; 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]); } return charid; } @@ -1574,45 +2638,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()); @@ -1624,11 +2678,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); @@ -1639,8 +2691,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()) @@ -1698,46 +2750,40 @@ 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){ + leaderbuf = ""; + std::string query = StringFormat("SELECT `groupid` FROM `group_id` WHERE `name = '%s'", name); auto results = QueryDatabase(query); - - if (!results.Success()) - { - std::cout << "Unable to get leader name: " << results.ErrorMessage() << std::endl; - return leaderbuf; + 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 (results.LengthOfColumn(0) != sizeof(PlayerProfile_Struct)) - return leaderbuf; - - auto row = results.begin(); - - memcpy(&pp, row[0], sizeof(PlayerProfile_Struct)); - strcpy(leaderbuf,pp.groupMembers[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; } -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"); @@ -1780,8 +2826,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); @@ -1806,8 +2851,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); @@ -1823,14 +2867,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 @@ -1846,8 +2888,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 f912c18d1..c4d3af336 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,9 +124,8 @@ 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 @@ -217,6 +222,8 @@ public: uint32 GetRaidID(const char* name); const char *GetRaidLeaderName(uint32 rid); + bool CheckDatabaseConversions(); + /* * Database Variables */ @@ -250,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/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/eq_packet_structs.h b/common/eq_packet_structs.h index 1751f4b4e..dab4bf0bb 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 = 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 + 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/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/guild_base.cpp b/common/guild_base.cpp index 6546918d0..e6df92569 100644 --- a/common/guild_base.cpp +++ b/common/guild_base.cpp @@ -756,7 +756,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()) { @@ -897,10 +897,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_` @@ -1241,7 +1241,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/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 diff --git a/common/mysql_request_result.cpp b/common/mysql_request_result.cpp index ffbc3ef40..e7f147b51 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,16 +22,17 @@ 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() { + safe_delete_array(m_ErrorBuffer); if (m_Result != nullptr) @@ -100,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 @@ -127,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 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.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_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 { 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/ruletypes.h b/common/ruletypes.h index c61590ac4..058d5ce6f 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 ) @@ -320,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/common/servertalk.h b/common/servertalk.h index 5337b6b1d..8c30715d9 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 @@ -179,13 +180,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 }; @@ -346,6 +349,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]; }; @@ -1092,6 +1096,11 @@ struct CZClientSignal_Struct { uint32 data; }; +struct CZNPCSignal_Struct { + uint32 npctype_id; + uint32 data; +}; + struct CZClientSignalByName_Struct { char Name[64]; uint32 data; @@ -1233,10 +1242,20 @@ struct CZMessagePlayer_Struct { char Message[512]; }; +struct CZSetEntVarByNPCTypeID_Struct { + uint32 npctype_id; + char id[256]; + char m_var[256]; +}; + struct ReloadWorld_Struct{ uint32 Option; }; +struct ServerRequestTellQueue_Struct { + char name[64]; +}; + #pragma pack() #endif diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 10e3d2012..aecfa0488 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "shareddb.h" #include "mysql.h" @@ -113,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; } @@ -348,12 +330,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; @@ -363,37 +344,26 @@ 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; + 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); + 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) + if(!myitem) continue; ItemInst* myinst = CreateBaseItem(myitem, charges); if(slot < 0) @@ -401,9 +371,6 @@ bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, inv->PutItem(slot, *myinst); safe_delete(myinst); } - - if(result) mysql_free_result(result); - return true; } @@ -420,7 +387,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 { @@ -637,8 +604,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_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))) { @@ -698,7 +670,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); @@ -1211,104 +1183,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; - - 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, 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) { @@ -2120,40 +1994,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; - - std::string msg = EscapeString(message->text); - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET inspectmessage='%s' WHERE name='%s'", msg.c_str(), 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 0fd72426c..eeef6125a 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -40,13 +40,10 @@ 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); - 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/ucs/database.cpp b/ucs/database.cpp index a2f61c438..43bb9e0e3 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/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/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/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/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/world/adventure_manager.cpp b/world/adventure_manager.cpp index 05337e0ad..aeee8e3c5 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/client.cpp b/world/client.cpp index f34f4e3df..ca90b1f46 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)), @@ -130,7 +128,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(); @@ -174,7 +172,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); @@ -471,8 +469,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 +480,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 +488,27 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { outapp->pBuffer = new uchar[1]; outapp->size = 1; - bool valid; - if(!database.CheckNameFilter(char_name)) { - valid = false; + bool valid = false; + 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; + /* 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 if (database.ReserveName(GetAccountID(), char_name)) { - valid = true; - } - else { - valid = false; + else { + valid = false; } + outapp->pBuffer[0] = valid? 1 : 0; QueuePacket(outapp); safe_delete(outapp); - if(!valid) { + if (!valid) memset(char_name, 0, sizeof(char_name)); - } return true; } @@ -642,13 +640,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 +653,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 +670,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,11 +707,10 @@ 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); + database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit); bool home_enabled = false; for(int x = 0; x < 10; ++x) @@ -733,12 +726,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(); @@ -749,7 +740,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) @@ -807,16 +798,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 +886,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 +1337,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 +1345,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 +1362,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,20 +1405,14 @@ 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 ); SetClassLanguages(&pp); pp.skills[SkillSenseHeading] = 200; // Some one fucking fix this to use a field name. -Doodman @@ -1453,24 +1420,25 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) // 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)); @@ -1479,11 +1447,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) @@ -1491,8 +1457,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) @@ -1505,14 +1470,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; @@ -1520,7 +1483,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]; @@ -1529,28 +1492,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/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/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/net.cpp b/world/net.cpp index 4bfdc3c1a..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) { @@ -222,9 +219,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; } @@ -276,6 +272,8 @@ 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/world/worlddb.cpp b/world/worlddb.cpp index 7a07c6f6f..f26061a5f 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 @@ -34,248 +33,196 @@ 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; +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], ""); cs->zone[i] = 0; cs->level[i] = 0; - cs->tutorial[i] = 0; + cs->tutorial[i] = 0; 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 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) { + PlayerProfile_Struct pp; + memset(&pp, 0, sizeof(PlayerProfile_Struct)); - // 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]); + 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]); - // 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, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial))) + cs->tutorial[char_num] = 1; - if(RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial))) - cs->tutorial[char_num] = 1; + if (RuleB(World, EnableReturnHomeButton)) { + int now = time(nullptr); + if ((now - atoi(row[8])) >= RuleI(World, MinOfflineTimeToReturnHome)) + cs->gohome[char_num] = 1; + } - if(RuleB(World, EnableReturnHomeButton)) { - int now = time(nullptr); - if((now - pp->lastlogin) >= RuleI(World, MinOfflineTimeToReturnHome)) - cs->gohome[char_num] = 1; + /* 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) { + 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); } - - - // 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}; - - // 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); - } - - // 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 - - - // 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; - } - } - } + /* 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; + } - 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], inv)) { + for (uint8 material = 0; material <= 8; material++) { + uint32 color = 0; + 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){ color = pp.item_tint[material].color; } + else{ color = item->GetItem()->Color; } + + cs->cs_colors[char_num][material].color = color; + + /* Weapons are handled a bit differently */ + 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; } return; } 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) @@ -444,12 +391,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); @@ -532,7 +483,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); @@ -548,7 +499,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/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/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 7c4f4f963..1023dbe69 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -437,45 +437,48 @@ 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); - } - 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, "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); - + if (cle == 0 || cle->Online() < CLE_Status_Zoning || + (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { + 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()) { + 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]; + 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 + + if (!sender) + break; + scm->noreply = true; + scm->queued = 1; // queued + strcpy(scm->deliverto, scm->from); + sender->Server()->SendPacket(pack); + } } - // 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) @@ -652,17 +655,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,40 +678,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); - 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); @@ -716,8 +710,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; @@ -725,27 +718,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); } } @@ -783,21 +773,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; @@ -1272,37 +1259,19 @@ 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); break; } case ServerOP_CZSignalClientByName: - case ServerOP_CZMessagePlayer: + case ServerOP_CZMessagePlayer: + case ServerOP_CZSignalNPC: + case ServerOP_CZSetEntityVariableByNPCTypeID: case ServerOP_CZSignalClient: { zoneserver_list.SendPacket(pack); @@ -1319,6 +1288,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/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/aa.cpp b/zone/aa.cpp index 2964dc6f3..1f933b30e 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" @@ -300,7 +302,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); @@ -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(); } @@ -525,7 +528,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); @@ -1035,8 +1038,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 @@ -1049,7 +1051,11 @@ void Client::BuyAA(AA_Action* action) m_pp.aapoints -= real_cost; - Save(); + /* 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)); + if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (aa2->hotkey_sid == 4294967295u)) && ((aa2->max_level == (cur_level + 1)) && aa2->sof_next_id)){ SendAA(aa2->id); @@ -1060,8 +1066,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){ @@ -1084,8 +1092,6 @@ void Client::BuyAA(AA_Action* action) } } - - SendAAStats(); CalcBonuses(); @@ -1514,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; @@ -1530,6 +1540,12 @@ void Client::ResetAA(){ m_pp.raid_leadership_points = 0; 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() @@ -1818,8 +1834,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 345bec4ca..fad7d1a80 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 @@ -479,60 +477,23 @@ void Client::ReportConnectingState() { }; } -bool Client::Save(uint8 iCommitNow) { -#if 0 -// Orig. Offset: 344 / 0x00000000 -// Length: 36 / 0x00000024 - unsigned char rawData[36] = -{ - 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 - - 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(){ + int first_entry = 0; + std::string rquery; + /* 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,42 +502,61 @@ bool Client::Save(uint8 iCommitNow) { } } } - m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; - - if (GetHP() <= 0) { - m_pp.cur_hp = GetMaxHP(); + for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { + if (aa[a]->AA > 0 && aa[a]->value){ + 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); + } } - else - m_pp.cur_hp = GetHP(); + auto results = database.QueryDatabase(rquery); + return true; +} + +bool Client::Save(uint8 iCommitNow) { + if(!ClientDataLoaded()) + return false; + + /* 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 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); + /* 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) { - dbasync->CancelWork(pQueuedSaveWorkID); - pQueuedSaveWorkID = 0; - } if (GetPet() && !GetPet()->IsFamiliar() && GetPet()->CastToNPC()->GetPetSpellID() && !dead) { NPC *pet = GetPet()->CastToNPC(); @@ -591,58 +571,19 @@ 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(); - - 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; - } - - /* Mirror Character Data */ - database.StoreCharacterLookup(this->CharacterID()); + database.SaveCharacterTribute(this->CharacterID(), &m_pp); + SaveTaskState(); /* Save Character Task */ + database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp, &m_epp); /* Save Character Data */ return true; } 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() @@ -1406,12 +1347,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); @@ -1427,10 +1370,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); @@ -2115,7 +2060,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper = copperpp; if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } silver -= copper; @@ -2130,7 +2075,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 +2095,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper += coppertest; if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } @@ -2168,7 +2113,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { if(updateclient) SendMoneyUpdate(); RecalcWeight(); - Save(); + SaveCurrency(); return true; } } @@ -2178,32 +2123,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) { @@ -2234,13 +2174,12 @@ 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); } -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 */ @@ -2249,6 +2188,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) @@ -2270,7 +2213,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 +2750,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) @@ -3134,10 +3072,19 @@ 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; SetTint(in_slot, new_color); + database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color); } // Still need to reconcile bracer01 versus bracer02 @@ -3165,6 +3112,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) @@ -3199,6 +3148,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; @@ -4270,7 +4220,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; @@ -4280,28 +4229,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; @@ -5718,7 +5657,7 @@ void Client::AddCrystals(uint32 Radiant, uint32 Ebon) m_pp.currentEbonCrystals += Ebon; m_pp.careerEbonCrystals += Ebon; - Save(); + SaveCurrency(); SendCrystalCounts(); } @@ -8024,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 { @@ -8036,8 +7975,9 @@ void Client::RefundAA() { } if(refunded) { + SaveAA(); Save(); - Kick(); + // Kick(); } } diff --git a/zone/client.h b/zone/client.h index 697e4e2d3..0ad8b043a 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 @@ -240,8 +236,6 @@ public: bool KeyRingCheck(uint32 item_id); 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); @@ -260,6 +254,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); @@ -315,6 +310,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); } @@ -657,14 +656,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); @@ -785,6 +784,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_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 261615169..928eca1ad 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 @@ -498,8 +495,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; @@ -507,19 +503,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,8 +522,9 @@ 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) { struct in_addr ghost_addr; ghost_addr.s_addr = eqs->GetRemoteIP(); @@ -539,29 +535,496 @@ 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; + + uint32 cid = CharacterID(); + character_id = cid; /* Global character_id reference */ + + /* 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` = %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; } + gmspeed = atoi(row[3]); + revoked = atoi(row[4]); + gmhideme = atoi(row[5]); + if (account_creation){ account_creation = atoul(row[6]); } } + + /* 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[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()); + + 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, &m_epp); /* Load Character Data from DB into PP as well as E_PP */ + database.LoadCharacterSkills(cid, &m_pp); /* Load Character Skills */ + 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 */ + + /* 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; + + if (level){ level = m_pp.level; } + + /* If GM, not trackable */ + 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; + /* 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; + + strcpy(name, m_pp.name); + strcpy(lastname, m_pp.last_name); + /* 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(); + 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_; + 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; + 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 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(); + 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; } + + /* Set Swimming Skill 100 by default if under 100 */ + 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); + + /* 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; } + + 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; + + /* 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 + + /* 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 */ + 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; } @@ -2104,7 +2567,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 @@ -2131,7 +2594,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 @@ -4340,7 +4803,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; } @@ -4536,6 +4999,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; @@ -4547,8 +5011,14 @@ 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.SaveCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot); + database.SaveCharacterSpell(this->CharacterID(), swapspelltemp, swapspell->to_slot); QueuePacket(app); return; @@ -4580,7 +5050,28 @@ 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) { @@ -4659,14 +5150,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) { @@ -4698,26 +5191,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; } @@ -7027,7 +7500,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 @@ -7044,7 +7517,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 @@ -8877,621 +9350,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_InfoForLogin: { - if (!FinishConnState2(dbaw)) - client_state = CLIENT_ERROR; - break; - } - case DBA_b1_Entity_Client_Save: { - 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; - break; - } - default: { - std::cout << "Error: Client::DBAWComplete(): Unknown workpt_b1" << std::endl; - break; - } - } -} - -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; - } - } - - // 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); - - conn_state = PlayerProfileLoaded; - - 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; - - //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_; - 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; - - 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; - } - - 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; - } - - //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; - - if(GetSkill(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 - aa[a] = &m_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; - } - - //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; - 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; - } - } - } - - KeyRingLoad(); - - 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; - } - - 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; - - 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(); @@ -9523,8 +9381,7 @@ void Client::CompleteConnect() { SendAppearancePacket(AT_GuildID, GuildID(), false); SendAppearancePacket(AT_GuildRank, rank, 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; } @@ -9577,7 +9434,7 @@ void Client::CompleteConnect() { if (!IsValidSpell(buffs[j1].spellid)) continue; - const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; + const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; int NimbusEffect = GetNimbusEffect(buffs[j1].spellid); if(NimbusEffect) { @@ -9714,20 +9571,18 @@ 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); entity_list.SendUntargetable(this); - client_data_loaded = true; int x; for (x = 0; x < 8; x++) SendWearChange(x); Mob *pet = GetPet(); if (pet != nullptr) { - for (x = 0; x < 8; x++) + for (x = 0; x < 8; x++) pet->SendWearChange(x); } @@ -9807,6 +9662,9 @@ 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(); @@ -9834,6 +9692,8 @@ void Client::CompleteConnect() { } entity_list.RefreshClientXTargets(this); + + worldserver.RequestTellQueue(GetName()); } void Client::Handle_OP_KeyRing(const EQApplicationPacket *app) @@ -9905,6 +9765,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 @@ -10814,8 +10676,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); @@ -10829,6 +10690,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 { @@ -10836,9 +10698,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) { @@ -13098,13 +12957,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) { @@ -13112,13 +12971,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 61f4936c8..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"); } } } @@ -1580,7 +1585,7 @@ void Client::OPMoveCoin(const EQApplicationPacket* app) safe_delete(outapp); } - Save(); + SaveCurrency(); } void Client::OPGMTraining(const EQApplicationPacket *app) @@ -1715,6 +1720,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()); @@ -1724,7 +1730,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 cf180f2bd..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) || @@ -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) || @@ -258,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) || @@ -333,8 +331,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) || @@ -407,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) || @@ -2158,8 +2153,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; @@ -2228,28 +2223,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); @@ -2424,8 +2397,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) { @@ -2492,14 +2465,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); } } @@ -2791,85 +2764,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], &charid); - 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], &charid); - - 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; @@ -5294,53 +5188,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)) @@ -6274,13 +6121,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 { @@ -6307,36 +6154,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]); @@ -6346,25 +6206,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); } } @@ -6374,7 +6229,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]); @@ -6382,22 +6237,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); @@ -6407,22 +6280,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."); + } } } @@ -8662,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; @@ -8736,6 +8588,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 4846dd09d..c02c127dc 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); @@ -151,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); @@ -217,7 +215,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); 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/corpse.cpp b/zone/corpse.cpp index da9ffab17..6b3b4ac82 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/effects.cpp b/zone/effects.cpp index a24a6b39b..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); @@ -616,13 +617,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/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/entity.cpp b/zone/entity.cpp index 7054ba8a1..8d21262c6 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/guild_mgr.cpp b/zone/guild_mgr.cpp index d21ae6bf6..bad060aef 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/inventory.cpp b/zone/inventory.cpp index a4625bf80..76841c86d 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 @@ -1877,7 +1870,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(), i, 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 +1893,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) { @@ -2417,10 +2412,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++) { @@ -2431,6 +2424,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); @@ -2438,21 +2432,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/mob.cpp b/zone/mob.cpp index b7f32bf94..77ff4938a 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1932,6 +1932,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/mob.h b/zone/mob.h index 07459eab9..4e72f0264 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/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/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 fc31178ae..e779a5aa7 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,18 +974,20 @@ 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 } - break; //continue the 1st loop + 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 } @@ -2949,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; @@ -2966,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; @@ -2976,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/spell_effects.cpp b/zone/spell_effects.cpp index 5f85de374..80a356e9f 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() diff --git a/zone/spells.cpp b/zone/spells.cpp index b54b0b66c..d400e46ce 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4841,6 +4841,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); @@ -4855,6 +4857,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); @@ -4882,6 +4886,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) @@ -4897,7 +4902,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)); @@ -4925,8 +4931,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/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! diff --git a/zone/trading.cpp b/zone/trading.cpp index c89af4010..cad191445 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/worldserver.cpp b/zone/worldserver.cpp index 791a34ee4..a1100b7cb 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, 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 // if it's a tell, echo back so it shows up scm->noreply = true; scm->chan_num = 14; @@ -322,7 +328,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); } @@ -1777,6 +1783,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; @@ -1856,6 +1880,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(); @@ -2180,3 +2205,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(); diff --git a/zone/zone.cpp b/zone/zone.cpp index 2f1052f73..36cb3504a 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); @@ -105,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)) { @@ -401,40 +399,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 +438,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 +467,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 +505,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 +526,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(){ @@ -665,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; } @@ -772,7 +706,6 @@ void Zone::Shutdown(bool quite) zone->ResetAuth(); safe_delete(zone); - dbasync->CommitWrites(); entity_list.ClearAreas(); parse->ReloadQuests(true); UpdateWindowTitle(); @@ -911,8 +844,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/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 4369d00cf..1327bd8ee 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; @@ -347,41 +349,6 @@ void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug){ } - -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) { std::string query = StringFormat("UPDATE npc_types SET npcspecialattks='%s' WHERE id = %i;", flag, id); @@ -599,8 +566,7 @@ TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id) return loadti; } -ItemInst* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) { - +ItemInst* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) { std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i AND serialnumber = %i " "ORDER BY slot_id LIMIT 80", CharID, SerialNumber); auto results = QueryDatabase(query); @@ -657,8 +623,7 @@ void ZoneDatabase::SaveTraderItem(uint32 CharID, uint32 ItemID, uint32 SerialNum } -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); std::string query = StringFormat("UPDATE trader SET charges = %i WHERE char_id = %i AND serialnumber = %i", @@ -752,8 +717,7 @@ void ZoneDatabase::DeleteBuyLines(uint32 CharID) { } -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) { std::string query = StringFormat("REPLACE INTO buyer VALUES(%i, %i, %i, \"%s\", %i, %i)", CharID, BuySlot, ItemID, ItemName, Quantity, Price); auto results = QueryDatabase(query); @@ -762,8 +726,7 @@ void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, cons } -void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { - +void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i AND buyslot = %i", CharID, BuySlot); auto results = QueryDatabase(query); if (!results.Success()) @@ -771,8 +734,7 @@ void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { } -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; @@ -787,106 +749,922 @@ void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) #define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1)) -// 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) { +bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ + 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, " + "`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++; // "`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` " + } + return true; +} - MYSQL_ROW row; - unsigned long* lengths; +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; +} - 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); +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]); + if (i < MAX_PP_MEMSPELL){ + pp->mem_spells[i] = atoi(row[1]); } + } + return true; +} - 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]); +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; + /* 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){ + pp->spell_book[i] = atoi(row[1]); + } + } + return true; +} - if (guilddbid) { - if(row[6] != nullptr) - *guilddbid = atoi(row[6]); - else - *guilddbid = GUILD_NONE; +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 `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]); + if (i < MAX_PP_LANGUAGE){ + pp->languages[i] = atoi(row[1]); } - if (guildrank) { - if(row[7] != nullptr) - *guildrank = atoi(row[7]); - else - *guildrank = GUILD_RANK_NONE; + } + 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 " + "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) { + if (i < MAX_PP_DISCIPLINES){ + pp->disciplines.values[i] = atoi(row[0]); } + i++; + } + return true; +} - if(ext) { - //SetExtendedProfile handles any conversion - SetExtendedProfile(ext, row[8], lengths[8]); +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; + /* 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]); + if (i < MAX_PP_SKILL){ + pp->skills[i] = atoi(row[1]); } + } + return true; +} - if(class_) - *class_ = atoi(row[9]); +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]); + } + return true; +} - if(level) - *level = atoi(row[10]); +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; +} - if(LFP) - *LFP = atoi(row[11]); - - if(LFG) - *LFG = atoi(row[12]); - - if(NumXTargets) - { - *NumXTargets = atoi(row[14]); +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; } - - - 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; + 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 `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; + /* 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::Debug, "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){ + 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::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; +} + +bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color){ + 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::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::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::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); + /* 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); + 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; +} + +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); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterBandolier", query); + 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; +} + +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; +} + +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` (" + " 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, " + " e_aa_effects, " + " e_percent_to_aa, " + " e_expended_aa_spent " + ") " + "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, " + "%i," // toxicity pp->toxicity, " toxicity, " + "%i," // hunger_level pp->hunger_level, " hunger_level, " + "%i," // 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," // e_aa_effects + "%u," // e_percent_to_aa + "%u" // e_expended_aa_spent + ")", + character_id, // " id, " + account_id, // " account_id, " + EscapeString(pp->name).c_str(), // " `name`, " + EscapeString(pp->last_name).c_str(), // " 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, " + EscapeString(pp->title).c_str(), // " title, " + EscapeString(pp->suffix).c_str(), // " 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) " + m_epp->aa_effects, + m_epp->perAA, + m_epp->expended_aa + ); + auto results = database.QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase:SaveCharacterData", query); + LogFile->write(EQEMuLog::Debug, "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; } + 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," + "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); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterCurrency", query); + LogFile->write(EQEMuLog::Debug, "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); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterAA", rquery); + LogFile->write(EQEMuLog::Debug, "Saving AA for character ID: %u, aa_id: %u current_level: %u", character_id, aa_id, current_level); + 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; +} + +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::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::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; } 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; @@ -2335,31 +3113,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-------------------------------------------------------------- @@ -2528,11 +3281,3 @@ bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, std::list + //#include "doors.h" struct wplist { @@ -212,9 +214,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 +223,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,38 +233,66 @@ 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 - */ - void StoreCharacterLookup(uint32 char_id); - 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); + /* General Character Related Stuff */ + bool SetServerFilters(char* name, ServerSideFilters_Struct *ssfs); + uint32 GetServerFilters(char* name, ServerSideFilters_Struct *ssfs); + void SaveBuffs(Client *c); void LoadBuffs(Client *c); void LoadPetInfo(Client *c); void SavePetInfo(Client *c); void RemoveTempFactions(Client *c); - /* - * Character Inventory - */ + /* 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, 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, ExtendedProfile_Struct* m_epp); + bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); + 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); + 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); + bool DeleteCharacterAAs(uint32 character_id); + + /* 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); @@ -293,21 +318,15 @@ 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 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 - */ + /* AAs */ bool LoadAAEffects(); bool LoadAAEffects2(); bool LoadSwarmSpells(); @@ -319,9 +338,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); @@ -329,9 +346,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); @@ -341,9 +356,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); @@ -359,9 +372,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); @@ -375,9 +386,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); @@ -389,9 +398,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); @@ -399,16 +406,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 */ @@ -417,14 +419,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); @@ -437,64 +435,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 579517d55..000000000 --- a/zone/zonedbasync.cpp +++ /dev/null @@ -1,127 +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_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) - 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 b50e43d33..6a8abb6d4 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 @@ -704,6 +704,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) {