123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539 |
- /*
- EQ2Emulator: Everquest II Server Emulator
- Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
- This file is part of EQ2Emulator.
- EQ2Emulator 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, either version 3 of the License, or
- (at your option) any later version.
- EQ2Emulator is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; 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 EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "../common/debug.h"
- #include <iostream>
- using namespace std;
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errmsg.h>
- //#include <mysqld_error.h>
- #include <limits.h>
- #include <ctype.h>
- #include <assert.h>
- #include <map>
- // Disgrace: for windows compile
- #ifdef WIN32
- #include <WinSock2.h>
- #include <windows.h>
- #define snprintf _snprintf
- #define strncasecmp _strnicmp
- #define strcasecmp _stricmp
- #else
- #include "unix.h"
- #include <netinet/in.h>
- #endif
- #include "database.h"
- #include "EQStream.h"
- #include "packet_functions.h"
- #include "emu_opcodes.h"
- #ifdef WORLD
- #include "../WorldServer/WorldDatabase.h"
- extern WorldDatabase database;
- #endif
- #ifdef LOGIN
- #include "../LoginServer/LoginDatabase.h"
- extern LoginDatabase database;
- #endif
- #ifdef PARSER
- #include "../PacketParser/ParserDatabase.h"
- extern ParserDatabase database;
- #endif
- #ifdef PATCHER
- #include "../PatchServer/PatcherDatabase.h"
- extern PatcherDatabase database;
- #endif
- #include "../common/EQEMuError.h"
- #include "../common/packet_dump.h"
- #include "../common/Log.h"
- #ifdef WORLD
- ThreadReturnType DBAsyncQueries(void* str)
- {
- // allow some buffer for multiple queries to collect
- Sleep(10);
- DBStruct* data = (DBStruct*)str;
- database.RunAsyncQueries(data->queryid);
- delete data;
- THREAD_RETURN(NULL);
- }
- #endif
- Database::Database()
- {
- InitVars();
- }
- bool Database::Init(bool silentLoad) {
- char host[200], user[200], passwd[200], database[200];
- int32 port=0;
- bool compression = false;
- bool items[6] = {false, false, false, false, false, false};
- const char* exampleIni[] = { "[Database]", "host = localhost", "user = root", "password = pass", "database = dbname", "### --- Assure each parameter is on a new line!" };
- if(!ReadDBINI(host, user, passwd, database, port, compression, items)) {
- //exit(1);
- return false;
- }
-
- if (!items[0] || !items[1] || !items[2] || !items[3])
- {
- LogWrite(DATABASE__ERROR, 0, "DB", "Database file %s is incomplete.", DB_INI_FILE);
- int i;
- for (i = 0; i < 4; i++)
- {
- if ( !items[i] )
- LogWrite(DATABASE__ERROR, 0, "DB", "Could not find parameter %s", exampleIni[i]);
- }
- LogWrite(DATABASE__ERROR, 0, "DB", "Example File:");
- int length = sizeof exampleIni / sizeof exampleIni[0];
- for(i=0;i<length;i++)
- LogWrite(DATABASE__ERROR, 0, "DB", "%s", exampleIni[i]);
- //exit (1);
- return false;
- }
-
- int32 errnum = 0;
- char errbuf[MYSQL_ERRMSG_SIZE];
- if (!Open(host, user, passwd, database,port, &errnum, errbuf))
- {
- LogWrite(DATABASE__ERROR, 0, "DB", "Failed to connect to database: Error: %s", errbuf);
- HandleMysqlError(errnum);
- //exit(1);
- return false;
- }
- else
- {
- if (!silentLoad)
- LogWrite(DATABASE__INFO, 0, "DB", "Using database '%s' at %s", database, host);
- }
- return true;
- }
- map<int16, int16> Database::GetVersions(){
- map<int16, int16> opcodes;
- Query query;
- MYSQL_ROW row;
- MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select distinct version_range1, version_range2 from opcodes");
- while(result && (row = mysql_fetch_row(result))){
- if(row[0] && row[1])
- opcodes[atoi(row[0])] = atoi(row[1]);
- }
- return opcodes;
- }
- map<string, uint16> Database::GetOpcodes(int16 version){
- map<string, uint16> opcodes;
- Query query;
- MYSQL_ROW row;
- MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select name, opcode from opcodes where %i between version_range1 and version_range2 order by version_range1, id", version);
- while(result && (row = mysql_fetch_row(result))){
- opcodes[row[0]] = atoi(row[1]);
- }
- return opcodes;
- }
- void Database::HandleMysqlError(int32 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;
- }
- }
- }
- void Database::InitVars() {
- }
- Database::~Database()
- {
- #ifdef WORLD
- DBQueryMutex.writelock(__FUNCTION__, __LINE__);
- activeQuerySessions.clear();
- DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__);
- DBAsyncMutex.writelock();
- continueAsync = false;
- map<int32, deque<Query*>>::iterator itr;
- for (itr = asyncQueries.begin(); itr != asyncQueries.end(); itr++)
- {
- asyncQueriesMutex[itr->first]->writelock();
- deque<Query*> queries = itr->second;
- while (queries.size() > 0)
- {
- Query* cur = queries.front();
- queries.pop_front();
- safe_delete(cur);
- }
- asyncQueriesMutex[itr->first]->releasewritelock();
- Mutex* mutex = asyncQueriesMutex[itr->first];
- asyncQueriesMutex.erase(itr->first);
- safe_delete(mutex);
- }
- asyncQueries.clear();
- asyncQueriesMutex.clear();
- DBAsyncMutex.releasewritelock();
- PurgeDBInstances();
- #endif
- }
- #ifdef WORLD
- void Query::AddQueryAsync(int32 queryID, Database* db, QUERY_TYPE type, const char* format, ...) {
- in_type = type;
- va_list args;
- va_start(args, format);
- #ifdef WIN32
- char* buffer;
- int buf_len = _vscprintf(format, args) + 1;
- buffer = new char[buf_len];
- vsprintf(buffer, format, args);
- #else
- char* buffer;
- int buf_len;
- va_list argcopy;
- va_copy(argcopy, args);
- buf_len = vsnprintf(NULL, 0, format, argcopy) + 1;
- va_end(argcopy);
- buffer = new char[buf_len];
- vsnprintf(buffer, buf_len, format, args);
- #endif
- va_end(args);
- query = string(buffer);
- Query* asyncQuery = new Query(this, queryID);
- safe_delete_array(buffer);
- db->AddAsyncQuery(asyncQuery);
- }
- void Query::RunQueryAsync(Database* db) {
- db->RunQuery(query.c_str(), query.length(), errbuf, &result, affected_rows, last_insert_id, &errnum, retry);
- }
- #endif
- MYSQL_RES* Query::RunQuery2(QUERY_TYPE type, const char* format, ...){
- va_list args;
- va_start( args, format );
- #ifdef WIN32
- char * buffer;
- int buf_len = _vscprintf( format, args ) + 1;
- buffer = new char[buf_len];
- vsprintf( buffer, format, args );
- #else
- char* buffer;
- int buf_len;
- va_list argcopy;
- va_copy(argcopy, args);
- buf_len = vsnprintf(NULL, 0, format, argcopy) + 1;
- va_end(argcopy);
-
- buffer = new char[buf_len];
- vsnprintf(buffer, buf_len, format, args);
- #endif
- va_end(args);
- query = string(buffer);
-
- safe_delete_array( buffer );
-
- return RunQuery2(query.c_str(), type);
- }
- MYSQL_RES* Query::RunQuery2(string in_query, QUERY_TYPE type){
- switch(type){
- case Q_SELECT:
- break;
- case Q_DBMS:
- case Q_REPLACE:
- case Q_DELETE:
- case Q_UPDATE:
- safe_delete(affected_rows);
- affected_rows = new int32;
- break;
- case Q_INSERT:
- safe_delete(last_insert_id);
- last_insert_id = new int32;
- }
- if(result){
- if(!multiple_results)
- multiple_results = new vector<MYSQL_RES*>();
- multiple_results->push_back(result);
- }
- query = in_query;
- #if defined WORLD && defined _DEBUG
- if (type == Q_UPDATE || type == Q_INSERT || type == Q_DELETE || type == Q_REPLACE)
- {
- char* filteredTables[] = { " characters", " character_", " `character_", " statistics", " variables", " char_colors", " `guild", " bugs" };
- bool match = false;
- for (int i = 0; i < sizeof(filteredTables) / sizeof(filteredTables[0]); i++)
- {
- if (query.find(filteredTables[i]) != std::string::npos) {
- match = true;
- break;
- }
- }
- try
- {
- if (!match)
- {
- FILE* pFile;
- pFile = fopen("sql_updates.sql", "a+");
- fwrite(query.c_str(), 1, query.length(), pFile);
- fwrite(";", sizeof(char), 1, pFile);
- fwrite("\n", sizeof(char), 1, pFile);
- fclose(pFile);
- }
- }
- catch (...) {}
- }
- #endif
-
- database.RunQuery(query.c_str(), query.length(), errbuf, &result, affected_rows, last_insert_id, &errnum, retry);
- return result;
- }
- #ifdef WORLD
- void Database::RunAsyncQueries(int32 queryid)
- {
- Database* asyncdb = FindFreeInstance();
- DBAsyncMutex.writelock();
- map<int32, deque<Query*>>::iterator itr = asyncQueries.find(queryid);
- if (itr == asyncQueries.end())
- {
- DBAsyncMutex.releasewritelock();
- return;
- }
- asyncQueriesMutex[queryid]->writelock();
- deque<Query*> queries;
- while (itr->second.size())
- {
- Query* cur = itr->second.front();
- queries.push_back(cur);
- itr->second.pop_front();
- }
- itr->second.clear();
- asyncQueries.erase(itr);
- DBAsyncMutex.releasewritelock();
- asyncQueriesMutex[queryid]->releasewritelock();
- int32 count = 0;
- while (queries.size() > 0)
- {
- Query* cur = queries.front();
- cur->RunQueryAsync(asyncdb);
- this->RemoveActiveQuery(cur);
- queries.pop_front();
- safe_delete(cur);
- }
- FreeDBInstance(asyncdb);
- bool isActive = IsActiveQuery(queryid);
- if (isActive)
- {
- continueAsync = true;
- DBStruct* tmp = new DBStruct;
- tmp->queryid = queryid;
- #ifdef WIN32
- _beginthread(DBAsyncQueries, 0, (void*)tmp);
- #else
- pthread_t t1;
- pthread_create(&t1, NULL, DBAsyncQueries, (void*)tmp);
- pthread_detach(t1);
- #endif
- }
- }
- void Database::AddAsyncQuery(Query* query)
- {
- DBAsyncMutex.writelock();
- map<int32, Mutex*>::iterator mutexItr = asyncQueriesMutex.find(query->GetQueryID());
- if (mutexItr == asyncQueriesMutex.end())
- {
- Mutex* queryMutex = new Mutex();
- queryMutex->SetName("AsyncQuery" + query->GetQueryID());
- asyncQueriesMutex.insert(make_pair(query->GetQueryID(), queryMutex));
- }
- map<int32, deque<Query*>>::iterator itr = asyncQueries.find(query->GetQueryID());
- asyncQueriesMutex[query->GetQueryID()]->writelock();
- if ( itr != asyncQueries.end())
- itr->second.push_back(query);
- else
- {
- deque<Query*> queue;
- queue.push_back(query);
- asyncQueries.insert(make_pair(query->GetQueryID(), queue));
- }
- AddActiveQuery(query);
- asyncQueriesMutex[query->GetQueryID()]->releasewritelock();
- DBAsyncMutex.releasewritelock();
- bool isActive = IsActiveQuery(query->GetQueryID(), query);
- if (!isActive)
- {
- continueAsync = true;
- DBStruct* tmp = new DBStruct;
- tmp->queryid = query->GetQueryID();
- #ifdef WIN32
- _beginthread(DBAsyncQueries, 0, (void*)tmp);
- #else
- pthread_t t1;
- pthread_create(&t1, NULL, DBAsyncQueries, (void*)tmp);
- pthread_detach(t1);
- #endif
- }
- }
- Database* Database::FindFreeInstance()
- {
- Database* db_inst = 0;
- map<Database*, bool>::iterator itr;
- DBInstanceMutex.writelock(__FUNCTION__, __LINE__);
- for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) {
- if (!itr->second)
- {
- db_inst = itr->first;
- itr->second = true;
- break;
- }
- }
- if (!db_inst)
- {
- WorldDatabase* tmp = new WorldDatabase();
- db_inst = (Database*)tmp;
- tmp->Init();
- tmp->ConnectNewDatabase();
- dbInstances.insert(make_pair(db_inst, true));
- }
- DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__);
- return db_inst;
- }
- void Database::PurgeDBInstances()
- {
- map<Database*, bool>::iterator itr;
- DBInstanceMutex.writelock(__FUNCTION__, __LINE__);
- for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) {
- Database* tmpInst = itr->first;
- safe_delete(tmpInst);
- }
- dbInstances.clear();
- DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__);
- }
- void Database::PingAsyncDatabase()
- {
- map<Database*, bool>::iterator itr;
- DBInstanceMutex.readlock(__FUNCTION__, __LINE__);
- for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) {
- Database* tmpInst = itr->first;
- tmpInst->ping();
- }
- DBInstanceMutex.releasereadlock(__FUNCTION__, __LINE__);
- }
- void Database::FreeDBInstance(Database* cur)
- {
- DBInstanceMutex.writelock(__FUNCTION__, __LINE__);
- dbInstances[cur] = false;
- DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__);
- }
- void Database::RemoveActiveQuery(Query* query)
- {
- DBQueryMutex.writelock(__FUNCTION__, __LINE__);
- vector<Query*>::iterator itr;
- for (itr = activeQuerySessions.begin(); itr != activeQuerySessions.end(); itr++)
- {
- Query* curQuery = *itr;
- if (query == curQuery)
- {
- activeQuerySessions.erase(itr);
- break;
- }
- }
- DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__);
- }
- void Database::AddActiveQuery(Query* query)
- {
- DBQueryMutex.writelock(__FUNCTION__, __LINE__);
- activeQuerySessions.push_back(query);
- DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__);
- }
- bool Database::IsActiveQuery(int32 id, Query* skip)
- {
- bool isActive = false;
- DBQueryMutex.readlock(__FUNCTION__, __LINE__);
- vector<Query*>::iterator itr;
- for (itr = activeQuerySessions.begin(); itr != activeQuerySessions.end(); itr++)
- {
- Query* query = *itr;
- if (query == skip)
- continue;
- if (query->GetQueryID() == id)
- {
- isActive = true;
- break;
- }
- }
- DBQueryMutex.releasereadlock(__FUNCTION__, __LINE__);
- return isActive;
- }
- #endif
|