diff --git a/src/CNLoginServer.cpp b/src/CNLoginServer.cpp index e2f8b89..c4bd394 100644 --- a/src/CNLoginServer.cpp +++ b/src/CNLoginServer.cpp @@ -124,7 +124,7 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) { Database::Account findUser = {}; Database::findAccount(&findUser, userLogin); - // if 0 was returned, account was not found + // account was not found if (findUser.AccountID == 0) return newAccount(sock, userLogin, userPassword, login->iClientVerC); @@ -144,7 +144,8 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) { loginSessions[sock].userID = findUser.AccountID; loginSessions[sock].lastHeartbeat = getTime(); - std::vector characters = Database::getCharInfo(loginSessions[sock].userID); + std::vector characters; + Database::getCharInfo(&characters, loginSessions[sock].userID); INITSTRUCT(sP_LS2CL_REP_LOGIN_SUCC, resp); memcpy(resp.szID, login->szID, sizeof(login->szID)); @@ -180,8 +181,14 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) { } void CNLoginServer::newAccount(CNSocket* sock, std::string userLogin, std::string userPassword, int32_t clientVerC) { + + int userID = Database::addAccount(userLogin, userPassword); + // if query somehow failed + if (userID == 0) + return loginFail(LoginError::DATABASE_ERROR, userLogin, sock); + loginSessions[sock] = CNLoginData(); - loginSessions[sock].userID = Database::addAccount(userLogin, userPassword); //TODO: Add logic if query fails + loginSessions[sock].userID = userID; loginSessions[sock].lastHeartbeat = getTime(); INITSTRUCT(sP_LS2CL_REP_LOGIN_SUCC, resp); @@ -245,8 +252,6 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) { errorCode = 4; } else if (!Database::isNameFree(U16toU8(save->szFirstName), U16toU8(save->szLastName))) { errorCode = 1; - } else if (!Database::isSlotFree(loginSessions[sock].userID, save->iSlotNum)) { - return invalidCharacter(sock); } if (errorCode != 0) { @@ -261,9 +266,18 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) { return; } + if (!Database::isSlotFree(loginSessions[sock].userID, save->iSlotNum)) + return invalidCharacter(sock); + + resp.iPC_UID = Database::createCharacter(save, loginSessions[sock].userID); + // if query somehow failed + if (resp.iPC_UID == 0) { + std::cout << "[WARN] Login Server: Database failed to create new character!" << std::endl; + return invalidCharacter(sock); + } resp.iSlotNum = save->iSlotNum; resp.iGender = save->iGender; - resp.iPC_UID = Database::createCharacter(save, loginSessions[sock].userID); + memcpy(resp.szFirstName, save->szFirstName, sizeof(resp.szFirstName)); memcpy(resp.szLastName, save->szLastName, sizeof(resp.szLastName)); @@ -321,11 +335,17 @@ void CNLoginServer::characterCreate(CNSocket* sock, CNPacketData* data) { sP_CL2LS_REQ_CHAR_CREATE* character = (sP_CL2LS_REQ_CHAR_CREATE*)data->buf; - if (!(Database::validateCharacter(character->PCStyle.iPC_UID, loginSessions[sock].userID) && validateCharacterCreation(character))) + if (!validateCharacterCreation(character)) + { + std::cout << "[WARN] Login Server: invalid CHAR_CREATE packet!" << std::endl; return invalidCharacter(sock); - - Database::finishCharacter(character); - + } + if (!Database::finishCharacter(character, loginSessions[sock].userID)) + { + std::cout << "[WARN] Login Server: Database failed to finish character creation!" << std::endl; + return invalidCharacter(sock); + } + Player player = {}; Database::getPlayer(&player, character->PCStyle.iPC_UID); @@ -333,7 +353,9 @@ void CNLoginServer::characterCreate(CNSocket* sock, CNPacketData* data) { resp.sPC_Style = player.PCStyle; resp.sPC_Style2 = player.PCStyle2; resp.iLevel = player.level; - resp.sOn_Item = character->sOn_Item; + resp.sOn_Item.iEquipUBID = player.Equip[1].iID; + resp.sOn_Item.iEquipLBID = player.Equip[2].iID; + resp.sOn_Item.iEquipFootID = player.Equip[3].iID; loginSessions[sock].lastHeartbeat = getTime(); @@ -366,10 +388,9 @@ void CNLoginServer::characterDelete(CNSocket* sock, CNPacketData* data) { sP_CL2LS_REQ_CHAR_DELETE* del = (sP_CL2LS_REQ_CHAR_DELETE*)data->buf; - if (!Database::validateCharacter(del->iPC_UID, loginSessions[sock].userID)) - return invalidCharacter(sock); - int removedSlot = Database::deleteCharacter(del->iPC_UID, loginSessions[sock].userID); + if (removedSlot == 0) + return invalidCharacter(sock); INITSTRUCT(sP_LS2CL_REP_CHAR_DELETE_SUCC, resp); resp.iSlotNum = removedSlot; @@ -406,6 +427,10 @@ void CNLoginServer::characterSelect(CNSocket* sock, CNPacketData* data) { // pass player to CNSharedData Player passPlayer = {}; Database::getPlayer(&passPlayer, selection->iPC_UID); + // this should never happen but for extra safety + if (passPlayer.iID == 0) + return invalidCharacter(sock); + passPlayer.FEKey = sock->getFEKey(); resp.iEnterSerialKey = passPlayer.iID; CNSharedData::setPlayer(resp.iEnterSerialKey, passPlayer); @@ -421,10 +446,9 @@ void CNLoginServer::finishTutorial(CNSocket* sock, CNPacketData* data) { return; sP_CL2LS_REQ_SAVE_CHAR_TUTOR* save = (sP_CL2LS_REQ_SAVE_CHAR_TUTOR*)data->buf; - if (!Database::validateCharacter(save->iPC_UID, loginSessions[sock].userID)) + if (!Database::finishTutorial(save->iPC_UID, loginSessions[sock].userID)) return invalidCharacter(sock); - Database::finishTutorial(save->iPC_UID); loginSessions[sock].lastHeartbeat = getTime(); // no response here @@ -439,9 +463,6 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) { sP_CL2LS_REQ_CHANGE_CHAR_NAME* save = (sP_CL2LS_REQ_CHANGE_CHAR_NAME*)data->buf; - if (!Database::validateCharacter(save->iPCUID, loginSessions[sock].userID)) - return invalidCharacter(sock); - int errorCode = 0; if (!CNLoginServer::isCharacterNameGood(U16toU8(save->szFirstName), U16toU8(save->szLastName))) { errorCode = 4; @@ -462,7 +483,8 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) { return; } - Database::changeName(save, loginSessions[sock].userID); + if (!Database::changeName(save, loginSessions[sock].userID)) + return invalidCharacter(sock); INITSTRUCT(sP_LS2CL_REP_CHANGE_CHAR_NAME_SUCC, resp); resp.iPC_UID = save->iPCUID; diff --git a/src/Database.cpp b/src/Database.cpp index 69015c9..c809381 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -218,6 +218,29 @@ int Database::getTableSize(std::string tableName) { return result; } +void Database::findAccount(Account* account, std::string login) { + std::lock_guard lock(dbCrit); + + const char* sql = R"( + SELECT "AccountID", "Password", "Selected" + FROM "Accounts" + WHERE "Login" = ? + LIMIT 1; + )"; + sqlite3_stmt* stmt; + + sqlite3_prepare_v2(db, sql, -1, &stmt, 0); + sqlite3_bind_text(stmt, 1, login.c_str(), -1, 0); + int rc = sqlite3_step(stmt); + if (rc == SQLITE_ROW) + { + account->AccountID = sqlite3_column_int(stmt, 0); + account->Password = std::string(reinterpret_cast(sqlite3_column_text(stmt, 1))); + account->Selected = sqlite3_column_int(stmt, 2); + } + sqlite3_finalize(stmt); +} + int Database::addAccount(std::string login, std::string password) { std::lock_guard lock(dbCrit); @@ -235,7 +258,11 @@ int Database::addAccount(std::string login, std::string password) { int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); - return rc==SQLITE_DONE ? sqlite3_last_insert_rowid(db) : 0; + if (rc != SQLITE_DONE) { + std::cout << "[WARN] Database: failed to add new character" << std::endl; + return 0; + } + return sqlite3_last_insert_rowid(db); } void Database::updateSelected(int accountId, int slot) { @@ -263,29 +290,6 @@ void Database::updateSelected(int accountId, int slot) { sqlite3_finalize(stmt); } -void Database::findAccount(Account* account, std::string login) { - std::lock_guard lock(dbCrit); - - const char* sql = R"( - SELECT "AccountID", "Password", "Selected" - FROM "Accounts" - WHERE "Login" = ? - LIMIT 1; - )"; - sqlite3_stmt* stmt; - - sqlite3_prepare_v2(db, sql, -1, &stmt, 0); - sqlite3_bind_text(stmt, 1, login.c_str(), -1, 0); - int rc = sqlite3_step(stmt); - if (rc == SQLITE_ROW) - { - account->AccountID = sqlite3_column_int(stmt, 0); - account->Password = std::string(reinterpret_cast(sqlite3_column_text(stmt, 1))); - account->Selected = sqlite3_column_int(stmt, 2); - } - sqlite3_finalize(stmt); -} - bool Database::validateCharacter(int characterID, int userID) { std::lock_guard lock(dbCrit); @@ -333,7 +337,7 @@ bool Database::isSlotFree(int accountId, int slotNum) { std::lock_guard lock(dbCrit); if (slotNum < 1 || slotNum > 4) { - std::cout << "[WARN] Invalid slot number passed to isSlotFree()! " << std::endl; + std::cout << "[WARN] Invalid slot number passed to isSlotFree()! "< lock(dbCrit); const char* sql = R"( SELECT "PlayerID" FROM "Players" - WHERE "PlayerID" = ? AND "AppearanceFlag" = 0 + WHERE "PlayerID" = ? AND "AccountID" = ? AND "AppearanceFlag" = 0 LIMIT 1; )"; sqlite3_stmt* stmt; sqlite3_prepare_v2(db, sql, -1, &stmt, 0); sqlite3_bind_int(stmt, 1, character->PCStyle.iPC_UID); + sqlite3_bind_int(stmt, 2, accountId); int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if ( rc != SQLITE_ROW) - { - std::cout << "[WARN] Player tried Character Creation on already existing character?!" << std::endl; + { + std::cout << "[WARN] Database: Invalid data submitted to finish character creation" << std::endl; return false; } @@ -522,22 +527,23 @@ bool Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character) { return true; } -bool Database::finishTutorial(int playerID) { +bool Database::finishTutorial(int playerID, int accountID) { std::lock_guard lock(dbCrit); const char* sql = R"( SELECT "PlayerID" FROM "Players" - WHERE "PlayerID" = ? AND "TutorialFlag" = 0 + WHERE "PlayerID" = ? AND "AccountID" = ? AND "TutorialFlag" = 0 LIMIT 1; )"; sqlite3_stmt* stmt; sqlite3_prepare_v2(db, sql, -1, &stmt, 0); sqlite3_bind_int(stmt, 1, playerID); + sqlite3_bind_int(stmt, 2, accountID); if (sqlite3_step(stmt) != SQLITE_ROW) { - std::cout << "[WARN] Player tried to finish tutorial on a character that already did?!" << std::endl; + std::cout << "[WARN] Database: Invalid data submitted to finish tutorial" << std::endl; sqlite3_finalize(stmt); return false; } @@ -655,8 +661,7 @@ int Database::deleteCharacter(int characterID, int userID) { return slot; } -std::vector Database::getCharInfo(int userID) { - std::vector result = std::vector(); +void Database::getCharInfo(std::vector * result, int userID) { std::lock_guard lock(dbCrit); const char* sql = R"( @@ -720,11 +725,9 @@ std::vector Database::getCharInfo(int userID) { } sqlite3_finalize(stmt2); - result.push_back(toAdd); + result->push_back(toAdd); } sqlite3_finalize(stmt); - return result; - } // XXX: This is never called? @@ -752,15 +755,22 @@ bool Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId) { const char* sql = R"( UPDATE "Players" SET "Firstname" = ?, - "LastName" = ? + "LastName" = ?, "NameCheck" = ? WHERE "PlayerID" = ? AND "AccountID" = ?; )"; sqlite3_stmt* stmt; sqlite3_prepare_v2(db, sql, -1, &stmt, 0); - sqlite3_bind_text(stmt, 1, U16toU8(save->szFirstName).c_str(), -1, 0); - sqlite3_bind_text(stmt, 2, U16toU8(save->szLastName).c_str(), -1, 0); - sqlite3_bind_int(stmt, 3, save->iPCUID); - sqlite3_bind_int(stmt, 4, accountId); + + std::string firstName = U16toU8(save->szFirstName); + std::string lastName = U16toU8(save->szLastName); + + sqlite3_bind_text(stmt, 1, firstName.c_str(), -1, 0); + sqlite3_bind_text(stmt, 2, lastName.c_str(), -1, 0); + // if FNCode isn't 0, it's a wheel name + int nameCheck = (settings::APPROVEALLNAMES || save->iFNCode) ? 1 : 0; + sqlite3_bind_int(stmt, 3, nameCheck); + sqlite3_bind_int(stmt, 4, save->iPCUID); + sqlite3_bind_int(stmt, 5, accountId); int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); @@ -1148,7 +1158,6 @@ void Database::updatePlayer(Player *player) { sqlite3_finalize(stmt); } - // note: do not use. explicitly add/remove records instead. void Database::updateBuddies(Player* player) { //db.begin_transaction(); @@ -1198,6 +1207,7 @@ void Database::removeExpiredVehicles(Player* player) { } } +// buddies int Database::getNumBuddies(Player* player) { std::lock_guard lock(dbCrit); @@ -1217,7 +1227,6 @@ int Database::getNumBuddies(Player* player) { return result > 50 ? 50 : result; } -// buddies void Database::addBuddyship(int playerA, int playerB) { std::lock_guard lock(dbCrit); diff --git a/src/Database.hpp b/src/Database.hpp index ece1ec4..3baec86 100644 --- a/src/Database.hpp +++ b/src/Database.hpp @@ -12,27 +12,6 @@ namespace Database { std::string Password; int Selected; }; - struct Inventory { - int playerId; - int slot; - int16_t Type; - int16_t id; - int32_t Opt; - int32_t TimeLimit; - }; - struct Nano { - int playerId; - int16_t iID; - int16_t iSkillID; - int16_t iStamina; - }; - struct DbQuest { - int PlayerId; - int32_t TaskId; - int RemainingNPCCount1; - int RemainingNPCCount2; - int RemainingNPCCount3; - }; struct Buddyship { int PlayerAId; int PlayerBId; @@ -65,49 +44,44 @@ namespace Database { #pragma endregion DatabaseStructs - // handles migrations void open(); void createTables(); - int getTableSize(std::string tableName); - // returns ID, 0 if something failed + + void findAccount(Account* account, std::string login); + /// returns ID, 0 if something failed int addAccount(std::string login, std::string password); void updateSelected(int accountId, int playerId); - void findAccount(Account* account, std::string login); + bool validateCharacter(int characterID, int userID); bool isNameFree(std::string firstName, std::string lastName); bool isSlotFree(int accountId, int slotNum); - // called after chosing name, returns ID + /// returns ID, 0 if something failed int createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID); - // called after finishing creation - bool finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character); - // called after tutorial - bool finishTutorial(int playerID); - // returns slot number + /// returns true if query succeeded + bool finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character, int accountId); + /// returns true if query succeeded + bool finishTutorial(int playerID, int accountID); + /// returns slot number if query succeeded int deleteCharacter(int characterID, int userID); - std::vector getCharInfo(int userID); - // accepting/declining custom name + void getCharInfo(std::vector * result, int userID); + /// accepting/declining custom name enum class CustomName { APPROVE = 1, DISAPPROVE = 2 }; void evaluateCustomName(int characterID, CustomName decision); + /// returns true if query succeeded bool changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId); // getting players void getPlayer(Player* plr, int id); - void updatePlayer(Player *player); - void updateInventory(Player *player); - void updateNanos(Player *player); - void updateQuests(Player* player); void updateBuddies(Player* player); - void removeExpiredVehicles(Player* player); - - int getNumBuddies(Player* player); - + // buddies + int getNumBuddies(Player* player); void addBuddyship(int playerA, int playerB); void removeBuddyship(int playerA, int playerB);