/* EQEMu: Everquest Server Emulator Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY except by those people which sell it, which are required to give you total support for your newly bought product; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" #include "../common/rulesys.h" #include #include #include #include #include #include #include #include #include #include // Disgrace: for windows compile #ifdef _WINDOWS #include #define snprintf _snprintf #define strncasecmp _strnicmp #define strcasecmp _stricmp #else #include "unix.h" #include #include #endif #include "database.h" #include "eq_packet_structs.h" #include "guilds.h" #include "StringUtil.h" #include "extprofile.h" extern Client client; /* Establish a connection to a mysql database with the supplied parameters Added a very simple .ini file parser - Bounce Modify to use for win32 & linux - misanthropicfiend */ Database::Database () { DBInitVars(); } /* Establish a connection to a mysql database with the supplied parameters */ Database::Database(const char* host, const char* user, const char* passwd, const char* database, uint32 port) { DBInitVars(); Connect(host, user, passwd, database, 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)) { LogFile->write(EQEMuLog::Error, "Failed to connect to database: Error: %s", errbuf); HandleMysqlError(errnum); return false; } else { LogFile->write(EQEMuLog::Status, "Using database '%s' at %s:%d",database,host,port); return true; } } 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 */ Database::~Database() { unsigned int x; if (varcache_array) { for (x=0; x= 50 || strlen(password) >= 50) return(0); char tmpUN[100]; char tmpPW[100]; DoEscapeString(tmpUN, name, strlen(name)); DoEscapeString(tmpPW, password, strlen(password)); auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT id, status FROM account WHERE name='%s' AND password is not null " "and length(password) > 0 and (password='%s' or password=MD5('%s'))", tmpUN, tmpPW, tmpPW)); if (!results.Success()) { safe_delete_array(query); std::cerr << "Error in CheckLogin query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } safe_delete_array(query); auto row = results.begin(); uint32 id = atoi(row[0]); if (oStatus) *oStatus = atoi(row[1]); return id; } //Get Banned IP Address List - Only return false if the incoming connection's IP address is not present in the banned_ips table. bool Database::CheckBannedIPs(const char* loginIP) { std::string query = StringFormat("SELECT ip_address FROM Banned_IPs WHERE ip_address='%s'", loginIP); auto results = QueryDatabase(query); if (!results.Success()) { std::cerr << "Error in CheckBannedIPs query '" << query << "' " << results.ErrorMessage() << std::endl; return true; } if (results.RowCount() != 0) return true; 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; 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); auto results = QueryDatabase(query); if (!results.Success()) return false; if (results.RowCount() == 1) return true; return false; } bool Database::AddGMIP(char* ip_address, char* name) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "INSERT into `gm_ips` SET `ip_address` = '%s', `name` = '%s'", ip_address, name)); safe_delete_array(query); return results.Success(); } void Database::LoginIP(uint32 AccountID, const char* LoginIP) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "INSERT INTO account_ip SET accid=%i, ip='%s' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()", AccountID, LoginIP)); if (!results.Success()) std::cerr << "Error in Log IP query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); } int16 Database::CheckStatus(uint32 account_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT `status`, UNIX_TIMESTAMP(`suspendeduntil`) as `suspendeduntil`, UNIX_TIMESTAMP() as `current`" " FROM `account` WHERE `id` = %i", account_id)); if (!results.Success()) { std::cerr << "Error in CheckStatus query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); if (results.RowCount() == 1) { auto row = results.begin(); int16 status = atoi(row[0]); int32 suspendeduntil = 0; // MariaDB initalizes with NULL if unix_timestamp() is out of range if (row[1] != nullptr) { suspendeduntil = atoi(row[1]); } int32 current = atoi(row[2]); if(suspendeduntil > current) return -1; return status; } return 0; } uint32 Database::CreateAccount(const char* name, const char* password, int16 status, uint32 lsaccount_id) { char *query = nullptr; uint32 queryLen; if (password) queryLen = MakeAnyLenString(&query, "INSERT INTO account SET name='%s', password='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name,password,status, lsaccount_id); else queryLen = MakeAnyLenString(&query, "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; auto results = QueryDatabase(query, queryLen); if (!results.Success()) { std::cerr << "Error in CreateAccount query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } if (results.LastInsertedID() == 0) { std::cerr << "Error in CreateAccount query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } return results.LastInsertedID(); } bool Database::DeleteAccount(const char* name) { char *query = nullptr; std::cerr << "Account Attempting to be deleted:" << name << std::endl; auto results = QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM account WHERE name='%s';",name)); if (!results.Success()) { std::cerr << "Error in DeleteAccount query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); return results.RowsAffected() == 1; } bool Database::SetLocalPassword(uint32 accid, const char* password) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "UPDATE account SET password=MD5('%s') where id=%i;", password, accid)); if (!results.Success()) { std::cerr << "Error in SetLocalPassword query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); return true; } bool Database::SetAccountStatus(const char* name, int16 status) { char *query = nullptr; std::cout << "Account being GM Flagged:" << name << ", Level: " << (int16) status << std::endl; auto results = QueryDatabase(query, MakeAnyLenString(&query, "UPDATE account SET status=%i WHERE name='%s';", status, name)); safe_delete_array(query); if (!results.Success()) return false; if (results.RowsAffected() == 0) { std::cout << "Account: " << name << " does not exist, therefore it cannot be flagged\n"; return false; } return true; } bool Database::ReserveName(uint32 account_id, char* name) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "INSERT into character_ SET account_id=%i, name='%s', profile=NULL", account_id, name)); if (!results.Success()) { std::cerr << "Error in ReserveName query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); return true; } /* Delete the character with the name "name" returns false on failure, true otherwise */ bool Database::DeleteCharacter(char *name) { char *query=nullptr; int charid; 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 auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT id from character_ WHERE name='%s'", name)); safe_delete_array(query); if(results.RowCount() != 1) { std::cerr << "DeleteCharacter error: got " << results.RowCount() << " rows matching '" << name << "'" << std::endl; return false; } auto row = results.begin(); charid = atoi(row[0]); #if DEBUG >= 5 std::cout << "DeleteCharacter: found '" << name << "' with char id: " << charid << std::endl; std::cout << "DeleteCharacter: deleting << '" << name << "' (id " << charid << "): " << std::endl; std::cout << " quest_globals"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE from quest_globals WHERE charid='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " character_tasks"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE from character_tasks WHERE charid='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " character_activities"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE from character_activities WHERE charid='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " character_enabledtasks"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE from character_enabledtasks WHERE charid='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " completed_tasks"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE from completed_tasks WHERE charid='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " friends"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE from friends WHERE charid='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " mail"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE from mail WHERE charid='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " ptimers"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE from timers WHERE char_id='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " inventory"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE from inventory WHERE charid='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " guild_members"; #endif #ifdef BOTS QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM guild_members WHERE char_id='%d' AND GetMobTypeById(%i) = 'C'", charid)); #else QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM guild_members WHERE char_id='%d'", charid)); #endif safe_delete_array(query); #if DEBUG >= 5 std::cout << " recipes"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM char_recipe_list WHERE char_id='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " adventure_stats"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM adventure_stats WHERE player_id='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " zone_flags"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM zone_flags WHERE charID='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " titles"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM titles WHERE char_id='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " titlesets"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM player_titlesets WHERE char_id='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " keyring"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM keyring WHERE char_id='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " factions"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM faction_values WHERE char_id='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " instances"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM instance_list_player WHERE charid='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << " _character"; #endif results = QueryDatabase(query, MakeAnyLenString(&query, "DELETE from character_ WHERE id='%d'", charid)); safe_delete_array(query); if(results.RowsAffected() != 1) // here we have to have a match or it's an error { LogFile->write(EQEMuLog::Error, "DeleteCharacter: error: delete operation affected %d rows\n", results.RowsAffected()); return false; } #if DEBUG >= 5 std::cout << " alternate currency"; #endif QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM character_alt_currency WHERE char_id='%d'", charid)); safe_delete_array(query); #if DEBUG >= 5 std::cout << std::endl; #endif std::cout << "DeleteCharacter: successfully deleted '" << name << "' (id " << charid << ")" << std::endl; 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; char* invquery = nullptr; uint32 charid = 0; char zone[50]; float x, y, z; charid = GetCharacterID(pp->name); 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... 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; } // now the inventory for (int16 i=0; i<=2270;) { const ItemInst* newinv = inv->GetItem(i); if (!newinv) { auto results = QueryDatabase(invquery, MakeAnyLenString(&invquery, "INSERT INTO inventory SET " "charid=%0u, slotid=%0d, itemid=%0u, charges=%0d, color=%0u", charid, i, newinv->GetItem()->ID, newinv->GetCharges(), newinv->GetColor())); if (!results.RowsAffected()) LogFile->write(EQEMuLog::Error, "StoreCharacter inventory failed. Query '%s' %s", invquery, results.ErrorMessage().c_str()); #if EQDEBUG >= 9 else LogFile->write(EQEMuLog::Debug, "StoreCharacter inventory succeeded. Query '%s'", invquery); #endif safe_delete_array(invquery); } if(i==30){ //end of standard inventory/cursor, jump to internals of bags/cursor i = 251; continue; } else if(i==340){ //end of internals of bags/cursor, jump to bank slots i = 2000; continue; } else if(i==2023){ //end of bank slots, jump to internals of bank bags i = 2031; 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); } /* 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) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT account_id, id FROM character_ WHERE name='%s'", charname)); if (!results.Success()) { std::cerr << "Error in GetAccountIDByChar query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); if (results.RowCount() == 1) { auto row = results.begin(); uint32 accountId = atoi(row[0]); if (oCharID) *oCharID = atoi(row[1]); return accountId; } return 0; } // Retrieve account_id for a given char_id uint32 Database::GetAccountIDByChar(uint32 char_id) { char* query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT account_id FROM character_ WHERE id=%i", char_id)); if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in GetAccountIDByChar query '%s': %s", query, results.ErrorMessage().c_str()); safe_delete_array(query); return 0; } safe_delete_array(query); if (results.RowCount() != 1) return 0; auto row = results.begin(); return atoi(row[0]); } uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* lsid) { char *query = nullptr; if (!isAlphaNumeric(accname)) return 0; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT id, status, lsaccount_id FROM account WHERE name='%s'", accname)); if (!results.Success()) { std::cerr << "Error in GetAccountIDByAcc query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); if (results.RowCount() != 1) return 0; auto row = results.begin(); uint32 id = atoi(row[0]); if (status) *status = atoi(row[1]); if (lsid) { if (row[2]) *lsid = atoi(row[2]); else *lsid = 0; } return id; } void Database::GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT name, lsaccount_id FROM account WHERE id='%i'", accountid)); if (!results.Success()) { std::cerr << "Error in GetAccountName query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return; } safe_delete_array(query); if (results.RowCount() != 1) return; auto row = results.begin(); strcpy(name, row[0]); if (row[1] && oLSAccountID) { *oLSAccountID = atoi(row[1]); } } void Database::GetCharName(uint32 char_id, char* name) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT name FROM character_ WHERE id='%i'", char_id)); if (!results.Success()) { std::cerr << "Error in GetCharName query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return; } safe_delete_array(query); auto row = results.begin(); strcpy(name, row[0]); } bool Database::LoadVariables() { char *query = nullptr; auto results = QueryDatabase(query, LoadVariables_MQ(&query)); if (!results.Success()) { std::cerr << "Error in LoadVariables query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); return LoadVariables_result(std::move(results)); } uint32 Database::LoadVariables_MQ(char** query) { return MakeAnyLenString(query, "SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= %d", varcache_lastupdate); } // always returns true? not sure about this. bool Database::LoadVariables_result(MySQLRequestResult results) { uint32 i = 0; LockMutex lock(&Mvarcache); if (results.RowCount() == 0) return true; if (!varcache_array) { varcache_max = results.RowCount(); varcache_array = new VarCache_Struct*[varcache_max]; for (i=0; ivarname, row[0]) == 0) { delete varcache_array[i]; varcache_array[i] = (VarCache_Struct*) new uint8[sizeof(VarCache_Struct) + strlen(row[1]) + 1]; strn0cpy(varcache_array[i]->varname, row[0], sizeof(varcache_array[i]->varname)); strcpy(varcache_array[i]->value, row[1]); break; } } else { varcache_array[i] = (VarCache_Struct*) new uint8[sizeof(VarCache_Struct) + strlen(row[1]) + 1]; strcpy(varcache_array[i]->varname, row[0]); strcpy(varcache_array[i]->value, row[1]); break; } } } uint32 max_used = 0; for (i=0; i max_used) max_used = i; } } varcache_max = max_used + 1; return true; } // Gets variable from 'variables' table bool Database::GetVariable(const char* varname, char* varvalue, uint16 varvalue_len) { varvalue[0] = '\0'; LockMutex lock(&Mvarcache); if (strlen(varname) <= 1) return false; for (uint32 i=0; ivarname, varname) == 0) { snprintf(varvalue, varvalue_len, "%s", varcache_array[i]->value); varvalue[varvalue_len-1] = 0; return true; } } else return false; } return false; } bool Database::SetVariable(const char* varname_in, const char* varvalue_in) { char *query = nullptr; char *varname,*varvalue; varname=(char *)malloc(strlen(varname_in)*2+1); varvalue=(char *)malloc(strlen(varvalue_in)*2+1); DoEscapeString(varname, varname_in, strlen(varname_in)); DoEscapeString(varvalue, varvalue_in, strlen(varvalue_in)); auto results = QueryDatabase(query, MakeAnyLenString(&query, "Update variables set value='%s' WHERE varname like '%s'", varvalue, varname)); if (!results.Success()) { std::cerr << "Error in SetVariable query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); free(varname); free(varvalue); return false; } safe_delete_array(query); if (results.RowsAffected() == 1) { LoadVariables(); // refresh cache free(varname); free(varvalue); return true; } results = QueryDatabase(query, MakeAnyLenString(&query, "Insert Into variables (varname, value) values ('%s', '%s')", varname, varvalue)); safe_delete_array(query); free(varname); free(varvalue); if (results.RowsAffected() == 1) { LoadVariables(); // refresh cache return true; } return false; } uint32 Database::GetMiniLoginAccount(char* ip){ char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT id FROM account WHERE minilogin_ip='%s'", ip)); if (!results.Success()) { std::cerr << "Error in GetMiniLoginAccount query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); auto row = results.begin(); return atoi(row[0]); } // Get zone starting points from DB bool Database::GetSafePoints(const char* short_name, uint32 version, float* safe_x, float* safe_y, float* safe_z, int16* minstatus, uint8* minlevel, char *flag_needed) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT safe_x, safe_y, safe_z, min_status, min_level, " " flag_needed FROM zone " " WHERE short_name='%s' AND (version=%i OR version=0) ORDER BY version DESC", short_name, version)); if (!results.Success()) { std::cerr << "Error in GetSafePoint query '" << query << "' " << results.ErrorMessage() << std::endl; std::cerr << "If it errors, run the following querys:\n"; std::cerr << "ALTER TABLE `zone` CHANGE `minium_level` `min_level` TINYINT(3) UNSIGNED DEFAULT \"0\" NOT NULL;\n"; std::cerr << "ALTER TABLE `zone` CHANGE `minium_status` `min_status` TINYINT(3) UNSIGNED DEFAULT \"0\" NOT NULL;\n"; std::cerr << "ALTER TABLE `zone` ADD flag_needed VARCHAR(128) NOT NULL DEFAULT '';\n"; safe_delete_array(query); return false; } safe_delete_array(query); if (results.RowCount() == 0) return false; auto row = results.begin(); if (safe_x != nullptr) *safe_x = atof(row[0]); if (safe_y != nullptr) *safe_y = atof(row[1]); if (safe_z != nullptr) *safe_z = atof(row[2]); if (minstatus != nullptr) *minstatus = atoi(row[3]); if (minlevel != nullptr) *minlevel = atoi(row[4]); if (flag_needed != nullptr) strcpy(flag_needed, row[5]); return true; } bool Database::GetZoneLongName(const char* short_name, char** long_name, char* file_name, float* safe_x, float* safe_y, float* safe_z, uint32* graveyard_id, uint32* maxclients) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT long_name, file_name, safe_x, safe_y, safe_z, graveyard_id, maxclients FROM zone WHERE short_name='%s' AND version=0", short_name)); if (!results.Success()) { std::cerr << "Error in GetZoneLongName query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); if (results.RowCount() == 0) return false; auto row = results.begin(); if (long_name != nullptr) *long_name = strcpy(new char[strlen(row[0])+1], row[0]); if (file_name != nullptr) { if (row[1] == nullptr) strcpy(file_name, short_name); else strcpy(file_name, row[1]); } if (safe_x != nullptr) *safe_x = atof(row[2]); if (safe_y != nullptr) *safe_y = atof(row[3]); if (safe_z != nullptr) *safe_z = atof(row[4]); if (graveyard_id != nullptr) *graveyard_id = atoi(row[5]); if (maxclients != nullptr) *maxclients = atoi(row[6]); return true; } uint32 Database::GetZoneGraveyardID(uint32 zone_id, uint32 version) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT graveyard_id FROM zone WHERE zoneidnumber='%u' AND (version=%i OR version=0) ORDER BY version DESC", zone_id, version)); if (!results.Success()) { std::cerr << "Error in GetZoneGraveyardID query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); if (results.RowCount() == 0) return 0; auto row = results.begin(); return atoi(row[0]); } bool Database::GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zoneid, float* graveyard_x, float* graveyard_y, float* graveyard_z, float* graveyard_heading) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT zone_id, x, y, z, heading FROM graveyard WHERE id=%i", graveyard_id)); if (!results.Success()){ std::cerr << "Error in GetZoneGraveyard query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); if (results.RowCount() != 1) return false; auto row = results.begin(); if(graveyard_zoneid != nullptr) *graveyard_zoneid = atoi(row[0]); if(graveyard_x != nullptr) *graveyard_x = atof(row[1]); if(graveyard_y != nullptr) *graveyard_y = atof(row[2]); if(graveyard_z != nullptr) *graveyard_z = atof(row[3]); if(graveyard_heading != nullptr) *graveyard_heading = atof(row[4]); return true; } bool Database::LoadZoneNames() { char *query = nullptr; auto results = QueryDatabase(query,MakeAnyLenString(&query, "SELECT zoneidnumber, short_name FROM zone")); if (!results.Success()) { std::cerr << "Error in LoadZoneNames query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); for (auto row= results.begin();row != results.end();++row) { uint32 zoneid = atoi(row[0]); std::string zonename = row[1]; zonename_array.insert(std::pair(zoneid,zonename)); } return true; } uint32 Database::GetZoneID(const char* zonename) { if (zonename == nullptr) return 0; for (auto iter = zonename_array.begin(); iter != zonename_array.end(); ++iter) if (strcasecmp(iter->second.c_str(), zonename) == 0) return iter->first; return 0; } const char* Database::GetZoneName(uint32 zoneID, bool ErrorUnknown) { auto iter = zonename_array.find(zoneID); if (iter != zonename_array.end()) return iter->second.c_str(); if (ErrorUnknown) return "UNKNOWN"; return 0; } uint8 Database::GetPEQZone(uint32 zoneID, uint32 version){ char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT peqzone from zone where zoneidnumber='%i' AND (version=%i OR version=0) ORDER BY version DESC", zoneID, version)); if (!results.Success()) { std::cerr << "Error in GetPEQZone query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); if (results.RowCount() == 0) return 0; auto row = results.begin(); return atoi(row[0]); } bool Database::CheckNameFilter(const char* name, bool surname) { std::string str_name = name; char *query = nullptr; if(surname) { // the minimum 4 is enforced by the client too if(!name || strlen(name) < 3) { return false; } } else { // the minimum 4 is enforced by the client too if(!name || strlen(name) < 4 || strlen(name) > 64) { return false; } } for (size_t i = 0; i < str_name.size(); i++) { if(!isalpha(str_name[i])) { return false; } } for(size_t x = 0; x < str_name.size(); ++x) { str_name[x] = tolower(str_name[x]); } char c = '\0'; uint8 num_c = 0; for(size_t x = 0; x < str_name.size(); ++x) { if(str_name[x] == c) { num_c++; } else { num_c = 1; c = str_name[x]; } if(num_c > 2) { return false; } } auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT name FROM name_filter")); if (!results.Success()) { std::cerr << "Error in CheckNameFilter query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); // false through to true? shouldn't it be falls through to false? return true; } safe_delete_array(query); for (auto row = results.begin();row != results.end();++row) { std::string current_row = row[0]; for(size_t x = 0; x < current_row.size(); ++x) current_row[x] = tolower(current_row[x]); if(str_name.find(current_row) != std::string::npos) return false; } return true; } bool Database::AddToNameFilter(const char* name) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "INSERT INTO name_filter (name) values ('%s')", name)); if (!results.Success()) { std::cerr << "Error in AddToNameFilter query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); if (results.RowsAffected() == 0) return false; return true; } uint32 Database::GetAccountIDFromLSID(uint32 iLSID, char* oAccountName, int16* oStatus) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT id, name, status FROM account WHERE lsaccount_id=%i", iLSID)); if (!results.Success()) { std::cerr << "Error in GetAccountIDFromLSID query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); if (results.RowCount() != 1) return 0; auto row = results.begin(); uint32 account_id = atoi(row[0]); if (oAccountName) strcpy(oAccountName, row[1]); if (oStatus) *oStatus = atoi(row[2]); return account_id; } void Database::GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT name, status FROM account WHERE id=%i", id)); if (!results.Success()) { std::cerr << "Error in GetAccountFromID query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return; } safe_delete_array(query); if (results.RowCount() != 1) return; auto row = results.begin(); if (oAccountName) strcpy(oAccountName, row[0]); if (oStatus) *oStatus = atoi(row[1]); } void Database::ClearMerchantTemp(){ char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "delete from merchantlist_temp")); if (!results.Success()) std::cerr << "Error in ClearMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); } bool Database::UpdateName(const char* oldname, const char* newname) { char *query = nullptr; std::cout << "Renaming " << oldname << " to " << newname << "..." << std::endl; auto results = QueryDatabase(query, MakeAnyLenString(&query, "UPDATE character_ SET name='%s' WHERE name='%s';", newname, oldname)); safe_delete_array(query); if (!results.Success()) return false; if (results.RowsAffected() == 0) return false; return true; } // If the name is used or an error occurs, it returns false, otherwise it returns true bool Database::CheckUsedName(const char* name) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT id FROM character_ where name='%s'", name)); if (!results.Success()) { std::cerr << "Error in CheckUsedName query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); if (results.RowCount() > 0) return false; return true; } uint8 Database::GetServerType() { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT value FROM variables WHERE varname='ServerType'")); if (!results.Success()) { std::cerr << "Error in GetServerType query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); if (results.RowCount() != 1) return 0; auto row = results.begin(); return atoi(row[0]); } bool Database::MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid) { char *query = nullptr; if(zonename == nullptr || strlen(zonename) == 0) return false; auto results = QueryDatabase(query, MakeAnyLenString(&query, "UPDATE character_ SET zonename = '%s',zoneid=%i,x=-1, y=-1, z=-1 WHERE name='%s'", zonename,zoneid, charname)); if (!results.Success()) { std::cerr << "Error in MoveCharacterToZone(name) query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); if (results.RowsAffected() == 0) return false; return true; } bool Database::MoveCharacterToZone(const char* charname, const char* zonename) { return MoveCharacterToZone(charname, zonename, GetZoneID(zonename)); } bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "UPDATE character_ SET zonename = '%s', zoneid=%i, x=-1, y=-1, z=-1 WHERE id=%i", iZonename, GetZoneID(iZonename), iCharID)); if (!results.Success()) { std::cerr << "Error in MoveCharacterToZone(id) query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); return results.RowsAffected() != 0; } uint8 Database::CopyCharacter(const char* oldname, const char* newname, uint32 acctid) { char *query = nullptr; PlayerProfile_Struct* pp; ExtendedProfile_Struct* ext; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT profile, extprofile FROM character_ WHERE name='%s'", oldname)); if (!results.Success()) { std::cerr << "Error in CopyCharacter read query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); 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) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "INSERT INTO hackers(account,name,hacked) values('%s','%s','%s')", accountname, charactername, hacked)); if (!results.Success()) { std::cerr << "Error in SetHackerFlag query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); return results.RowsAffected() != 0; } bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) { //Utilize the "hacker" table, but also give zone information. char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "INSERT INTO hackers(account,name,hacked,zone) values('%s','%s','%s','%s')", accountname, charactername, hacked, zone)); if (!results.Success()) { safe_delete_array(query); std::cerr << "Error in SetMQDetectionFlag query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } safe_delete_array(query); return results.RowsAffected() != 0; } uint8 Database::GetRaceSkill(uint8 skillid, uint8 in_race) { uint16 race_cap = 0; char *query = nullptr; //Check for a racial cap! auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT skillcap from race_skillcaps where skill = %i && race = %i", skillid, in_race)); safe_delete_array(query); if (!results.Success()) return 0; if (results.RowCount() == 0) return 0; auto row = results.begin(); return atoi(row[0]); } uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level) { uint8 skill_level = 0, skill_formula = 0; uint16 base_cap = 0, skill_cap = 0, skill_cap2 = 0, skill_cap3 = 0; char *query = nullptr; //Fetch the data from DB. auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT level, formula, pre50cap, post50cap, post60cap from skillcaps where skill = %i && class = %i", skillid, in_class)); safe_delete_array(query); if (results.Success() && results.RowsAffected() != 0) { auto row = results.begin(); skill_level = atoi(row[0]); skill_formula = atoi(row[1]); skill_cap = atoi(row[2]); if (atoi(row[3]) > skill_cap) skill_cap2 = (atoi(row[3])-skill_cap)/10; //Split the post-50 skill cap into difference between pre-50 cap and post-50 cap / 10 to determine amount of points per level. skill_cap3 = atoi(row[4]); } int race_skill = GetRaceSkill(skillid,in_race); if (race_skill > 0 && (race_skill > skill_cap || skill_cap == 0 || in_level < skill_level)) return race_skill; if (skill_cap == 0) //Can't train this skill at all. return 255; //Untrainable if (in_level < skill_level) return 254; //Untrained //Determine pre-51 level-based cap if (skill_formula > 0) base_cap = in_level*skill_formula+skill_formula; if (base_cap > skill_cap || skill_formula == 0) base_cap = skill_cap; //If post 50, add post 50 cap to base cap. if (in_level > 50 && skill_cap2 > 0) base_cap += skill_cap2*(in_level-50); //No cap should ever go above its post50cap if (skill_cap3 > 0 && base_cap > skill_cap3) base_cap = skill_cap3; //Base cap is now the max value at the person's level, return it! return base_cap; } uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZoneID, uint32* oInstanceID, float* oX, float* oY, float* oZ) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT id, account_id, zonename, instanceid, x, y, z FROM character_ WHERE name='%s'", iName)); if (!results.Success()) { std::cerr << "Error in GetCharacterInfo query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); if (results.RowCount() != 1) 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]); return charid; } bool Database::UpdateLiveChar(char* charname,uint32 lsaccount_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "UPDATE account SET charname='%s' WHERE id=%i;",charname, lsaccount_id)); if (!results.Success()) { std::cerr << "Error in UpdateLiveChar query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); return true; } bool Database::GetLiveChar(uint32 account_id, char* cname) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT charname FROM account WHERE id=%i", account_id)); if (!results.Success()) { std::cerr << "Error in GetLiveChar query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return false; } safe_delete_array(query); if (results.RowCount() != 1) return false; auto row = results.begin(); strcpy(cname,row[0]); return true; } void Database::SetLFP(uint32 CharID, bool LFP) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "update character_ set lfp=%i where id=%i",LFP, CharID)); safe_delete_array(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) { char *query = nullptr; auto results = QueryDatabase(query,MakeAnyLenString(&query, "update character_ set lfp=%i, lfg=%i, firstlogon=%i where id=%i",LFP, LFG, firstlogon, CharID)); safe_delete_array(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) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "update character_ set lfg=%i where id=%i",LFG, CharID)); safe_delete_array(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) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "update character_ set firstlogon=%i where id=%i",firstlogon, CharID)); safe_delete_array(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) { char *query = nullptr; char *escape_str = new char[lines.size()*2+1]; DoEscapeString(escape_str, lines.c_str(), lines.size()); auto results = QueryDatabase(query, MakeAnyLenString(&query, "INSERT INTO reports (name, reported, reported_text) VALUES('%s', '%s', '%s')", who.c_str(), against.c_str(), escape_str)); safe_delete_array(query); safe_delete_array(escape_str); if (!results.Success()) 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){ char *query = nullptr; if (id == 0) { // removing from group auto results = QueryDatabase(query, MakeAnyLenString(&query, "delete from group_id where charid=%i and name='%s' and ismerc=%i",charid, name, ismerc)); safe_delete_array(query); if (!results.Success()) LogFile->write(EQEMuLog::Error, "Error deleting character from group id: %s", results.ErrorMessage().c_str()); return; } // adding to group auto results = QueryDatabase(query, MakeAnyLenString(&query, "replace into group_id set charid=%i, groupid=%i, name='%s', ismerc='%i'",charid, id, name, ismerc)); safe_delete_array(query); if (!results.Success()) LogFile->write(EQEMuLog::Error, "Error adding character to group id: %s", results.ErrorMessage().c_str()); } void Database::ClearAllGroups(void) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "delete from group_id")); safe_delete_array(query); if (!results.Success()) std::cout << "Unable to clear groups: " << results.ErrorMessage() << std::endl; return; } void Database::ClearGroup(uint32 gid) { ClearGroupLeader(gid); char *query = nullptr; if(gid == 0) { //clear all groups ClearAllGroups(); return; } //clear a specific group auto results = QueryDatabase(query, MakeAnyLenString(&query, "delete from group_id where groupid = %lu", (unsigned long)gid)); safe_delete_array(query); if (!results.Success()) std::cout << "Unable to clear groups: " << results.ErrorMessage() << std::endl; } uint32 Database::GetGroupID(const char* name){ char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT groupid from group_id where name='%s'", name)); safe_delete_array(query); if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error getting group id: %s", results.ErrorMessage().c_str()); return 0; } if (results.RowCount() == 0) { LogFile->write(EQEMuLog::Debug, "Character not in a group: %s", name); return 0; } auto row = results.begin(); return atoi(row[0]); } char* Database::GetGroupLeaderForLogin(const char* name,char* leaderbuf){ char *query = nullptr; PlayerProfile_Struct pp; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT profile from character_ where name='%s'", name)); safe_delete_array(query); if (!results.Success()) { std::cout << "Unable to get leader name: " << results.ErrorMessage() << std::endl; return leaderbuf; } if (results.LengthOfColumn(0) != sizeof(PlayerProfile_Struct)) return leaderbuf; auto row = results.begin(); memcpy(&pp, row[0], sizeof(PlayerProfile_Struct)); strcpy(leaderbuf,pp.groupMembers[0]); return leaderbuf; } void Database::SetGroupLeaderName(uint32 gid, const char* name) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "Replace into group_leaders set gid=%lu, leadername='%s'",(unsigned long)gid,name)); safe_delete_array(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){ char* query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT leadername, maintank, assist, puller, marknpc, leadershipaa FROM group_leaders WHERE gid=%lu",(unsigned long)gid)); safe_delete_array(query); if (!results.Success() || results.RowCount() == 0) { if(leaderbuf) strcpy(leaderbuf, "UNKNOWN"); if(maintank) maintank[0] = '\0'; if(assist) assist[0] = '\0'; if(puller) puller[0] = '\0'; if(marknpc) marknpc[0] = '\0'; return leaderbuf; } auto row = results.begin(); if(leaderbuf) strcpy(leaderbuf, row[0]); if(maintank) strcpy(maintank, row[1]); if(assist) strcpy(assist, row[2]); if(puller) strcpy(puller, row[3]); if(marknpc) strcpy(marknpc, row[4]); if(GLAA && results.LengthOfColumn(5) == sizeof(GroupLeadershipAA_Struct)) memcpy(GLAA, row[5], sizeof(GroupLeadershipAA_Struct)); return leaderbuf; } // Clearing all group leaders void Database::ClearAllGroupLeaders(void) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "DELETE from group_leaders")); safe_delete_array(query); if (!results.Success()) std::cout << "Unable to clear group leaders: " << results.ErrorMessage() << std::endl; return; } void Database::ClearGroupLeader(uint32 gid) { char *query = nullptr; if(gid == 0) { ClearAllGroupLeaders(); return; } auto results = QueryDatabase(query, MakeAnyLenString(&query, "DELETE from group_leaders where gid = %lu", (unsigned long)gid)); safe_delete_array(query); if (!results.Success()) std::cout << "Unable to clear group leader: " << results.ErrorMessage() << std::endl; } uint8 Database::GetAgreementFlag(uint32 acctid) { char* query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT rulesflag FROM account WHERE id=%i",acctid)); safe_delete_array(query); if (!results.Success()) return 0; if (results.RowCount() != 1) return 0; auto row = results.begin(); return atoi(row[0]); } void Database::SetAgreementFlag(uint32 acctid) { char *query = nullptr; QueryDatabase(query, MakeAnyLenString(&query, "UPDATE account SET rulesflag=1 where id=%i", acctid)); safe_delete_array(query); } void Database::ClearRaid(uint32 rid) { char *query = nullptr; if(rid == 0) { //clear all raids ClearAllRaids(); return; } //clear a specific group auto results = QueryDatabase(query, MakeAnyLenString(&query, "delete from raid_members where raidid = %lu", (unsigned long)rid)); safe_delete_array(query); if (!results.Success()) std::cout << "Unable to clear raids: " << results.ErrorMessage() << std::endl; } void Database::ClearAllRaids(void) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "delete from raid_members")); safe_delete_array(query); if (!results.Success()) std::cout << "Unable to clear raids: " << results.ErrorMessage() << std::endl; } void Database::ClearAllRaidDetails(void) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "delete from raid_details")); safe_delete_array(query); if (!results.Success()) std::cout << "Unable to clear raid details: " << results.ErrorMessage() << std::endl; return; } void Database::ClearRaidDetails(uint32 rid) { char *query = nullptr; if(rid == 0) { //clear all raids ClearAllRaidDetails(); return; } //clear a specific group auto results = QueryDatabase(query, MakeAnyLenString(&query, "delete from raid_details where raidid = %lu", (unsigned long)rid)); safe_delete_array(query); if (!results.Success()) std::cout << "Unable to clear raid details: " << results.ErrorMessage() << std::endl; } // returns 0 on error or no raid for that character, or // the raid id that the character is a member of. uint32 Database::GetRaidID(const char* name){ char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT raidid from raid_members where name='%s'", name)); safe_delete_array(query); if (!results.Success()) { std::cout << "Unable to get raid id: " << results.ErrorMessage() << std::endl; return 0; } auto row = results.begin(); if (row == results.end()) { std::cout << "Unable to get raid id, char not found!" << std::endl; return 0; } if (row[0]) // would it ever be possible to have a null here? return atoi(row[0]); return 0; } const char* Database::GetRaidLeaderName(uint32 rid) { // Would be a good idea to fix this to be a passed in variable and // make the caller responsible. static local variables like this are // not guaranteed to be thread safe (nor is the internal guard // variable). C++0x standard states this should be thread safe // but may not be fully supported in some compilers. static char name[128]; char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT name FROM raid_members WHERE raidid=%u AND israidleader=1",rid)); safe_delete_array(query); if (!results.Success()) { std::cout << "Unable to get raid id: " << results.ErrorMessage() << std::endl; return "UNKNOWN"; } auto row = results.begin(); if (row == results.end()) { std::cout << "Unable to get raid id, char not found!" << std::endl; return "UNKNOWN"; } memset(name, 0, sizeof(name)); strcpy(name, row[0]); return name; } bool Database::VerifyInstanceAlive(uint16 instance_id, uint32 char_id) { //we are not saved to this instance so set our instance to 0 if(!GlobalInstance(instance_id) && !CharacterInInstanceGroup(instance_id, char_id)) return false; if(CheckInstanceExpired(instance_id)) { DeleteInstance(instance_id); return false; } return true; } bool Database::VerifyZoneInstance(uint32 zone_id, uint16 instance_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where id=%u AND zone=%u",instance_id, zone_id)); safe_delete_array(query); if (!results.Success()) return false; if (results.RowCount() == 0) return false; return true; } bool Database::CharacterInInstanceGroup(uint16 instance_id, uint32 char_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT charid FROM instance_list_player where id=%u AND charid=%u",instance_id, char_id)); safe_delete_array(query); if (!results.Success()) return false; if (results.RowCount() != 1) return false; return true; } void Database::DeleteInstance(uint16 instance_id) { char *query = nullptr; QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM instance_list WHERE id=%u", instance_id)); safe_delete_array(query); QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM instance_list_player WHERE id=%u", instance_id)); safe_delete_array(query); QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM respawn_times WHERE instance_id=%u", instance_id)); safe_delete_array(query); QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM spawn_condition_values WHERE instance_id=%u", instance_id)); safe_delete_array(query); BuryCorpsesInInstance(instance_id); } bool Database::CheckInstanceExpired(uint16 instance_id) { char *query = nullptr; int32 start_time = 0; int32 duration = 0; uint32 never_expires = 0; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT start_time, duration, never_expires FROM instance_list WHERE id=%u", instance_id)); safe_delete_array(query); if (!results.Success()) return true; if (results.RowCount() == 0) return true; auto row = results.begin(); start_time = atoi(row[0]); duration = atoi(row[1]); never_expires = atoi(row[2]); if(never_expires == 1) return false; timeval tv; gettimeofday(&tv, nullptr); if((start_time + duration) <= tv.tv_sec) return true; return false; } uint32 Database::ZoneIDFromInstanceID(uint16 instance_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT zone FROM instance_list where id=%u", instance_id)); safe_delete_array(query); if (!results.Success()) return 0; if (results.RowCount() == 0) return 0; auto row = results.begin(); return atoi(row[0]); } uint32 Database::VersionFromInstanceID(uint16 instance_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT version FROM instance_list where id=%u", instance_id)); safe_delete_array(query); if (!results.Success()) return 0; if (results.RowCount() == 0) return 0; auto row = results.begin(); return atoi(row[0]); } uint32 Database::GetTimeRemainingInstance(uint16 instance_id, bool &is_perma) { char *query = nullptr; uint32 start_time = 0; uint32 duration = 0; uint32 never_expires = 0; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT start_time, duration, never_expires FROM instance_list WHERE id=%u", instance_id)); safe_delete_array(query); if (!results.Success()) { is_perma = false; return 0; } if (results.RowCount() == 0) { is_perma = false; return 0; } auto row = results.begin(); start_time = atoi(row[0]); duration = atoi(row[1]); never_expires = atoi(row[2]); if(never_expires == 1) { is_perma = true; return 0; } is_perma = false; timeval tv; gettimeofday(&tv, nullptr); return ((start_time + duration) - tv.tv_sec); } bool Database::GetUnusedInstanceID(uint16 &instance_id) { char *query = nullptr; uint32 count = RuleI(Zone, ReservedInstances); uint32 max = 65535; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT IFNULL(MAX(id),%u)+1 FROM instance_list WHERE id > %u", count, count)); safe_delete_array(query); if (!results.Success()) { instance_id = 0; return false; } if (results.RowCount() == 0) { instance_id = 0; return false; } auto row = results.begin(); if (atoi(row[0]) <= max) { instance_id = atoi(row[0]); return true; } results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where id > %u ORDER BY id", count)); safe_delete_array(query); if (!results.Success()) { instance_id = 0; return false; } if (results.RowCount() == 0) { instance_id = 0; return false; } count++; for (auto row = results.begin();row != results.end();++row) { if(count < atoi(row[0])) { instance_id = count; return true; } if(count > max) { instance_id = 0; return false; } count++; } instance_id = count; return true; } //perhaps purge any expireds too bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "INSERT INTO instance_list (id, zone, version, start_time, duration)" " values(%lu, %lu, %lu, UNIX_TIMESTAMP(), %lu)", (unsigned long)instance_id, (unsigned long)zone_id, (unsigned long)version, (unsigned long)duration)); safe_delete_array(query); return results.Success(); } void Database::PurgeExpiredInstances() { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where (start_time+duration) <= UNIX_TIMESTAMP() and never_expires = 0")); safe_delete_array(query); if (!results.Success()) return; if (results.RowCount() == 0) return; for (auto row = results.begin();row != results.end();++row) DeleteInstance(atoi(row[0])); } bool Database::AddClientToInstance(uint16 instance_id, uint32 char_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "INSERT INTO instance_list_player(id, charid) " "values(%lu, %lu)", (unsigned long)instance_id, (unsigned long)char_id)); safe_delete_array(query); return results.Success(); } bool Database::RemoveClientFromInstance(uint16 instance_id, uint32 char_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM instance_list_player WHERE id=%lu AND charid=%lu", (unsigned long)instance_id, (unsigned long)char_id)); safe_delete_array(query); return results.Success(); } bool Database::RemoveClientsFromInstance(uint16 instance_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "DELETE FROM instance_list_player WHERE id=%lu", (unsigned long)instance_id)); safe_delete_array(query); return results.Success(); } bool Database::CheckInstanceExists(uint16 instance_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT * FROM instance_list where id=%u", instance_id)); safe_delete_array(query); if (!results.Success()) return false; if (results.RowCount() == 0) return false; return true; } void Database::BuryCorpsesInInstance(uint16 instance_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "UPDATE player_corpses SET IsBurried=1, instanceid=0 WHERE instanceid=%u", instance_id)); safe_delete_array(query); } uint16 Database::GetInstanceVersion(uint16 instance_id) { if(instance_id == 0) return 0; char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT version FROM instance_list where id=%u", instance_id)); safe_delete_array(query); if (!results.Success()) return 0; if (results.RowCount() == 0) return 0; auto row = results.begin(); return atoi(row[0]); } uint16 Database::GetInstanceID(const char* zone, uint32 charid, int16 version) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT instance_list.id FROM instance_list, instance_list_player " "WHERE instance_list.zone=%u AND instance_list.version=%u AND instance_list.id=instance_list_player.id AND " "instance_list_player.charid=%u LIMIT 1;", GetZoneID(zone), version, charid, charid)); safe_delete_array(query); if (!results.Success()) return 0; if (results.RowCount() == 0) return 0; auto row = results.begin(); return atoi(row[0]); } uint16 Database::GetInstanceID(uint32 zone, uint32 charid, int16 version) { if(!zone) return 0; char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT instance_list.id FROM instance_list, instance_list_player " "WHERE instance_list.zone=%u AND instance_list.version=%u AND instance_list.id=instance_list_player.id AND " "instance_list_player.charid=%u LIMIT 1;", zone, version, charid)); safe_delete_array(query); if (!results.Success()) return 0; if (results.RowCount() == 0) return 0; auto row = results.begin(); return atoi(row[0]); } void Database::GetCharactersInInstance(uint16 instance_id, std::list &charid_list) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT charid FROM instance_list_player WHERE id=%u", instance_id)); if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in GetCharactersInInstace query '%s': %s", query, results.ErrorMessage().c_str()); safe_delete_array(query); return; } safe_delete_array(query); for(auto row=results.begin();row != results.end();++row) charid_list.push_back(atoi(row[0])); } void Database::AssignGroupToInstance(uint32 gid, uint32 instance_id) { char *query = nullptr; uint32 zone_id = ZoneIDFromInstanceID(instance_id); uint16 version = VersionFromInstanceID(instance_id); auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT charid FROM group_id WHERE groupid=%u", gid)); safe_delete_array(query); if (!results.Success()) return; for (auto row=results.begin();row != results.end();++row) { uint32 charid = atoi(row[0]); if(GetInstanceID(zone_id, charid, version) == 0) AddClientToInstance(instance_id, charid); } } void Database::AssignRaidToInstance(uint32 rid, uint32 instance_id) { char *query = nullptr; uint32 zone_id = ZoneIDFromInstanceID(instance_id); uint16 version = VersionFromInstanceID(instance_id); auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT charid FROM raid_members WHERE raidid=%u", rid)); safe_delete_array(query); if (!results.Success()) return; for (auto row=results.begin();row!=results.end();++row) { uint32 charid = atoi(row[0]); if(GetInstanceID(zone_id, charid, version) == 0) AddClientToInstance(instance_id, charid); } } void Database::FlagInstanceByGroupLeader(uint32 zone, int16 version, uint32 charid, uint32 gid) { uint16 id = GetInstanceID(zone, charid, version); if(id != 0) return; char ln[128]; memset(ln, 0, 128); strcpy(ln, GetGroupLeadershipInfo(gid, ln)); uint32 l_charid = GetCharacterID((const char*)ln); uint16 l_id = GetInstanceID(zone, l_charid, version); if(l_id == 0) return; AddClientToInstance(l_id, charid); } void Database::FlagInstanceByRaidLeader(uint32 zone, int16 version, uint32 charid, uint32 rid) { uint16 id = GetInstanceID(zone, charid, version); if(id != 0) return; uint32 l_charid = GetCharacterID(GetRaidLeaderName(rid)); uint16 l_id = GetInstanceID(zone, l_charid, version); if(l_id == 0) return; AddClientToInstance(l_id, charid); } void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "UPDATE `instance_list` SET start_time=UNIX_TIMESTAMP(), " "duration=%u WHERE id=%u", new_duration, instance_id)); safe_delete_array(query); } bool Database::GlobalInstance(uint16 instance_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT is_global from instance_list where id=%u LIMIT 1", instance_id)); safe_delete_array(query); if (!results.Success()) return false; if (results.RowCount() == 0) return false; auto row = results.begin(); return (atoi(row[0]) == 1) ? true : false; } void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win) { char *query = nullptr; std::string field; switch(theme) { case 1: { field = "guk_"; break; } case 2: { field = "mir_"; break; } case 3: { field = "mmc_"; break; } case 4: { field = "ruj_"; break; } case 5: { field = "tak_"; break; } default: { return; } } if (win) field += "wins"; else field += "losses"; auto results = QueryDatabase(query, MakeAnyLenString(&query, "UPDATE `adventure_stats` SET %s=%s+1 WHERE player_id=%u", field.c_str(), field.c_str(), char_id)); safe_delete_array(query); if (results.RowsAffected() != 0) return; QueryDatabase(query, MakeAnyLenString(&query, "INSERT INTO `adventure_stats` SET %s=1, player_id=%u", field.c_str(), char_id)); safe_delete_array(query); } bool Database::GetAdventureStats(uint32 char_id, uint32 &guk_w, uint32 &mir_w, uint32 &mmc_w, uint32 &ruj_w, uint32 &tak_w, uint32 &guk_l, uint32 &mir_l, uint32 &mmc_l, uint32 &ruj_l, uint32 &tak_l) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT `guk_wins`, `mir_wins`, `mmc_wins`, `ruj_wins`, `tak_wins`, " "`guk_losses`, `mir_losses`, `mmc_losses`, `ruj_losses`, `tak_losses` FROM `adventure_stats` WHERE player_id=%u", char_id)); safe_delete_array(query); if (!results.Success()) return false; if (results.RowCount() == 0) return false; auto row = results.begin(); guk_w = atoi(row[0]); mir_w = atoi(row[1]); mmc_w = atoi(row[2]); ruj_w = atoi(row[3]); tak_w = atoi(row[4]); guk_l = atoi(row[5]); mir_l = atoi(row[6]); mmc_l = atoi(row[7]); ruj_l = atoi(row[8]); tak_l = atoi(row[9]); return true; } uint32 Database::GetGuildIDByCharID(uint32 char_id) { char *query = nullptr; auto results = QueryDatabase(query, MakeAnyLenString(&query, "SELECT guild_id FROM guild_members WHERE char_id='%i'", char_id)); if (!results.Success()) { std::cerr << "Error in GetGuildIDByChar query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); return 0; } safe_delete_array(query); if (results.RowCount() == 0) return 0; auto row = results.begin(); return atoi(row[0]); }