diff --git a/client/main.cpp b/client/main.cpp index c8584e5..2115115 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -33,22 +33,22 @@ using namespace std; int main(int argc, char* argv[]) { try { //create the singletons - ConfigUtility::Create(); - UDPNetworkUtility::Create(); + ConfigUtility::CreateSingleton(); + UDPNetworkUtility::CreateSingleton(); //call the server's routines - ClientApplication::Create(); + ClientApplication::CreateSingleton(); ClientApplication& app = ClientApplication::GetSingleton(); app.Init(argc, argv); app.Proc(); app.Quit(); - ClientApplication::Delete(); + ClientApplication::DeleteSingleton(); //delete the singletons - ConfigUtility::Delete(); - UDPNetworkUtility::Delete(); + ConfigUtility::DeleteSingleton(); + UDPNetworkUtility::DeleteSingleton(); } catch(exception& e) { cerr << "Fatal exception thrown: " << e.what() << endl; diff --git a/common/utilities/singleton.hpp b/common/utilities/singleton.hpp index 379003a..1754b84 100644 --- a/common/utilities/singleton.hpp +++ b/common/utilities/singleton.hpp @@ -33,13 +33,13 @@ public: } return *ptr; } - static void Create() { + static void CreateSingleton() { if (ptr) { throw(std::logic_error("This singleton has already been created")); } ptr = new T(); } - static void Delete() { + static void DeleteSingleton() { if (!ptr) { throw(std::logic_error("A non-existant singleton cannot be deleted")); } diff --git a/server/accounts/account_manager.cpp b/server/accounts/account_manager.cpp index c29bd29..e16ad3c 100644 --- a/server/accounts/account_manager.cpp +++ b/server/accounts/account_manager.cpp @@ -31,12 +31,13 @@ static const char* CREATE_USER_ACCOUNT = "INSERT INTO Accounts (username) VALUES static const char* LOAD_USER_ACCOUNT = "SELECT * FROM Accounts WHERE username = ?;"; static const char* SAVE_USER_ACCOUNT = "UPDATE OR FAIL Accounts SET blacklisted = ?2, whitelisted = ?3, mod = ?4, admin = ?5 WHERE uid = ?1;"; static const char* DELETE_USER_ACCOUNT = "DELETE FROM Accounts WHERE uid = ?;"; +static const char* COUNT_USER_ACCOUNT_RECORDS = "SELECT COUNT(*) FROM Accounts;"; //------------------------- //Define the public methods //------------------------- -int AccountManager::CreateAccount(std::string username, int clientIndex) { +int AccountManager::Create(std::string username, int clientIndex) { //create this user account, failing if it exists, leave this account in memory sqlite3_stmt* statement = nullptr; @@ -60,10 +61,10 @@ int AccountManager::CreateAccount(std::string username, int clientIndex) { sqlite3_finalize(statement); //load this account into memory - return LoadAccount(username, clientIndex); + return Load(username, clientIndex); } -int AccountManager::LoadAccount(std::string username, int clientIndex) { +int AccountManager::Load(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; @@ -109,13 +110,13 @@ int AccountManager::LoadAccount(std::string username, int clientIndex) { if (ret == SQLITE_DONE) { //create the non-existant account instead - return CreateAccount(username, clientIndex); + return Create(username, clientIndex); } throw(std::runtime_error(std::string() + "Unknown SQL error in LoadAccount: " + sqlite3_errmsg(database) )); } -int AccountManager::SaveAccount(int uid) { +int AccountManager::Save(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. @@ -158,14 +159,14 @@ int AccountManager::SaveAccount(int uid) { return 0; } -void AccountManager::UnloadAccount(int uid) { +void AccountManager::Unload(int uid) { //save this user account, and then unload it //NOTE: the associated characters are unloaded externally - SaveAccount(uid); + Save(uid); accountMap.erase(uid); } -void AccountManager::DeleteAccount(int uid) { +void AccountManager::Delete(int uid) { //delete a user account from the database, and remove it from memory //NOTE: the associated characters should be deleted externally sqlite3_stmt* statement = nullptr; @@ -194,16 +195,29 @@ void AccountManager::DeleteAccount(int uid) { void AccountManager::UnloadAll() { for (auto& it : accountMap) { - SaveAccount(it.first); + Save(it.first); } accountMap.clear(); } +void AccountManager::UnloadIf(std::function)> fn) { + //replicate std::remove_if, using custom code + for (std::map::iterator it = accountMap.begin(); it != accountMap.end(); /* empty */) { + if (fn(*it)) { + Save(it->first); + it = accountMap.erase(it); + continue; + } + ++it; + } +} + //------------------------- //Define the accessors and mutators //------------------------- -AccountData* AccountManager::GetAccount(int uid) { +AccountData* AccountManager::Get(int uid) { + //TODO: could this load an account first? std::map::iterator it = accountMap.find(uid); if (it == accountMap.end()) { @@ -213,6 +227,28 @@ AccountData* AccountManager::GetAccount(int uid) { return &it->second; } +int AccountManager::GetLoadedCount() { + return accountMap.size(); +} + +int AccountManager::GetTotalCount() { + //a lot just to count something. + sqlite3_stmt* statement = nullptr; + + //prep + if (sqlite3_prepare_v2(database, COUNT_USER_ACCOUNT_RECORDS, -1, &statement, nullptr) != SQLITE_OK) { + throw( std::runtime_error(std::string() + "Failed to prepare an SQL statement: " + sqlite3_errmsg(database)) ); + } + + //execute & retrieve the result + sqlite3_step(statement); + int ret = sqlite3_column_int(statement, 0); + + //finish the routine + sqlite3_finalize(statement); + return ret; +} + std::map* AccountManager::GetContainer() { return &accountMap; } diff --git a/server/accounts/account_manager.hpp b/server/accounts/account_manager.hpp index 2e631b5..327eb3a 100644 --- a/server/accounts/account_manager.hpp +++ b/server/accounts/account_manager.hpp @@ -27,21 +27,25 @@ #include "sqlite3/sqlite3.h" +#include #include class AccountManager : public Singleton { public: //public access methods - int CreateAccount(std::string username, int clientIndex); - int LoadAccount(std::string username, int clientIndex); - int SaveAccount(int uid); - void UnloadAccount(int uid); - void DeleteAccount(int uid); + int Create(std::string username, int clientIndex); + int Load(std::string username, int clientIndex); + int Save(int uid); + void Unload(int uid); + void Delete(int uid); void UnloadAll(); + void UnloadIf(std::function)> fn); //accessors and mutators - AccountData* GetAccount(int uid); + AccountData* Get(int uid); + int GetLoadedCount(); + int GetTotalCount(); std::map* GetContainer(); sqlite3* SetDatabase(sqlite3* db); diff --git a/server/characters/character_manager.cpp b/server/characters/character_manager.cpp index 4d2078a..080b0ea 100644 --- a/server/characters/character_manager.cpp +++ b/server/characters/character_manager.cpp @@ -23,6 +23,7 @@ #include "sqlite3/sqlite3.h" +#include #include //------------------------- @@ -33,13 +34,14 @@ static const char* CREATE_CHARACTER = "INSERT INTO Characters (owner, handle, av static const char* LOAD_CHARACTER = "SELECT * FROM Characters WHERE handle = ?;"; static const char* SAVE_CHARACTER = "UPDATE OR FAIL Characters SET roomIndex = ?2, originX = ?3, originY = ?4 WHERE uid = ?1;"; static const char* DELETE_CHARACTER = "DELETE FROM Characters WHERE uid = ?;"; +static const char* COUNT_CHARACTER_RECORDS = "SELECT COUNT(*) FROM Characters;"; //------------------------- //Define the methods //------------------------- //NOTE: default baseStats as a parameter would be good for different beggining states or multiple classes -int CharacterManager::CreateCharacter(int owner, std::string handle, std::string avatar) { +int CharacterManager::Create(int owner, std::string handle, std::string avatar) { //Create the character, failing if it exists sqlite3_stmt* statement = nullptr; @@ -69,10 +71,10 @@ int CharacterManager::CreateCharacter(int owner, std::string handle, std::string sqlite3_finalize(statement); //load this character into memory - return LoadCharacter(owner, handle, avatar); + return Load(owner, handle, avatar); } -int CharacterManager::LoadCharacter(int owner, std::string handle, std::string avatar) { +int CharacterManager::Load(int owner, std::string handle, std::string avatar) { //load the specified character, creating it if it doesn't exist //fail if it is already loaded, or does not belong to this account sqlite3_stmt* statement = nullptr; @@ -132,13 +134,13 @@ int CharacterManager::LoadCharacter(int owner, std::string handle, std::string a if (ret == SQLITE_DONE) { //create the non-existant character instead - return CreateCharacter(owner, handle, avatar); + return Create(owner, handle, avatar); } throw(std::runtime_error(std::string() + "Unknown SQL error in LoadCharacter: " + sqlite3_errmsg(database) )); } -int CharacterManager::SaveCharacter(int uid) { +int CharacterManager::Save(int uid) { //save this character 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. @@ -182,13 +184,13 @@ int CharacterManager::SaveCharacter(int uid) { return 0; } -void CharacterManager::UnloadCharacter(int uid) { +void CharacterManager::Unload(int uid) { //save this character, then unload it - SaveCharacter(uid); + Save(uid); characterMap.erase(uid); } -void CharacterManager::DeleteCharacter(int uid) { +void CharacterManager::Delete(int uid) { //delete this character from the database, then remove it from memory sqlite3_stmt* statement = nullptr; @@ -214,30 +216,30 @@ void CharacterManager::DeleteCharacter(int uid) { characterMap.erase(uid); } -void CharacterManager::UnloadCharacterIf(std::function::iterator)> f) { - //save this character, then unload it if the parameter returns true - for (std::map::iterator it = characterMap.begin(); it != characterMap.end(); /* EMPTY */ ) { - if (f(it)) { - SaveCharacter(it->first); +void CharacterManager::UnloadAll() { + for (auto& it : characterMap) { + Save(it.first); + } + characterMap.clear(); +} + +void CharacterManager::UnloadIf(std::function)> fn) { + //replicate std::remove_if, using custom code + for (std::map::iterator it = characterMap.begin(); it != characterMap.end(); /* empty */) { + if (fn(*it)) { + Save(it->first); it = characterMap.erase(it); continue; } - it++; + ++it; } } -void CharacterManager::UnloadAll() { - for (auto& it : characterMap) { - SaveCharacter(it.first); - } - characterMap.clear(); -} - //------------------------- //Define the accessors and mutators //------------------------- -CharacterData* CharacterManager::GetCharacter(int uid) { +CharacterData* CharacterManager::Get(int uid) { std::map::iterator it = characterMap.find(uid); if (it == characterMap.end()) { @@ -247,6 +249,28 @@ CharacterData* CharacterManager::GetCharacter(int uid) { return &it->second; } +int CharacterManager::GetLoadedCount() { + return characterMap.size(); +} + +int CharacterManager::GetTotalCount() { + //a lot just to count something. + sqlite3_stmt* statement = nullptr; + + //prep + if (sqlite3_prepare_v2(database, COUNT_CHARACTER_RECORDS, -1, &statement, nullptr) != SQLITE_OK) { + throw( std::runtime_error(std::string() + "Failed to prepare an SQL statement: " + sqlite3_errmsg(database)) ); + } + + //execute & retrieve the result + sqlite3_step(statement); + int ret = sqlite3_column_int(statement, 0); + + //finish the routine + sqlite3_finalize(statement); + return ret; +} + std::map* CharacterManager::GetContainer() { return &characterMap; } diff --git a/server/characters/character_manager.hpp b/server/characters/character_manager.hpp index 084d595..68b58aa 100644 --- a/server/characters/character_manager.hpp +++ b/server/characters/character_manager.hpp @@ -27,24 +27,25 @@ #include "sqlite3/sqlite3.h" -#include #include +#include class CharacterManager : public Singleton { public: //public access methods - int CreateCharacter(int owner, std::string handle, std::string avatar); - int LoadCharacter(int owner, std::string handle, std::string avatar); - int SaveCharacter(int uid); - void UnloadCharacter(int uid); - void DeleteCharacter(int uid); - - void UnloadCharacterIf(std::function::iterator)> f); + int Create(int owner, std::string handle, std::string avatar); + int Load(int owner, std::string handle, std::string avatar); + int Save(int uid); + void Unload(int uid); + void Delete(int uid); void UnloadAll(); + void UnloadIf(std::function)> fn); //accessors and mutators - CharacterData* GetCharacter(int uid); + CharacterData* Get(int uid); + int GetLoadedCount(); + int GetTotalCount(); std::map* GetContainer(); sqlite3* SetDatabase(sqlite3* db); diff --git a/server/main.cpp b/server/main.cpp index 4cca4da..fdd4894 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -22,7 +22,10 @@ #include "server_application.hpp" //singletons +#include "account_manager.hpp" +#include "character_manager.hpp" #include "config_utility.hpp" +#include "room_manager.hpp" #include "udp_network_utility.hpp" #include @@ -33,28 +36,28 @@ using namespace std; int main(int argc, char* argv[]) { try { //create the singletons - AccountManager::Create(); - CharacterManager::Create(); - ConfigUtility::Create(); - RoomManager::Create(); - UDPNetworkUtility::Create(); + AccountManager::CreateSingleton(); + CharacterManager::CreateSingleton(); + ConfigUtility::CreateSingleton(); + RoomManager::CreateSingleton(); + UDPNetworkUtility::CreateSingleton(); //call the server's routines - ServerApplication::Create(); + ServerApplication::CreateSingleton(); ServerApplication& app = ServerApplication::GetSingleton(); app.Init(argc, argv); app.Proc(); app.Quit(); - ServerApplication::Delete(); + ServerApplication::DeleteSingleton(); //delete the singletons - AccountManager::Delete(); - CharacterManager::Delete(); - ConfigUtility::Delete(); - RoomManager::Delete(); - UDPNetworkUtility::Delete(); + AccountManager::DeleteSingleton(); + CharacterManager::DeleteSingleton(); + ConfigUtility::DeleteSingleton(); + RoomManager::DeleteSingleton(); + UDPNetworkUtility::DeleteSingleton(); } catch(exception& e) { cerr << "Fatal exception thrown: " << e.what() << endl; diff --git a/server/server_logic.cpp b/server/server_logic.cpp index 08aa8c1..6c85454 100644 --- a/server/server_logic.cpp +++ b/server/server_logic.cpp @@ -22,7 +22,7 @@ #include "server_application.hpp" //utility functions -#include "sql_utility.hpp" +#include "sql_tools.hpp" #include "utility.hpp" #include diff --git a/server/server_methods.cpp b/server/server_methods.cpp index 5501d15..b211d8f 100644 --- a/server/server_methods.cpp +++ b/server/server_methods.cpp @@ -52,7 +52,7 @@ void ServerApplication::HandleBroadcastRequest(ServerPacket* const argPacket) { newPacket.type = SerialPacketType::BROADCAST_RESPONSE; strncpy(newPacket.name, config["server.name"].c_str(), PACKET_STRING_SIZE); - newPacket.playerCount = characterMgr.GetContainer()->size(); + newPacket.playerCount = characterMgr.GetLoadedCount(); newPacket.version = NETWORK_VERSION; network.SendTo(argPacket->srcAddress, static_cast(&newPacket)); @@ -61,7 +61,7 @@ void ServerApplication::HandleBroadcastRequest(ServerPacket* const argPacket) { void ServerApplication::HandleJoinRequest(ClientPacket* const argPacket) { //load the user account //TODO: handle passwords - int accountIndex = accountMgr.LoadAccount(argPacket->username, clientIndex); + int accountIndex = accountMgr.Load(argPacket->username, clientIndex); //Cannot load if (accountIndex < 0) { @@ -88,7 +88,7 @@ void ServerApplication::HandleJoinRequest(ClientPacket* const argPacket) { clientMap[clientIndex++] = newClient; //finished this routine - std::cout << "New connection, " << clientMap.size() << " clients and " << accountMgr.GetContainer()->size() << " accounts total" << std::endl; + std::cout << "New connection, " << clientMap.size() << " clients and " << accountMgr.GetLoadedCount() << " accounts total" << std::endl; } void ServerApplication::HandleDisconnect(ClientPacket* const argPacket) { @@ -105,26 +105,26 @@ void ServerApplication::HandleDisconnect(ClientPacket* const argPacket) { //forward to the specified client network.SendTo( - clientMap[ accountMgr.GetAccount(argPacket->accountIndex)->GetClientIndex() ].GetAddress(), + clientMap[ accountMgr.Get(argPacket->accountIndex)->GetClientIndex() ].GetAddress(), static_cast(argPacket) ); //save and unload this account's characters - characterMgr.UnloadCharacterIf([&](std::map::iterator it) -> bool { - if (argPacket->accountIndex == it->second.GetOwner()) { + characterMgr.UnloadIf([&](std::pair it) -> bool { + if (argPacket->accountIndex == it.second.GetOwner()) { //pump the unload message to all remaining clients - PumpCharacterUnload(it->first); + PumpCharacterUnload(it.first); return true; } return false; }); //erase the in-memory stuff - clientMap.erase(accountMgr.GetAccount(argPacket->accountIndex)->GetClientIndex()); - accountMgr.UnloadAccount(argPacket->accountIndex); + clientMap.erase(accountMgr.Get(argPacket->accountIndex)->GetClientIndex()); + accountMgr.Unload(argPacket->accountIndex); //finished this routine - std::cout << "Disconnection, " << clientMap.size() << " clients and " << accountMgr.GetContainer()->size() << " accounts total" << std::endl; + std::cout << "Disconnection, " << clientMap.size() << " clients and " << accountMgr.GetLoadedCount() << " accounts total" << std::endl; } void ServerApplication::HandleShutdown(ClientPacket* const argPacket) { @@ -173,7 +173,7 @@ void ServerApplication::HandleRegionRequest(RegionPacket* const argPacket) { void ServerApplication::HandleCharacterNew(CharacterPacket* const argPacket) { //NOTE: misnomer, try to load the character first - int characterIndex = characterMgr.LoadCharacter(argPacket->accountIndex, argPacket->handle, argPacket->avatar); + int characterIndex = characterMgr.Load(argPacket->accountIndex, argPacket->handle, argPacket->avatar); //cannot load or create if (characterIndex < 0) { @@ -207,10 +207,10 @@ void ServerApplication::HandleCharacterDelete(CharacterPacket* const argPacket) //NOTE: Disconnecting only unloads a character, this explicitly deletes it //Authenticate the owner is doing this - int characterIndex = characterMgr.LoadCharacter(argPacket->accountIndex, argPacket->handle, argPacket->avatar); + int characterIndex = characterMgr.Load(argPacket->accountIndex, argPacket->handle, argPacket->avatar); //if this is not your character - if (characterIndex < 0 && characterMgr.GetCharacter(characterIndex)->GetOwner() != argPacket->accountIndex) { + if (characterIndex < 0 && characterMgr.Get(characterIndex)->GetOwner() != argPacket->accountIndex) { //send the rejection packet TextPacket newPacket; newPacket.type = SerialPacketType::CHARACTER_REJECTION; @@ -220,13 +220,13 @@ void ServerApplication::HandleCharacterDelete(CharacterPacket* const argPacket) //unload an unneeded character if (characterIndex != -1) { - characterMgr.UnloadCharacter(characterIndex); + characterMgr.Unload(characterIndex); } return; } //delete it - characterMgr.DeleteCharacter(characterIndex); + characterMgr.Delete(characterIndex); //TODO: success packet @@ -235,7 +235,7 @@ void ServerApplication::HandleCharacterDelete(CharacterPacket* const argPacket) } void ServerApplication::HandleCharacterUpdate(CharacterPacket* const argPacket) { - CharacterData* character = characterMgr.GetCharacter(argPacket->characterIndex); + CharacterData* character = characterMgr.Get(argPacket->characterIndex); //make a new character if this one doesn't exist if (!character) { @@ -327,8 +327,8 @@ void ServerApplication::CleanupLostConnection(int clientIndex) { network.SendTo(clientMap[clientIndex].GetAddress(), &newPacket); //clean up this mess - characterMgr.UnloadCharacter(characterIndex); - accountMgr.UnloadAccount(accountIndex); + characterMgr.Unload(characterIndex); + accountMgr.Unload(accountIndex); clientMap.erase(clientIndex); PumpCharacterUnload(characterIndex); @@ -338,7 +338,7 @@ void ServerApplication::CleanupLostConnection(int clientIndex) { std::cerr << "\tClient: " << clientIndex << std::endl; std::cerr << "\tAccount: " << accountIndex << std::endl; std::cerr << "\tCharacter: " << characterIndex << std::endl; - std::cout << clientMap.size() << " clients and " << accountMgr.GetContainer()->size() << " accounts total" << std::endl; + std::cout << clientMap.size() << " clients and " << accountMgr.GetLoadedCount() << " accounts total" << std::endl; } //TODO: a function that only sends to characters in a certain proximity @@ -359,7 +359,7 @@ void ServerApplication::PumpCharacterUnload(int uid) { } void ServerApplication::CopyCharacterToPacket(CharacterPacket* const packet, int characterIndex) { - CharacterData* character = characterMgr.GetCharacter(characterIndex); + CharacterData* character = characterMgr.Get(characterIndex); if (!character) { throw(std::runtime_error("Failed to copy a character to a packet")); } diff --git a/server/server_utilities/sql_utility.cpp b/server/server_utilities/sql_tools.cpp similarity index 98% rename from server/server_utilities/sql_utility.cpp rename to server/server_utilities/sql_tools.cpp index 5d586d4..19e0ddb 100644 --- a/server/server_utilities/sql_utility.cpp +++ b/server/server_utilities/sql_tools.cpp @@ -19,7 +19,7 @@ * 3. This notice may not be removed or altered from any source * distribution. */ -#include "sql_utility.hpp" +#include "sql_tools.hpp" #include "utility.hpp" diff --git a/server/server_utilities/sql_utility.hpp b/server/server_utilities/sql_tools.hpp similarity index 100% rename from server/server_utilities/sql_utility.hpp rename to server/server_utilities/sql_tools.hpp