diff options
Diffstat (limited to 'src/query.cc')
| -rw-r--r-- | src/query.cc | 392 |
1 files changed, 265 insertions, 127 deletions
diff --git a/src/query.cc b/src/query.cc index 35b9aca..c55806d 100644 --- a/src/query.cc +++ b/src/query.cc @@ -25,124 +25,62 @@ struct TWorker{ pthread_t Thread; }; +static int g_NumWorkers; static TWorker *g_Workers; static TQueryQueue *g_QueryQueue; -// Query Request +// Query Name //============================================================================== -TWriteBuffer QueryBeginRequest(TQuery *Query, int QueryType){ - Query->Request = TReadBuffer{}; - TWriteBuffer WriteBuffer = TWriteBuffer(Query->Buffer, Query->BufferSize); - WriteBuffer.Write8((uint8)QueryType); - return WriteBuffer; -} - -bool QueryFinishRequest(TQuery *Query, TWriteBuffer WriteBuffer){ - ASSERT(WriteBuffer.Buffer == Query->Buffer - && WriteBuffer.Size == Query->BufferSize - && WriteBuffer.Position >= 1); - bool Result = !WriteBuffer.Overflowed(); - if(Result){ - Query->Request = TReadBuffer(WriteBuffer.Buffer, WriteBuffer.Position); - } - return Result; +const char *QueryName(int QueryType){ + const char *Name = "UNKNOWN"; + switch(QueryType){ + case QUERY_LOGIN: Name = "LOGIN"; break; + case QUERY_INTERNAL_RESOLVE_WORLD: Name = "INTERNAL_RESOLVE_WORLD"; break; + case QUERY_CHECK_ACCOUNT_PASSWORD: Name = "CHECK_ACCOUNT_PASSWORD"; break; + case QUERY_LOGIN_ACCOUNT: Name = "LOGIN_ACCOUNT"; break; + case QUERY_LOGIN_ADMIN: Name = "LOGIN_ADMIN"; break; + case QUERY_LOGIN_GAME: Name = "LOGIN_GAME"; break; + case QUERY_LOGOUT_GAME: Name = "LOGOUT_GAME"; break; + case QUERY_SET_NAMELOCK: Name = "SET_NAMELOCK"; break; + case QUERY_BANISH_ACCOUNT: Name = "BANISH_ACCOUNT"; break; + case QUERY_SET_NOTATION: Name = "SET_NOTATION"; break; + case QUERY_REPORT_STATEMENT: Name = "REPORT_STATEMENT"; break; + case QUERY_BANISH_IP_ADDRESS: Name = "BANISH_IP_ADDRESS"; break; + case QUERY_LOG_CHARACTER_DEATH: Name = "LOG_CHARACTER_DEATH"; break; + case QUERY_ADD_BUDDY: Name = "ADD_BUDDY"; break; + case QUERY_REMOVE_BUDDY: Name = "REMOVE_BUDDY"; break; + case QUERY_DECREMENT_IS_ONLINE: Name = "DECREMENT_IS_ONLINE"; break; + case QUERY_FINISH_AUCTIONS: Name = "FINISH_AUCTIONS"; break; + case QUERY_TRANSFER_HOUSES: Name = "TRANSFER_HOUSES"; break; + case QUERY_EVICT_FREE_ACCOUNTS: Name = "EVICT_FREE_ACCOUNTS"; break; + case QUERY_EVICT_DELETED_CHARACTERS: Name = "EVICT_DELETED_CHARACTERS"; break; + case QUERY_EVICT_EX_GUILDLEADERS: Name = "EVICT_EX_GUILDLEADERS"; break; + case QUERY_INSERT_HOUSE_OWNER: Name = "INSERT_HOUSE_OWNER"; break; + case QUERY_UPDATE_HOUSE_OWNER: Name = "UPDATE_HOUSE_OWNER"; break; + case QUERY_DELETE_HOUSE_OWNER: Name = "DELETE_HOUSE_OWNER"; break; + case QUERY_GET_HOUSE_OWNERS: Name = "GET_HOUSE_OWNERS"; break; + case QUERY_GET_AUCTIONS: Name = "GET_AUCTIONS"; break; + case QUERY_START_AUCTION: Name = "START_AUCTION"; break; + case QUERY_INSERT_HOUSES: Name = "INSERT_HOUSES"; break; + case QUERY_CLEAR_IS_ONLINE: Name = "CLEAR_IS_ONLINE"; break; + case QUERY_CREATE_PLAYERLIST: Name = "CREATE_PLAYERLIST"; break; + case QUERY_LOG_KILLED_CREATURES: Name = "LOG_KILLED_CREATURES"; break; + case QUERY_LOAD_PLAYERS: Name = "LOAD_PLAYERS"; break; + case QUERY_EXCLUDE_FROM_AUCTIONS: Name = "EXCLUDE_FROM_AUCTIONS"; break; + case QUERY_CANCEL_HOUSE_TRANSFER: Name = "CANCEL_HOUSE_TRANSFER"; break; + case QUERY_LOAD_WORLD_CONFIG: Name = "LOAD_WORLD_CONFIG"; break; + case QUERY_CREATE_ACCOUNT: Name = "CREATE_ACCOUNT"; break; + case QUERY_CREATE_CHARACTER: Name = "CREATE_CHARACTER"; break; + case QUERY_GET_ACCOUNT_SUMMARY: Name = "GET_ACCOUNT_SUMMARY"; break; + case QUERY_GET_CHARACTER_PROFILE: Name = "GET_CHARACTER_PROFILE"; break; + case QUERY_GET_WORLDS: Name = "GET_WORLDS"; break; + case QUERY_GET_ONLINE_CHARACTERS: Name = "GET_ONLINE_CHARACTERS"; break; + case QUERY_GET_KILL_STATISTICS: Name = "GET_KILL_STATISTICS"; break; + default: Name = "UNKNOWN"; break; + } + return Name; } -bool QueryInternalResolveWorld(TQuery *Query, const char *World){ - TWriteBuffer WriteBuffer = QueryBeginRequest(Query, QUERY_INTERNAL_RESOLVE_WORLD); - WriteBuffer.WriteString(World); - return QueryFinishRequest(Query, WriteBuffer); -} - -// Query Response -//============================================================================== -TWriteBuffer *QueryBeginResponse(TQuery *Query, int Status){ - Query->QueryStatus = Status; - Query->Response = TWriteBuffer(Query->Buffer, Query->BufferSize); - Query->Response.Write16(0); - Query->Response.Write8((uint8)Status); - return &Query->Response; -} - -bool QueryFinishResponse(TQuery *Query){ - TWriteBuffer *Response = &Query->Response; - if(Response->Position <= 2){ - LOG_ERR("Invalid response size"); - return false; - } - - int PayloadSize = Response->Position - 2; - if(PayloadSize < 0xFFFF){ - Response->Rewrite16(0, (uint16)PayloadSize); - }else{ - Response->Rewrite16(0, 0xFFFF); - Response->Insert32(2, (uint32)PayloadSize); - } - - return !Response->Overflowed(); -} - -void QueryOk(TQuery *Query){ - QueryBeginResponse(Query, QUERY_STATUS_OK); - QueryFinishResponse(Query); -} - -void QueryError(TQuery *Query, int ErrorCode){ - TWriteBuffer *Response = QueryBeginResponse(Query, QUERY_STATUS_ERROR); - Response->Write8((uint8)ErrorCode); - QueryFinishResponse(Query); -} - -void QueryFailed(TQuery *Query){ - QueryBeginResponse(Query, QUERY_STATUS_FAILED); - QueryFinishResponse(Query); -} - - -// Query Processing -//============================================================================== -static bool ProcessInternalResolveWorld(TDatabase *Database, TQuery *Query); -static bool ProcessCheckAccountPassword(TDatabase *Database, TQuery *Query); -static bool ProcessLoginAccount(TDatabase *Database, TQuery *Query); -static bool ProcessLoginAdmin(TDatabase *Database, TQuery *Query); -static bool ProcessLoginGame(TDatabase *Database, TQuery *Query); -static bool ProcessLogoutGame(TDatabase *Database, TQuery *Query); -static bool ProcessSetNamelock(TDatabase *Database, TQuery *Query); -static bool ProcessBanishAccount(TDatabase *Database, TQuery *Query); -static bool ProcessSetNotation(TDatabase *Database, TQuery *Query); -static bool ProcessReportStatement(TDatabase *Database, TQuery *Query); -static bool ProcessBanishIpAddress(TDatabase *Database, TQuery *Query); -static bool ProcessLogCharacterDeath(TDatabase *Database, TQuery *Query); -static bool ProcessAddBuddy(TDatabase *Database, TQuery *Query); -static bool ProcessRemoveBuddy(TDatabase *Database, TQuery *Query); -static bool ProcessDecrementIsOnline(TDatabase *Database, TQuery *Query); -static bool ProcessFinishAuctions(TDatabase *Database, TQuery *Query); -static bool ProcessTransferHouses(TDatabase *Database, TQuery *Query); -static bool ProcessEvictFreeAccounts(TDatabase *Database, TQuery *Query); -static bool ProcessEvictDeletedCharacters(TDatabase *Database, TQuery *Query); -static bool ProcessEvictExGuildleaders(TDatabase *Database, TQuery *Query); -static bool ProcessInsertHouseOwner(TDatabase *Database, TQuery *Query); -static bool ProcessUpdateHouseOwner(TDatabase *Database, TQuery *Query); -static bool ProcessDeleteHouseOwner(TDatabase *Database, TQuery *Query); -static bool ProcessGetHouseOwners(TDatabase *Database, TQuery *Query); -static bool ProcessGetAuctions(TDatabase *Database, TQuery *Query); -static bool ProcessStartAuction(TDatabase *Database, TQuery *Query); -static bool ProcessInsertHouses(TDatabase *Database, TQuery *Query); -static bool ProcessClearIsOnline(TDatabase *Database, TQuery *Query); -static bool ProcessCreatePlayerlist(TDatabase *Database, TQuery *Query); -static bool ProcessLogKilledCreatures(TDatabase *Database, TQuery *Query); -static bool ProcessLoadPlayers(TDatabase *Database, TQuery *Query); -static bool ProcessExcludeFromAuctions(TDatabase *Database, TQuery *Query); -static bool ProcessCancelHouseTransfer(TDatabase *Database, TQuery *Query); -static bool ProcessLoadWorldConfig(TDatabase *Database, TQuery *Query); -static bool ProcessCreateAccount(TDatabase *Database, TQuery *Query); -static bool ProcessCreateCharacter(TDatabase *Database, TQuery *Query); -static bool ProcessGetAccountSummary(TDatabase *Database, TQuery *Query); -static bool ProcessGetCharacterProfile(TDatabase *Database, TQuery *Query); -static bool ProcessGetWorlds(TDatabase *Database, TQuery *Query); -static bool ProcessGetOnlineCharacters(TDatabase *Database, TQuery *Query); -static bool ProcessGetKillStatistics(TDatabase *Database, TQuery *Query); - // Query Queue and Workers //============================================================================== TQuery *QueryNew(void){ @@ -232,26 +170,26 @@ static void *WorkerThread(void *Data){ ASSERT(Data != NULL); TWorker *Worker = (TWorker*)Data; if(AtomicLoad(&Worker->Stop)){ - LOG_WARN("Worker#%d Stopping on entry...", Worker->WorkerID); + LOG_WARN("Worker#%d: Stopping on entry...", Worker->WorkerID); AtomicStore(&Worker->Status, WORKER_STATUS_DONE); return NULL; } TDatabase *Database = DatabaseOpen(); if(Database == NULL){ - LOG_ERR("Worker#%d Failed to connect to database", Worker->WorkerID); + LOG_ERR("Worker#%d: Failed to connect to database", Worker->WorkerID); AtomicStore(&Worker->Status, WORKER_STATUS_DONE); return NULL; } - LOG("Worker#%d ACTIVE...", Worker->WorkerID); + LOG("Worker#%d: ACTIVE...", Worker->WorkerID); AtomicStore(&Worker->Status, WORKER_STATUS_ACTIVE); while(TQuery *Query = QueryDequeue(&Worker->Stop)){ bool (*ProcessQuery)(TDatabase*, TQuery*) = NULL; Query->QueryType = Query->Request.Read8(); - /*switch(Query->QueryType){ + switch(Query->QueryType){ case QUERY_INTERNAL_RESOLVE_WORLD: ProcessQuery = ProcessInternalResolveWorld; break; - case QUERY_CHECK_ACCOUNT_PASSWORD: ProcessQuery = ProcessCheckAccountPassword; break; +/* case QUERY_CHECK_ACCOUNT_PASSWORD: ProcessQuery = ProcessCheckAccountPassword; break; case QUERY_LOGIN_ACCOUNT: ProcessQuery = ProcessLoginAccount; break; case QUERY_LOGIN_ADMIN: ProcessQuery = ProcessLoginAdmin; break; case QUERY_LOGIN_GAME: ProcessQuery = ProcessLoginGame; break; @@ -291,7 +229,8 @@ static void *WorkerThread(void *Data){ case QUERY_GET_WORLDS: ProcessQuery = ProcessGetWorlds; break; case QUERY_GET_ONLINE_CHARACTERS: ProcessQuery = ProcessGetOnlineCharacters; break; case QUERY_GET_KILL_STATISTICS: ProcessQuery = ProcessGetKillStatistics; break; - }*/ +*/ + } if(ProcessQuery != NULL && DatabaseCheckpoint(Database)){ // NOTE(fusion): A minimum of 1 attempt is ASSUMED. @@ -302,6 +241,12 @@ static void *WorkerThread(void *Data){ break; } Attempts -= 1; + + // NOTE(fusion): This one is important because we want to know + // whether some query is failing too often, in which case there + // may be a problem with it. + LOG_WARN("Worker#%d: Query %s failed, retrying...", + Worker->WorkerID, QueryName(Query->QueryType)); } }else{ QueryFailed(Query); @@ -310,7 +255,7 @@ static void *WorkerThread(void *Data){ QueryDone(Query); } - LOG("Worker#%d DONE...", Worker->WorkerID); + LOG("Worker#%d: DONE...", Worker->WorkerID); DatabaseClose(Database); AtomicStore(&Worker->Status, WORKER_STATUS_DONE); return NULL; @@ -331,9 +276,13 @@ bool InitQuery(void){ g_QueryQueue->MaxQueries = 2 * g_Config.MaxConnections; g_QueryQueue->Queries = (TQuery**)calloc(g_QueryQueue->MaxQueries, sizeof(TQuery*)); - int NumWorkers = g_Config.QueryWorkerThreads; - g_Workers = (TWorker*)calloc(NumWorkers, sizeof(TWorker)); - for(int i = 0; i < NumWorkers; i += 1){ + g_NumWorkers = g_Config.QueryWorkerThreads; + if(g_NumWorkers > DatabaseMaxConcurrency()){ + g_NumWorkers = DatabaseMaxConcurrency(); + } + + g_Workers = (TWorker*)calloc(g_NumWorkers, sizeof(TWorker)); + for(int i = 0; i < g_NumWorkers; i += 1){ TWorker *Worker = &g_Workers[i]; Worker->WorkerID = i; AtomicStore(&Worker->Status, WORKER_STATUS_SPAWNING); @@ -350,7 +299,7 @@ bool InitQuery(void){ int NumWorkersSpawning = 0; int NumWorkersActive = 0; int NumWorkersDone = 0; - for(int i = 0; i < NumWorkers; i += 1){ + for(int i = 0; i < g_NumWorkers; i += 1){ int Status = AtomicLoad(&g_Workers[i].Status); if(Status == WORKER_STATUS_SPAWNING){ NumWorkersSpawning += 1; @@ -373,7 +322,7 @@ bool InitQuery(void){ return false; } - ASSERT(NumWorkersActive == NumWorkers); + ASSERT(NumWorkersActive == g_NumWorkers); break; } @@ -384,15 +333,15 @@ void ExitQuery(void){ if(g_Workers != NULL){ ASSERT(g_QueryQueue != NULL); - for(int i = 0; i < g_Config.QueryWorkerThreads; i += 1){ + for(int i = 0; i < g_NumWorkers; i += 1){ AtomicStore(&g_Workers[i].Stop, 1); } pthread_cond_broadcast(&g_QueryQueue->WorkAvailable); - for(int i = 0; i < g_Config.QueryWorkerThreads; i += 1){ + for(int i = 0; i < g_NumWorkers; i += 1){ // IMPORTANT(fusion): There is no "invalid" pthread handle so this // is non-standard behaviour. Nevertheless the game server uses it - // and seems to be consistent on Linux, which is what matters at + // and it seems to be consistent on Linux, which is what matters at // the end of the day. if(g_Workers[i].Thread != 0){ pthread_join(g_Workers[i].Thread, NULL); @@ -420,6 +369,164 @@ void ExitQuery(void){ } } +// Query Request +//============================================================================== +TWriteBuffer QueryBeginRequest(TQuery *Query, int QueryType){ + Query->Request = TReadBuffer{}; + TWriteBuffer WriteBuffer = TWriteBuffer(Query->Buffer, Query->BufferSize); + WriteBuffer.Write8((uint8)QueryType); + return WriteBuffer; +} + +bool QueryFinishRequest(TQuery *Query, TWriteBuffer WriteBuffer){ + ASSERT(WriteBuffer.Buffer == Query->Buffer + && WriteBuffer.Size == Query->BufferSize + && WriteBuffer.Position >= 1); + bool Result = !WriteBuffer.Overflowed(); + if(Result){ + Query->Request = TReadBuffer(WriteBuffer.Buffer, WriteBuffer.Position); + } + return Result; +} + +bool QueryInternalResolveWorld(TQuery *Query, const char *World){ + TWriteBuffer WriteBuffer = QueryBeginRequest(Query, QUERY_INTERNAL_RESOLVE_WORLD); + WriteBuffer.WriteString(World); + return QueryFinishRequest(Query, WriteBuffer); +} + +// Query Response +//============================================================================== +TWriteBuffer *QueryBeginResponse(TQuery *Query, int Status){ + Query->QueryStatus = Status; + Query->Response = TWriteBuffer(Query->Buffer, Query->BufferSize); + Query->Response.Write16(0); + Query->Response.Write8((uint8)Status); + return &Query->Response; +} + +bool QueryFinishResponse(TQuery *Query){ + TWriteBuffer *Response = &Query->Response; + if(Response->Position <= 2){ + LOG_ERR("Invalid response size"); + return false; + } + + int PayloadSize = Response->Position - 2; + if(PayloadSize < 0xFFFF){ + Response->Rewrite16(0, (uint16)PayloadSize); + }else{ + Response->Rewrite16(0, 0xFFFF); + Response->Insert32(2, (uint32)PayloadSize); + } + + return !Response->Overflowed(); +} + +void QueryOk(TQuery *Query){ + QueryBeginResponse(Query, QUERY_STATUS_OK); + QueryFinishResponse(Query); +} + +void QueryError(TQuery *Query, int ErrorCode){ + TWriteBuffer *Response = QueryBeginResponse(Query, QUERY_STATUS_ERROR); + Response->Write8((uint8)ErrorCode); + QueryFinishResponse(Query); +} + +void QueryFailed(TQuery *Query){ + QueryBeginResponse(Query, QUERY_STATUS_FAILED); + QueryFinishResponse(Query); +} + +// Query Processing +//============================================================================== +// IMPORTANT(fusion): Query functions will return TRUE when they executed normally, +// and FALSE when they were interrupted by some unexpected error such as a database +// failure. The query response should only be written at the very end, when we know +// it has SUCCEEDED, so that request data is kept intact (because they share the +// query buffer) if the caller decides to retry the operation. + +// NOTE(fusion): These should be used with query functions to reduce clutter. +#define QUERY_STOP_IF(Cond) \ + do{ \ + if(Cond){ \ + return false; \ + } \ + }while(0) + +#define QUERY_ERROR_IF(Cond, ErrorCode) \ + do{ \ + if(Cond){ \ + QueryError(Query, ErrorCode); \ + return true; \ + } \ + }while(0) + +#define QUERY_FAIL_IF(Cond) \ + do{ \ + if(Cond){ \ + QueryFailed(Query); \ + return true; \ + } \ + }while(0) + +bool ProcessInternalResolveWorld(TDatabase *Database, TQuery *Query){ + char World[30]; + TReadBuffer Request = Query->Request; + Request.ReadString(World, sizeof(World)); + + int WorldID; + QUERY_STOP_IF(!GetWorldID(Database, World, &WorldID)); + QUERY_FAIL_IF(WorldID <= 0); + + Query->WorldID = WorldID; + QueryOk(Query); + return true; +} + +bool ProcessCheckAccountPassword(TDatabase *Database, TQuery *Query); +bool ProcessLoginAccount(TDatabase *Database, TQuery *Query); +bool ProcessLoginAdmin(TDatabase *Database, TQuery *Query); +bool ProcessLoginGame(TDatabase *Database, TQuery *Query); +bool ProcessLogoutGame(TDatabase *Database, TQuery *Query); +bool ProcessSetNamelock(TDatabase *Database, TQuery *Query); +bool ProcessBanishAccount(TDatabase *Database, TQuery *Query); +bool ProcessSetNotation(TDatabase *Database, TQuery *Query); +bool ProcessReportStatement(TDatabase *Database, TQuery *Query); +bool ProcessBanishIpAddress(TDatabase *Database, TQuery *Query); +bool ProcessLogCharacterDeath(TDatabase *Database, TQuery *Query); +bool ProcessAddBuddy(TDatabase *Database, TQuery *Query); +bool ProcessRemoveBuddy(TDatabase *Database, TQuery *Query); +bool ProcessDecrementIsOnline(TDatabase *Database, TQuery *Query); +bool ProcessFinishAuctions(TDatabase *Database, TQuery *Query); +bool ProcessTransferHouses(TDatabase *Database, TQuery *Query); +bool ProcessEvictFreeAccounts(TDatabase *Database, TQuery *Query); +bool ProcessEvictDeletedCharacters(TDatabase *Database, TQuery *Query); +bool ProcessEvictExGuildleaders(TDatabase *Database, TQuery *Query); +bool ProcessInsertHouseOwner(TDatabase *Database, TQuery *Query); +bool ProcessUpdateHouseOwner(TDatabase *Database, TQuery *Query); +bool ProcessDeleteHouseOwner(TDatabase *Database, TQuery *Query); +bool ProcessGetHouseOwners(TDatabase *Database, TQuery *Query); +bool ProcessGetAuctions(TDatabase *Database, TQuery *Query); +bool ProcessStartAuction(TDatabase *Database, TQuery *Query); +bool ProcessInsertHouses(TDatabase *Database, TQuery *Query); +bool ProcessClearIsOnline(TDatabase *Database, TQuery *Query); +bool ProcessCreatePlayerlist(TDatabase *Database, TQuery *Query); +bool ProcessLogKilledCreatures(TDatabase *Database, TQuery *Query); +bool ProcessLoadPlayers(TDatabase *Database, TQuery *Query); +bool ProcessExcludeFromAuctions(TDatabase *Database, TQuery *Query); +bool ProcessCancelHouseTransfer(TDatabase *Database, TQuery *Query); +bool ProcessLoadWorldConfig(TDatabase *Database, TQuery *Query); +bool ProcessCreateAccount(TDatabase *Database, TQuery *Query); +bool ProcessCreateCharacter(TDatabase *Database, TQuery *Query); +bool ProcessGetAccountSummary(TDatabase *Database, TQuery *Query); +bool ProcessGetCharacterProfile(TDatabase *Database, TQuery *Query); +bool ProcessGetWorlds(TDatabase *Database, TQuery *Query); +bool ProcessGetOnlineCharacters(TDatabase *Database, TQuery *Query); +bool ProcessGetKillStatistics(TDatabase *Database, TQuery *Query); + + // TODO(fusion): These are the old query processing functions. The new ones will // be very similar, except that we want a way to tell whether there was a database // failure, such as a connection reset, so we can automatically retry them up to @@ -1846,6 +1953,37 @@ void ProcessCreateAccountQuery(TConnection *Connection, TReadBuffer *Buffer){ SendQueryStatusOk(Connection); } +bool ProcessCreateAccountQuery(TConnection *Connection, TReadBuffer *Buffer){ + // TODO(fusion): We'd ideally want to automatically generate an account number + // and return it in case of success but that would also require a more robust + // website infrastructure with verification e-mails, etc... + char Email[100]; + char Password[30]; + int AccountID = (int)Buffer->Read32(); + Buffer->ReadString(Email, sizeof(Email)); + Buffer->ReadString(Password, sizeof(Password)); + + // NOTE(fusion): Inputs should be checked before hand. + QUERY_FAIL_IF(AccountID <= 0); + QUERY_FAIL_IF(StringEmpty(Email)); + QUERY_FAIL_IF(StringEmpty(Password)); + + uint8 Auth[64]; + QUERY_FAIL_IF(!GenerateAuth(Password, Auth, sizeof(Auth))); + + TransactionScope Tx("CreateAccount"); + QUERY_RETRY_IF(!Tx.Begin(Database)); + QUERY_RETRY_IF(!AccountNumberExists(Database, AccountID, &AccountExists)); + QUERY_ERROR_IF(AccountExists, 1); + QUERY_RETRY_IF(!AccountEmailExists(Database, Email, &AccountExists)); + QUERY_ERROR_IF(AccountExists, 2); + QUERY_FAIL_IF(!CreateAccount(AccountID, Email, Auth, sizeof(Auth))); + QUERY_RETRY_IF(!Tx.Commit()); + + QueryOk(Query); + return true; +} + void ProcessCreateCharacterQuery(TConnection *Connection, TReadBuffer *Buffer){ char WorldName[30]; char CharacterName[30]; |
