#include "query.hh" #include "config.hh" #include #include #include #include #include #include static int ApplicationType; static char LoginData[30]; void SetQueryManagerLoginData(int Type, const char *Data){ ApplicationType = Type; if(Data != NULL){ strncpy(LoginData, Data, sizeof(LoginData)); LoginData[sizeof(LoginData) - 1] = 0; }else{ LoginData[0] = 0; } } // TQueryManagerConnection // ============================================================================= // TODO(fusion): An initializer list executes in the same order fields are // declared. This initialization should work as long as `BufferSize` is the // first field declared in `TQueryManagerConnection`, which it is. TQueryManagerConnection::TQueryManagerConnection(int QueryBufferSize) : BufferSize(std::max(QueryBufferSize, KB(16))), Buffer(new uint8[this->BufferSize]), ReadBuffer(this->Buffer, this->BufferSize), WriteBuffer(this->Buffer, this->BufferSize), Socket(-1), QueryOk(false) { this->connect(); } TQueryManagerConnection::~TQueryManagerConnection(void){ this->disconnect(); delete[] this->Buffer; } bool ResolveHostNameAddress(const char *HostName, in_addr_t *OutAddr){ // TODO(fusion): Just use `getaddrinfo`? int ErrorCode; hostent HostEnt; hostent *HostEntResult; char Buffer[2048]; int Ret = gethostbyname_r(HostName, &HostEnt, Buffer, sizeof(Buffer), &HostEntResult, &ErrorCode); if(Ret != 0 || HostEntResult == NULL){ return false; } if(OutAddr){ *OutAddr = ((in_addr_t)HostEntResult->h_addr_list[0][0] << 24) | ((in_addr_t)HostEntResult->h_addr_list[0][1] << 16) | ((in_addr_t)HostEntResult->h_addr_list[0][2] << 8) | ((in_addr_t)HostEntResult->h_addr_list[0][3] << 0); } return true; } void TQueryManagerConnection::connect(void){ for(int i = 0; i < NumberOfQueryManagers; i += 1){ in_addr_t Addr = inet_addr(QUERY_MANAGER[i].Host); if(Addr == INADDR_NONE && !ResolveHostNameAddress(QUERY_MANAGER[i].Host, &Addr)){ print(2, "TQueryManagerConnection::connect: Kann Rechnernamen nicht auflösen.\n"); continue; } this->Socket = socket(AF_INET, SOCK_STREAM, 0); if(this->Socket == -1){ print(2, "TQueryManagerConnection::connect: Kann Socket nicht öffnen.\n"); continue; } struct sockaddr_in QueryManagerAddress = {}; QueryManagerAddress.sin_family = AF_INET; QueryManagerAddress.sin_port = htons(QUERY_MANAGER[i].Port); QueryManagerAddress.sin_addr.s_addr = Addr; if(::connect(this->Socket, (struct sockaddr*)&QueryManagerAddress, sizeof(QueryManagerAddress)) == -1){ print(2, "TQueryManagerConnection::connect: Kann Verbindung nicht herstellen.\n"); this->disconnect(); continue; } // NOTE(fusion): Make socket non-blocking AFTER connecting. This is to // better align the behaviour of both TQueryManagerConnection::read and // TQueryManagerConnection::write. { int SocketFlags = fcntl(this->Socket, F_GETFL); if(SocketFlags == -1 || fcntl(this->Socket, F_SETFL, (SocketFlags | O_NONBLOCK)) == -1){ error("TQueryManagerConnection::connect: Failed to set socket as non-blocking: (%d) %s\n", errno, GetErrorDescription(errno)); this->disconnect(); continue; } } this->prepareQuery(0); this->sendByte((uint8)ApplicationType); this->sendString(QUERY_MANAGER[i].Password); if(ApplicationType == 1){ this->sendString(LoginData); } int Status = this->executeQuery(30, false); if(Status != QUERY_STATUS_OK){ print(2, "TQueryManagerConnection::connect: Anmeldung fehlgeschlagen (%d).\n", Status); this->disconnect(); continue; } break; } if(!this->isConnected()){ print(2, "TQueryManagerConnection::connect: Kein Query-Manager verfügbar.\n"); } } void TQueryManagerConnection::disconnect(void){ if(this->Socket != -1){ if(close(this->Socket) == -1){ error("TQueryManagerConnection::disconnect: Fehler %d beim Schließen der Socket.\n", errno); } this->Socket = -1; } } int TQueryManagerConnection::write(const uint8 *Buffer, int Size){ int Attempts = 50; int BytesToWrite = Size; const uint8 *WritePtr = Buffer; while(BytesToWrite > 0){ int BytesWritten = (int)::write(this->Socket, WritePtr, BytesToWrite); if(BytesWritten > 0){ BytesToWrite -= BytesWritten; WritePtr += BytesWritten; }else if(BytesWritten == 0){ break; }else if(errno != EINTR){ if(errno != EAGAIN){ return -errno; }else if(Attempts <= 0){ return -ETIMEDOUT; } DelayThread(0, 100000); Attempts -= 1; } } return Size - BytesToWrite; } int TQueryManagerConnection::read(uint8 *Buffer, int Size, int Timeout){ // NOTE(fusion): Wait until there is any inbound data then read everything // in one go. Data from the query manager is sent in a burst so we don't need // to be polling every step along the way. int TimeoutMS = Timeout * 1000; int64 Deadline = GetClockMonotonicMS() + TimeoutMS; while(true){ struct pollfd pollfd = {}; pollfd.fd = this->Socket; pollfd.events = POLLIN; pollfd.revents = 0; int ret = poll(&pollfd, 1, TimeoutMS); if(ret == 1) break; if(ret == 0) return -ETIMEDOUT; if(ret == -1 && errno != EINTR) return -errno; // NOTE(fusion): We should get a timeout before this turns negative, but // just in case... We don't want poll to block indefinitely... TimeoutMS = (int)(Deadline - GetClockMonotonicMS()); if(TimeoutMS < 0){ return -ETIMEDOUT; } } int Attempts = 50; int BytesToRead = Size; uint8 *ReadPtr = Buffer; while(BytesToRead > 0){ int BytesRead = ::read(this->Socket, ReadPtr, BytesToRead); if(BytesRead > 0){ BytesToRead -= BytesRead; ReadPtr += BytesRead; }else if(BytesRead == 0){ break; }else if(errno != EINTR){ if(errno != EAGAIN){ return -errno; }else if(Attempts <= 0){ return -ETIMEDOUT; } DelayThread(0, 100000); Attempts -= 1; } } return Size - BytesToRead; } void TQueryManagerConnection::prepareQuery(int QueryType){ this->QueryOk = true; this->WriteBuffer.Position = 0; try{ this->WriteBuffer.writeWord(0); this->WriteBuffer.writeByte((uint8)QueryType); }catch(const char *str){ error("TQueryManagerConnection::prepareQuery: %s.\n", str); this->QueryOk = false; } } void TQueryManagerConnection::sendFlag(bool Flag){ this->sendByte(Flag ? 0x01 : 0x00); } void TQueryManagerConnection::sendByte(uint8 Byte){ try{ this->WriteBuffer.writeByte(Byte); }catch(const char *str){ error("TQueryManagerConnection::sendByte: %s.\n", str); this->QueryOk = false; } } void TQueryManagerConnection::sendWord(uint16 Word){ try{ this->WriteBuffer.writeWord(Word); }catch(const char *str){ error("TQueryManagerConnection::sendWord: %s.\n", str); this->QueryOk = false; } } void TQueryManagerConnection::sendQuad(uint32 Quad){ try{ this->WriteBuffer.writeQuad(Quad); }catch(const char *str){ error("TQueryManagerConnection::sendQuad: %s.\n", str); this->QueryOk = false; } } void TQueryManagerConnection::sendString(const char *String){ try{ this->WriteBuffer.writeString(String); }catch(const char *str){ error("TQueryManagerConnection::sendString: %s.\n", str); this->QueryOk = false; } } void TQueryManagerConnection::sendBytes(const uint8 *Buffer, int Count){ try{ this->WriteBuffer.writeBytes(Buffer, Count); }catch(const char *str){ error("TQueryManagerConnection::sendBytes: %s.\n", str); this->QueryOk = false; } } bool TQueryManagerConnection::getFlag(void){ return this->getByte() != 0; } uint8 TQueryManagerConnection::getByte(void){ uint8 Result = 0; try{ Result = this->ReadBuffer.readByte(); }catch(const char *str){ error("TQueryManagerConnection::getByte: %s.\n", str); } return Result; } uint16 TQueryManagerConnection::getWord(void){ uint16 Result = 0; try{ Result = this->ReadBuffer.readWord(); }catch(const char *str){ error("TQueryManagerConnection::getWord: %s.\n", str); } return Result; } uint32 TQueryManagerConnection::getQuad(void){ uint32 Result = 0; try{ Result = this->ReadBuffer.readQuad(); }catch(const char *str){ error("TQueryManagerConnection::getQuad: %s.\n", str); } return Result; } void TQueryManagerConnection::getString(char *Buffer, int MaxLength){ try{ this->ReadBuffer.readString(Buffer, MaxLength); }catch(const char *str){ error("TQueryManagerConnection::getString: %s.\n", str); if(MaxLength > 0){ Buffer[0] = 0; } } } void TQueryManagerConnection::getBytes(uint8 *Buffer, int Count){ try{ this->ReadBuffer.readBytes(Buffer, Count); }catch(const char *str){ error("TQueryManagerConnection::getBytes: %s.\n", str); } } int TQueryManagerConnection::executeQuery(int Timeout, bool AutoReconnect){ // NOTE(fusion): `prepareQuery` will already leave two extra bytes at the // beginning of the buffer for writing the request's length. int PacketSize = this->WriteBuffer.Position; int PayloadSize = PacketSize - 2; this->WriteBuffer.Position = 0; if(PayloadSize < 0xFFFF){ this->sendWord((uint16)PayloadSize); }else{ PacketSize += 4; if(PacketSize > this->BufferSize){ error("TQueryManagerConnection::executeQuery: Puffer zu klein.\n"); return QUERY_STATUS_FAILED; } memmove(&this->Buffer[6], &this->Buffer[2], PayloadSize); this->sendWord(0xFFFF); this->sendQuad((uint32)PayloadSize); } if(!this->QueryOk){ error("TQueryManagerConnection::executeQuery: Fehler beim Zusammenbauen der Anfrage.\n"); return QUERY_STATUS_FAILED; } const int MaxAttempts = 2; for(int Attempt = 1; true; Attempt += 1){ if(!this->isConnected()){ if(!AutoReconnect){ return QUERY_STATUS_FAILED; } // TODO(fusion): There was also a "fast" path that allocated on the // stack with variable length arrays. It would be a problem to mimic // that with `alloca` since its memory is only released at the end // of the function and we're in a loop. uint8 *TempBuffer = new uint8[PacketSize]; memcpy(TempBuffer, this->Buffer, PacketSize); this->connect(); memcpy(this->Buffer, TempBuffer, PacketSize); delete[] TempBuffer; if(!this->isConnected()){ return QUERY_STATUS_FAILED; } } if(this->write(this->Buffer, PacketSize) != PacketSize){ this->disconnect(); if(Attempt >= MaxAttempts){ error("TQueryManagerConnection::executeQuery: Fehler beim Abschicken der Anfrage.\n"); return QUERY_STATUS_FAILED; } continue; } uint8 Help[4]; int BytesRead = this->read(Help, 2, Timeout); if(BytesRead != 2){ this->disconnect(); if(BytesRead == -ETIMEDOUT || Attempt >= MaxAttempts){ return QUERY_STATUS_FAILED; } continue; } int ResponseSize = ((int)Help[0]) | ((int)Help[1] << 8); if(ResponseSize == 0xFFFF){ if(this->read(Help, 4, Timeout) != 4){ this->disconnect(); return QUERY_STATUS_FAILED; } ResponseSize = ((int)Help[0]) | ((int)Help[1] << 8) | ((int)Help[2] << 16) | ((int)Help[3] << 24); } if(ResponseSize <= 0 || ResponseSize > this->BufferSize){ this->disconnect(); error("TQueryManagerConnection::executeQuery: Ungültige Datengröße %d.\n", ResponseSize); return QUERY_STATUS_FAILED; } if(this->read(this->Buffer, ResponseSize, Timeout) != ResponseSize){ this->disconnect(); error("TQueryManagerConnection::executeQuery: Fehler beim Auslesen der Daten.\n"); return QUERY_STATUS_FAILED; } this->ReadBuffer.Size = ResponseSize; this->ReadBuffer.Position = 0; int Status = this->getByte(); if(Status == QUERY_STATUS_FAILED){ error("TQueryManagerConnection::executeQuery: Anfrage fehlgeschlagen.\n"); } return Status; } } int TQueryManagerConnection::checkAccountPassword(uint32 AccountID, const char *Password, const char *IPAddress){ this->prepareQuery(10); this->sendQuad(AccountID); this->sendString(Password); this->sendString(IPAddress); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode >= 1 && ErrorCode <= 4){ Result = ErrorCode; }else{ error("TQueryManagerConnection::checkAccountPassword: Ungültiger Fehlercode %d.\n", ErrorCode); } } return Result; } int TQueryManagerConnection::loginAdmin(uint32 AccountID, bool PrivateWorld, int *NumberOfCharacters, char (*Characters)[30], char (*Worlds)[30], uint8 (*IPAddresses)[4], uint16 *Ports, uint16 *PremiumDaysLeft){ this->prepareQuery(12); this->sendQuad(AccountID); this->sendFlag(PrivateWorld); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *NumberOfCharacters = this->getByte(); for(int i = 0; i < *NumberOfCharacters; i += 1){ this->getString(Characters[i], 30); this->getString(Worlds[i], 30); this->getBytes(IPAddresses[i], 4); Ports[i] = this->getWord(); } *PremiumDaysLeft = this->getWord(); }else if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode == 1){ Result = ErrorCode; }else{ error("TQueryManagerConnection::loginAdmin: Ungültiger Fehlercode %d.\n", ErrorCode); } } return Result; } int TQueryManagerConnection::loadWorldConfig(int *WorldType, int *RebootTime, int *IPAddress, int *Port, int *MaxPlayers, int *PremiumPlayerBuffer, int *MaxNewbies, int *PremiumNewbieBuffer){ this->prepareQuery(53); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *WorldType = this->getByte(); *RebootTime = (int)this->getByte() * 60; IPAddress[0] = this->getByte(); IPAddress[1] = this->getByte(); IPAddress[2] = this->getByte(); IPAddress[3] = this->getByte(); *Port = this->getWord(); *MaxPlayers = this->getWord(); *PremiumPlayerBuffer = this->getWord(); *MaxNewbies = this->getWord(); *PremiumNewbieBuffer = this->getWord(); } return Result; } static int GetRightByName(const char *RightName){ int Result = -1; if(strcmp(RightName, "PREMIUM_ACCOUNT") == 0){ Result = PREMIUM_ACCOUNT; }else if(strcmp(RightName, "NOTATION") == 0){ Result = NOTATION; }else if(strcmp(RightName, "NAMELOCK") == 0){ Result = NAMELOCK; }else if(strcmp(RightName, "STATEMENT_REPORT") == 0){ Result = STATEMENT_REPORT; }else if(strcmp(RightName, "BANISHMENT") == 0){ Result = BANISHMENT; }else if(strcmp(RightName, "FINAL_WARNING") == 0){ Result = FINAL_WARNING; }else if(strcmp(RightName, "IP_BANISHMENT") == 0){ Result = IP_BANISHMENT; }else if(strcmp(RightName, "KICK") == 0){ Result = KICK; }else if(strcmp(RightName, "HOME_TELEPORT") == 0){ Result = HOME_TELEPORT; }else if(strcmp(RightName, "GAMEMASTER_BROADCAST") == 0){ Result = GAMEMASTER_BROADCAST; }else if(strcmp(RightName, "ANONYMOUS_BROADCAST") == 0){ Result = ANONYMOUS_BROADCAST; }else if(strcmp(RightName, "NO_BANISHMENT") == 0){ Result = NO_BANISHMENT; }else if(strcmp(RightName, "ALLOW_MULTICLIENT") == 0){ Result = ALLOW_MULTICLIENT; }else if(strcmp(RightName, "LOG_COMMUNICATION") == 0){ Result = LOG_COMMUNICATION; }else if(strcmp(RightName, "READ_GAMEMASTER_CHANNEL") == 0){ Result = READ_GAMEMASTER_CHANNEL; }else if(strcmp(RightName, "READ_TUTOR_CHANNEL") == 0){ Result = READ_TUTOR_CHANNEL; }else if(strcmp(RightName, "HIGHLIGHT_HELP_CHANNEL") == 0){ Result = HIGHLIGHT_HELP_CHANNEL; }else if(strcmp(RightName, "SEND_BUGREPORTS") == 0){ Result = SEND_BUGREPORTS; }else if(strcmp(RightName, "NAME_INSULTING") == 0){ Result = NAME_INSULTING; }else if(strcmp(RightName, "NAME_SENTENCE") == 0){ Result = NAME_SENTENCE; }else if(strcmp(RightName, "NAME_NONSENSICAL_LETTERS") == 0){ Result = NAME_NONSENSICAL_LETTERS; }else if(strcmp(RightName, "NAME_BADLY_FORMATTED") == 0){ Result = NAME_BADLY_FORMATTED; }else if(strcmp(RightName, "NAME_NO_PERSON") == 0){ Result = NAME_NO_PERSON; }else if(strcmp(RightName, "NAME_CELEBRITY") == 0){ Result = NAME_CELEBRITY; }else if(strcmp(RightName, "NAME_COUNTRY") == 0){ Result = NAME_COUNTRY; }else if(strcmp(RightName, "NAME_FAKE_IDENTITY") == 0){ Result = NAME_FAKE_IDENTITY; }else if(strcmp(RightName, "NAME_FAKE_POSITION") == 0){ Result = NAME_FAKE_POSITION; }else if(strcmp(RightName, "STATEMENT_INSULTING") == 0){ Result = STATEMENT_INSULTING; }else if(strcmp(RightName, "STATEMENT_SPAMMING") == 0){ Result = STATEMENT_SPAMMING; }else if(strcmp(RightName, "STATEMENT_ADVERT_OFFTOPIC") == 0){ Result = STATEMENT_ADVERT_OFFTOPIC; }else if(strcmp(RightName, "STATEMENT_ADVERT_MONEY") == 0){ Result = STATEMENT_ADVERT_MONEY; }else if(strcmp(RightName, "STATEMENT_NON_ENGLISH") == 0){ Result = STATEMENT_NON_ENGLISH; }else if(strcmp(RightName, "STATEMENT_CHANNEL_OFFTOPIC") == 0){ Result = STATEMENT_CHANNEL_OFFTOPIC; }else if(strcmp(RightName, "STATEMENT_VIOLATION_INCITING") == 0){ Result = STATEMENT_VIOLATION_INCITING; }else if(strcmp(RightName, "CHEATING_BUG_ABUSE") == 0){ Result = CHEATING_BUG_ABUSE; }else if(strcmp(RightName, "CHEATING_GAME_WEAKNESS") == 0){ Result = CHEATING_GAME_WEAKNESS; }else if(strcmp(RightName, "CHEATING_MACRO_USE") == 0){ Result = CHEATING_MACRO_USE; }else if(strcmp(RightName, "CHEATING_MODIFIED_CLIENT") == 0){ Result = CHEATING_MODIFIED_CLIENT; }else if(strcmp(RightName, "CHEATING_HACKING") == 0){ Result = CHEATING_HACKING; }else if(strcmp(RightName, "CHEATING_MULTI_CLIENT") == 0){ Result = CHEATING_MULTI_CLIENT; }else if(strcmp(RightName, "CHEATING_ACCOUNT_TRADING") == 0){ Result = CHEATING_ACCOUNT_TRADING; }else if(strcmp(RightName, "CHEATING_ACCOUNT_SHARING") == 0){ Result = CHEATING_ACCOUNT_SHARING; }else if(strcmp(RightName, "GAMEMASTER_THREATENING") == 0){ Result = GAMEMASTER_THREATENING; }else if(strcmp(RightName, "GAMEMASTER_PRETENDING") == 0){ Result = GAMEMASTER_PRETENDING; }else if(strcmp(RightName, "GAMEMASTER_INFLUENCE") == 0){ Result = GAMEMASTER_INFLUENCE; }else if(strcmp(RightName, "GAMEMASTER_FALSE_REPORTS") == 0){ Result = GAMEMASTER_FALSE_REPORTS; }else if(strcmp(RightName, "KILLING_EXCESSIVE_UNJUSTIFIED") == 0){ Result = KILLING_EXCESSIVE_UNJUSTIFIED; }else if(strcmp(RightName, "DESTRUCTIVE_BEHAVIOUR") == 0){ Result = DESTRUCTIVE_BEHAVIOUR; }else if(strcmp(RightName, "SPOILING_AUCTION") == 0){ Result = SPOILING_AUCTION; }else if(strcmp(RightName, "INVALID_PAYMENT") == 0){ Result = INVALID_PAYMENT; }else if(strcmp(RightName, "TELEPORT_TO_CHARACTER") == 0){ Result = TELEPORT_TO_CHARACTER; }else if(strcmp(RightName, "TELEPORT_TO_MARK") == 0){ Result = TELEPORT_TO_MARK; }else if(strcmp(RightName, "TELEPORT_VERTICAL") == 0){ Result = TELEPORT_VERTICAL; }else if(strcmp(RightName, "TELEPORT_TO_COORDINATE") == 0){ Result = TELEPORT_TO_COORDINATE; }else if(strcmp(RightName, "LEVITATE") == 0){ Result = LEVITATE; }else if(strcmp(RightName, "SPECIAL_MOVEUSE") == 0){ Result = SPECIAL_MOVEUSE; }else if(strcmp(RightName, "MODIFY_GOSTRENGTH") == 0){ Result = MODIFY_GOSTRENGTH; }else if(strcmp(RightName, "SHOW_COORDINATE") == 0){ Result = SHOW_COORDINATE; }else if(strcmp(RightName, "RETRIEVE") == 0){ Result = RETRIEVE; }else if(strcmp(RightName, "ENTER_HOUSES") == 0){ Result = ENTER_HOUSES; }else if(strcmp(RightName, "OPEN_NAMEDOORS") == 0){ // TODO(fusion): The actual name was "OPEN_NAMEDDOORS" but could be a typo? // Either way, it doesn't make that much of a difference since there is no // actual query manager outside the ones we might build ourselves. Result = OPEN_NAMEDOORS; }else if(strcmp(RightName, "INVULNERABLE") == 0){ Result = INVULNERABLE; }else if(strcmp(RightName, "UNLIMITED_MANA") == 0){ Result = UNLIMITED_MANA; }else if(strcmp(RightName, "KEEP_INVENTORY") == 0){ Result = KEEP_INVENTORY; }else if(strcmp(RightName, "ALL_SPELLS") == 0){ Result = ALL_SPELLS; }else if(strcmp(RightName, "UNLIMITED_CAPACITY") == 0){ Result = UNLIMITED_CAPACITY; }else if(strcmp(RightName, "ZERO_CAPACITY") == 0){ Result = ZERO_CAPACITY; }else if(strcmp(RightName, "ATTACK_EVERYWHERE") == 0){ Result = ATTACK_EVERYWHERE; }else if(strcmp(RightName, "NO_ATTACK") == 0){ Result = NO_ATTACK; }else if(strcmp(RightName, "NO_RUNES") == 0){ Result = NO_RUNES; }else if(strcmp(RightName, "NO_LOGOUT_BLOCK") == 0){ Result = NO_LOGOUT_BLOCK; }else if(strcmp(RightName, "GAMEMASTER_OUTFIT") == 0){ Result = GAMEMASTER_OUTFIT; }else if(strcmp(RightName, "ILLUMINATE") == 0){ Result = ILLUMINATE; }else if(strcmp(RightName, "CHANGE_PROFESSION") == 0){ Result = CHANGE_PROFESSION; }else if(strcmp(RightName, "IGNORED_BY_MONSTERS") == 0){ Result = IGNORED_BY_MONSTERS; }else if(strcmp(RightName, "SHOW_KEYHOLE_NUMBERS") == 0){ Result = SHOW_KEYHOLE_NUMBERS; }else if(strcmp(RightName, "CREATE_OBJECTS") == 0){ Result = CREATE_OBJECTS; }else if(strcmp(RightName, "CREATE_MONEY") == 0){ Result = CREATE_MONEY; }else if(strcmp(RightName, "CREATE_MONSTERS") == 0){ Result = CREATE_MONSTERS; }else if(strcmp(RightName, "CHANGE_SKILLS") == 0){ Result = CHANGE_SKILLS; }else if(strcmp(RightName, "CLEANUP_FIELDS") == 0){ Result = CLEANUP_FIELDS; }else if(strcmp(RightName, "NO_STATISTICS") == 0){ Result = NO_STATISTICS; } return Result; } // NOTE(fusion): These seem to be related to forums and an admin panel. static bool IsAdministrativeRight(const char *RightName){ return strcmp(RightName, "CLEAR_CHARACTER_INFO") == 0 || strcmp(RightName, "CLEAR_GUILDS") == 0 || strcmp(RightName, "DELETE_GUILDS") == 0 || strcmp(RightName, "BOARD_REPORT") == 0 || strcmp(RightName, "BOARD_MODERATION") == 0 || strcmp(RightName, "BOARD_ANONYMOUS_EDIT") == 0 || strcmp(RightName, "BOARD_PRECONFIRMED") == 0 || strcmp(RightName, "KEEP_ACCOUNT") == 0 || strcmp(RightName, "INVITED") == 0 || strcmp(RightName, "CIPWATCH_ADMIN") == 0 || strcmp(RightName, "CIPWATCH_USER") == 0 || strcmp(RightName, "CREATECHAR_GAMEMASTER") == 0 || strcmp(RightName, "CREATECHAR_GOD") == 0 || strcmp(RightName, "CREATECHAR_TEST") == 0 || strcmp(RightName, "VIEW_ACCOUNT") == 0 || strcmp(RightName, "VIEW_GAMEMASTER_RECORD") == 0 || strcmp(RightName, "VIEW_CRIMINAL_RECORD") == 0 || strcmp(RightName, "VIEW_LOG_FILES") == 0 || strcmp(RightName, "MODIFY_BANISHMENT") == 0 || strcmp(RightName, "APPOINT_CIP") == 0 || strcmp(RightName, "APPOINT_SGM") == 0 || strcmp(RightName, "APPOINT_JGM") == 0 || strcmp(RightName, "APPOINT_SENATOR") == 0 || strcmp(RightName, "SET_ACCOUNT_RIGHTS") == 0 || strcmp(RightName, "SET_CHARACTER_RIGHTS") == 0 || strcmp(RightName, "SET_ACCOUNTGROUP_RIGHTS") == 0 || strcmp(RightName, "SET_CHARACTERGROUP_RIGHTS") == 0 || strcmp(RightName, "KEEP_CHARACTER") == 0 || strcmp(RightName, "EXTRA_CHARACTER") == 0; } // NOTE(fusion): `PlayerName` is an input and output parameter. It will contain // the correct player name with upper and lower case letters if the operation is // successful. int TQueryManagerConnection::loginGame(uint32 AccountID, char *PlayerName, const char *Password, const char *IPAddress, bool PrivateWorld, bool PremiumAccountRequired, bool GamemasterRequired, uint32 *CharacterID, int *Sex, char *Guild, char *Rank, char *Title, int *NumberOfBuddies, uint32 *BuddyIDs, char (*BuddyNames)[30], uint8 *Rights, bool *PremiumAccountActivated){ this->prepareQuery(20); this->sendQuad(AccountID); this->sendString(PlayerName); this->sendString(Password); this->sendString(IPAddress); this->sendFlag(PrivateWorld); this->sendFlag(PremiumAccountRequired); this->sendFlag(GamemasterRequired); int Status = this->executeQuery(120, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *CharacterID = this->getQuad(); this->getString(PlayerName, 30); *Sex = this->getByte(); this->getString(Guild, 30); this->getString(Rank, 30); this->getString(Title, 30); *NumberOfBuddies = this->getByte(); int SkipBuddies = 0; if(*NumberOfBuddies > 100){ // MAX_BUDDIES error("TQueryManagerConnection::loginGame: zu viele Buddys (%d) für %s.\n", *NumberOfBuddies, PlayerName); SkipBuddies = *NumberOfBuddies - 100; *NumberOfBuddies = 100; } for(int i = 0; i < *NumberOfBuddies; i += 1){ BuddyIDs[i] = this->getQuad(); this->getString(BuddyNames[i], 30); } if(SkipBuddies > 0){ char BuddyName[30]; for(int i = 0; i < SkipBuddies; i += 1){ this->getQuad(); this->getString(BuddyName, 30); } } int NumberOfRights = this->getByte(); memset(Rights, 0, 12); // MAX_RIGHT_BYTES ? for(int i = 0; i < NumberOfRights; i += 1){ char RightName[30]; this->getString(RightName, 30); int Right = GetRightByName(RightName); if(Right != -1){ SetBit(Rights, Right); }else if(!IsAdministrativeRight(RightName)){ error("TQueryManagerConnection::loginGame: Unbekanntes Recht %s.\n", RightName); } } *PremiumAccountActivated = this->getFlag(); }else if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode >= 1 && ErrorCode <= 15){ Result = ErrorCode; }else{ error("TQueryManagerConnection::loginGame: Ungültiger Fehlercode %d.\n", ErrorCode); } } return Result; } int TQueryManagerConnection::logoutGame(uint32 CharacterID, int Level, const char *Profession, const char *Residence, time_t LastLoginTime, int TutorActivities){ this->prepareQuery(21); this->sendQuad(CharacterID); this->sendWord(Level); this->sendString(Profession); this->sendString(Residence); this->sendQuad((uint32)LastLoginTime); this->sendWord((uint16)TutorActivities); int Status = this->executeQuery(120, true); return (Status == QUERY_STATUS_OK ? 0 : -1); } int TQueryManagerConnection::setNotation(uint32 GamemasterID, const char *PlayerName, const char *IPAddress, const char *Reason, const char *Comment, uint32 *BanishmentID){ this->prepareQuery(26); this->sendQuad(GamemasterID); this->sendString(PlayerName); this->sendString(IPAddress); this->sendString(Reason); this->sendString(Comment); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *BanishmentID = this->getQuad(); }else if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode >= 1 && ErrorCode <= 2){ Result = ErrorCode; }else{ error("TQueryManagerConnection::setNotation: Ungültiger Fehlercode %d.\n", ErrorCode); } }else{ error("TQueryManagerConnection::setNotation: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::setNamelock(uint32 GamemasterID, const char *PlayerName, const char *IPAddress, const char *Reason, const char *Comment){ this->prepareQuery(23); this->sendQuad(GamemasterID); this->sendString(PlayerName); this->sendString(IPAddress); this->sendString(Reason); this->sendString(Comment); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode >= 1 && ErrorCode <= 4){ Result = ErrorCode; }else{ error("TQueryManagerConnection::setNamelock: Ungültiger Fehlercode %d.\n", ErrorCode); } }else if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::setNamelock: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::banishAccount(uint32 GamemasterID, const char *PlayerName, const char *IPAddress, const char *Reason, const char *Comment, bool *FinalWarning, int *Days, uint32 *BanishmentID){ this->prepareQuery(25); this->sendQuad(GamemasterID); this->sendString(PlayerName); this->sendString(IPAddress); this->sendString(Reason); this->sendString(Comment); this->sendFlag(*FinalWarning); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *BanishmentID = this->getQuad(); *Days = this->getByte(); *FinalWarning = this->getFlag(); if(*Days == 0xFF){ *Days = -1; } }else if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode >= 1 && ErrorCode <= 3){ Result = ErrorCode; }else{ error("TQueryManagerConnection::banishAccount: Ungültiger Fehlercode %d.\n", ErrorCode); } }else{ error("TQueryManagerConnection::banishAccount: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::reportStatement(uint32 ReporterID, const char *PlayerName, const char *Reason, const char *Comment, uint32 BanishmentID, uint32 StatementID, int NumberOfStatements, uint32 *StatementIDs, int *TimeStamps, uint32 *CharacterIDs, const char (*Channels)[30], const char (*Texts)[256]){ this->prepareQuery(27); this->sendQuad(ReporterID); this->sendString(PlayerName); this->sendString(Reason); this->sendString(Comment); this->sendQuad(BanishmentID); this->sendQuad(StatementID); int NonZeroStatements = 0; for(int i = 0; i < NumberOfStatements; i += 1){ if(StatementIDs[i] != 0){ NonZeroStatements += 1; } } this->sendWord((uint16)NonZeroStatements); for(int i = 0; i < NumberOfStatements; i += 1){ if(StatementIDs[i] != 0){ this->sendQuad(StatementIDs[i]); this->sendQuad((uint32)TimeStamps[i]); this->sendQuad(CharacterIDs[i]); this->sendString(Channels[i]); this->sendString(Texts[i]); } } int Status = this->executeQuery(180, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode >= 1 && ErrorCode <= 2){ Result = ErrorCode; }else{ error("TQueryManagerConnection::reportStatement: Ungültiger Fehlercode %d.\n", ErrorCode); } }else if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::reportStatement: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::banishIPAddress(uint32 GamemasterID, const char *PlayerName, const char *IPAddress, const char *Reason, const char *Comment){ this->prepareQuery(28); this->sendQuad(GamemasterID); this->sendString(PlayerName); this->sendString(IPAddress); this->sendString(Reason); this->sendString(Comment); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode >= 1 && ErrorCode <= 2){ Result = ErrorCode; }else{ error("TQueryManagerConnection::banishIPAddress: Ungültiger Fehlercode %d.\n", ErrorCode); } }else if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::banishIPAddress: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::logCharacterDeath(uint32 CharacterID, int Level, uint32 Offender, const char *Remark, bool Unjustified, time_t Time){ this->prepareQuery(29); this->sendQuad(CharacterID); this->sendWord((uint16)Level); this->sendQuad(Offender); this->sendString(Remark); this->sendFlag(Unjustified); this->sendQuad((uint32)Time); int Status = this->executeQuery(90, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::logCharacterDeath: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::addBuddy(uint32 AccountID, uint32 Buddy){ this->prepareQuery(30); this->sendQuad(AccountID); this->sendQuad(Buddy); int Status = this->executeQuery(90, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::addBuddy: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::removeBuddy(uint32 AccountID, uint32 Buddy){ this->prepareQuery(31); this->sendQuad(AccountID); this->sendQuad(Buddy); int Status = this->executeQuery(90, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::removeBuddy: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::decrementIsOnline(uint32 CharacterID){ this->prepareQuery(32); this->sendQuad(CharacterID); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::decrementIsOnline: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::finishAuctions(int *NumberOfAuctions, uint16 *HouseIDs, uint32 *CharacterIDs, char (*CharacterNames)[30], int *Bids){ this->prepareQuery(33); int Status = this->executeQuery(60, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ int MaxNumberOfAuctions = *NumberOfAuctions; *NumberOfAuctions = this->getWord(); if(*NumberOfAuctions > MaxNumberOfAuctions){ error("TQueryManagerConnection::finishAuctions: zu viele Auktionen (%d>%d).\n", *NumberOfAuctions, MaxNumberOfAuctions); return -1; } for(int i = 0; i < *NumberOfAuctions; i += 1){ HouseIDs[i] = this->getWord(); CharacterIDs[i] = this->getQuad(); this->getString(CharacterNames[i], 30); Bids[i] = (int)this->getQuad(); } }else{ error("TQueryManagerConnection::finishAuctions: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::excludeFromAuctions(uint32 CharacterID, bool Banish){ this->prepareQuery(51); this->sendQuad(CharacterID); this->sendFlag(Banish); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::excludeFromAuctions: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::transferHouses(int *NumberOfTransfers, uint16 *HouseIDs, uint32 *NewOwnerIDs, char (*NewOwnerNames)[30], int *Prices){ this->prepareQuery(35); int Status = this->executeQuery(60, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ int MaxNumberOfTransfers = *NumberOfTransfers; *NumberOfTransfers = this->getWord(); if(*NumberOfTransfers > MaxNumberOfTransfers){ error("TQueryManagerConnection::transferHouses: zu viele Transfers (%d>%d).\n", *NumberOfTransfers, MaxNumberOfTransfers); return -1; } for(int i = 0; i < *NumberOfTransfers; i += 1){ HouseIDs[i] = this->getWord(); NewOwnerIDs[i] = this->getQuad(); this->getString(NewOwnerNames[i], 30); Prices[i] = (int)this->getQuad(); } }else{ error("TQueryManagerConnection::transferHouses: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::cancelHouseTransfer(uint16 HouseID){ this->prepareQuery(52); this->sendWord(HouseID); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::cancelHouseTransfer: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::evictFreeAccounts(int *NumberOfEvictions, uint16 *HouseIDs, uint32 *OwnerIDs){ this->prepareQuery(36); int Status = this->executeQuery(60, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ int MaxNumberOfEvictions = *NumberOfEvictions; *NumberOfEvictions = this->getWord(); if(*NumberOfEvictions > MaxNumberOfEvictions){ error("TQueryManagerConnection::evictFreeAccounts: zu viele Räumungen (%d>%d).\n", *NumberOfEvictions, MaxNumberOfEvictions); return -1; } for(int i = 0; i < *NumberOfEvictions; i += 1){ HouseIDs[i] = this->getWord(); OwnerIDs[i] = this->getQuad(); } }else{ error("TQueryManagerConnection::evictFreeAccounts: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::evictDeletedCharacters(int *NumberOfEvictions, uint16 *HouseIDs){ this->prepareQuery(37); int Status = this->executeQuery(60, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ int MaxNumberOfEvictions = *NumberOfEvictions; *NumberOfEvictions = this->getWord(); if(*NumberOfEvictions > MaxNumberOfEvictions){ error("TQueryManagerConnection::evictDeletedCharacters: zu viele Räumungen (%d>%d).\n", *NumberOfEvictions, MaxNumberOfEvictions); return -1; } for(int i = 0; i < *NumberOfEvictions; i += 1){ HouseIDs[i] = this->getWord(); } }else{ error("TQueryManagerConnection::evictDeletedCharacters: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::evictExGuildleaders(int NumberOfGuildhouses, int *NumberOfEvictions, uint16 *HouseIDs, uint32 *Guildleaders){ this->prepareQuery(38); this->sendWord((uint16)NumberOfGuildhouses); for(int i = 0; i < NumberOfGuildhouses; i += 1){ this->sendWord(HouseIDs[i]); this->sendQuad(Guildleaders[i]); } int Status = this->executeQuery(60, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *NumberOfEvictions = this->getWord(); for(int i = 0; i < *NumberOfEvictions; i += 1){ HouseIDs[i] = this->getWord(); } }else{ error("TQueryManagerConnection::evictExGuildleaders: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::insertHouseOwner(uint16 HouseID, uint32 OwnerID, int PaidUntil){ this->prepareQuery(39); this->sendWord(HouseID); this->sendQuad(OwnerID); this->sendQuad((uint32)PaidUntil); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::insertHouseOwner: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::updateHouseOwner(uint16 HouseID, uint32 OwnerID, int PaidUntil){ this->prepareQuery(40); this->sendWord(HouseID); this->sendQuad(OwnerID); this->sendQuad((uint32)PaidUntil); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::updateHouseOwner: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::deleteHouseOwner(uint16 HouseID){ this->prepareQuery(41); this->sendWord(HouseID); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::deleteHouseOwner: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::getHouseOwners(int *NumberOfOwners, uint16 *HouseIDs, uint32 *OwnerIDs, char (*OwnerNames)[30], int *PaidUntils){ this->prepareQuery(42); int Status = this->executeQuery(60, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ int MaxNumberOfOwners = *NumberOfOwners; *NumberOfOwners = this->getWord(); if(*NumberOfOwners > MaxNumberOfOwners){ error("TQueryManagerConnection::getHouseOwners: zu viele Häuser (%d>%d).\n", *NumberOfOwners, MaxNumberOfOwners); return -1; } for(int i = 0; i < *NumberOfOwners; i += 1){ HouseIDs[i] = this->getWord(); OwnerIDs[i] = this->getQuad(); this->getString(OwnerNames[i], 30); PaidUntils[i] = (int)this->getQuad(); } }else{ error("TQueryManagerConnection::getHouseOwners: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::getAuctions(int *NumberOfAuctions, uint16 *HouseIDs){ this->prepareQuery(43); int Status = this->executeQuery(120, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ int MaxNumberOfAuctions = *NumberOfAuctions; *NumberOfAuctions = this->getWord(); if(*NumberOfAuctions > MaxNumberOfAuctions){ error("TQueryManagerConnection::getAuctions: zu viele Auktionen (%d>%d).\n", *NumberOfAuctions, MaxNumberOfAuctions); return -1; } for(int i = 0; i < *NumberOfAuctions; i += 1){ HouseIDs[i] = this->getWord(); } }else{ error("TQueryManagerConnection::getAuctions: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::startAuction(uint16 HouseID){ this->prepareQuery(44); this->sendWord(HouseID); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::startAuction: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::insertHouses(int NumberOfHouses, uint16 *HouseIDs, const char **Names, int *Rents, const char **Descriptions, int *Sizes, int *PositionsX,int *PositionsY,int *PositionsZ, char (*Towns)[30], bool *Guildhouses){ this->prepareQuery(45); this->sendWord(NumberOfHouses); for(int i = 0; i < NumberOfHouses; i += 1){ this->sendWord(HouseIDs[i]); this->sendString(Names[i]); this->sendQuad((int)Rents[i]); this->sendString(Descriptions[i]); this->sendWord((uint16)Sizes[i]); this->sendWord((uint16)PositionsX[i]); this->sendWord((uint16)PositionsY[i]); this->sendByte((uint8)PositionsZ[i]); this->sendString(Towns[i]); this->sendFlag(Guildhouses[i]); } int Status = this->executeQuery(60, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::insertHouses: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::clearIsOnline(int *NumberOfAffectedPlayers){ this->prepareQuery(46); int Status = this->executeQuery(120, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *NumberOfAffectedPlayers = this->getWord(); }else{ error("TQueryManagerConnection::clearIsOnline: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::createPlayerlist(int NumberOfPlayers, const char **Names, int *Levels, const char (*Professions)[30], bool *NewRecord){ this->prepareQuery(47); if(NumberOfPlayers >= 0){ this->sendWord((uint16)NumberOfPlayers); for(int i = 0; i < NumberOfPlayers; i += 1){ this->sendString(Names[i]); this->sendWord((uint16)Levels[i]); this->sendString(Professions[i]); } }else{ this->sendWord(0xFFFF); } int Status = this->executeQuery(240, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *NewRecord = this->getFlag(); }else{ error("TQueryManagerConnection::createPlayerlist: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::logKilledCreatures(int NumberOfRaces, const char **Names, int *KilledPlayers, int *KilledCreatures){ this->prepareQuery(48); this->sendWord((uint16)NumberOfRaces); for(int i = 0; i < NumberOfRaces; i += 1){ this->sendString(Names[i]); this->sendQuad((uint32)KilledPlayers[i]); this->sendQuad((uint32)KilledCreatures[i]); } int Status = this->executeQuery(240, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::logKilledCreatures: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::loadPlayers(uint32 MinimumCharacterID, int *NumberOfPlayers, char (*Names)[30], uint32 *CharacterIDs){ this->prepareQuery(50); this->sendQuad(MinimumCharacterID); int Status = this->executeQuery(900, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ // TODO(fusion): This is called to fill the player index in `InitPlayerIndex` // but there is no explicit parameter that limits how many results are returned // which could be a problem. *NumberOfPlayers = (int)this->getQuad(); for(int i = 0; i < *NumberOfPlayers; i += 1){ this->getString(Names[i], 30); CharacterIDs[i] = this->getQuad(); } }else{ error("TQueryManagerConnection::loadPlayers: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::getKeptCharacters(uint32 MinimumCharacterID, int *NumberOfPlayers, uint32 *CharacterIDs){ this->prepareQuery(200); this->sendQuad(MinimumCharacterID); int Status = this->executeQuery(1800, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ // TODO(fusion): Same as `loadPlayers`. *NumberOfPlayers = (int)this->getQuad(); for(int i = 0; i < *NumberOfPlayers; i += 1){ CharacterIDs[i] = this->getQuad(); } }else{ error("TQueryManagerConnection::getKeptCharacters: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::getDeletedCharacters(uint32 MinimumCharacterID, int *NumberOfPlayers, uint32 *CharacterIDs){ this->prepareQuery(201); this->sendQuad(MinimumCharacterID); int Status = this->executeQuery(900, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ // TODO(fusion): Same as `loadPlayers`. *NumberOfPlayers = (int)this->getQuad(); for(int i = 0; i < *NumberOfPlayers; i += 1){ CharacterIDs[i] = this->getQuad(); } }else{ error("TQueryManagerConnection::getKeptCharacters: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::deleteOldCharacter(uint32 CharacterID){ this->prepareQuery(202); this->sendQuad(CharacterID); int Status = this->executeQuery(30, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::deleteOldCharacter: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::getHiddenCharacters(uint32 MinimumCharacterID, int *NumberOfPlayers, uint32 *CharacterIDs){ this->prepareQuery(203); this->sendQuad(MinimumCharacterID); int Status = this->executeQuery(120, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ // TODO(fusion): Same as `loadPlayers`. *NumberOfPlayers = (int)this->getQuad(); for(int i = 0; i < *NumberOfPlayers; i += 1){ CharacterIDs[i] = this->getQuad(); } }else{ error("TQueryManagerConnection::getHiddenCharacters: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::createHighscores(int NumberOfPlayers, uint32 *CharacterIDs, int *ExpPoints, int *ExpLevel, int *Fist, int *Club, int *Axe, int *Sword, int *Distance, int *Shielding, int *Magic, int *Fishing){ this->prepareQuery(204); this->sendQuad((uint32)NumberOfPlayers); for(int i = 0; i < NumberOfPlayers; i += 1){ this->sendQuad(CharacterIDs[i]); this->sendQuad((uint32)ExpPoints[i]); this->sendWord((uint16)ExpLevel[i]); this->sendWord((uint16)Fist[i]); this->sendWord((uint16)Club[i]); this->sendWord((uint16)Axe[i]); this->sendWord((uint16)Sword[i]); this->sendWord((uint16)Distance[i]); this->sendWord((uint16)Shielding[i]); this->sendWord((uint16)Magic[i]); this->sendWord((uint16)Fishing[i]); } int Status = this->executeQuery(120, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::createHighscores: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::createCensus(void){ this->prepareQuery(205); int Status = this->executeQuery(600, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::createCensus: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::createKillStatistics(void){ this->prepareQuery(206); int Status = this->executeQuery(300, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status != QUERY_STATUS_OK){ error("TQueryManagerConnection::createKillStatistics: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::getPlayersOnline(int *NumberOfWorlds, char (*Names)[30], uint16 *Players){ this->prepareQuery(207); int Status = this->executeQuery(120, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *NumberOfWorlds = this->getByte(); for(int i = 0; i < *NumberOfWorlds; i += 1){ this->getString(Names[i], 30); Players[i] = this->getWord(); } }else{ error("TQueryManagerConnection::getPlayersOnline: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::getWorlds(int *NumberOfWorlds, char (*Names)[30]){ this->prepareQuery(208); int Status = this->executeQuery(120, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *NumberOfWorlds = this->getByte(); for(int i = 0; i < *NumberOfWorlds; i += 1){ this->getString(Names[i], 30); } }else{ error("TQueryManagerConnection::getWorlds: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::getServerLoad(const char *World, int Period, int *Data){ this->prepareQuery(209); this->sendString(World); this->sendByte((uint8)Period); int Status = this->executeQuery(360, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ // TODO(fusion): Another hardcoded constant? for(int i = 0; i < 600; i += 0){ Data[i] = this->getWord(); if(Data[i] == 0xFFFF){ Data[i] = -1; } } }else{ error("TQueryManagerConnection::getServerLoad: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::insertPaymentDataOld(uint32 PurchaseNr, uint32 ReferenceNr, const char *FirstName, const char *LastName, const char *Company, const char *Street, const char *Zip, const char *City, const char *Country, const char *State, const char *Phone, const char *Fax, const char *EMail, const char *PaymentMethod, uint32 ProductID, const char *Registrant, uint32 AccountID, uint32 *PaymentID){ this->prepareQuery(210); this->sendQuad(PurchaseNr); this->sendQuad(ReferenceNr); this->sendString(FirstName); this->sendString(LastName); this->sendString(Company); this->sendString(Street); this->sendString(Zip); this->sendString(City); this->sendString(Country); this->sendString(State); this->sendString(Phone); this->sendString(Fax); this->sendString(EMail); this->sendString(PaymentMethod); this->sendQuad(ProductID); this->sendString(Registrant); this->sendQuad(AccountID); int Status = this->executeQuery(360, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *PaymentID = this->getQuad(); }else if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode == 1){ Result = ErrorCode; }else{ error("TQueryManagerConnection::insertPaymentDataOld: Ungültiger Fehlercode %d.\n", ErrorCode); } }else{ error("TQueryManagerConnection::insertPaymentDataOld: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::addPaymentOld(uint32 AccountID, const char *Description, uint32 PaymentID, int Days, int *ActionTaken){ this->prepareQuery(211); this->sendQuad(AccountID); this->sendString(Description); this->sendQuad(PaymentID); this->sendWord((uint16)Days); int Status = this->executeQuery(360, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *ActionTaken = this->getByte(); }else if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode >= 1 && ErrorCode <= 2){ Result = ErrorCode; }else{ error("TQueryManagerConnection::addPaymentOld: Ungültiger Fehlercode %d.\n", ErrorCode); } }else{ error("TQueryManagerConnection::addPaymentOld: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::cancelPaymentOld(uint32 PurchaseNr, uint32 ReferenceNr, uint32 AccountID, bool *IllegalUse, char *EMailAddress){ this->prepareQuery(212); this->sendQuad(PurchaseNr); this->sendQuad(ReferenceNr); this->sendQuad(AccountID); int Status = this->executeQuery(360, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *IllegalUse = this->getFlag(); this->getString(EMailAddress, 50); }else if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode >= 1 && ErrorCode <= 2){ Result = ErrorCode; }else{ error("TQueryManagerConnection::cancelPaymentOld: Ungültiger Fehlercode %d.\n", ErrorCode); } }else{ error("TQueryManagerConnection::cancelPaymentOld: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::insertPaymentDataNew(uint32 PurchaseNr, uint32 ReferenceNr, const char *FirstName, const char *LastName, const char *Company, const char *Street, const char *Zip, const char *City, const char *Country, const char *State, const char *Phone, const char *Fax, const char *EMail, const char *PaymentMethod, uint32 ProductID, const char *Registrant, const char *PaymentKey, uint32 *PaymentID){ this->prepareQuery(213); this->sendWord(PurchaseNr); this->sendWord(ReferenceNr); this->sendString(FirstName); this->sendString(LastName); this->sendString(Company); this->sendString(Street); this->sendString(Zip); this->sendString(City); this->sendString(Country); this->sendString(State); this->sendString(Phone); this->sendString(Fax); this->sendString(EMail); this->sendString(PaymentMethod); this->sendWord(ProductID); this->sendString(Registrant); this->sendString(PaymentKey); int Status = this->executeQuery(360, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *PaymentID = this->getQuad(); }else if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode == 1){ Result = ErrorCode; }else{ error("TQueryManagerConnection::insertPaymentDataNew: Ungültiger Fehlercode %d.\n", ErrorCode); } }else{ error("TQueryManagerConnection::insertPaymentDataNew: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::addPaymentNew(const char *PaymentKey, uint32 PaymentID, int *ActionTaken, char *EMailReceiver){ this->prepareQuery(214); this->sendString(PaymentKey); this->sendQuad(PaymentID); int Status = this->executeQuery(360, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *ActionTaken = this->getByte(); if(*ActionTaken == 5){ this->getString(EMailReceiver, 100); } }else if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode >= 1 && ErrorCode <= 3){ Result = ErrorCode; }else{ error("TQueryManagerConnection::addPaymentNew: Ungültiger Fehlercode %d.\n", ErrorCode); } }else{ error("TQueryManagerConnection::addPaymentNew: Anfrage fehlgeschlagen.\n"); } return Result; } int TQueryManagerConnection::cancelPaymentNew(uint32 PurchaseNr, uint32 ReferenceNr, const char *PaymentKey, bool *IllegalUse, bool *Present, char *EMailAddress){ this->prepareQuery(215); this->sendQuad(PurchaseNr); this->sendQuad(ReferenceNr); this->sendString(PaymentKey); int Status = this->executeQuery(360, true); int Result = (Status == QUERY_STATUS_OK ? 0 : -1); if(Status == QUERY_STATUS_OK){ *IllegalUse = this->getFlag(); *Present = this->getFlag(); this->getString(EMailAddress, 50); }else if(Status == QUERY_STATUS_ERROR){ int ErrorCode = this->getByte(); if(ErrorCode == 1){ Result = ErrorCode; }else{ error("TQueryManagerConnection::cancelPaymentNew: Ungültiger Fehlercode %d.\n", ErrorCode); } }else{ error("TQueryManagerConnection::cancelPaymentNew: Anfrage fehlgeschlagen.\n"); } return Result; } // TQueryManagerConnectionPool // ============================================================================= // TODO(fusion): Same as `TQueryManagerConnection::TQueryManagerConnection`. TQueryManagerConnectionPool::TQueryManagerConnectionPool(int Connections) : NumberOfConnections(std::max(Connections, 1)), QueryManagerConnection(NULL), QueryManagerConnectionFree(NULL), FreeQueryManagerConnections(this->NumberOfConnections), QueryManagerConnectionMutex(1) { if(Connections <= 0){ error("TQueryManagerConnectionPool::TQueryManagerConnectionPool:" " Ungültige Verbindungsanzahl %d.\n", Connections); } } void TQueryManagerConnectionPool::init(void){ this->QueryManagerConnection = new TQueryManagerConnection[this->NumberOfConnections]; this->QueryManagerConnectionFree = new bool[this->NumberOfConnections]; for(int i = 0; i < this->NumberOfConnections; i += 1){ if(!this->QueryManagerConnection[i].isConnected()){ error("TQueryManagerConnectionPool::init: Kann nicht zum Query-Manager verbinden.\n"); throw "cannot connect to query manager"; } this->QueryManagerConnectionFree[i] = true; } } void TQueryManagerConnectionPool::exit(void){ for(int i = 0; i < this->NumberOfConnections; i += 1){ this->FreeQueryManagerConnections.down(); } delete[] this->QueryManagerConnection; delete[] this->QueryManagerConnectionFree; } TQueryManagerConnection *TQueryManagerConnectionPool::getConnection(void){ int ConnectionIndex = -1; this->FreeQueryManagerConnections.down(); this->QueryManagerConnectionMutex.down(); for(int i = 0; i < this->NumberOfConnections; i += 1){ if(this->QueryManagerConnectionFree[i]){ this->QueryManagerConnectionFree[i] = false; ConnectionIndex = i; break; } } this->QueryManagerConnectionMutex.up(); if(ConnectionIndex == -1){ error("TQueryManagerConnectionPool::getConnection: Keine freie Verbindung gefunden.\n"); return NULL; } return &this->QueryManagerConnection[ConnectionIndex]; } void TQueryManagerConnectionPool::releaseConnection(TQueryManagerConnection *Connection){ int ConnectionIndex = -1; for(int i = 0; i < this->NumberOfConnections; i += 1){ if(&this->QueryManagerConnection[i] == Connection){ ConnectionIndex = i; break; } } if(ConnectionIndex == -1){ error("TQueryManagerConnectionPool::releaseConnection: Verbindung nicht gefunden.\n"); return; } this->QueryManagerConnectionFree[ConnectionIndex] = true; this->FreeQueryManagerConnections.up(); } // TQueryManagerPoolConnection // ============================================================================= TQueryManagerPoolConnection::TQueryManagerPoolConnection(TQueryManagerConnectionPool *Pool) : QueryManagerConnectionPool(Pool), QueryManagerConnection(NULL) { if(this->QueryManagerConnectionPool == NULL){ error("TQueryManagerPoolConnection::TQueryManagerPoolConnection: Pool ist NULL.\n"); return; } this->QueryManagerConnection = this->QueryManagerConnectionPool->getConnection(); } TQueryManagerPoolConnection::~TQueryManagerPoolConnection(void){ if(this->QueryManagerConnection != NULL){ ASSERT(this->QueryManagerConnectionPool != NULL); this->QueryManagerConnectionPool->releaseConnection(this->QueryManagerConnection); } }