diff --git a/client/makefile b/client/makefile index bba3c67..947b822 100644 --- a/client/makefile +++ b/client/makefile @@ -1,5 +1,5 @@ #config -INCLUDES+=. ../common/gameplay ../common/graphics ../common/map ../common/network ../common/ui ../common/utilities +INCLUDES+=. ../common/gameplay ../common/graphics ../common/map ../common/network ../common/network/packet ../common/network/serial ../common/ui ../common/utilities LIBS+=../libcommon.a -lSDL_net -lwsock32 -liphlpapi -lmingw32 -lSDLmain -lSDL -llua CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES)) -DGRAPHICS diff --git a/common/gameplay/character_data.hpp b/common/gameplay/character_data.hpp index ff129b2..361788b 100644 --- a/common/gameplay/character_data.hpp +++ b/common/gameplay/character_data.hpp @@ -45,7 +45,7 @@ struct CharacterData { std::string avatar; //world position - int mapIndex = 0; + int roomIndex = 0; Vector2 origin = {0.0,0.0}; Vector2 motion = {0.0,0.0}; Vector2 bounds = {0.0,0.0}; diff --git a/common/gameplay/sanity_check.cpp b/common/gameplay/sanity_check.cpp index 719db9f..8669811 100644 --- a/common/gameplay/sanity_check.cpp +++ b/common/gameplay/sanity_check.cpp @@ -19,12 +19,9 @@ * 3. This notice may not be removed or altered from any source * distribution. */ -#include "account_data.hpp" #include "character_data.hpp" -#include "client_data.hpp" #include "combat_data.hpp" #include "enemy_data.hpp" -#include "room_data.hpp" #include "statistics.hpp" /* DOCS: Sanity check, read more diff --git a/common/map/map_file_format.hpp b/common/map/map_file_format.hpp index 92f0c8d..75f5204 100644 --- a/common/map/map_file_format.hpp +++ b/common/map/map_file_format.hpp @@ -28,6 +28,8 @@ #include +//TODO: I'm unhappy with using this system, there needs to be a way to handle saving/loading better + class DummyFormat { public: void Load(Region** const, int x, int y); diff --git a/common/map/region.hpp b/common/map/region.hpp index 560161b..4c0d2cd 100644 --- a/common/map/region.hpp +++ b/common/map/region.hpp @@ -22,9 +22,9 @@ #ifndef REGION_HPP_ #define REGION_HPP_ -#define REGION_WIDTH 20 -#define REGION_HEIGHT 20 -#define REGION_DEPTH 3 +constexpr int REGION_WIDTH = 20; +constexpr int REGION_HEIGHT = 20; +constexpr int REGION_DEPTH = 3; class Region { public: diff --git a/common/network/packet/serial_packet.hpp b/common/network/packet/serial_packet.hpp index 7b7137f..44198aa 100644 --- a/common/network/packet/serial_packet.hpp +++ b/common/network/packet/serial_packet.hpp @@ -31,4 +31,14 @@ //NOTE: SerialPacket is defined in serial_packet_base.hpp +union MaxPacket { + CharacterPacket a; + ClientPacket b; + CombatPacket c; + EnemyPacket d; + RegionPacket e; + ServerPacket f; +}; +constexpr int MAX_PACKET_SIZE = sizeof(MaxPacket); + #endif diff --git a/common/network/packet/serial_packet_base.hpp b/common/network/packet/serial_packet_base.hpp index 16279c8..760e54a 100644 --- a/common/network/packet/serial_packet_base.hpp +++ b/common/network/packet/serial_packet_base.hpp @@ -30,6 +30,7 @@ #include "SDL/SDL_net.h" +#define NETWORK_VERSION 20140607 #define PACKET_STRING_SIZE 100 struct SerialPacketBase { @@ -39,7 +40,7 @@ struct SerialPacketBase { typedef SerialPacketType Type; - virtual ~SerialPacketBase(); + virtual ~SerialPacketBase() {}; }; typedef SerialPacketBase SerialPacket; diff --git a/common/script/map_api.cpp b/common/script/map_api.cpp index 9ecfea4..0bcaf80 100644 --- a/common/script/map_api.cpp +++ b/common/script/map_api.cpp @@ -27,6 +27,8 @@ #include "region_pager.hpp" //NOTE: When operating on a region, setTile() & getTile() *are not* zero indexed, but when operating on the entire map they *are* zero indexed. +//TODO: enforce all possible parameter counts +//TODO: update the map API to handle multiple rooms static int setTile(lua_State* L) { if (lua_gettop(L) == 5) { diff --git a/rsc/scripts/setup_server.sql b/rsc/scripts/setup_server.sql index 04a6aab..88d0a82 100644 --- a/rsc/scripts/setup_server.sql +++ b/rsc/scripts/setup_server.sql @@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS Characters ( birth timestamp NOT NULL DEFAULT (datetime()), --position - mapIndex INTEGER DEFAULT 0, + roomIndex INTEGER DEFAULT 0, originX INTEGER DEFAULT 0, originY INTEGER DEFAULT 0, diff --git a/common/gameplay/account_data.hpp b/server/account_data.hpp similarity index 100% rename from common/gameplay/account_data.hpp rename to server/account_data.hpp diff --git a/server/account_management.cpp b/server/account_manager.cpp similarity index 84% rename from server/account_management.cpp rename to server/account_manager.cpp index eb298d8..2a5a968 100644 --- a/server/account_management.cpp +++ b/server/account_manager.cpp @@ -19,9 +19,7 @@ * 3. This notice may not be removed or altered from any source * distribution. */ -#include "server_application.hpp" - -#include "sqlite3/sqlite3.h" +#include "account_manager.hpp" #include @@ -35,10 +33,20 @@ static const char* SAVE_USER_ACCOUNT = "UPDATE OR FAIL Accounts SET blacklisted static const char* DELETE_USER_ACCOUNT = "DELETE FROM Accounts WHERE uid = ?;"; //------------------------- -//Define the methods +//Define the public methods //------------------------- -int ServerApplication::CreateUserAccount(std::string username, int clientIndex) { +AccountManager::AccountManager() { + // +} + +AccountManager::~AccountManager() { + for (auto& it : accountMap) { + SaveAccount(it.first); + } +} + +int AccountManager::CreateAccount(std::string username, int clientIndex) { //create this user account, failing if it exists, leave this account in memory sqlite3_stmt* statement = nullptr; @@ -62,10 +70,10 @@ int ServerApplication::CreateUserAccount(std::string username, int clientIndex) sqlite3_finalize(statement); //load this account into memory - return LoadUserAccount(username, clientIndex); + return LoadAccount(username, clientIndex); } -int ServerApplication::LoadUserAccount(std::string username, int clientIndex) { +int AccountManager::LoadAccount(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; @@ -111,13 +119,13 @@ int ServerApplication::LoadUserAccount(std::string username, int clientIndex) { if (ret == SQLITE_DONE) { //create the non-existant account instead - return CreateUserAccount(username, clientIndex); + return CreateAccount(username, clientIndex); } - throw(std::runtime_error(std::string() + "Unknown SQL error in LoadUserAccount: " + sqlite3_errmsg(database) )); + throw(std::runtime_error(std::string() + "Unknown SQL error in LoadAccount: " + sqlite3_errmsg(database) )); } -int ServerApplication::SaveUserAccount(int uid) { +int AccountManager::SaveAccount(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. @@ -160,14 +168,14 @@ int ServerApplication::SaveUserAccount(int uid) { return 0; } -void ServerApplication::UnloadUserAccount(int uid) { +void AccountManager::UnloadAccount(int uid) { //save this user account, and then unload it //NOTE: the associated characters are unloaded externally - SaveUserAccount(uid); + SaveAccount(uid); accountMap.erase(uid); } -void ServerApplication::DeleteUserAccount(int uid) { +void AccountManager::DeleteAccount(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; @@ -193,3 +201,29 @@ void ServerApplication::DeleteUserAccount(int uid) { sqlite3_finalize(statement); accountMap.erase(uid); } + +//------------------------- +//Define the accessors and mutators +//------------------------- + +AccountData* AccountManager::GetAccount(int uid) { + std::map::iterator it = accountMap.find(uid); + + if (it == accountMap.end()) { + return nullptr; + } + + return &it->second; +} + +std::map* AccountManager::GetContainer() { + return &accountMap; +} + +sqlite3* AccountManager::SetDatabase(sqlite3* db) { + return database = db; +} + +sqlite3* AccountManager::GetDatabase() { + return database; +} \ No newline at end of file diff --git a/server/account_manager.hpp b/server/account_manager.hpp new file mode 100644 index 0000000..5bf3841 --- /dev/null +++ b/server/account_manager.hpp @@ -0,0 +1,55 @@ +/* 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. +*/ +#ifndef ACCOUNTMANAGER_HPP_ +#define ACCOUNTMANAGER_HPP_ + +#include "account_data.hpp" + +#include "sqlite3/sqlite3.h" + +#include + +class AccountManager { +public: + AccountManager(); + ~AccountManager(); + + //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); + + //accessors and mutators + AccountData* GetAccount(int uid); + std::map* GetContainer(); + + sqlite3* SetDatabase(sqlite3* db); + sqlite3* GetDatabase(); + +private: + std::map accountMap; + sqlite3* database = nullptr; +}; + +#endif diff --git a/server/character_management.cpp b/server/character_manager.cpp similarity index 85% rename from server/character_management.cpp rename to server/character_manager.cpp index 3991305..0a1d4d7 100644 --- a/server/character_management.cpp +++ b/server/character_manager.cpp @@ -19,7 +19,7 @@ * 3. This notice may not be removed or altered from any source * distribution. */ -#include "server_application.hpp" +#include "character_manager.hpp" #include "sqlite3/sqlite3.h" @@ -33,7 +33,7 @@ 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 " - "mapIndex = ?2," + "roomIndex = ?2," "originX = ?3," "originY = ?4," "level = ?5," @@ -58,9 +58,18 @@ static const char* DELETE_CHARACTER = "DELETE FROM Characters WHERE uid = ?;"; //Define the methods //------------------------- -//TODO: should statistics be stored separately? +CharacterManager::CharacterManager() { + // +} + +CharacterManager::~CharacterManager() { + for (auto& it : characterMap) { + SaveCharacter(it.first); + } +} + //TODO: default stats as a parameter? This would be good for differing beggining states or multiple classes -int ServerApplication::CreateCharacter(int owner, std::string handle, std::string avatar) { +int CharacterManager::CreateCharacter(int owner, std::string handle, std::string avatar) { //Create the character, failing if it exists sqlite3_stmt* statement = nullptr; @@ -93,7 +102,7 @@ int ServerApplication::CreateCharacter(int owner, std::string handle, std::strin return LoadCharacter(owner, handle, avatar); } -int ServerApplication::LoadCharacter(int owner, std::string handle, std::string avatar) { +int CharacterManager::LoadCharacter(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; @@ -138,7 +147,7 @@ int ServerApplication::LoadCharacter(int owner, std::string handle, std::string //Don't cache the birth //world origin - newChar.mapIndex = sqlite3_column_int(statement, 5); + newChar.roomIndex = sqlite3_column_int(statement, 5); newChar.origin.x = (double)sqlite3_column_int(statement, 6); newChar.origin.y = (double)sqlite3_column_int(statement, 7); @@ -178,7 +187,7 @@ int ServerApplication::LoadCharacter(int owner, std::string handle, std::string throw(std::runtime_error(std::string() + "Unknown SQL error in LoadCharacter: " + sqlite3_errmsg(database) )); } -int ServerApplication::SaveCharacter(int uid) { +int CharacterManager::SaveCharacter(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. @@ -198,7 +207,7 @@ int ServerApplication::SaveCharacter(int uid) { //parameters bool ret = false; ret |= sqlite3_bind_int(statement, 1, uid) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 2, character.mapIndex) != SQLITE_OK; + ret |= sqlite3_bind_int(statement, 2, character.roomIndex) != SQLITE_OK; ret |= sqlite3_bind_int(statement, 3, (int)character.origin.x) != SQLITE_OK; ret |= sqlite3_bind_int(statement, 4, (int)character.origin.y) != SQLITE_OK; @@ -241,13 +250,13 @@ int ServerApplication::SaveCharacter(int uid) { return 0; } -void ServerApplication::UnloadCharacter(int uid) { +void CharacterManager::UnloadCharacter(int uid) { //save this character, then unload it SaveCharacter(uid); characterMap.erase(uid); } -void ServerApplication::DeleteCharacter(int uid) { +void CharacterManager::DeleteCharacter(int uid) { //delete this character from the database, then remove it from memory sqlite3_stmt* statement = nullptr; @@ -272,3 +281,41 @@ void ServerApplication::DeleteCharacter(int uid) { sqlite3_finalize(statement); 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); + it = characterMap.erase(it); + continue; + } + it++; + } +} + +//------------------------- +//Define the accessors and mutators +//------------------------- + +CharacterData* CharacterManager::GetCharacter(int uid) { + std::map::iterator it = characterMap.find(uid); + + if (it == characterMap.end()) { + return nullptr; + } + + return &it->second; +} + +std::map* CharacterManager::GetContainer() { + return &characterMap; +} + +sqlite3* CharacterManager::SetDatabase(sqlite3* db) { + return database = db; +} + +sqlite3* CharacterManager::GetDatabase() { + return database; +} \ No newline at end of file diff --git a/server/character_manager.hpp b/server/character_manager.hpp new file mode 100644 index 0000000..812d337 --- /dev/null +++ b/server/character_manager.hpp @@ -0,0 +1,58 @@ +/* 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. +*/ +#ifndef CHARACTERMANAGER_HPP_ +#define CHARACTERMANAGER_HPP_ + +#include "character_data.hpp" + +#include "sqlite3/sqlite3.h" + +#include +#include + +class CharacterManager { +public: + CharacterManager(); + ~CharacterManager(); + + //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); + + //accessors and mutators + CharacterData* GetCharacter(int uid); + std::map* GetContainer(); + + sqlite3* SetDatabase(sqlite3* db); + sqlite3* GetDatabase(); + +private: + std::map characterMap; + sqlite3* database = nullptr; +}; + +#endif diff --git a/common/gameplay/client_data.hpp b/server/client_data.hpp similarity index 100% rename from common/gameplay/client_data.hpp rename to server/client_data.hpp diff --git a/server/combat_manager.cpp b/server/combat_manager.cpp new file mode 100644 index 0000000..be4f9ac --- /dev/null +++ b/server/combat_manager.cpp @@ -0,0 +1,54 @@ +/* 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 "combat_manager.hpp" + +//------------------------- +//public access methods +//------------------------- + +//TODO + +//------------------------- +//accessors and mutators +//------------------------- + +CombatData* CombatManager::GetCombat(int uid) { + std::map::iterator it = combatMap.find(uid); + + if (it == combatMap.end()) { + return nullptr; + } + + return &it->second; +} + +std::map* CombatManager::GetContainer() { + return &combatMap; +} + +lua_State* CombatManager::SetLuaState(lua_State* L) { + return luaState = L; +} + +lua_State* CombatManager::GetLuaState() { + return luaState; +} diff --git a/server/combat_manager.hpp b/server/combat_manager.hpp new file mode 100644 index 0000000..77573ab --- /dev/null +++ b/server/combat_manager.hpp @@ -0,0 +1,51 @@ +/* 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. +*/ +#ifndef COMBATMANAGER_HPP_ +#define COMBATMANAGER_HPP_ + +#include "combat_data.hpp" + +#include "lua/lua.hpp" + +#include + +class CombatManager { +public: + CombatManager() = default; + ~CombatManager() = default; + + //public access methods + //TODO + + //accessors and mutators + CombatData* GetCombat(int uid); + std::map* GetContainer(); + + lua_State* SetLuaState(lua_State*); + lua_State* GetLuaState(); + +private: + std::map combatMap; + lua_State* luaState = nullptr; +}; + +#endif \ No newline at end of file diff --git a/server/enemy_manager.cpp b/server/enemy_manager.cpp new file mode 100644 index 0000000..2b49d3d --- /dev/null +++ b/server/enemy_manager.cpp @@ -0,0 +1,54 @@ +/* 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 "enemy_manager.hpp" + +//------------------------- +//public access methods +//------------------------- + +//TODO + +//------------------------- +//accessors and mutators +//------------------------- + +EnemyData* EnemyManager::GetEnemy(int uid) { + std::map::iterator it = enemyMap.find(uid); + + if (it == enemyMap.end()) { + return nullptr; + } + + return &it->second; +} + +std::map* EnemyManager::GetContainer() { + return &enemyMap; +} + +lua_State* EnemyManager::SetLuaState(lua_State* L) { + return luaState = L; +} + +lua_State* EnemyManager::GetLuaState() { + return luaState; +} diff --git a/server/enemy_manager.hpp b/server/enemy_manager.hpp new file mode 100644 index 0000000..dae86ad --- /dev/null +++ b/server/enemy_manager.hpp @@ -0,0 +1,51 @@ +/* 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. +*/ +#ifndef ENEMYMANAGER_HPP_ +#define ENEMYMANAGER_HPP_ + +#include "enemy_data.hpp" + +#include "lua/lua.hpp" + +#include + +class EnemyManager { +public: + EnemyManager() = default; + ~EnemyManager() = default; + + //public access methods + //TODO + + //accessors and mutators + EnemyData* GetEnemy(int uid); + std::map* GetContainer(); + + lua_State* SetLuaState(lua_State*); + lua_State* GetLuaState(); + +private: + std::map enemyMap; + lua_State* luaState = nullptr; +}; + +#endif \ No newline at end of file diff --git a/server/makefile b/server/makefile index 901f8a6..7c733ea 100644 --- a/server/makefile +++ b/server/makefile @@ -1,5 +1,5 @@ #config -INCLUDES+=. ../common/gameplay ../common/map ../common/network ../common/script ../common/utilities +INCLUDES+=. ../common/gameplay ../common/map ../common/network ../common/network/packet ../common/network/serial ../common/script ../common/utilities LIBS+=../libcommon.a -lSDL_net -lwsock32 -liphlpapi -lmingw32 -lSDLmain -lSDL -llua -lsqlite3 CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES)) diff --git a/common/gameplay/room_data.hpp b/server/room_data.hpp similarity index 83% rename from common/gameplay/room_data.hpp rename to server/room_data.hpp index 6826314..c436d54 100644 --- a/common/gameplay/room_data.hpp +++ b/server/room_data.hpp @@ -22,6 +22,11 @@ #ifndef ROOMDATA_HPP_ #define ROOMDATA_HPP_ +//map system +#include "map_allocator.hpp" +#include "map_file_format.hpp" +#include "region_pager.hpp" + struct RoomData { enum class RoomType { OVERWORLD, @@ -31,11 +36,12 @@ struct RoomData { CAVES, }; - /* TODO: more - * "multiple rooms system" using this structure - * Pager - * collision map - */ + //members + RegionPager pager; + RoomType type; + + //TODO: collision map + //TODO: NPCs? }; #endif diff --git a/server/room_manager.cpp b/server/room_manager.cpp new file mode 100644 index 0000000..3cb43dd --- /dev/null +++ b/server/room_manager.cpp @@ -0,0 +1,54 @@ +/* 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 "room_manager.hpp" + +//------------------------- +//public access methods +//------------------------- + +//TODO + +//------------------------- +//accessors and mutators +//------------------------- + +RoomData* RoomManager::GetRoom(int uid) { + std::map::iterator it = roomMap.find(uid); + + if (it == roomMap.end()) { + return nullptr; + } + + return &it->second; +} + +std::map* RoomManager::GetContainer() { + return &roomMap; +} + +lua_State* RoomManager::SetLuaState(lua_State* L) { + return luaState = L; +} + +lua_State* RoomManager::GetLuaState() { + return luaState; +} diff --git a/server/room_manager.hpp b/server/room_manager.hpp new file mode 100644 index 0000000..2caadaa --- /dev/null +++ b/server/room_manager.hpp @@ -0,0 +1,52 @@ +/* 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. +*/ +#ifndef ROOMMANAGER_HPP_ +#define ROOMMANAGER_HPP_ + +#include "room_data.hpp" + +#include "lua/lua.hpp" + +#include + +class RoomManager { +public: + RoomManager() = default; + ~RoomManager() = default; + + //public access methods + //TODO + //TODO: setup the pagers and functors of each room object + + //accessors and mutators + RoomData* GetRoom(int uid); + std::map* GetContainer(); + + lua_State* SetLuaState(lua_State*); + lua_State* GetLuaState(); + +private: + std::map roomMap; + lua_State* luaState = nullptr; +}; + +#endif \ No newline at end of file diff --git a/server/server_application.cpp b/server/server_application.cpp new file mode 100644 index 0000000..44467a0 --- /dev/null +++ b/server/server_application.cpp @@ -0,0 +1,478 @@ +/* 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 "sql_utility.hpp" +#include "serial.hpp" + +#include +#include +#include + +//------------------------- +//public methods +//------------------------- + +void ServerApplication::Init(int argc, char** argv) { + //NOTE: I might need to rearrange the init process so that lua & SQL can interact with the map system as needed. + std::cout << "Beginning startup" << std::endl; + + //initial setup + config.Load("rsc\\config.cfg"); + + //------------------------- + //Initialize the APIs + //------------------------- + + //Init SDL + if (SDL_Init(0)) { + throw(std::runtime_error("Failed to initialize SDL")); + } + std::cout << "Initialized SDL" << std::endl; + + //Init SDL_net + if (SDLNet_Init()) { + throw(std::runtime_error("Failed to initialize SDL_net")); + } + network.Open(config.Int("server.port")); + std::cout << "Initialized SDL_net" << std::endl; + + //Init SQL + int ret = sqlite3_open_v2(config["server.dbname"].c_str(), &database, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, nullptr); + if (ret != SQLITE_OK || !database) { + throw(std::runtime_error(std::string() + "Failed to initialize SQL: " + sqlite3_errmsg(database) )); + } + std::cout << "Initialized SQL" << std::endl; + + //Init lua + luaState = luaL_newstate(); + if (!luaState) { + throw(std::runtime_error("Failed to initialize lua")); + } + luaL_openlibs(luaState); + std::cout << "Initialized lua" << std::endl; + + //------------------------- + //Setup the objects + //------------------------- + + accountMgr.SetDatabase(database); + characterMgr.SetDatabase(database); + + combatMgr.SetLuaState(luaState); + roomMgr.SetLuaState(luaState); + enemyMgr.SetLuaState(luaState); + + std::cout << "Internal managers ready" << std::endl; + + //------------------------- + //Run the startup scripts + //------------------------- + + //setup the database + if (runSQLScript(database, config["dir.scripts"] + "setup_server.sql")) { + throw(std::runtime_error("Failed to initialize SQL's setup script")); + } + std::cout << "Completed SQL's setup script" << std::endl; + + //run lua's startup script + if (luaL_dofile(luaState, (config["dir.scripts"] + "setup_server.lua").c_str())) { + throw(std::runtime_error(std::string() + "Failed to initialize lua's setup script: " + lua_tostring(luaState, -1) )); + } + std::cout << "Completed lua's setup script" << std::endl; + + //------------------------- + //debug output + //------------------------- + + std::cout << "Internal sizes:" << std::endl; + std::cout << "\tTile Size: " << sizeof(Region::type_t) << std::endl; + std::cout << "\tRegion Format: " << REGION_WIDTH << ", " << REGION_HEIGHT << ", " << REGION_DEPTH << std::endl; + std::cout << "\tRegion Content Footprint: " << REGION_WIDTH * REGION_HEIGHT * REGION_DEPTH * sizeof(Region::type_t) << std::endl; + std::cout << "\tPACKET_BUFFER_SIZE (max size): " << PACKET_BUFFER_SIZE << std::endl; + + //------------------------- + //finalize the startup + //------------------------- + + std::cout << "Startup completed successfully" << std::endl; + + //------------------------- + //debugging + //------------------------- + + std::cout << "Debugging dump:" << std::endl; + std::cout << "\tMAX_PACKET_SIZE:\t\t" << MAX_PACKET_SIZE << std::endl; + std::cout << "\tsizeof(SerialPacket):\t\t" << sizeof(SerialPacket) << std::endl; + std::cout << "\tsizeof(CharacterPacket):\t" << sizeof(CharacterPacket) << std::endl; + std::cout << "\t\tsizeof(Statistics):\t" << sizeof(Statistics) << std::endl; + std::cout << "\tsizeof(ClientPacket):\t\t" << sizeof(ClientPacket) << std::endl; + std::cout << "\tsizeof(CombatPacket):\t\t" << sizeof(CombatPacket) << std::endl; + std::cout << "\tsizeof(EnemyPacket):\t\t" << sizeof(EnemyPacket) << std::endl; + std::cout << "\tsizeof(RegionPacket):\t\t" << sizeof(RegionPacket) << std::endl; + std::cout << "\tsizeof(ServerPacket):\t\t" << sizeof(ServerPacket) << std::endl; +} + +void ServerApplication::Proc() { + char packetBuffer[MAX_PACKET_SIZE]; + while(running) { + //suck in the waiting packets & process them + while(network.Receive(reinterpret_cast(packetBuffer))) { + HandlePacket(reinterpret_cast(packetBuffer)); + } + //update the internals + //TODO: update the internals i.e. player positions + //give the computer a break + SDL_Delay(10); + } +} + +void ServerApplication::Quit() { + std::cout << "Shutting down" << std::endl; + + //APIs + lua_close(luaState); + sqlite3_close_v2(database); + network.Close(); + SDLNet_Quit(); + SDL_Quit(); + + std::cout << "Shutdown finished" << std::endl; +} + +//------------------------- +//handle incoming traffic +//------------------------- + +void ServerApplication::HandlePacket(SerialPacket* const argPacket) { + switch(argPacket->type) { + //basic connections + case SerialPacketType::BROADCAST_REQUEST: + HandleBroadcastRequest(dynamic_cast(argPacket)); + break; + case SerialPacketType::JOIN_REQUEST: + HandleJoinRequest(dynamic_cast(argPacket)); + break; + case SerialPacketType::DISCONNECT: + HandleDisconnect(dynamic_cast(argPacket)); + break; + case SerialPacketType::SHUTDOWN: + HandleShutdown(dynamic_cast(argPacket)); + break; + + //map management + case SerialPacketType::REGION_REQUEST: + HandleRegionRequest(dynamic_cast(argPacket)); + break; + + //combat management + //TODO: combat management + + //character management + case SerialPacketType::CHARACTER_NEW: + HandleCharacterNew(dynamic_cast(argPacket)); + break; + case SerialPacketType::CHARACTER_DELETE: + HandleCharacterDelete(dynamic_cast(argPacket)); + break; + case SerialPacketType::CHARACTER_UPDATE: + case SerialPacketType::CHARACTER_STATS_REQUEST: + HandleCharacterUpdate(dynamic_cast(argPacket)); + break; + + //enemy management + //TODO: enemy management + + //mismanagement + case SerialPacketType::SYNCHRONIZE: + HandleSynchronize(dynamic_cast(argPacket)); + break; + + //handle errors + default: + throw(std::runtime_error(std::string() + "Unknown SerialPacketType encountered in the server: " + to_string_custom(int(argPacket->type)))); + break; + } +} + +//------------------------- +//basic connections +//------------------------- + +void ServerApplication::HandleBroadcastRequest(SerialPacket* const argPacket) { + //send the server's data + ServerPacket newPacket; + + newPacket.type = SerialPacketType::BROADCAST_RESPONSE; + snprintf(newPacket.name, PACKET_STRING_SIZE, "%s", config["server.name"].c_str()); + newPacket.playerCount = characterMgr.GetContainer()->size(); + newPacket.version = NETWORK_VERSION; + + network.SendTo(&argPacket->srcAddress, dynamic_cast(&newPacket)); +} + +void ServerApplication::HandleJoinRequest(ClientPacket* const argPacket) { + //create the new client + ClientData newClient; + newClient.address = argPacket->srcAddress; + + //load the user account + //TODO: handle passwords + int accountIndex = accountMgr.LoadAccount(argPacket->username, clientUID); + if (accountIndex < 0) { + //TODO: send rejection packet + std::cerr << "Error: Account already loaded: " << accountIndex << std::endl; + return; + } + + //send the client their info + ClientPacket newPacket; + newPacket.type = SerialPacketType::JOIN_RESPONSE; + newPacket.clientIndex = clientUID; + newPacket.accountIndex = accountIndex; + + network.SendTo(&newClient.address, dynamic_cast(&newPacket)); + + //finished this routine + clientMap[clientUID++] = newClient; + std::cout << "New connection, " << clientMap.size() << " clients and " << accountMgr.GetContainer()->size() << " accounts total" << std::endl; +} + +void ServerApplication::HandleDisconnect(ClientPacket* const argPacket) { + //TODO: authenticate who is disconnecting/kicking + + //forward to the specified client + network.SendTo( + &clientMap[ accountMgr.GetAccount(argPacket->accountIndex)->clientIndex ].address, + dynamic_cast(argPacket) + ); + + //save and unload this account's characters + //pump the unload message to all remaining clients + characterMgr.UnloadCharacterIf([&](std::map::iterator it) -> bool { + if (argPacket->accountIndex == it->second.owner) { + PumpCharacterUnload(it->first); + return true; + } + return false; + }); + + //erase the in-memory stuff + clientMap.erase(accountMgr.GetAccount(argPacket->accountIndex)->clientIndex); + accountMgr.UnloadAccount(argPacket->accountIndex); + + //finished this routine + std::cout << "Disconnection, " << clientMap.size() << " clients and " << accountMgr.GetContainer()->size() << " accounts total" << std::endl; +} + +void ServerApplication::HandleShutdown(SerialPacket* const argPacket) { + //TODO: authenticate who is shutting the server down + + //end the server + running = false; + + //disconnect all clients + SerialPacket newPacket; + newPacket.type = SerialPacketType::DISCONNECT; + PumpPacket(&newPacket); + + //finished this routine + std::cout << "Shutdown signal accepted" << std::endl; +} + +//------------------------- +//map management +//------------------------- + +void ServerApplication::HandleRegionRequest(RegionPacket* const argPacket) { + RegionPacket newPacket; + + newPacket.type = SerialPacketType::REGION_CONTENT; + newPacket.roomIndex = argPacket->roomIndex; + newPacket.x = argPacket->x; + newPacket.y = argPacket->y; + + newPacket.region = roomMgr.GetRoom(argPacket->roomIndex)->pager.GetRegion(argPacket->x, argPacket->y); + + //send the content + network.SendTo(&argPacket->srcAddress, dynamic_cast(argPacket)); +} + +//------------------------- +//combat management +//------------------------- + +//TODO: combat management + +//------------------------- +//Character Management +//------------------------- + +void ServerApplication::HandleCharacterNew(CharacterPacket* const argPacket) { + int characterIndex = characterMgr.CreateCharacter(argPacket->accountIndex, argPacket->handle, argPacket->avatar); + + if (characterIndex == -1) { + //TODO: rejection packet + std::cerr << "Warning: Character already loaded" << std::endl; + return; + } + + if (characterIndex == -2) { + //TODO: rejection packet + std::cerr << "Warning: Character already exists" << std::endl; + return; + } + + //send this new character to all clients + CharacterPacket newPacket; + newPacket.type = SerialPacketType::CHARACTER_NEW; + CopyCharacterToPacket(&newPacket, characterIndex); + PumpPacket(&newPacket); +} + +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); + + //if this is not your character + if (characterIndex == -2) { + //TODO: rejection packet + std::cerr << "Warning: Character cannot be deleted" << std::endl; + + //unload an unneeded character + if (characterIndex != -1) { + characterMgr.UnloadCharacter(characterIndex); + } + return; + } + + //delete it + characterMgr.DeleteCharacter(characterIndex); + + //TODO: success packet + + //Unload this character from all clients + PumpCharacterUnload(characterIndex); +} + +void ServerApplication::HandleCharacterUpdate(CharacterPacket* const argPacket) { + CharacterData* character = characterMgr.GetCharacter(argPacket->characterIndex); + + //make a new character if this one doesn't exist + if (!character) { + //this isn't normal + std::cerr << "Warning: HandleCharacterUpdate() is passing to HandleCharacterNew()" << std::endl; + HandleCharacterNew(argPacket); + return; + } + + /* TODO: rewrite this design flaw, read more + * Slaving the client to the server here is a terrible idea, instead there + * needs to be a utility function to update and send the server-side character + * to the clients. + * + * Other things to consider include functionality to reequip the character, + * apply status effects and to change the stats as well. These should all be + * handled server-side. + */ + character->roomIndex = argPacket->roomIndex; + character->origin = argPacket->origin; + character->motion = argPacket->motion; + + character->stats = argPacket->stats; + + //TODO: equipment + //TODO: items + //TODO: buffs + //TODO: debuffs + + PumpPacket(argPacket); +} + +//------------------------- +//enemy management +//------------------------- + +//TODO: enemy management + +//------------------------- +//mismanagement +//------------------------- + +void ServerApplication::HandleSynchronize(ClientPacket* const argPacket) { + //TODO: compensate for large distances + //NOTE: I quite dislike this function + + //send all of the server's data to this client + ClientData& client = clientMap[argPacket->clientIndex]; + + //send all characters + CharacterPacket newPacket; + newPacket.type = SerialPacketType::CHARACTER_UPDATE; + + for (auto& it : *characterMgr.GetContainer()) { + newPacket.characterIndex = it.first; + CopyCharacterToPacket(&newPacket, it.first); + network.SendTo(&client.address, dynamic_cast(&newPacket)); + } + + //TODO: more in HandleSynchronize() +} + +//------------------------- +//utility methods +//------------------------- + +//TODO: a function that only sends to characters in a certain proximity + +void ServerApplication::PumpPacket(SerialPacket* const argPacket) { + for (auto& it : clientMap) { + network.SendTo(&it.second.address, argPacket); + } +} + +void ServerApplication::PumpCharacterUnload(int uid) { + //delete the client-side character(s) + CharacterPacket newPacket; + newPacket.type = SerialPacketType::CHARACTER_DELETE; + newPacket.characterIndex = uid; + PumpPacket(dynamic_cast(&newPacket)); +} + +void ServerApplication::CopyCharacterToPacket(CharacterPacket* const packet, int characterIndex) { + CharacterData* character = characterMgr.GetCharacter(characterIndex); + if (!character) { + throw(std::runtime_error("Failed to copy a character to a packet")); + } + + //TODO: keep this up to date when the character changes + packet->characterIndex = characterIndex; + snprintf(packet->handle, PACKET_STRING_SIZE, "%s", character->handle.c_str()); + snprintf(packet->avatar, PACKET_STRING_SIZE, "%s", character->avatar.c_str()); + packet->accountIndex = character->owner; + packet->roomIndex = character->roomIndex; + packet->origin = character->origin; + packet->motion = character->motion; + packet->stats = character->stats; +} \ No newline at end of file diff --git a/server/server_application.hpp b/server/server_application.hpp index cbc7511..ca2129b 100644 --- a/server/server_application.hpp +++ b/server/server_application.hpp @@ -23,23 +23,16 @@ #define SERVERAPPLICATION_HPP_ //server specific stuff +#include "account_manager.hpp" +#include "character_manager.hpp" #include "client_data.hpp" -#include "account_data.hpp" -#include "character_data.hpp" -#include "combat_data.hpp" -#include "enemy_factory_generic.hpp" +#include "combat_manager.hpp" +#include "enemy_manager.hpp" +#include "room_manager.hpp" -//maps -#include "map_allocator.hpp" -#include "map_file_format.hpp" -#include "region_pager.hpp" - -//networking +//common utilities #include "udp_network_utility.hpp" - -//common #include "config_utility.hpp" -#include "vector2.hpp" //APIs #include "lua/lua.hpp" @@ -51,10 +44,9 @@ #include //The main application class -//TODO: modulate this god class class ServerApplication { public: - //standard functions + //public methods ServerApplication() = default; ~ServerApplication() = default; @@ -63,61 +55,57 @@ public: void Quit(); private: - void HandlePacket(SerialPacket); - //handle incoming traffic - void HandleBroadcastRequest(SerialPacket); - void HandleJoinRequest(SerialPacket); - void HandleSynchronize(SerialPacket); - void HandleDisconnect(SerialPacket); - void HandleShutdown(SerialPacket); - void HandleCharacterUpdate(SerialPacket); - void HandleRegionRequest(SerialPacket); + void HandlePacket(SerialPacket* const); - //TODO: a function that only sends to characters in a certain proximity - void PumpPacket(SerialPacket); - void PumpCharacterUnload(int uid); + //basic connections + void HandleBroadcastRequest(SerialPacket* const); + void HandleJoinRequest(ClientPacket* const); + void HandleDisconnect(ClientPacket* const); + void HandleShutdown(SerialPacket* const); - //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); - - //character management - 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); + //map management + void HandleRegionRequest(RegionPacket* const); + //combat management //TODO: combat management - //APIs - UDPNetworkUtility network; + //character management + void HandleCharacterNew(CharacterPacket* const); + void HandleCharacterDelete(CharacterPacket* const); + void HandleCharacterUpdate(CharacterPacket* const); + + //enemy management + //TODO: enemy management + + //mismanagement + void HandleSynchronize(ClientPacket* const); + + //utility methods + //TODO: a function that only sends to characters in a certain proximity + void PumpPacket(SerialPacket* const); + void PumpCharacterUnload(int uid); + void CopyCharacterToPacket(CharacterPacket* const packet, int characterIndex); + + //APIs and utilities sqlite3* database = nullptr; lua_State* luaState = nullptr; + UDPNetworkUtility network; + ConfigUtility config; - //server tables + //simple tables std::map clientMap; - std::map accountMap; - std::map characterMap; - std::map combatMap; - std::map enemyMap; - //maps - //TODO: I need to handle multiple map objects - //TODO: Unload regions that are distant from any characters - RegionPager regionPager; - EnemyFactoryGeneric enemyFactory; + //managers + AccountManager accountMgr; + CharacterManager characterMgr; + CombatManager combatMgr; + EnemyManager enemyMgr; + RoomManager roomMgr; //misc bool running = true; - ConfigUtility config; int clientUID = 0; - int combatUID = 0; - int enemyUID = 0; }; #endif diff --git a/server/server_connections.cpp b/server/server_connections.cpp deleted file mode 100644 index 047462f..0000000 --- a/server/server_connections.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* Copyright: (c) Kayne Ruse 2013, 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 -#include - -//------------------------- -//Handle various network input -//------------------------- - -void ServerApplication::HandleBroadcastRequest(SerialPacket packet) { - //pack the server's data - packet.meta.type = SerialPacket::Type::BROADCAST_RESPONSE; - packet.serverInfo.networkVersion = NETWORK_VERSION; - snprintf(packet.serverInfo.name, PACKET_STRING_SIZE, "%s", config["server.name"].c_str()); - packet.serverInfo.playerCount = characterMap.size(); - - //bounce this packet - network.SendTo(&packet.meta.srcAddress, &packet); -} - -void ServerApplication::HandleJoinRequest(SerialPacket packet) { - //create the new client - ClientData newClient; - newClient.address = packet.meta.srcAddress; - - //load the user account - int accountIndex = LoadUserAccount(packet.clientInfo.username, clientUID); - if (accountIndex < 0) { - //TODO: send rejection packet - std::cerr << "Error: Account already loaded: " << accountIndex << std::endl; - return; - } - - //load the new character - int characterIndex = LoadCharacter(accountIndex, packet.clientInfo.handle, packet.clientInfo.avatar); - if (characterIndex < 0) { - //TODO: send rejection packet - std::cerr << "Error: Character already loaded: " << characterIndex << std::endl; - UnloadUserAccount(accountIndex); - return; - } - - //send the client their info - packet.meta.type = SerialPacket::Type::JOIN_RESPONSE; - packet.clientInfo.clientIndex = clientUID; - packet.clientInfo.accountIndex = accountIndex; - packet.clientInfo.characterIndex = characterIndex; - - //bounce this packet - network.SendTo(&newClient.address, &packet); - - //reference to prevent multiple lookups - //TODO: I need a way to pack structures unto packets more easily - //NOTE: this chunk of code is similar to HandleSynchronize - CharacterData& character = characterMap[characterIndex]; - - //send the new character to all clients - packet.meta.type = SerialPacket::Type::CHARACTER_NEW; - packet.characterInfo.characterIndex = characterIndex; - strncpy(packet.characterInfo.handle, character.handle.c_str(), PACKET_STRING_SIZE); - strncpy(packet.characterInfo.avatar, character.avatar.c_str(), PACKET_STRING_SIZE); - packet.characterInfo.mapIndex = character.mapIndex; - packet.characterInfo.origin = character.origin; - packet.characterInfo.motion = character.motion; - packet.characterInfo.stats = character.stats; - - PumpPacket(packet); - - //TODO: don't send anything to a certain client until they send the OK (the sync packet? or ignore client side?) - //finished this routine - clientMap[clientUID++] = newClient; - std::cout << "Connect, total: " << clientMap.size() << std::endl; -} - -void ServerApplication::HandleSynchronize(SerialPacket packet) { - //TODO: compensate for large distances - - //send all the server's data to this client - SerialPacket newPacket; - - //characters - newPacket.meta.type = SerialPacket::Type::CHARACTER_UPDATE; - for (auto& it : characterMap) { - //TODO: update this for the expanded CharacterData structure - newPacket.characterInfo.characterIndex = it.first; - snprintf(newPacket.characterInfo.handle, PACKET_STRING_SIZE, "%s", it.second.handle.c_str()); - snprintf(newPacket.characterInfo.avatar, PACKET_STRING_SIZE, "%s", it.second.avatar.c_str()); - newPacket.characterInfo.mapIndex = it.second.mapIndex; - newPacket.characterInfo.origin = it.second.origin; - newPacket.characterInfo.motion = it.second.motion; - newPacket.characterInfo.stats = it.second.stats; - - network.SendTo(&clientMap[packet.clientInfo.clientIndex].address, &newPacket); - } -} - -void ServerApplication::HandleDisconnect(SerialPacket packet) { - //TODO: authenticate who is disconnecting/kicking - - //forward to the specified client - network.SendTo(&clientMap[accountMap[packet.clientInfo.accountIndex].clientIndex].address, &packet); - - //unload client and server-side characters - for (std::map::iterator it = characterMap.begin(); it != characterMap.end(); /* EMPTY */ ) { - if (it->second.owner == packet.clientInfo.accountIndex) { - PumpCharacterUnload(it->first); - SaveCharacter(it->first); - it = characterMap.erase(it); //efficient - continue; - } - else { - ++it; - } - } - - //erase the in-memory stuff - clientMap.erase(accountMap[packet.clientInfo.accountIndex].clientIndex); - UnloadUserAccount(packet.clientInfo.accountIndex); - - //finished this routine - std::cout << "Disconnect, total: " << clientMap.size() << std::endl; -} - -void ServerApplication::HandleShutdown(SerialPacket packet) { - //TODO: authenticate who is shutting the server down - - //end the server - running = false; - - //disconnect all clients - packet.meta.type = SerialPacket::Type::DISCONNECT; - PumpPacket(packet); - - //finished this routine - std::cout << "Shutdown signal accepted" << std::endl; -} - -void ServerApplication::HandleCharacterUpdate(SerialPacket packet) { - //TODO: this should be moved elsewhere - if (characterMap.find(packet.characterInfo.characterIndex) == characterMap.end()) { - throw(std::runtime_error("Cannot update a non-existant character")); - } - - //TODO: the server needs it's own movement system too - characterMap[packet.characterInfo.characterIndex].origin = packet.characterInfo.origin; - characterMap[packet.characterInfo.characterIndex].motion = packet.characterInfo.motion; - - PumpPacket(packet); -} - -void ServerApplication::HandleRegionRequest(SerialPacket packet) { - //TODO: this should be moved elsewhere - packet.meta.type = SerialPacket::Type::REGION_CONTENT; - packet.regionInfo.region = regionPager.GetRegion(packet.regionInfo.x, packet.regionInfo.y); - - //send the content - network.SendTo(&packet.meta.srcAddress, &packet); -} - -void ServerApplication::PumpPacket(SerialPacket packet) { - //NOTE: I don't really like this, but it'll do for now - for (auto& it : clientMap) { - network.SendTo(&it.second.address, &packet); - } -} - -void ServerApplication::PumpCharacterUnload(int uid) { - //delete the client-side character(s) - SerialPacket delPacket; - delPacket.meta.type = SerialPacket::Type::CHARACTER_DELETE; - delPacket.characterInfo.characterIndex = uid; - PumpPacket(delPacket); -} \ No newline at end of file diff --git a/server/server_internals.cpp b/server/server_internals.cpp deleted file mode 100644 index c5298fc..0000000 --- a/server/server_internals.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* 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 "sql_utility.hpp" -#include "serial.hpp" - -#include -#include -#include - -//------------------------- -//Define the public members -//------------------------- - -void ServerApplication::Init(int argc, char** argv) { - //NOTE: I might need to rearrange the init process so that lua & SQL can interact with the map system as needed. - std::cout << "Beginning startup" << std::endl; - - //initial setup - config.Load("rsc\\config.cfg"); - - //------------------------- - //Initialize the APIs - //------------------------- - - //Init SDL - if (SDL_Init(0)) { - throw(std::runtime_error("Failed to initialize SDL")); - } - std::cout << "Initialized SDL" << std::endl; - - //Init SDL_net - if (SDLNet_Init()) { - throw(std::runtime_error("Failed to initialize SDL_net")); - } - network.Open(config.Int("server.port")); - std::cout << "Initialized SDL_net" << std::endl; - - //Init SQL - int ret = sqlite3_open_v2(config["server.dbname"].c_str(), &database, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, nullptr); - if (ret != SQLITE_OK || !database) { - throw(std::runtime_error(std::string() + "Failed to initialize SQL: " + sqlite3_errmsg(database) )); - } - std::cout << "Initialized SQL" << std::endl; - - //Init lua - luaState = luaL_newstate(); - if (!luaState) { - throw(std::runtime_error("Failed to initialize lua")); - } - luaL_openlibs(luaState); - std::cout << "Initialized lua" << std::endl; - - //------------------------- - //Setup the objects - //------------------------- - - //setup the map object - regionPager.GetAllocator()->SetLuaState(luaState); - regionPager.GetFormat()->SetLuaState(luaState); - regionPager.GetFormat()->SetSaveDir(config["dir.maps"] + config["map.savename"]); - std::cout << "Prepared the map system" << std::endl; - - //push the pager onto the lua registry - lua_pushstring(luaState, "pager"); - lua_pushlightuserdata(luaState, reinterpret_cast(®ionPager)); - lua_settable(luaState, LUA_REGISTRYINDEX); - std::cout << "Registered the map system in lua" << std::endl; - - //------------------------- - //Run the startup scripts - //------------------------- - - //setup the database - if (runSQLScript(database, config["dir.scripts"] + "setup_server.sql")) { - throw(std::runtime_error("Failed to initialize SQL's setup script")); - } - std::cout << "Completed SQL's setup script" << std::endl; - - //run lua's startup script - if (luaL_dofile(luaState, (config["dir.scripts"] + "setup_server.lua").c_str())) { - throw(std::runtime_error(std::string() + "Failed to initialize lua's setup script: " + lua_tostring(luaState, -1) )); - } - std::cout << "Completed lua's setup script" << std::endl; - - //debug output - std::cout << "Internal sizes:" << std::endl; - std::cout << "\tsizeof(SerialPacket): " << sizeof(SerialPacket) << std::endl; - std::cout << "\tPACKET_BUFFER_SIZE: " << PACKET_BUFFER_SIZE << std::endl; - - //finalize the startup - std::cout << "Startup completed successfully" << std::endl; - - //debugging - // -} - -void ServerApplication::Proc() { - SerialPacket packet; - while(running) { - //suck in the waiting packets & process them - while(network.Receive(&packet)) { - HandlePacket(packet); - } - //update the internals - //TODO: update the internals i.e. player positions - //give the computer a break - SDL_Delay(10); - } -} - -void ServerApplication::Quit() { - std::cout << "Shutting down" << std::endl; - - //save the server state - for (auto& it : accountMap) { - SaveUserAccount(it.first); - } - for (auto& it : characterMap) { - SaveCharacter(it.first); - } - - //empty the members - accountMap.clear(); - characterMap.clear(); - regionPager.UnloadAll(); - - //APIs - lua_close(luaState); - sqlite3_close_v2(database); - network.Close(); - SDLNet_Quit(); - SDL_Quit(); - - std::cout << "Shutdown finished" << std::endl; -} - -//------------------------- -//Define the uber switch -//------------------------- - -void ServerApplication::HandlePacket(SerialPacket packet) { - switch(packet.meta.type) { - case SerialPacket::Type::BROADCAST_REQUEST: - HandleBroadcastRequest(packet); - break; - case SerialPacket::Type::JOIN_REQUEST: - HandleJoinRequest(packet); - break; - case SerialPacket::Type::SYNCHRONIZE: - HandleSynchronize(packet); - break; - case SerialPacket::Type::DISCONNECT: - HandleDisconnect(packet); - break; - case SerialPacket::Type::SHUTDOWN: - HandleShutdown(packet); - break; - case SerialPacket::Type::CHARACTER_UPDATE: - HandleCharacterUpdate(packet); - break; - case SerialPacket::Type::REGION_REQUEST: - HandleRegionRequest(packet); - break; - //handle errors - default: - throw(std::runtime_error("Unknown SerialPacketType encountered")); - break; - } -} diff --git a/todo.txt b/todo.txt index fa7d60d..605e60a 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,11 @@ -TODO: Modulate this god class -TODO: Segment SerialPacket? -TODO: Not all structures in common/gameplay are needed by the client +TODO: MapLoader, in place of FileFormat +TODO: Get the rooms working +TODO: update the map API to handle multiple rooms + +TODO: Rejection packets +TODO: Authentication +TODO: server is slaved to the client + TODO: I need to keep the documentation up to date. Namely, the GDD is getting out of date. TODO: I completely forgot about status ailments TODO: Time delay for requesting region packets