From aa31732158d3e457dfe630f3b2e56f53de89e2b1 Mon Sep 17 00:00:00 2001 From: fusion32 Date: Tue, 7 Oct 2025 18:50:02 -0300 Subject: make connections properly handle async queries This moves queries from `src/connections.cc` to `src/query.cc` and adds a few missing details. The only thing left is to review query processing and tie database operations to the `TDatabase` struct. --- src/connections.cc | 2007 ++++++---------------------------------------------- 1 file changed, 232 insertions(+), 1775 deletions(-) (limited to 'src/connections.cc') diff --git a/src/connections.cc b/src/connections.cc index 26630cc..ecd8d4e 100644 --- a/src/connections.cc +++ b/src/connections.cc @@ -88,7 +88,7 @@ int ListenerAccept(int Listener, uint32 *OutAddr, uint16 *OutPort){ uint32 Addr = ntohl(SocketAddr.sin_addr.s_addr); uint16 Port = ntohs(SocketAddr.sin_port); if(Addr != INADDR_LOOPBACK){ - LOG_ERR("Rejecting remote connection from %08X:%d", Addr, Port); + LOG_ERR("Rejecting connection %08X:%d: remote connection", Addr, Port); close(Socket); continue; } @@ -125,22 +125,9 @@ void CloseConnection(TConnection *Connection){ } } -void EnsureConnectionBuffer(TConnection *Connection){ - if(Connection->Buffer == NULL){ - Connection->Buffer = (uint8*)malloc(g_MaxConnectionPacketSize); - } -} - -void DeleteConnectionBuffer(TConnection *Connection){ - if(Connection->Buffer != NULL){ - free(Connection->Buffer); - Connection->Buffer = NULL; - } -} - TConnection *AssignConnection(int Socket, uint32 Addr, uint16 Port){ int ConnectionIndex = -1; - for(int i = 0; i < g_MaxConnections; i += 1){ + for(int i = 0; i < g_Config.MaxConnections; i += 1){ if(g_Connections[i].State == CONNECTION_FREE){ ConnectionIndex = i; break; @@ -172,7 +159,7 @@ void ReleaseConnection(TConnection *Connection){ if(Connection->State != CONNECTION_FREE){ LOG("Connection %s released", Connection->RemoteAddress); CloseConnection(Connection); - DeleteConnectionBuffer(Connection); + QueryDone(Connection->Query); memset(Connection, 0, sizeof(TConnection)); Connection->State = CONNECTION_FREE; } @@ -190,7 +177,12 @@ void CheckConnectionInput(TConnection *Connection, int Events){ return; } - EnsureConnectionBuffer(Connection); + if(Connection->Query == NULL){ + Connection->Query = QueryNew(); + } + + uint8 *Buffer = Connection->Query->Buffer; + int BufferSize = Connection->Query->BufferSize; while(true){ int ReadSize = Connection->RWSize; if(ReadSize == 0){ @@ -203,8 +195,8 @@ void CheckConnectionInput(TConnection *Connection, int Events){ } int BytesRead = read(Connection->Socket, - (Connection->Buffer + Connection->RWPosition), - (ReadSize - Connection->RWPosition)); + (Buffer + Connection->RWPosition), + (ReadSize - Connection->RWPosition)); if(BytesRead == -1){ if(errno != EAGAIN){ // NOTE(fusion): Connection error. @@ -220,12 +212,13 @@ void CheckConnectionInput(TConnection *Connection, int Events){ Connection->RWPosition += BytesRead; if(Connection->RWPosition >= ReadSize){ if(Connection->RWSize != 0){ - Connection->State = CONNECTION_PENDING_QUERY; + Connection->State = CONNECTION_REQUEST; Connection->LastActive = g_MonotonicTimeMS; + Connection->Query->Request = TReadBuffer(Buffer, Connection->RWSize); break; }else if(Connection->RWPosition == 2){ - int PayloadSize = BufferRead16LE(Connection->Buffer); - if(PayloadSize <= 0 || PayloadSize > g_MaxConnectionPacketSize){ + int PayloadSize = BufferRead16LE(Buffer); + if(PayloadSize <= 0 || PayloadSize > BufferSize){ CloseConnection(Connection); break; } @@ -235,8 +228,8 @@ void CheckConnectionInput(TConnection *Connection, int Events){ Connection->RWPosition = 0; } }else if(Connection->RWPosition == 6){ - int PayloadSize = (int)BufferRead32LE(Connection->Buffer + 2); - if(PayloadSize <= 0 || PayloadSize > g_MaxConnectionPacketSize){ + int PayloadSize = (int)BufferRead32LE(Buffer + 2); + if(PayloadSize <= 0 || PayloadSize > BufferSize){ CloseConnection(Connection); break; } @@ -251,16 +244,197 @@ void CheckConnectionInput(TConnection *Connection, int Events){ } } -void CheckConnectionQuery(TConnection *Connection){ - if(Connection->State == CONNECTION_PENDING_QUERY){ - //ProcessConnectionQuery(Connection); - }else if(Connection->State == CONNECTION_PROCESSING_QUERY - && QueryRefCount(Connection->Query) == 1){ - // - //Connection->State = CONNECTION_WRITING; +void SendQueryResponse(TConnection *Connection){ + ASSERT(Connection->Query != NULL); + if(Connection->State != CONNECTION_RESPONSE){ + LOG_ERR("Connection %s is not in a RESPONSE state (State: %d)", + Connection->RemoteAddress, Connection->State); + CloseConnection(Connection); + return; + } + + TWriteBuffer *Response = &Connection->Query->Response; + if(!Response->Overflowed()){ + Connection->State = CONNECTION_WRITING; + Connection->RWSize = Response->Position; + Connection->RWPosition = 0; + }else{ + LOG_ERR("Query buffer overflowed when writing to %s", + Connection->RemoteAddress); + CloseConnection(Connection); + } +} + +void SendQueryOk(TConnection *Connection){ + ASSERT(Connection->Query != NULL); + QueryOk(Connection->Query); + SendQueryResponse(Connection); +} + +void SendQueryError(TConnection *Connection, int ErrorCode){ + ASSERT(Connection->Query != NULL); + QueryError(Connection->Query, ErrorCode); + SendQueryResponse(Connection); +} + +void SendQueryFailed(TConnection *Connection){ + ASSERT(Connection->Query != NULL); + QueryFailed(Connection->Query); + SendQueryResponse(Connection); +} + +void CheckConnectionQueryRequest(TConnection *Connection){ + if(Connection->State != CONNECTION_REQUEST){ + return; + } + + ASSERT(Connection->Query != NULL); + TQuery *Query = Connection->Query; + TReadBuffer Request = Query->Request; + int QueryType = Request.Read8(); + if(!Connection->Authorized){ + if(QueryType != QUERY_LOGIN){ + LOG_ERR("Unauthorized query %d from %s", QueryType, Connection->RemoteAddress); + CloseConnection(Connection); + return; + } + + char Password[30] = {}; + char LoginData[30] = {}; + int ApplicationType = Request.Read8(); + Request.ReadString(Password, sizeof(Password)); + if(ApplicationType == APPLICATION_TYPE_GAME){ + Request.ReadString(LoginData, sizeof(LoginData)); + } + + if(!StringEq(g_Config.QueryManagerPassword, Password)){ + LOG_WARN("Invalid login attempt from %s", Connection->RemoteAddress); + SendQueryFailed(Connection); + return; + } + + // NOTE(fusion): The connection is AUTHORIZED at this point but we still + // need to check whether the application type is valid, and for the case + // of a game server, check whether the world name is valid. + if(ApplicationType == APPLICATION_TYPE_GAME){ + if(QueryInternalResolveWorld(Query, LoginData)){ + LOG("Connection %s AUTHORIZED to game server \"%s\"", + Connection->RemoteAddress, LoginData); + Connection->Authorized = true; + Connection->ApplicationType = APPLICATION_TYPE_GAME; + QueryEnqueue(Query); + }else{ + // TODO(fusion): This should probably be a PANIC? + LOG_ERR("Rejecting connection %s: unable to rewrite login query..." + " Try increasing the query buffer size", + Connection->RemoteAddress); + SendQueryFailed(Connection); + } + }else if(ApplicationType == APPLICATION_TYPE_LOGIN){ + LOG("Connection %s AUTHORIZED to login server", Connection->RemoteAddress); + Connection->Authorized = true; + Connection->ApplicationType = APPLICATION_TYPE_LOGIN; + SendQueryOk(Connection); + }else if(ApplicationType == APPLICATION_TYPE_WEB){ + LOG("Connection %s AUTHORIZED to web server", Connection->RemoteAddress); + Connection->Authorized = true; + Connection->ApplicationType = APPLICATION_TYPE_WEB; + SendQueryOk(Connection); + }else{ + LOG_WARN("Rejecting connection %s: unknown application type %d", + Connection->RemoteAddress, ApplicationType); + SendQueryFailed(Connection); + } + }else if(Connection->ApplicationType == APPLICATION_TYPE_GAME){ + // minimal check to see if the query type is valid and enqueue + if(QueryType == QUERY_LOGIN_GAME + || QueryType == QUERY_LOGOUT_GAME + || QueryType == QUERY_SET_NAMELOCK + || QueryType == QUERY_BANISH_ACCOUNT + || QueryType == QUERY_SET_NOTATION + || QueryType == QUERY_REPORT_STATEMENT + || QueryType == QUERY_BANISH_IP_ADDRESS + || QueryType == QUERY_LOG_CHARACTER_DEATH + || QueryType == QUERY_ADD_BUDDY + || QueryType == QUERY_REMOVE_BUDDY + || QueryType == QUERY_DECREMENT_IS_ONLINE + || QueryType == QUERY_FINISH_AUCTIONS + || QueryType == QUERY_TRANSFER_HOUSES + || QueryType == QUERY_EVICT_FREE_ACCOUNTS + || QueryType == QUERY_EVICT_DELETED_CHARACTERS + || QueryType == QUERY_EVICT_EX_GUILDLEADERS + || QueryType == QUERY_INSERT_HOUSE_OWNER + || QueryType == QUERY_UPDATE_HOUSE_OWNER + || QueryType == QUERY_DELETE_HOUSE_OWNER + || QueryType == QUERY_GET_HOUSE_OWNERS + || QueryType == QUERY_GET_AUCTIONS + || QueryType == QUERY_START_AUCTION + || QueryType == QUERY_INSERT_HOUSES + || QueryType == QUERY_CLEAR_IS_ONLINE + || QueryType == QUERY_CREATE_PLAYERLIST + || QueryType == QUERY_LOG_KILLED_CREATURES + || QueryType == QUERY_LOAD_PLAYERS + || QueryType == QUERY_EXCLUDE_FROM_AUCTIONS + || QueryType == QUERY_CANCEL_HOUSE_TRANSFER + || QueryType == QUERY_LOAD_WORLD_CONFIG){ + QueryEnqueue(Connection->Query); + }else{ + LOG_ERR("Unknown GAME query %d from %s", + QueryType, Connection->RemoteAddress); + SendQueryFailed(Connection); + } + }else if(Connection->ApplicationType == APPLICATION_TYPE_LOGIN){ + if(QueryType == QUERY_LOGIN_ACCOUNT){ + QueryEnqueue(Connection->Query); + }else{ + LOG_ERR("Unknown LOGIN query %d from %s", + QueryType, Connection->RemoteAddress); + SendQueryFailed(Connection); + } + }else if(Connection->ApplicationType == APPLICATION_TYPE_WEB){ + if(QueryType == QUERY_CHECK_ACCOUNT_PASSWORD + || QueryType == QUERY_CREATE_ACCOUNT + || QueryType == QUERY_CREATE_CHARACTER + || QueryType == QUERY_GET_ACCOUNT_SUMMARY + || QueryType == QUERY_GET_CHARACTER_PROFILE + || QueryType == QUERY_GET_WORLDS + || QueryType == QUERY_GET_ONLINE_CHARACTERS + || QueryType == QUERY_GET_KILL_STATISTICS){ + QueryEnqueue(Connection->Query); + }else{ + LOG_ERR("Unknown WEB query %d from %s", + QueryType, Connection->RemoteAddress); + SendQueryFailed(Connection); + } + } +} + +void CheckConnectionQueryResponse(TConnection *Connection){ + if(Connection->State != CONNECTION_RESPONSE){ + return; + } + + ASSERT(Connection->Query != NULL); + TQuery *Query = Connection->Query; + if(QueryRefCount(Connection->Query) != 1){ + return; + } + + if(Query->QueryType == QUERY_INTERNAL_RESOLVE_WORLD){ + if(Query->QueryStatus == QUERY_STATUS_OK){ + ASSERT(Query->WorldID != 0); + SendQueryOk(Connection); + }else{ + LOG_WARN("Dropping connection %s: unknown game world", + Connection->RemoteAddress); + SendQueryFailed(Connection); + } + }else{ + SendQueryResponse(Connection); } } + void CheckConnectionOutput(TConnection *Connection, int Events){ if((Events & POLLOUT) == 0 || Connection->Socket == -1){ return; @@ -270,9 +444,11 @@ void CheckConnectionOutput(TConnection *Connection, int Events){ return; } + ASSERT(Connection->Query != NULL); + uint8 *Buffer = Connection->Query->Buffer; while(true){ int BytesWritten = write(Connection->Socket, - (Connection->Buffer + Connection->RWPosition), + (Buffer + Connection->RWPosition), (Connection->RWSize - Connection->RWPosition)); if(BytesWritten == -1){ if(errno != EAGAIN){ @@ -286,6 +462,12 @@ void CheckConnectionOutput(TConnection *Connection, int Events){ Connection->State = CONNECTION_READING; Connection->RWSize = 0; Connection->RWPosition = 0; + + // NOTE(fusion): Close the connection if it's not authorized after + // the first query. + if(!Connection->Authorized){ + CloseConnection(Connection); + } break; } } @@ -298,9 +480,9 @@ void CheckConnection(TConnection *Connection, int Events){ CloseConnection(Connection); } - if(g_MaxConnectionIdleTime > 0){ + if(g_Config.MaxConnectionIdleTime > 0){ int IdleTime = (g_MonotonicTimeMS - Connection->LastActive); - if(IdleTime >= g_MaxConnectionIdleTime){ + if(IdleTime >= g_Config.MaxConnectionIdleTime){ LOG_WARN("Dropping connection %s due to inactivity", Connection->RemoteAddress); CloseConnection(Connection); @@ -323,17 +505,18 @@ void ProcessConnections(void){ } if(AssignConnection(Socket, Addr, Port) == NULL){ - LOG_ERR("Rejecting connection %08X:%d due to max number of" - " connections being reached (%d)", Addr, Port, g_MaxConnections); + LOG_ERR("Rejecting connection %08X:%d:" + " reached max number of connections (%d)", + Addr, Port, g_Config.MaxConnections); close(Socket); } } // NOTE(fusion): Gather active connections. int NumConnections = 0; - int *ConnectionIndices = (int*)alloca(g_MaxConnections * sizeof(int)); - pollfd *ConnectionFds = (pollfd*)alloca(g_MaxConnections * sizeof(pollfd)); - for(int i = 0; i < g_MaxConnections; i += 1){ + int *ConnectionIndices = (int*)alloca(g_Config.MaxConnections * sizeof(int)); + pollfd *ConnectionFds = (pollfd*)alloca(g_Config.MaxConnections * sizeof(pollfd)); + for(int i = 0; i < g_Config.MaxConnections; i += 1){ if(g_Connections[i].State == CONNECTION_FREE || g_Connections[i].Socket == -1){ continue; } @@ -361,7 +544,8 @@ void ProcessConnections(void){ TConnection *Connection = &g_Connections[ConnectionIndices[i]]; int Events = (int)ConnectionFds[i].revents; CheckConnectionInput(Connection, Events); - CheckConnectionQuery(Connection); + CheckConnectionQueryRequest(Connection); + CheckConnectionQueryResponse(Connection); CheckConnectionOutput(Connection, Events); CheckConnection(Connection, Events); } @@ -371,20 +555,20 @@ bool InitConnections(void){ ASSERT(g_Listener == -1); ASSERT(g_Connections == NULL); - LOG("Query manager port: %d", g_QueryManagerPort); - LOG("Max connections: %d", g_MaxConnections); - LOG("Max connection idle time: %dms", g_MaxConnectionIdleTime); - LOG("Max connection packet size: %d", g_MaxConnectionPacketSize); + LOG("Query manager port: %d", g_Config.QueryManagerPort); + LOG("Query buffer size: %d", g_Config.QueryBufferSize); + LOG("Max connections: %d", g_Config.MaxConnections); + LOG("Max connection idle time: %dms", g_Config.MaxConnectionIdleTime); - g_Listener = ListenerBind((uint16)g_QueryManagerPort); + g_Listener = ListenerBind((uint16)g_Config.QueryManagerPort); if(g_Listener == -1){ LOG_ERR("Failed to bind listener"); return false; } g_Connections = (TConnection*)calloc( - g_MaxConnections, sizeof(TConnection)); - for(int i = 0; i < g_MaxConnections; i += 1){ + g_Config.MaxConnections, sizeof(TConnection)); + for(int i = 0; i < g_Config.MaxConnections; i += 1){ g_Connections[i].State = CONNECTION_FREE; } @@ -398,7 +582,7 @@ void ExitConnections(void){ } if(g_Connections != NULL){ - for(int i = 0; i < g_MaxConnections; i += 1){ + for(int i = 0; i < g_Config.MaxConnections; i += 1){ ReleaseConnection(&g_Connections[i]); } @@ -407,1730 +591,3 @@ void ExitConnections(void){ } } -// Connection Queries -//============================================================================== -void CompoundBanishment(TBanishmentStatus Status, int *Days, bool *FinalWarning){ - // TODO(fusion): We might want to add all these constants as config values. - ASSERT(Days != NULL && FinalWarning != NULL); - if(Status.FinalWarning){ - *FinalWarning = false; - *Days = 0; // permanent - }else if(Status.TimesBanished > 5 || *FinalWarning){ - *FinalWarning = true; - if(*Days < 30){ - *Days = 30; - }else{ - *Days *= 2; - } - } -} - -TWriteBuffer PrepareResponse(TConnection *Connection, int Status){ - if(Connection->State != CONNECTION_PROCESSING_QUERY){ - LOG_ERR("Connection %s is not processing query (State: %d)", - Connection->RemoteAddress, Connection->State); - CloseConnection(Connection); - return TWriteBuffer(NULL, 0); - } - - TWriteBuffer WriteBuffer(Connection->Buffer, g_MaxConnectionPacketSize); - WriteBuffer.Write16(0); - WriteBuffer.Write8((uint8)Status); - return WriteBuffer; -} - -void SendResponse(TConnection *Connection, TWriteBuffer *WriteBuffer){ - if(Connection->State != CONNECTION_PROCESSING_QUERY){ - LOG_ERR("Connection %s is not processing query (State: %d)", - Connection->RemoteAddress, Connection->State); - CloseConnection(Connection); - return; - } - - ASSERT(WriteBuffer != NULL - && WriteBuffer->Buffer == Connection->Buffer - && WriteBuffer->Size == g_MaxConnectionPacketSize - && WriteBuffer->Position > 2); - - int PayloadSize = WriteBuffer->Position - 2; - if(PayloadSize < 0xFFFF){ - WriteBuffer->Rewrite16(0, (uint16)PayloadSize); - }else{ - WriteBuffer->Rewrite16(0, 0xFFFF); - WriteBuffer->Insert32(2, (uint32)PayloadSize); - } - - if(!WriteBuffer->Overflowed()){ - Connection->State = CONNECTION_WRITING; - Connection->RWSize = WriteBuffer->Position; - Connection->RWPosition = 0; - }else{ - LOG_ERR("Write buffer overflowed when writing response to %s", - Connection->RemoteAddress); - CloseConnection(Connection); - } -} - -void SendQueryStatusOk(TConnection *Connection){ - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - SendResponse(Connection, &WriteBuffer); -} - -void SendQueryStatusError(TConnection *Connection, int ErrorCode){ - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_ERROR); - WriteBuffer.Write8((uint8)ErrorCode); - SendResponse(Connection, &WriteBuffer); -} - -void SendQueryStatusFailed(TConnection *Connection){ - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_FAILED); - SendResponse(Connection, &WriteBuffer); -} - -void ProcessLoginQuery(TConnection *Connection, TReadBuffer *Buffer){ - char Password[30]; - char LoginData[30]; - int ApplicationType = Buffer->Read8(); - Buffer->ReadString(Password, sizeof(Password)); - if(ApplicationType == APPLICATION_TYPE_GAME){ - Buffer->ReadString(LoginData, sizeof(LoginData)); - } - - // TODO(fusion): Probably just disconnect on failed login attempt? Implement - // write then disconnect? - if(!StringEq(g_QueryManagerPassword, Password)){ - LOG_WARN("Invalid login attempt from %s", Connection->RemoteAddress); - SendQueryStatusFailed(Connection); - return; - } - - int WorldID = 0; - if(ApplicationType == APPLICATION_TYPE_GAME){ - WorldID = GetWorldID(LoginData); - if(WorldID == 0){ - LOG_WARN("Rejecting connection %s from unknown game server \"%s\"", - Connection->RemoteAddress, LoginData); - SendQueryStatusFailed(Connection); - return; - } - LOG("Connection %s AUTHORIZED to game server \"%s\" (%d)", - Connection->RemoteAddress, LoginData, WorldID); - }else if(ApplicationType == APPLICATION_TYPE_LOGIN){ - LOG("Connection %s AUTHORIZED to login server", Connection->RemoteAddress); - }else if(ApplicationType == APPLICATION_TYPE_WEB){ - LOG("Connection %s AUTHORIZED to web server", Connection->RemoteAddress); - }else{ - LOG_WARN("Rejecting connection %s from unknown application type %d", - Connection->RemoteAddress, ApplicationType); - SendQueryStatusFailed(Connection); - return; - } - - Connection->Authorized = true; - Connection->ApplicationType = ApplicationType; - Connection->WorldID = WorldID; - SendQueryStatusOk(Connection); -} - -static int CheckAccountPasswordTransaction(int AccountID, const char *Password, int IPAddress){ - TransactionScope Tx("CheckAccountPassword"); - if(!Tx.Begin()){ - return -1; - } - - TAccount Account; - if(!GetAccountData(AccountID, &Account)){ - return -1; - } - - if(Account.AccountID == 0){ - return 1; - } - - if(!TestPassword(Account.Auth, sizeof(Account.Auth), Password)){ - return 2; - } - - if(GetAccountFailedLoginAttempts(Account.AccountID, 5 * 60) > 10){ - return 3; - } - - if(GetIPAddressFailedLoginAttempts(IPAddress, 30 * 60) > 20){ - return 4; - } - - if(!Tx.Commit()){ - return -1; - } - - return 0; -} - -void ProcessCheckAccountPasswordQuery(TConnection *Connection, TReadBuffer *Buffer){ - char Password[30]; - char IPString[16]; - int AccountID = (int)Buffer->Read32(); - Buffer->ReadString(Password, sizeof(Password)); - Buffer->ReadString(IPString, sizeof(IPString)); - - int IPAddress = 0; - if(!ParseIPAddress(IPString, &IPAddress)){ - SendQueryStatusFailed(Connection); - return; - } - - // NOTE(fusion): Similar to `ProcessLoginAccountQuery`. - int Result = CheckAccountPasswordTransaction(AccountID, Password, IPAddress); - InsertLoginAttempt(AccountID, IPAddress, (Result != 0)); - if(Result == -1){ - SendQueryStatusFailed(Connection); - }else if(Result != 0){ - SendQueryStatusError(Connection, Result); - }else{ - SendQueryStatusOk(Connection); - } -} - -int LoginAccountTransaction(int AccountID, const char *Password, int IPAddress, - DynamicArray *Characters, int *PremiumDays){ - TransactionScope Tx("LoginAccount"); - if(!Tx.Begin()){ - return -1; - } - - TAccount Account; - if(!GetAccountData(AccountID, &Account)){ - return -1; - } - - if(Account.AccountID == 0){ - return 1; - } - - if(!TestPassword(Account.Auth, sizeof(Account.Auth), Password)){ - return 2; - } - - if(GetAccountFailedLoginAttempts(Account.AccountID, 5 * 60) > 10){ - return 3; - } - - if(GetIPAddressFailedLoginAttempts(IPAddress, 30 * 60) > 20){ - return 4; - } - - if(IsAccountBanished(Account.AccountID)){ - return 5; - } - - if(IsIPBanished(IPAddress)){ - return 6; - } - - if(!GetCharacterEndpoints(Account.AccountID, Characters)){ - return -1; - } - - if(!Tx.Commit()){ - return -1; - } - - *PremiumDays = Account.PremiumDays + Account.PendingPremiumDays; - return 0; -} - -void ProcessLoginAccountQuery(TConnection *Connection, TReadBuffer *Buffer){ - char Password[30]; - char IPString[16]; - int AccountID = (int)Buffer->Read32(); - Buffer->ReadString(Password, sizeof(Password)); - Buffer->ReadString(IPString, sizeof(IPString)); - - int IPAddress = 0; - if(!ParseIPAddress(IPString, &IPAddress)){ - SendQueryStatusFailed(Connection); - return; - } - - int PremiumDays = 0; - DynamicArray Characters; - int Result = LoginAccountTransaction(AccountID, Password, - IPAddress, &Characters, &PremiumDays); - - // NOTE(fusion): Similar to `ProcessLoginGameQuery` except we don't modify - // any tables inside the login transaction. - // TODO(fusion): Maybe have different login attempt tables or types? - InsertLoginAttempt(AccountID, IPAddress, (Result != 0)); - - if(Result == -1){ - SendQueryStatusFailed(Connection); - return; - } - - if(Result != 0){ - SendQueryStatusError(Connection, Result); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumCharacters = std::min(Characters.Length(), UINT8_MAX); - WriteBuffer.Write8((uint8)NumCharacters); - for(int i = 0; i < NumCharacters; i += 1){ - WriteBuffer.WriteString(Characters[i].Name); - WriteBuffer.WriteString(Characters[i].WorldName); - WriteBuffer.Write32BE((uint32)Characters[i].WorldAddress); - WriteBuffer.Write16((uint16)Characters[i].WorldPort); - } - WriteBuffer.Write16((uint16)PremiumDays); - SendResponse(Connection, &WriteBuffer); -} - -void ProcessLoginAdminQuery(TConnection *Connection, TReadBuffer *Buffer){ - // TODO(fusion): I thought for a second this could be the query used with - // the login server but it doesn't take a password or ip address for basic - // checks. Even if it's used in combination with `CheckAccountPassword`, - // it doesn't make sense to split what should have been a single query which - // is what the new `LoginAccount` query does. - SendQueryStatusFailed(Connection); -} - -static int LoginGameTransaction(int WorldID, int AccountID, const char *CharacterName, - const char *Password, int IPAddress, bool PrivateWorld, bool GamemasterRequired, - TCharacterLoginData *Character, DynamicArray *Buddies, - DynamicArray *Rights, bool *PremiumAccountActivated){ - TransactionScope Tx("LoginGame"); - if(!Tx.Begin()){ - return -1; - } - - if(!GetCharacterLoginData(CharacterName, Character)){ - return -1; - } - - if(Character->CharacterID == 0){ - return 1; - } - - if(Character->Deleted){ - return 2; - } - - if(Character->WorldID != WorldID){ - return 3; - } - - if(PrivateWorld){ - if(!GetWorldInvitation(WorldID, Character->CharacterID)){ - return 4; - } - } - - TAccount Account; - if(!GetAccountData(AccountID, &Account)){ - return -1; - } - - if(Account.AccountID == 0 || Account.AccountID != Character->AccountID){ - // NOTE(fusion): This is correct, there is no error code 5. - return 15; - } - - if(Account.Deleted){ - return 8; - } - - if(!TestPassword(Account.Auth, sizeof(Account.Auth), Password)){ - return 6; - } - - if(GetAccountFailedLoginAttempts(Account.AccountID, 5 * 60) > 10){ - return 7; - } - - if(GetIPAddressFailedLoginAttempts(IPAddress, 30 * 60) > 20){ - return 9; - } - - if(IsAccountBanished(Account.AccountID)){ - return 10; - } - - if(IsCharacterNamelocked(Character->CharacterID)){ - return 11; - } - - if(IsIPBanished(IPAddress)){ - return 12; - } - - // TODO(fusion): Probably merge these into a single operation? - if(!GetCharacterRight(Character->CharacterID, "ALLOW_MULTICLIENT") - && GetAccountOnlineCharacters(Account.AccountID) > 0 - && !IsCharacterOnline(Character->CharacterID)){ - return 13; - } - - if(GamemasterRequired){ - if(!GetCharacterRight(Character->CharacterID, "GAMEMASTER_OUTFIT")){ - return 14; - } - } - - if(!GetBuddies(WorldID, Account.AccountID, Buddies)){ - return -1; - } - - if(!GetCharacterRights(Character->CharacterID, Rights)){ - return -1; - } - - if(Account.PremiumDays == 0 && Account.PendingPremiumDays > 0){ - if(!ActivatePendingPremiumDays(Account.AccountID)){ - return -1; - } - - Account.PremiumDays += Account.PendingPremiumDays; - Account.PendingPremiumDays = 0; - *PremiumAccountActivated = true; - } - - if(Account.PremiumDays > 0){ - Rights->Push(TCharacterRight{"PREMIUM_ACCOUNT"}); - } - - if(!IncrementIsOnline(WorldID, Character->CharacterID)){ - return -1; - } - - if(!Tx.Commit()){ - return -1; - } - - return 0; -} - -void ProcessLoginGameQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - char CharacterName[30]; - char Password[30]; - char IPString[16]; - int AccountID = (int)Buffer->Read32(); - Buffer->ReadString(CharacterName, sizeof(CharacterName)); - Buffer->ReadString(Password, sizeof(Password)); - Buffer->ReadString(IPString, sizeof(IPString)); - bool PrivateWorld = Buffer->ReadFlag(); - Buffer->ReadFlag(); // "PremiumAccountRequired" unused - bool GamemasterRequired = Buffer->ReadFlag(); - - int IPAddress = 0; - if(!ParseIPAddress(IPString, &IPAddress)){ - SendQueryStatusFailed(Connection); - return; - } - - TCharacterLoginData Character; - DynamicArray Buddies; - DynamicArray Rights; - bool PremiumAccountActivated = false; - int Result = LoginGameTransaction(Connection->WorldID, AccountID, - CharacterName, Password, IPAddress, PrivateWorld, - GamemasterRequired, &Character, &Buddies, &Rights, - &PremiumAccountActivated); - - // IMPORTANT(fusion): We need to insert login attempts outside the login game - // transaction or we could end up not having it recorded at all due to rollbacks. - // It is also the reason the whole transaction had to be pulled to its own function. - // IMPORTANT(fusion): Don't return if we fail to insert the login attempt as the - // result of the whole operation was already determined by the transaction function. - InsertLoginAttempt(AccountID, IPAddress, (Result != 0)); - - if(Result == -1){ - SendQueryStatusFailed(Connection); - return; - } - - if(Result != 0){ - SendQueryStatusError(Connection, Result); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - WriteBuffer.Write32((uint32)Character.CharacterID); - WriteBuffer.WriteString(Character.Name); - WriteBuffer.Write8((uint8)Character.Sex); - WriteBuffer.WriteString(Character.Guild); - WriteBuffer.WriteString(Character.Rank); - WriteBuffer.WriteString(Character.Title); - - int NumBuddies = std::min(Buddies.Length(), UINT8_MAX); - WriteBuffer.Write8((uint8)NumBuddies); - for(int i = 0; i < NumBuddies; i += 1){ - WriteBuffer.Write32((uint32)Buddies[i].CharacterID); - WriteBuffer.WriteString(Buddies[i].Name); - } - - int NumRights = std::min(Rights.Length(), UINT8_MAX); - WriteBuffer.Write8((uint8)NumRights); - for(int i = 0; i < NumRights; i += 1){ - WriteBuffer.WriteString(Rights[i].Name); - } - - WriteBuffer.WriteFlag(PremiumAccountActivated); - - SendResponse(Connection, &WriteBuffer); -} - -void ProcessLogoutGameQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - char Profession[30]; - char Residence[30]; - int CharacterID = (int)Buffer->Read32(); - int Level = Buffer->Read16(); - Buffer->ReadString(Profession, sizeof(Profession)); - Buffer->ReadString(Residence, sizeof(Residence)); - int LastLoginTime = (int)Buffer->Read32(); - int TutorActivities = Buffer->Read16(); - - if(!LogoutCharacter(Connection->WorldID, CharacterID, Level, - Profession, Residence, LastLoginTime, TutorActivities)){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessSetNamelockQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - char CharacterName[30]; - char IPString[16]; - char Reason[200]; - char Comment[200]; - int GamemasterID = (int)Buffer->Read32(); - Buffer->ReadString(CharacterName, sizeof(CharacterName)); - Buffer->ReadString(IPString, sizeof(IPString)); - Buffer->ReadString(Reason, sizeof(Reason)); - Buffer->ReadString(Comment, sizeof(Comment)); - - int IPAddress = 0; - if(!StringEmpty(IPString) && !ParseIPAddress(IPString, &IPAddress)){ - SendQueryStatusFailed(Connection); - return; - } - - TransactionScope Tx("SetNamelock"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - int CharacterID = GetCharacterID(Connection->WorldID, CharacterName); - if(CharacterID == 0){ - SendQueryStatusError(Connection, 1); - return; - } - - // TODO(fusion): Might be `NO_BANISHMENT`. - if(GetCharacterRight(CharacterID, "NAMELOCK")){ - SendQueryStatusError(Connection, 2); - return; - } - - TNamelockStatus Status = GetNamelockStatus(CharacterID); - if(Status.Namelocked){ - SendQueryStatusError(Connection, (Status.Approved ? 4 : 3)); - return; - } - - if(!InsertNamelock(CharacterID, IPAddress, GamemasterID, Reason, Comment)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessBanishAccountQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - char CharacterName[30]; - char IPString[16]; - char Reason[200]; - char Comment[200]; - int GamemasterID = (int)Buffer->Read32(); - Buffer->ReadString(CharacterName, sizeof(CharacterName)); - Buffer->ReadString(IPString, sizeof(IPString)); - Buffer->ReadString(Reason, sizeof(Reason)); - Buffer->ReadString(Comment, sizeof(Comment)); - bool FinalWarning = Buffer->ReadFlag(); - - int IPAddress = 0; - if(!StringEmpty(IPString) && !ParseIPAddress(IPString, &IPAddress)){ - SendQueryStatusFailed(Connection); - return; - } - - TransactionScope Tx("BanishAccount"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - int CharacterID = GetCharacterID(Connection->WorldID, CharacterName); - if(CharacterID == 0){ - SendQueryStatusError(Connection, 1); - return; - } - - // TODO(fusion): Might be `NO_BANISHMENT`. - if(GetCharacterRight(CharacterID, "BANISHMENT")){ - SendQueryStatusError(Connection, 2); - return; - } - - TBanishmentStatus Status = GetBanishmentStatus(CharacterID); - if(Status.Banished){ - SendQueryStatusError(Connection, 3); - return; - } - - int BanishmentID = 0; - int Days = 7; - CompoundBanishment(Status, &Days, &FinalWarning); - if(!InsertBanishment(CharacterID, IPAddress, GamemasterID, - Reason, Comment, FinalWarning, Days * 86400, &BanishmentID)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - WriteBuffer.Write32((uint32)BanishmentID); - WriteBuffer.Write8(Days > 0 ? Days : 0xFF); - WriteBuffer.WriteFlag(FinalWarning); - SendResponse(Connection, &WriteBuffer); -} - -void ProcessSetNotationQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - char CharacterName[30]; - char IPString[16]; - char Reason[200]; - char Comment[200]; - int GamemasterID = Buffer->Read32(); - Buffer->ReadString(CharacterName, sizeof(CharacterName)); - Buffer->ReadString(IPString, sizeof(IPString)); - Buffer->ReadString(Reason, sizeof(Reason)); - Buffer->ReadString(Comment, sizeof(Comment)); - - int IPAddress = 0; - if(!StringEmpty(IPString) && !ParseIPAddress(IPString, &IPAddress)){ - SendQueryStatusFailed(Connection); - return; - } - - TransactionScope Tx("SetNotation"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - int CharacterID = GetCharacterID(Connection->WorldID, CharacterName); - if(CharacterID == 0){ - SendQueryStatusError(Connection, 1); - return; - } - - // TODO(fusion): Might be `NO_BANISHMENT`. - if(GetCharacterRight(CharacterID, "NOTATION")){ - SendQueryStatusError(Connection, 2); - return; - } - - int BanishmentID = 0; - if(GetNotationCount(CharacterID) >= 5){ - int BanishmentDays = 7; - bool FinalWarning = false; - TBanishmentStatus Status = GetBanishmentStatus(CharacterID); - CompoundBanishment(Status, &BanishmentDays, &FinalWarning); - if(!InsertBanishment(CharacterID, IPAddress, 0, "Excessive Notations", - "", FinalWarning, BanishmentDays, &BanishmentID)){ - SendQueryStatusFailed(Connection); - return; - } - } - - if(!InsertNotation(CharacterID, IPAddress, GamemasterID, Reason, Comment)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - WriteBuffer.Write32((uint32)BanishmentID); - SendResponse(Connection, &WriteBuffer); -} - -void ProcessReportStatementQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - char CharacterName[30]; - char Reason[200]; - char Comment[200]; - int ReporterID = Buffer->Read32(); - Buffer->ReadString(CharacterName, sizeof(CharacterName)); - Buffer->ReadString(Reason, sizeof(Reason)); - Buffer->ReadString(Comment, sizeof(Comment)); - int BanishmentID = Buffer->Read32(); - int StatementID = Buffer->Read32(); - int NumStatements = Buffer->Read16(); - - if(StatementID == 0){ - LOG_ERR("Missing reported statement id"); - SendQueryStatusFailed(Connection); - return; - } - - if(NumStatements == 0){ - LOG_ERR("Missing report statements"); - SendQueryStatusFailed(Connection); - return; - } - - TStatement *ReportedStatement = NULL; - TStatement *Statements = (TStatement*)alloca(NumStatements * sizeof(TStatement)); - for(int i = 0; i < NumStatements; i += 1){ - Statements[i].StatementID = (int)Buffer->Read32(); - Statements[i].Timestamp = (int)Buffer->Read32(); - Statements[i].CharacterID = (int)Buffer->Read32(); - Buffer->ReadString(Statements[i].Channel, sizeof(Statements[i].Channel)); - Buffer->ReadString(Statements[i].Text, sizeof(Statements[i].Text)); - - if(Statements[i].StatementID == StatementID){ - if(ReportedStatement != NULL){ - LOG_WARN("Reported statement (%d, %d, %d) appears multiple times", - Connection->WorldID, Statements[i].Timestamp, - Statements[i].StatementID); - } - ReportedStatement = &Statements[i]; - } - } - - if(ReportedStatement == NULL){ - LOG_ERR("Missing reported statement"); - SendQueryStatusFailed(Connection); - return; - } - - TransactionScope Tx("ReportStatement"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - int CharacterID = GetCharacterID(Connection->WorldID, CharacterName); - if(CharacterID == 0){ - SendQueryStatusError(Connection, 1); - return; - }else if(ReportedStatement->CharacterID != CharacterID){ - LOG_ERR("Reported statement character mismatch"); - SendQueryStatusFailed(Connection); - return; - } - - if(IsStatementReported(Connection->WorldID, ReportedStatement)){ - SendQueryStatusError(Connection, 2); - return; - } - - if(!InsertStatements(Connection->WorldID, NumStatements, Statements)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!InsertReportedStatement(Connection->WorldID, ReportedStatement, - BanishmentID, ReporterID, Reason, Comment)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessBanishIPAddressQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - char CharacterName[30]; - char IPString[16]; - char Reason[200]; - char Comment[200]; - int GamemasterID = Buffer->Read16(); - Buffer->ReadString(CharacterName, sizeof(CharacterName)); - Buffer->ReadString(IPString, sizeof(IPString)); - Buffer->ReadString(Reason, sizeof(Reason)); - Buffer->ReadString(Comment, sizeof(Comment)); - - int IPAddress = 0; - if(!ParseIPAddress(IPString, &IPAddress)){ - SendQueryStatusFailed(Connection); - return; - } - - TransactionScope Tx("BanishIP"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - int CharacterID = GetCharacterID(Connection->WorldID, CharacterName); - if(CharacterID == 0){ - SendQueryStatusError(Connection, 1); - return; - } - - // TODO(fusion): Might be `NO_BANISHMENT`. - if(GetCharacterRight(CharacterID, "IP_BANISHMENT")){ - SendQueryStatusError(Connection, 2); - return; - } - - // IMPORTANT(fusion): It is not a good idea to ban an IP address, specially - // V4 addresses, as they may be dynamically assigned or represent the address - // of a public ISP router that manages multiple clients. - int BanishmentDays = 3; - if(!InsertIPBanishment(CharacterID, IPAddress, GamemasterID, - Reason, Comment, BanishmentDays * 86400)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessLogCharacterDeathQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - char Remark[30]; - int CharacterID = (int)Buffer->Read32(); - int Level = Buffer->Read16(); - int OffenderID = (int)Buffer->Read32(); - Buffer->ReadString(Remark, sizeof(Remark)); - bool Unjustified = Buffer->ReadFlag(); - int Timestamp = (int)Buffer->Read32(); - if(!InsertCharacterDeath(Connection->WorldID, CharacterID, Level, - OffenderID, Remark, Unjustified, Timestamp)){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessAddBuddyQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - int AccountID = (int)Buffer->Read32(); - int BuddyID = (int)Buffer->Read32(); - if(!InsertBuddy(Connection->WorldID, AccountID, BuddyID)){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessRemoveBuddyQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - int AccountID = (int)Buffer->Read32(); - int BuddyID = (int)Buffer->Read32(); - if(!DeleteBuddy(Connection->WorldID, AccountID, BuddyID)){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessDecrementIsOnlineQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - int CharacterID = (int)Buffer->Read32(); - if(!DecrementIsOnline(Connection->WorldID, CharacterID)){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessFinishAuctionsQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - DynamicArray Auctions; - if(!FinishHouseAuctions(Connection->WorldID, &Auctions)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumAuctions = std::min(Auctions.Length(), UINT16_MAX); - WriteBuffer.Write16((uint16)NumAuctions); - for(int i = 0; i < NumAuctions; i += 1){ - WriteBuffer.Write16((uint16)Auctions[i].HouseID); - WriteBuffer.Write32((uint32)Auctions[i].BidderID); - WriteBuffer.WriteString(Auctions[i].BidderName); - WriteBuffer.Write32((uint32)Auctions[i].BidAmount); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessTransferHousesQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - DynamicArray Transfers; - if(!FinishHouseTransfers(Connection->WorldID, &Transfers)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumTransfers = std::min(Transfers.Length(), UINT16_MAX); - WriteBuffer.Write16((uint16)NumTransfers); - for(int i = 0; i < NumTransfers; i += 1){ - WriteBuffer.Write16((uint16)Transfers[i].HouseID); - WriteBuffer.Write32((uint32)Transfers[i].NewOwnerID); - WriteBuffer.WriteString(Transfers[i].NewOwnerName); - WriteBuffer.Write32((uint32)Transfers[i].Price); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessEvictFreeAccountsQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - DynamicArray Evictions; - if(!GetFreeAccountEvictions(Connection->WorldID, &Evictions)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumEvictions = std::min(Evictions.Length(), UINT16_MAX); - WriteBuffer.Write16((uint16)NumEvictions); - for(int i = 0; i < NumEvictions; i += 1){ - WriteBuffer.Write16((uint16)Evictions[i].HouseID); - WriteBuffer.Write32((uint32)Evictions[i].OwnerID); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessEvictDeletedCharactersQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - DynamicArray Evictions; - if(!GetDeletedCharacterEvictions(Connection->WorldID, &Evictions)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumEvictions = std::min(Evictions.Length(), UINT16_MAX); - WriteBuffer.Write16((uint16)NumEvictions); - for(int i = 0; i < NumEvictions; i += 1){ - WriteBuffer.Write16((uint16)Evictions[i].HouseID); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessEvictExGuildleadersQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - // NOTE(fusion): This is a bit different from the other eviction functions. - // The server doesn't maintain guild information for characters so it will - // send a list of guild houses with their owners and we're supposed to check - // whether the owner is still a guild leader. I don't think we should check - // any other information as the server is authoritative on house information. - DynamicArray Evictions; - int NumGuildHouses = Buffer->Read16(); - for(int i = 0; i < NumGuildHouses; i += 1){ - int HouseID = Buffer->Read16(); - int OwnerID = (int)Buffer->Read32(); - if(!GetGuildLeaderStatus(Connection->WorldID, OwnerID)){ - Evictions.Push(HouseID); - } - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumEvictions = std::min(Evictions.Length(), UINT16_MAX); - WriteBuffer.Write16((uint16)NumEvictions); - for(int i = 0; i < NumEvictions; i += 1){ - WriteBuffer.Write16((uint16)Evictions[i]); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessInsertHouseOwnerQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - int HouseID = Buffer->Read16(); - int OwnerID = (int)Buffer->Read32(); - int PaidUntil = (int)Buffer->Read32(); - if(!InsertHouseOwner(Connection->WorldID, HouseID, OwnerID, PaidUntil)){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessUpdateHouseOwnerQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - int HouseID = Buffer->Read16(); - int OwnerID = (int)Buffer->Read32(); - int PaidUntil = (int)Buffer->Read32(); - if(!UpdateHouseOwner(Connection->WorldID, HouseID, OwnerID, PaidUntil)){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessDeleteHouseOwnerQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - int HouseID = Buffer->Read16(); - if(!DeleteHouseOwner(Connection->WorldID, HouseID)){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessGetHouseOwnersQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - DynamicArray Owners; - if(!GetHouseOwners(Connection->WorldID, &Owners)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumOwners = std::min(Owners.Length(), UINT16_MAX); - WriteBuffer.Write16((uint16)NumOwners); - for(int i = 0; i < NumOwners; i += 1){ - WriteBuffer.Write16((uint16)Owners[i].HouseID); - WriteBuffer.Write32((uint32)Owners[i].OwnerID); - WriteBuffer.WriteString(Owners[i].OwnerName); - WriteBuffer.Write32((uint32)Owners[i].PaidUntil); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessGetAuctionsQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - DynamicArray Auctions; - if(!GetHouseAuctions(Connection->WorldID, &Auctions)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumAuctions = std::min(Auctions.Length(), UINT16_MAX); - WriteBuffer.Write16((uint16)NumAuctions); - for(int i = 0; i < NumAuctions; i += 1){ - WriteBuffer.Write16((uint16)Auctions[i]); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessStartAuctionQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - int HouseID = Buffer->Read16(); - if(!StartHouseAuction(Connection->WorldID, HouseID)){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessInsertHousesQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - TransactionScope Tx("InsertHouses"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - if(!DeleteHouses(Connection->WorldID)){ - SendQueryStatusFailed(Connection); - return; - } - - int NumHouses = Buffer->Read16(); - if(NumHouses > 0){ - THouse *Houses = (THouse*)alloca(NumHouses * sizeof(THouse)); - for(int i = 0; i < NumHouses; i += 1){ - Houses[i].HouseID = Buffer->Read16(); - Buffer->ReadString(Houses[i].Name, sizeof(Houses[i].Name)); - Houses[i].Rent = (int)Buffer->Read32(); - Buffer->ReadString(Houses[i].Description, sizeof(Houses[i].Description)); - Houses[i].Size = Buffer->Read16(); - Houses[i].PositionX = Buffer->Read16(); - Houses[i].PositionY = Buffer->Read16(); - Houses[i].PositionZ = Buffer->Read8(); - Buffer->ReadString(Houses[i].Town, sizeof(Houses[i].Town)); - Houses[i].GuildHouse = Buffer->ReadFlag(); - } - - if(!InsertHouses(Connection->WorldID, NumHouses, Houses)){ - SendQueryStatusFailed(Connection); - return; - } - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessClearIsOnlineQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - int NumAffectedCharacters; - if(!ClearIsOnline(Connection->WorldID, &NumAffectedCharacters)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - WriteBuffer.Write16((uint16)NumAffectedCharacters); - SendResponse(Connection, &WriteBuffer); -} - -void ProcessCreatePlayerlistQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - TransactionScope Tx("OnlineList"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - if(!DeleteOnlineCharacters(Connection->WorldID)){ - SendQueryStatusFailed(Connection); - return; - } - - // TODO(fusion): I think `NumCharacters` may be used to signal that the - // server is going OFFLINE, in which case we'd have to add an `Online` - // column to `Worlds` and update it here. - - bool NewRecord = false; - int NumCharacters = Buffer->Read16(); - if(NumCharacters != 0xFFFF && NumCharacters > 0){ - TOnlineCharacter *Characters = (TOnlineCharacter*)alloca(NumCharacters * sizeof(TOnlineCharacter)); - for(int i = 0; i < NumCharacters; i += 1){ - Buffer->ReadString(Characters[i].Name, sizeof(Characters[i].Name)); - Characters[i].Level = Buffer->Read16(); - Buffer->ReadString(Characters[i].Profession, sizeof(Characters[i].Profession)); - } - - if(!InsertOnlineCharacters(Connection->WorldID, NumCharacters, Characters)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!CheckOnlineRecord(Connection->WorldID, NumCharacters, &NewRecord)){ - SendQueryStatusFailed(Connection); - return; - } - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - WriteBuffer.WriteFlag(NewRecord); - SendResponse(Connection, &WriteBuffer); -} - -void ProcessLogKilledCreaturesQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - int NumStats = Buffer->Read16(); - TKillStatistics *Stats = (TKillStatistics*)alloca(NumStats * sizeof(TKillStatistics)); - for(int i = 0; i < NumStats; i += 1){ - Buffer->ReadString(Stats[i].RaceName, sizeof(Stats[i].RaceName)); - Stats[i].PlayersKilled = (int)Buffer->Read32(); - Stats[i].TimesKilled = (int)Buffer->Read32(); - } - - if(NumStats > 0){ - TransactionScope Tx("LogKilledCreatures"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - if(!MergeKillStatistics(Connection->WorldID, NumStats, Stats)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - } - - SendQueryStatusOk(Connection); -} - -void ProcessLoadPlayersQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - // IMPORTANT(fusion): The server expect 10K entries at most. It is probably - // some shared hard coded constant. - int NumEntries; - TCharacterIndexEntry Entries[10000]; - int MinimumCharacterID = (int)Buffer->Read32(); - if(!GetCharacterIndexEntries(Connection->WorldID, - MinimumCharacterID, NARRAY(Entries), &NumEntries, Entries)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - WriteBuffer.Write32((uint32)NumEntries); - for(int i = 0; i < NumEntries; i += 1){ - WriteBuffer.WriteString(Entries[i].Name); - WriteBuffer.Write32((uint32)Entries[i].CharacterID); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessExcludeFromAuctionsQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - TransactionScope Tx("ExcludeFromAuctions"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - int CharacterID = (int)Buffer->Read32(); - bool Banish = Buffer->ReadFlag(); - int ExclusionDays = 7; - int BanishmentID = 0; - if(Banish){ - int BanishmentDays = 7; - bool FinalWarning = false; - TBanishmentStatus Status = GetBanishmentStatus(CharacterID); - CompoundBanishment(Status, &BanishmentDays, &FinalWarning); - if(!InsertBanishment(CharacterID, 0, 0, "Spoiling Auction", - "", FinalWarning, BanishmentDays * 86400, &BanishmentID)){ - SendQueryStatusFailed(Connection); - return; - } - } - - if(!ExcludeFromAuctions(Connection->WorldID, - CharacterID, ExclusionDays * 86400, BanishmentID)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessCancelHouseTransferQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - // TODO(fusion): Not sure what this is used for. Maybe house transfer rows - // are kept permanently and this query is used to delete/flag it, in case - // the it didn't complete. We might need to refine `FinishHouseTransfers`. - //int HouseID = Buffer->Read16(); - SendQueryStatusOk(Connection); -} - -void ProcessLoadWorldConfigQuery(TConnection *Connection, TReadBuffer *Buffer){ - if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ - SendQueryStatusFailed(Connection); - return; - } - - TWorldConfig WorldConfig = {}; - if(!GetWorldConfig(Connection->WorldID, &WorldConfig)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - WriteBuffer.Write8((uint8)WorldConfig.Type); - WriteBuffer.Write8((uint8)WorldConfig.RebootTime); - WriteBuffer.Write32BE((uint32)WorldConfig.IPAddress); - WriteBuffer.Write16((uint16)WorldConfig.Port); - WriteBuffer.Write16((uint16)WorldConfig.MaxPlayers); - WriteBuffer.Write16((uint16)WorldConfig.PremiumPlayerBuffer); - WriteBuffer.Write16((uint16)WorldConfig.MaxNewbies); - WriteBuffer.Write16((uint16)WorldConfig.PremiumNewbieBuffer); - SendResponse(Connection, &WriteBuffer); -} - -void 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. - if(AccountID <= 0 || StringEmpty(Email) || StringEmpty(Password)){ - SendQueryStatusFailed(Connection); - return; - } - - uint8 Auth[64]; - if(!GenerateAuth(Password, Auth, sizeof(Auth))){ - SendQueryStatusFailed(Connection); - return; - } - - TransactionScope Tx("CreateAccount"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - if(AccountNumberExists(AccountID)){ - SendQueryStatusError(Connection, 1); - return; - } - - if(AccountEmailExists(Email)){ - SendQueryStatusError(Connection, 2); - return; - } - - if(!CreateAccount(AccountID, Email, Auth, sizeof(Auth))){ - SendQueryStatusFailed(Connection); - return; - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessCreateCharacterQuery(TConnection *Connection, TReadBuffer *Buffer){ - char WorldName[30]; - char CharacterName[30]; - Buffer->ReadString(WorldName, sizeof(WorldName)); - int AccountID = (int)Buffer->Read32(); - Buffer->ReadString(CharacterName, sizeof(CharacterName)); - int Sex = Buffer->Read8(); - - // NOTE(fusion): Inputs should be checked before hand. - if(AccountID <= 0 || (Sex != 1 && Sex != 2) - || StringEmpty(WorldName) - || StringEmpty(CharacterName)){ - SendQueryStatusFailed(Connection); - return; - } - - TransactionScope Tx("CreateCharacter"); - if(!Tx.Begin()){ - SendQueryStatusFailed(Connection); - return; - } - - int WorldID = GetWorldID(WorldName); - if(WorldID == 0){ - SendQueryStatusError(Connection, 1); - return; - } - - if(!AccountNumberExists(AccountID)){ - SendQueryStatusError(Connection, 2); - return; - } - - if(CharacterNameExists(CharacterName)){ - SendQueryStatusError(Connection, 3); - return; - } - - if(!CreateCharacter(WorldID, AccountID, CharacterName, Sex)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!Tx.Commit()){ - SendQueryStatusFailed(Connection); - return; - } - - SendQueryStatusOk(Connection); -} - -void ProcessGetAccountSummaryQuery(TConnection *Connection, TReadBuffer *Buffer){ - int AccountID = (int)Buffer->Read32(); - - if(AccountID <= 0){ - SendQueryStatusFailed(Connection); - return; - } - - TAccount Account; - if(!GetAccountData(AccountID, &Account)){ - SendQueryStatusFailed(Connection); - return; - } - - if(Account.AccountID != AccountID){ - SendQueryStatusFailed(Connection); - return; - } - - DynamicArray Characters; - if(!GetCharacterSummaries(AccountID, &Characters)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - WriteBuffer.WriteString(Account.Email); - WriteBuffer.Write16((uint16)Account.PremiumDays); - WriteBuffer.Write16((uint16)Account.PendingPremiumDays); - WriteBuffer.WriteFlag(Account.Deleted); - int NumCharacters = std::min(Characters.Length(), UINT8_MAX); - WriteBuffer.Write8((uint8)NumCharacters); - for(int i = 0; i < NumCharacters; i += 1){ - WriteBuffer.WriteString(Characters[i].Name); - WriteBuffer.WriteString(Characters[i].World); - WriteBuffer.Write16((uint16)Characters[i].Level); - WriteBuffer.WriteString(Characters[i].Profession); - WriteBuffer.WriteFlag(Characters[i].Online); - WriteBuffer.WriteFlag(Characters[i].Deleted); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessGetCharacterProfileQuery(TConnection *Connection, TReadBuffer *Buffer){ - char CharacterName[30]; - Buffer->ReadString(CharacterName, sizeof(CharacterName)); - - if(StringEmpty(CharacterName)){ - SendQueryStatusFailed(Connection); - return; - } - - TCharacterProfile Character; - if(!GetCharacterProfile(CharacterName, &Character)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!StringEqCI(Character.Name, CharacterName)){ - SendQueryStatusError(Connection, 1); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - WriteBuffer.WriteString(Character.Name); - WriteBuffer.WriteString(Character.World); - WriteBuffer.Write8((uint8)Character.Sex); - WriteBuffer.WriteString(Character.Guild); - WriteBuffer.WriteString(Character.Rank); - WriteBuffer.WriteString(Character.Title); - WriteBuffer.Write16((uint16)Character.Level); - WriteBuffer.WriteString(Character.Profession); - WriteBuffer.WriteString(Character.Residence); - WriteBuffer.Write32((uint32)Character.LastLogin); - WriteBuffer.Write16((uint16)Character.PremiumDays); - WriteBuffer.WriteFlag(Character.Online); - WriteBuffer.WriteFlag(Character.Deleted); - SendResponse(Connection, &WriteBuffer); -} - -void ProcessGetWorldsQuery(TConnection *Connection, TReadBuffer *Buffer){ - DynamicArray Worlds; - if(!GetWorlds(&Worlds)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumWorlds = std::min(Worlds.Length(), UINT8_MAX); - WriteBuffer.Write8((uint8)NumWorlds); - for(int i = 0; i < NumWorlds; i += 1){ - WriteBuffer.WriteString(Worlds[i].Name); - WriteBuffer.Write8((uint8)Worlds[i].Type); - WriteBuffer.Write16((uint16)Worlds[i].NumPlayers); - WriteBuffer.Write16((uint16)Worlds[i].MaxPlayers); - WriteBuffer.Write16((uint16)Worlds[i].OnlineRecord); - WriteBuffer.Write32((uint32)Worlds[i].OnlineRecordTimestamp); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessGetOnlineCharactersQuery(TConnection *Connection, TReadBuffer *Buffer){ - char WorldName[30]; - Buffer->ReadString(WorldName, sizeof(WorldName)); - - int WorldID = GetWorldID(WorldName); - if(WorldID == 0){ - SendQueryStatusFailed(Connection); - return; - } - - DynamicArray Characters; - if(!GetOnlineCharacters(WorldID, &Characters)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumCharacters = std::min(Characters.Length(), UINT16_MAX); - WriteBuffer.Write16((uint16)NumCharacters); - for(int i = 0; i < NumCharacters; i += 1){ - WriteBuffer.WriteString(Characters[i].Name); - WriteBuffer.Write16((uint16)Characters[i].Level); - WriteBuffer.WriteString(Characters[i].Profession); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessGetKillStatisticsQuery(TConnection *Connection, TReadBuffer *Buffer){ - char WorldName[30]; - Buffer->ReadString(WorldName, sizeof(WorldName)); - - int WorldID = GetWorldID(WorldName); - if(WorldID == 0){ - SendQueryStatusFailed(Connection); - return; - } - - DynamicArray Stats; - if(!GetKillStatistics(WorldID, &Stats)){ - SendQueryStatusFailed(Connection); - return; - } - - TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK); - int NumStats = std::min(Stats.Length(), UINT16_MAX); - WriteBuffer.Write16((uint16)NumStats); - for(int i = 0; i < NumStats; i += 1){ - WriteBuffer.WriteString(Stats[i].RaceName); - WriteBuffer.Write32((uint32)Stats[i].PlayersKilled); - WriteBuffer.Write32((uint32)Stats[i].TimesKilled); - } - SendResponse(Connection, &WriteBuffer); -} - -void ProcessConnectionQuery(TConnection *Connection){ - // TODO(fusion): Ideally we'd create a new query and dispatch it to the - // database thread for processing. Realistically, it wouldn't make a big - // difference since we're already handling connections asynchronously and - // the only blocking system calls are done by SQLite when interacting with - // the disk. - - TReadBuffer Buffer(Connection->Buffer, Connection->RWSize); - int Query = Buffer.Read8(); - if(!Connection->Authorized){ - if(Query == QUERY_LOGIN){ - ProcessLoginQuery(Connection, &Buffer); - }else{ - LOG_ERR("Expected login query"); - CloseConnection(Connection); - } - return; - } - - switch(Query){ - case QUERY_CHECK_ACCOUNT_PASSWORD: ProcessCheckAccountPasswordQuery(Connection, &Buffer); break; - case QUERY_LOGIN_ACCOUNT: ProcessLoginAccountQuery(Connection, &Buffer); break; - case QUERY_LOGIN_ADMIN: ProcessLoginAdminQuery(Connection, &Buffer); break; - case QUERY_LOGIN_GAME: ProcessLoginGameQuery(Connection, &Buffer); break; - case QUERY_LOGOUT_GAME: ProcessLogoutGameQuery(Connection, &Buffer); break; - case QUERY_SET_NAMELOCK: ProcessSetNamelockQuery(Connection, &Buffer); break; - case QUERY_BANISH_ACCOUNT: ProcessBanishAccountQuery(Connection, &Buffer); break; - case QUERY_SET_NOTATION: ProcessSetNotationQuery(Connection, &Buffer); break; - case QUERY_REPORT_STATEMENT: ProcessReportStatementQuery(Connection, &Buffer); break; - case QUERY_BANISH_IP_ADDRESS: ProcessBanishIPAddressQuery(Connection, &Buffer); break; - case QUERY_LOG_CHARACTER_DEATH: ProcessLogCharacterDeathQuery(Connection, &Buffer); break; - case QUERY_ADD_BUDDY: ProcessAddBuddyQuery(Connection, &Buffer); break; - case QUERY_REMOVE_BUDDY: ProcessRemoveBuddyQuery(Connection, &Buffer); break; - case QUERY_DECREMENT_IS_ONLINE: ProcessDecrementIsOnlineQuery(Connection, &Buffer); break; - case QUERY_FINISH_AUCTIONS: ProcessFinishAuctionsQuery(Connection, &Buffer); break; - case QUERY_TRANSFER_HOUSES: ProcessTransferHousesQuery(Connection, &Buffer); break; - case QUERY_EVICT_FREE_ACCOUNTS: ProcessEvictFreeAccountsQuery(Connection, &Buffer); break; - case QUERY_EVICT_DELETED_CHARACTERS: ProcessEvictDeletedCharactersQuery(Connection, &Buffer); break; - case QUERY_EVICT_EX_GUILDLEADERS: ProcessEvictExGuildleadersQuery(Connection, &Buffer); break; - case QUERY_INSERT_HOUSE_OWNER: ProcessInsertHouseOwnerQuery(Connection, &Buffer); break; - case QUERY_UPDATE_HOUSE_OWNER: ProcessUpdateHouseOwnerQuery(Connection, &Buffer); break; - case QUERY_DELETE_HOUSE_OWNER: ProcessDeleteHouseOwnerQuery(Connection, &Buffer); break; - case QUERY_GET_HOUSE_OWNERS: ProcessGetHouseOwnersQuery(Connection, &Buffer); break; - case QUERY_GET_AUCTIONS: ProcessGetAuctionsQuery(Connection, &Buffer); break; - case QUERY_START_AUCTION: ProcessStartAuctionQuery(Connection, &Buffer); break; - case QUERY_INSERT_HOUSES: ProcessInsertHousesQuery(Connection, &Buffer); break; - case QUERY_CLEAR_IS_ONLINE: ProcessClearIsOnlineQuery(Connection, &Buffer); break; - case QUERY_CREATE_PLAYERLIST: ProcessCreatePlayerlistQuery(Connection, &Buffer); break; - case QUERY_LOG_KILLED_CREATURES: ProcessLogKilledCreaturesQuery(Connection, &Buffer); break; - case QUERY_LOAD_PLAYERS: ProcessLoadPlayersQuery(Connection, &Buffer); break; - case QUERY_EXCLUDE_FROM_AUCTIONS: ProcessExcludeFromAuctionsQuery(Connection, &Buffer); break; - case QUERY_CANCEL_HOUSE_TRANSFER: ProcessCancelHouseTransferQuery(Connection, &Buffer); break; - case QUERY_LOAD_WORLD_CONFIG: ProcessLoadWorldConfigQuery(Connection, &Buffer); break; - case QUERY_CREATE_ACCOUNT: ProcessCreateAccountQuery(Connection, &Buffer); break; - case QUERY_CREATE_CHARACTER: ProcessCreateCharacterQuery(Connection, &Buffer); break; - case QUERY_GET_ACCOUNT_SUMMARY: ProcessGetAccountSummaryQuery(Connection, &Buffer); break; - case QUERY_GET_CHARACTER_PROFILE: ProcessGetCharacterProfileQuery(Connection, &Buffer); break; - case QUERY_GET_WORLDS: ProcessGetWorldsQuery(Connection, &Buffer); break; - case QUERY_GET_ONLINE_CHARACTERS: ProcessGetOnlineCharactersQuery(Connection, &Buffer); break; - case QUERY_GET_KILL_STATISTICS: ProcessGetKillStatisticsQuery(Connection, &Buffer); break; - default:{ - LOG_ERR("Unknown query %d from %s", Query, Connection->RemoteAddress); - SendQueryStatusFailed(Connection); - break; - } - } -} -- cgit v1.2.3