diff --git a/client/scenes/in_world.cpp b/client/scenes/in_world.cpp index 74a24df..6d43e9d 100644 --- a/client/scenes/in_world.cpp +++ b/client/scenes/in_world.cpp @@ -277,7 +277,6 @@ void InWorld::HandleDisconnect(SerialPacket packet) { void InWorld::HandleRegionContent(SerialPacket packet) { //replace existing regions - //TODO: account for map index if (regionPager.FindRegion(packet.regionInfo.x, packet.regionInfo.y)) { regionPager.UnloadRegion(packet.regionInfo.x, packet.regionInfo.y); } diff --git a/common/network/serial_packet.hpp b/common/network/serial_packet.hpp index 4c052fa..7da184a 100644 --- a/common/network/serial_packet.hpp +++ b/common/network/serial_packet.hpp @@ -88,6 +88,7 @@ union SerialPacket { //information about the client struct ClientInformation { Metadata meta; + //TODO: change clientIndex to accountIndex for player ID int clientIndex; int characterIndex; char username[PACKET_STRING_SIZE]; diff --git a/server/client_data.cpp b/server/account_data.hpp similarity index 81% rename from server/client_data.cpp rename to server/account_data.hpp index d05f95c..220d831 100644 --- a/server/client_data.cpp +++ b/server/account_data.hpp @@ -19,6 +19,18 @@ * 3. This notice may not be removed or altered from any source * distribution. */ -#include "client_data.hpp" +#ifndef ACCOUNTDATA_HPP_ +#define ACCOUNTDATA_HPP_ -int ClientData::uidCounter = 0; +#include + +struct AccountData { + std::string username; + //password + bool blackListed = false; + bool whiteListed = true; + + int clientIndex; +}; + +#endif diff --git a/server/character_data.hpp b/server/character_data.hpp index 5cdad1c..3a12f36 100644 --- a/server/character_data.hpp +++ b/server/character_data.hpp @@ -31,7 +31,6 @@ struct CharacterData { //metadata int clientIndex; - std::string username; std::string handle; std::string avatar; diff --git a/server/character_data.cpp b/server/combat_instance.hpp similarity index 88% rename from server/character_data.cpp rename to server/combat_instance.hpp index 6761b2c..9f4dedb 100644 --- a/server/character_data.cpp +++ b/server/combat_instance.hpp @@ -19,6 +19,12 @@ * 3. This notice may not be removed or altered from any source * distribution. */ -#include "character_data.hpp" +#ifndef COMBATINSTANCE_HPP_ +#define COMBATINSTANCE_HPP_ -int CharacterData::uidCounter = 0; +struct CombatInstance { + //uid + static int uidCounter; +}; + +#endif diff --git a/server/server_application.hpp b/server/server_application.hpp index a465707..ea58017 100644 --- a/server/server_application.hpp +++ b/server/server_application.hpp @@ -24,7 +24,9 @@ //server specific stuff #include "client_data.hpp" +#include "account_data.hpp" #include "character_data.hpp" +#include "combat_instance.hpp" //maps #include "map_allocator.hpp" @@ -74,7 +76,15 @@ private: //TODO: a function that only sends to characters in a certain proximity void PumpPacket(SerialPacket); - //TODO: manage the database + //Account management + int CreateUserAccount(std::string username, int clientIndex); + int LoadUserAccount(std::string username, int clientIndex); + int SaveUserAccount(int uid); + void UnloadUserAccount(int uid); + void DeleteUserAccount(int uid); + + //TODO: character management + //TODO: combat systems //APIs @@ -84,7 +94,9 @@ private: //server tables std::map clientMap; + std::map accountMap; std::map characterMap; + std::map combatMap; //maps //TODO: I need to handle multiple map objects diff --git a/server/server_connections.cpp b/server/server_connections.cpp index f43eb80..3d1387b 100644 --- a/server/server_connections.cpp +++ b/server/server_connections.cpp @@ -46,11 +46,17 @@ void ServerApplication::HandleJoinRequest(SerialPacket packet) { ClientData newClient; newClient.address = packet.meta.srcAddress; + //load the user account + int uid = LoadUserAccount(packet.clientInfo.username, ClientData::uidCounter); + if (uid < 0) { + std::cerr << "Error: Account already loaded: " << uid << std::endl; + return; + } + //TODO: move this into the character management code //create the new character CharacterData newCharacter; newCharacter.clientIndex = ClientData::uidCounter; - newCharacter.username = packet.clientInfo.username; newCharacter.handle = packet.clientInfo.handle; newCharacter.avatar = packet.clientInfo.avatar; @@ -113,6 +119,15 @@ void ServerApplication::HandleDisconnect(SerialPacket packet) { network.Send(&clientMap[packet.clientInfo.clientIndex].address, buffer, PACKET_BUFFER_SIZE); clientMap.erase(packet.clientInfo.clientIndex); + //unload the client's account + //TODO: change clientIndex to accountIndex for player ID + for (auto it : accountMap) { + if (it.second.clientIndex == packet.clientInfo.clientIndex) { + UnloadUserAccount(it.first); + break; + } + } + //prep the delete packet SerialPacket delPacket; delPacket.meta.type = SerialPacket::Type::CHARACTER_DELETE; diff --git a/server/server_database.cpp b/server/server_database.cpp new file mode 100644 index 0000000..4f34947 --- /dev/null +++ b/server/server_database.cpp @@ -0,0 +1,190 @@ +/* Copyright: (c) Kayne Ruse 2014 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#include "server_application.hpp" + +#include "sqlite3/sqlite3.h" + +#include + +//------------------------- +//Define the queries +//------------------------- + +static const char* CREATE_USER_ACCOUNT = "INSERT INTO UserAccounts (username) VALUES (?);"; +static const char* LOAD_USER_ACCOUNT = "SELECT * FROM UserAccounts WHERE username = ?;"; +static const char* SAVE_USER_ACCOUNT = "INSERT OR REPLACE INTO UserAccounts VALUES (?, ?, ?, ?);"; +static const char* DELETE_USER_ACCOUNT = "DELETE FROM UserAccounts WHERE uid = ?;"; + +//------------------------- +//Define the methods +//------------------------- + +int ServerApplication::CreateUserAccount(std::string username, int clientIndex) { + //create this user account, failing if it exists, leave this account in memory + sqlite3_stmt* statement = nullptr; + + //prep + if (sqlite3_prepare_v2(database, CREATE_USER_ACCOUNT, -1, &statement, nullptr) != SQLITE_OK) { + throw( std::runtime_error(std::string() + "Failed to prepare an SQL statement: " + sqlite3_errmsg(database)) ); + } + + //parameter + if (sqlite3_bind_text(statement, 1, username.c_str(), username.size() + 1, SQLITE_STATIC) != SQLITE_OK) { + throw( std::runtime_error(std::string() + "Failed to replace a prepared statement's parameter: " + sqlite3_errmsg(database)) ); + } + + //execute + if (sqlite3_step(statement) != SQLITE_DONE) { + //if this fails, than this account exists + sqlite3_finalize(statement); + return -1; + } + + sqlite3_finalize(statement); + + //load this account into memory + return LoadUserAccount(username, clientIndex); +} + +int ServerApplication::LoadUserAccount(std::string username, int clientIndex) { + //load this user account, failing if it is in memory, creating it if it doesn't exist + sqlite3_stmt* statement = nullptr; + + //prep + if (sqlite3_prepare_v2(database, LOAD_USER_ACCOUNT, -1, &statement, nullptr) != SQLITE_OK) { + throw( std::runtime_error(std::string() + "Failed to prepare an SQL statement: " + sqlite3_errmsg(database)) ); + } + + //parameter + if (sqlite3_bind_text(statement, 1, username.c_str(), username.size() + 1, SQLITE_STATIC) != SQLITE_OK) { + throw( std::runtime_error(std::string() + "Failed to replace a prepared statement's parameter: " + sqlite3_errmsg(database)) ); + } + + //execute + int ret = sqlite3_step(statement); + + //process the result + if (ret == SQLITE_ROW) { + //get the index + int uid = sqlite3_column_int(statement, 0); + + //check to see if this account is already loaded + if (accountMap.find(uid) != accountMap.end()) { + sqlite3_finalize(statement); + return -1; + } + + //extract the data into memory + AccountData& newAccount = accountMap[uid]; + newAccount.username = reinterpret_cast(sqlite3_column_text(statement, 1)); + newAccount.blackListed = sqlite3_column_int(statement, 2); + newAccount.whiteListed = sqlite3_column_int(statement, 3); + newAccount.clientIndex = clientIndex; + + //finish the routine + sqlite3_finalize(statement); + return uid; + } + + sqlite3_finalize(statement); + + if (ret == SQLITE_DONE) { + //create the non-existant account instead + return CreateUserAccount(username, clientIndex); + } + + throw(std::runtime_error(std::string() + "Unknown SQL error in LoadUserAccount: " + sqlite3_errmsg(database) )); +} + +int ServerApplication::SaveUserAccount(int uid) { + //save this user account from memory, replacing it if it exists in the database + //DOCS: To use this method, change the in-memory copy, and then call this function using that object's UID. + + //this method fails if this account is not loaded + if (accountMap.find(uid) == accountMap.end()) { + return -1; + } + + AccountData& account = accountMap[uid]; + sqlite3_stmt* statement = nullptr; + + //prep + if (sqlite3_prepare_v2(database, SAVE_USER_ACCOUNT, -1, &statement, nullptr) != SQLITE_OK) { + throw( std::runtime_error(std::string() + "Failed to prepare an SQL statement: " + sqlite3_errmsg(database)) ); + } + + //parameters + bool ret = false; + ret |= sqlite3_bind_int(statement, 1, uid) != SQLITE_OK; + ret |= sqlite3_bind_text(statement, 2, account.username.c_str(), account.username.size() + 1, SQLITE_STATIC) != SQLITE_OK; + ret |= sqlite3_bind_int(statement, 3, account.blackListed) != SQLITE_OK; + ret |= sqlite3_bind_int(statement, 4, account.whiteListed) != SQLITE_OK; + + //check for binding errors + if (ret) { + throw( std::runtime_error(std::string() + "Failed to replace a prepared statement's parameter: " + sqlite3_errmsg(database)) ); + } + + //execute + if (sqlite3_step(statement) != SQLITE_DONE) { + //if this fails, than something went horribly wrong + sqlite3_finalize(statement); + throw( std::runtime_error(std::string() + "Unknown SQL error when saving an account: " + sqlite3_errmsg(database)) ); + } + + sqlite3_finalize(statement); + + //successful execution + return 0; +} + +void ServerApplication::UnloadUserAccount(int uid) { + //save this user account, and then unload it + SaveUserAccount(uid); + accountMap.erase(uid); +} + +void ServerApplication::DeleteUserAccount(int uid) { + //delete a user account from the database, and remove it from memory + sqlite3_stmt* statement = nullptr; + + //prep + if (sqlite3_prepare_v2(database, DELETE_USER_ACCOUNT, -1, &statement, nullptr) != SQLITE_OK) { + throw( std::runtime_error(std::string() + "Failed to prepare an SQL statement: " + sqlite3_errmsg(database)) ); + } + + //parameter + if (sqlite3_bind_int(statement, 1, uid) != SQLITE_OK) { + throw( std::runtime_error(std::string() + "Failed to replace a prepared statement's parameter: " + sqlite3_errmsg(database)) ); + } + + //execute + if (sqlite3_step(statement) != SQLITE_DONE) { + //if this fails, than something went horribly wrong + sqlite3_finalize(statement); + throw( std::runtime_error(std::string() + "Unknown SQL error when deleting an account: " + sqlite3_errmsg(database)) ); + } + + //finish the routine + sqlite3_finalize(statement); + accountMap.erase(uid); +} diff --git a/server/server_internals.cpp b/server/server_internals.cpp index f3ad870..d4fd93b 100644 --- a/server/server_internals.cpp +++ b/server/server_internals.cpp @@ -27,6 +27,14 @@ #include #include +//------------------------- +//Define the various UIDs +//------------------------- + +int ClientData::uidCounter = 0; +int CharacterData::uidCounter = 0; +int CombatInstance::uidCounter = 0; + //------------------------- //Define the public members //-------------------------