diff --git a/common/network/serial_packet.hpp b/common/network/serial_packet.hpp index 2075c75..180c8dd 100644 --- a/common/network/serial_packet.hpp +++ b/common/network/serial_packet.hpp @@ -34,7 +34,7 @@ typedef SerialPacketBase SerialPacket; //DOCS: NETWORK_VERSION is used to discern compatible servers and clients -constexpr int NETWORK_VERSION = 20150214; +constexpr int NETWORK_VERSION = 20150221; union MaxPacket { CharacterPacket a; diff --git a/common/network/serial_packet_type.hpp b/common/network/serial_packet_type.hpp index d538c24..062fa05 100644 --- a/common/network/serial_packet_type.hpp +++ b/common/network/serial_packet_type.hpp @@ -25,6 +25,7 @@ /* DOCS: The headers indicate what packet type is used for each message * different messages under the same header will carry different amounts of * valid data, but it will still be carried in that packet's format. + * FORMAT_* is for internal use, deviding the different format bounds. */ enum class SerialPacketType { @@ -36,6 +37,8 @@ enum class SerialPacketType { // name, player count, version //------------------------- + FORMAT_SERVER, + //heartbeat PING, PONG, @@ -44,11 +47,15 @@ enum class SerialPacketType { BROADCAST_REQUEST, BROADCAST_RESPONSE, + FORMAT_END_SERVER, + //------------------------- //ClientPacket // client index, account index, username //------------------------- + FORMAT_CLIENT, + //Connecting to a server as a client JOIN_REQUEST, JOIN_RESPONSE, @@ -69,15 +76,21 @@ enum class SerialPacketType { //shut down the server ADMIN_SHUTDOWN_REQUEST, + FORMAT_END_CLIENT, + //------------------------- //RegionPacket // room index, x, y, raw data //------------------------- + FORMAT_REGION, + //map data REGION_REQUEST, REGION_CONTENT, + FORMAT_END_REGION, + //------------------------- //CharacterPacket // character index, @@ -86,6 +99,11 @@ enum class SerialPacketType { // room index, origin, motion //------------------------- + FORMAT_CHARACTER, + + //full data update + CHARACTER_UPDATE, + //character management CHARACTER_CREATE, CHARACTER_DELETE, @@ -97,7 +115,7 @@ enum class SerialPacketType { QUERY_CHARACTER_STATS, QUERY_CHARACTER_LOCATION, - //set the info in the server + //actions taken CHARACTER_MOVEMENT, CHARACTER_ATTACK, CHARACTER_DAMAGE, @@ -105,6 +123,8 @@ enum class SerialPacketType { //admin control // ADMIN_SET_CHARACTER_ORIGIN, + FORMAT_END_CHARACTER, + //------------------------- //MonsterPacket // monster index, @@ -113,22 +133,34 @@ enum class SerialPacketType { // room index, origin, motion //------------------------- + FORMAT_MONSTER, + + //full data update + MONSTER_UPDATE, + + //character management MONSTER_CREATE, MONSTER_DELETE, + //find out info from the server QUERY_MONSTER_EXISTS, QUERY_MONSTER_STATS, QUERY_MONSTER_LOCATION, + //actions taken MONSTER_MOVEMENT, MONSTER_ATTACK, MONSTER_DAMAGE, + FORMAT_END_MONSTER, + //------------------------- //TextPacket // name, text //------------------------- + FORMAT_TEXT, + //general speech TEXT_BROADCAST, TEXT_SPEECH, @@ -143,6 +175,8 @@ enum class SerialPacketType { SHUTDOWN_REJECTION, QUERY_REJECTION, + FORMAT_END_TEXT, + //------------------------- //not used //------------------------- diff --git a/common/network/serial_utility.cpp b/common/network/serial_utility.cpp index 25a3dfc..d002891 100644 --- a/common/network/serial_utility.cpp +++ b/common/network/serial_utility.cpp @@ -31,6 +31,9 @@ #include +//macros +#define BOUNDS(type, lower, upper) ((type) > (lower) && (type) < (upper)) + //raw memory copy void serialCopy(void** buffer, void* data, int size) { memcpy(*buffer, data, size); @@ -46,63 +49,28 @@ void deserialCopy(void** buffer, void* data, int size) { //main switch functions void serializePacket(void* buffer, SerialPacketBase* packet) { - switch(packet->type) { - case SerialPacketType::PING: - case SerialPacketType::PONG: - case SerialPacketType::BROADCAST_REQUEST: - case SerialPacketType::BROADCAST_RESPONSE: - serializeServer(buffer, static_cast(packet)); - break; - case SerialPacketType::JOIN_REQUEST: - case SerialPacketType::JOIN_RESPONSE: - case SerialPacketType::DISCONNECT_REQUEST: - case SerialPacketType::DISCONNECT_RESPONSE: - case SerialPacketType::ADMIN_DISCONNECT_FORCED: - case SerialPacketType::LOGIN_REQUEST: - case SerialPacketType::LOGIN_RESPONSE: - case SerialPacketType::LOGOUT_REQUEST: - case SerialPacketType::LOGOUT_RESPONSE: - case SerialPacketType::ADMIN_SHUTDOWN_REQUEST: - serializeClient(buffer, static_cast(packet)); - break; - case SerialPacketType::REGION_REQUEST: - case SerialPacketType::REGION_CONTENT: - serializeRegion(buffer, static_cast(packet)); - break; - case SerialPacketType::CHARACTER_CREATE: - case SerialPacketType::CHARACTER_DELETE: - case SerialPacketType::CHARACTER_LOAD: - case SerialPacketType::CHARACTER_UNLOAD: - case SerialPacketType::QUERY_CHARACTER_EXISTS: - case SerialPacketType::QUERY_CHARACTER_STATS: - case SerialPacketType::QUERY_CHARACTER_LOCATION: - case SerialPacketType::CHARACTER_MOVEMENT: - case SerialPacketType::CHARACTER_ATTACK: - case SerialPacketType::CHARACTER_DAMAGE: - serializeCharacter(buffer, static_cast(packet)); - break; - case SerialPacketType::MONSTER_CREATE: - case SerialPacketType::MONSTER_DELETE: - case SerialPacketType::QUERY_MONSTER_EXISTS: - case SerialPacketType::QUERY_MONSTER_STATS: - case SerialPacketType::QUERY_MONSTER_LOCATION: - case SerialPacketType::MONSTER_MOVEMENT: - case SerialPacketType::MONSTER_ATTACK: - case SerialPacketType::MONSTER_DAMAGE: - serializeMonster(buffer, static_cast(packet)); - break; - case SerialPacketType::TEXT_BROADCAST: - case SerialPacketType::TEXT_SPEECH: - case SerialPacketType::TEXT_WHISPER: - case SerialPacketType::JOIN_REJECTION: - case SerialPacketType::LOGIN_REJECTION: - case SerialPacketType::REGION_REJECTION: - case SerialPacketType::CHARACTER_REJECTION: - case SerialPacketType::MONSTER_REJECTION: - case SerialPacketType::SHUTDOWN_REJECTION: - case SerialPacketType::QUERY_REJECTION: - serializeText(buffer, static_cast(packet)); - break; + if (BOUNDS(packet->type, SerialPacketType::FORMAT_SERVER, SerialPacketType::FORMAT_END_SERVER)) { + serializeServer(buffer, static_cast(packet)); + } + + if (BOUNDS(packet->type, SerialPacketType::FORMAT_CLIENT, SerialPacketType::FORMAT_END_CLIENT)) { + serializeClient(buffer, static_cast(packet)); + } + + if (BOUNDS(packet->type, SerialPacketType::FORMAT_REGION, SerialPacketType::FORMAT_END_REGION)) { + serializeRegion(buffer, static_cast(packet)); + } + + if (BOUNDS(packet->type, SerialPacketType::FORMAT_CHARACTER, SerialPacketType::FORMAT_END_CHARACTER)) { + serializeCharacter(buffer, static_cast(packet)); + } + + if (BOUNDS(packet->type, SerialPacketType::FORMAT_MONSTER, SerialPacketType::FORMAT_END_MONSTER)) { + serializeMonster(buffer, static_cast(packet)); + } + + if (BOUNDS(packet->type, SerialPacketType::FORMAT_TEXT, SerialPacketType::FORMAT_END_TEXT)) { + serializeText(buffer, static_cast(packet)); } } @@ -111,62 +79,27 @@ void deserializePacket(void* buffer, SerialPacketBase* packet) { SerialPacketType type; memcpy(&type, buffer, sizeof(SerialPacketType)); - switch(type) { - case SerialPacketType::PING: - case SerialPacketType::PONG: - case SerialPacketType::BROADCAST_REQUEST: - case SerialPacketType::BROADCAST_RESPONSE: - deserializeServer(buffer, static_cast(packet)); - break; - case SerialPacketType::JOIN_REQUEST: - case SerialPacketType::JOIN_RESPONSE: - case SerialPacketType::DISCONNECT_REQUEST: - case SerialPacketType::DISCONNECT_RESPONSE: - case SerialPacketType::ADMIN_DISCONNECT_FORCED: - case SerialPacketType::LOGIN_REQUEST: - case SerialPacketType::LOGIN_RESPONSE: - case SerialPacketType::LOGOUT_REQUEST: - case SerialPacketType::LOGOUT_RESPONSE: - case SerialPacketType::ADMIN_SHUTDOWN_REQUEST: - deserializeClient(buffer, static_cast(packet)); - break; - case SerialPacketType::REGION_REQUEST: - case SerialPacketType::REGION_CONTENT: - deserializeRegion(buffer, static_cast(packet)); - break; - case SerialPacketType::CHARACTER_CREATE: - case SerialPacketType::CHARACTER_DELETE: - case SerialPacketType::CHARACTER_LOAD: - case SerialPacketType::CHARACTER_UNLOAD: - case SerialPacketType::QUERY_CHARACTER_EXISTS: - case SerialPacketType::QUERY_CHARACTER_STATS: - case SerialPacketType::QUERY_CHARACTER_LOCATION: - case SerialPacketType::CHARACTER_MOVEMENT: - case SerialPacketType::CHARACTER_ATTACK: - case SerialPacketType::CHARACTER_DAMAGE: - deserializeCharacter(buffer, static_cast(packet)); - break; - case SerialPacketType::MONSTER_CREATE: - case SerialPacketType::MONSTER_DELETE: - case SerialPacketType::QUERY_MONSTER_EXISTS: - case SerialPacketType::QUERY_MONSTER_STATS: - case SerialPacketType::QUERY_MONSTER_LOCATION: - case SerialPacketType::MONSTER_MOVEMENT: - case SerialPacketType::MONSTER_ATTACK: - case SerialPacketType::MONSTER_DAMAGE: - deserializeMonster(buffer, static_cast(packet)); - break; - case SerialPacketType::TEXT_BROADCAST: - case SerialPacketType::TEXT_SPEECH: - case SerialPacketType::TEXT_WHISPER: - case SerialPacketType::JOIN_REJECTION: - case SerialPacketType::LOGIN_REJECTION: - case SerialPacketType::REGION_REJECTION: - case SerialPacketType::CHARACTER_REJECTION: - case SerialPacketType::MONSTER_REJECTION: - case SerialPacketType::SHUTDOWN_REJECTION: - case SerialPacketType::QUERY_REJECTION: - deserializeText(buffer, static_cast(packet)); - break; + if (BOUNDS(type, SerialPacketType::FORMAT_SERVER, SerialPacketType::FORMAT_END_SERVER)) { + deserializeServer(buffer, static_cast(packet)); + } + + if (BOUNDS(type, SerialPacketType::FORMAT_CLIENT, SerialPacketType::FORMAT_END_CLIENT)) { + deserializeClient(buffer, static_cast(packet)); + } + + if (BOUNDS(type, SerialPacketType::FORMAT_REGION, SerialPacketType::FORMAT_END_REGION)) { + deserializeRegion(buffer, static_cast(packet)); + } + + if (BOUNDS(type, SerialPacketType::FORMAT_CHARACTER, SerialPacketType::FORMAT_END_CHARACTER)) { + deserializeCharacter(buffer, static_cast(packet)); + } + + if (BOUNDS(type, SerialPacketType::FORMAT_MONSTER, SerialPacketType::FORMAT_END_MONSTER)) { + deserializeMonster(buffer, static_cast(packet)); + } + + if (BOUNDS(type, SerialPacketType::FORMAT_TEXT, SerialPacketType::FORMAT_END_TEXT)) { + deserializeText(buffer, static_cast(packet)); } } \ No newline at end of file diff --git a/rsc/scripts/setup_server.lua b/rsc/scripts/setup_server.lua index 8e498a3..bdb0974 100644 --- a/rsc/scripts/setup_server.lua +++ b/rsc/scripts/setup_server.lua @@ -3,6 +3,7 @@ print("Lua script check") mapMaker = require("map_maker") mapSaver = require("map_saver") roomSystem = require("room_system") +characterSystem = require("character_system") local function dumpTable(t) print(t) @@ -11,6 +12,25 @@ local function dumpTable(t) end end +--test the room hooks +roomSystem.RoomManager.SetOnCreate(function(room, index) + print("", "Creating room: ", roomSystem.Room.GetName(room), index) + + --called ~60 times per second + roomSystem.Room.SetOnTick(room, function(room) + local character = characterSystem.CharacterManager.GetCharacter("handle") + if character ~= nil then + --debugging + local x, y = characterSystem.Character.GetOrigin(character) + print("character.Origin(x, y): ", x, y) + end + end) +end) + +roomSystem.RoomManager.SetOnUnload(function(room, index) + print("", "Unloading room: ", roomSystem.Room.GetName(room), index) +end) + --NOTE: room 0 is the first that the client asks for, therefore it must exist local overworld, uid = roomSystem.RoomManager.CreateRoom("overworld", "overworld.bmp") roomSystem.Room.Initialize(overworld, mapSaver.Load, mapSaver.Save, mapMaker.DebugIsland, mapSaver.Save) diff --git a/server/characters/character_api.cpp b/server/characters/character_api.cpp index bf8b6f0..c6a0414 100644 --- a/server/characters/character_api.cpp +++ b/server/characters/character_api.cpp @@ -23,11 +23,56 @@ #include "character_data.hpp" +#include "entity_api.hpp" + +static int getOwner(lua_State* L) { + CharacterData* character = static_cast(lua_touserdata(L, 1)); + lua_pushinteger(L, character->GetOwner()); + return 1; +} + +static int getHandle(lua_State* L) { + CharacterData* character = static_cast(lua_touserdata(L, 1)); + lua_pushstring(L, character->GetHandle().c_str()); + return 1; +} + +static int getAvatar(lua_State* L) { + CharacterData* character = static_cast(lua_touserdata(L, 1)); + lua_pushstring(L, character->GetAvatar().c_str()); + return 1; +} + static const luaL_Reg characterLib[] = { +// {"GetOwner", getOwner}, //unusable without account API + {"GetHandle", getHandle}, + {"GetAvatar", getAvatar}, {nullptr, nullptr} }; LUAMOD_API int openCharacterAPI(lua_State* L) { + //the local table luaL_newlib(L, characterLib); + + //get the parent table + luaL_requiref(L, TORTUGA_ENTITY_API, openEntityAPI, false); + + //clone the parent table into the local table + lua_pushnil(L); //first key + while(lua_next(L, -2)) { + //copy the key-value pair + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + + //push the copy to the local table + lua_settable(L, -6); + + //pop the original value before continuing + lua_pop(L, 1); + } + + //remove the parent table, leaving the expanded child table + lua_pop(L, 1); + return 1; } \ No newline at end of file diff --git a/server/characters/character_data.hpp b/server/characters/character_data.hpp index 873bfa4..6c02134 100644 --- a/server/characters/character_data.hpp +++ b/server/characters/character_data.hpp @@ -35,9 +35,6 @@ public: CharacterData() = default; ~CharacterData() = default; - //accessors and mutators - //... - //database stuff int GetOwner(); std::string GetHandle(); diff --git a/server/characters/character_manager.cpp b/server/characters/character_manager.cpp index 2d7cf7c..300ee18 100644 --- a/server/characters/character_manager.cpp +++ b/server/characters/character_manager.cpp @@ -193,6 +193,7 @@ void CharacterManager::Unload(int uid) { } void CharacterManager::Delete(int uid) { + //TODO: when deleting a character, move it to an archive table //delete this character from the database, then remove it from memory sqlite3_stmt* statement = nullptr; @@ -252,6 +253,15 @@ CharacterData* CharacterManager::Get(int uid) { return &it->second; } +CharacterData* CharacterManager::Get(std::string handle) { + for (std::map::iterator it = elementMap.begin(); it != elementMap.end(); ++it) { + if (it->second.GetHandle() == handle) { + return &it->second; + } + } + return nullptr; +} + int CharacterManager::GetLoadedCount() { return elementMap.size(); } diff --git a/server/characters/character_manager.hpp b/server/characters/character_manager.hpp index 3a24c34..48cc54f 100644 --- a/server/characters/character_manager.hpp +++ b/server/characters/character_manager.hpp @@ -44,13 +44,18 @@ public: //accessors and mutators CharacterData* Get(int uid); + CharacterData* Get(std::string handle); int GetLoadedCount(); int GetTotalCount(); std::map* GetContainer(); + //API interface sqlite3* SetDatabase(sqlite3* db); sqlite3* GetDatabase(); + //hooks + //TODO: character hooks + private: friend Singleton; diff --git a/server/characters/character_manager_api.cpp b/server/characters/character_manager_api.cpp index b249ba9..eca3d6f 100644 --- a/server/characters/character_manager_api.cpp +++ b/server/characters/character_manager_api.cpp @@ -23,7 +23,70 @@ #include "character_manager.hpp" +static int setOnCreate(lua_State* L) { + //TODO: (9) empty +} + +static int setOnLoad(lua_State* L) { + //TODO: (9) empty +} + +static int setOnSave(lua_State* L) { + //TODO: (9) empty +} + +static int setOnUnload(lua_State* L) { + //TODO: (9) empty +} + +static int setOnDelete(lua_State* L) { + //TODO: (9) empty +} + +static int getCharacter(lua_State* L) { + //integer vs name + CharacterManager& characterMgr = CharacterManager::GetSingleton(); + CharacterData* characterData = nullptr; + + switch(lua_type(L, 1)) { + case LUA_TNUMBER: + characterData = characterMgr.Get(lua_tointeger(L, 1)); + break; + case LUA_TSTRING: + //access characters via their handles + characterData = characterMgr.Get(lua_tostring(L, 1)); + break; + } + + if (characterData) { + lua_pushlightuserdata(L, static_cast(characterData)); + } + else { + lua_pushnil(L); + } + + return 1; +} + +static int getLoadedCount(lua_State* L) { + CharacterManager& characterMgr = CharacterManager::GetSingleton(); + lua_pushinteger(L, characterMgr.GetLoadedCount()); + return 1; +} + +static int forEach(lua_State* L) { + //TODO: (1) find a way to update the clients when a script alters a character's data +} + static const luaL_Reg characterManagerLib[] = { +// {"SetOnCreate", setOnCreate}, +// {"SetOnLoad", setOnLoad}, +// {"SetOnSave", setOnSave}, +// {"SetOnUnload", setOnUnload}, +// {"SetOnDelete", setOnDelete}, + {"GetCharacter", getCharacter}, + {"GetLoadedCount", getLoadedCount}, +// {"ForEach", forEach}, {nullptr, nullptr} }; diff --git a/server/characters/character_system_api.cpp b/server/characters/character_system_api.cpp new file mode 100644 index 0000000..bac7893 --- /dev/null +++ b/server/characters/character_system_api.cpp @@ -0,0 +1,55 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 "character_system_api.hpp" + +//all character API headers +#include "character_api.hpp" +#include "character_manager_api.hpp" + +//useful "globals" +//... + +//This mimics linit.c to create a nested collection of all character modules. +static const luaL_Reg funcs[] = { + {nullptr, nullptr} +}; + +static const luaL_Reg libs[] = { + {"Character", openCharacterAPI}, + {"CharacterManager", openCharacterManagerAPI}, + {nullptr, nullptr} +}; + +int openCharacterSystemAPI(lua_State* L) { + //create the table + luaL_newlibtable(L, libs); + + //push the "global" functions + luaL_setfuncs(L, funcs, 0); + + //push the substable + for (const luaL_Reg* lib = libs; lib->func; lib++) { + lib->func(L); + lua_setfield(L, -2, lib->name); + } + return 1; +} diff --git a/server/characters/character_system_api.hpp b/server/characters/character_system_api.hpp new file mode 100644 index 0000000..67b47a7 --- /dev/null +++ b/server/characters/character_system_api.hpp @@ -0,0 +1,30 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 CHARACTERSYSTEMAPI_HPP_ +#define CHARACTERSYSTEMAPI_HPP_ + +#include "lua.hpp" + +#define TORTUGA_CHARACTER_SYSTEM_API "character_system" +LUAMOD_API int openCharacterSystemAPI(lua_State* L); + +#endif \ No newline at end of file diff --git a/server/clients/client_manager.cpp b/server/clients/client_manager.cpp index 488a742..80d6942 100644 --- a/server/clients/client_manager.cpp +++ b/server/clients/client_manager.cpp @@ -21,11 +21,15 @@ */ #include "client_manager.hpp" +#include "ip_operators.hpp" #include "udp_network_utility.hpp" #include -int ClientManager::CheckConnections() { +std::list ClientManager::CheckConnections() { + //list of clients to disconnect + std::list returnList; + for (auto& it : elementMap) { //3 seconds between beats if (ClientData::Clock::now() - it.second.GetLastBeat() > std::chrono::seconds(3)) { @@ -38,21 +42,17 @@ int ClientManager::CheckConnections() { for (auto& it : elementMap) { if (it.second.GetAttempts() > 2) { - int ret = it.first; -// elementMap.erase(it.first); - return ret; + returnList.push_back(it.first); } } - return -1; + return returnList; } void ClientManager::HandlePong(ServerPacket* const argPacket) { //find and update the specified client for (auto& it : elementMap) { - if (it.second.GetAddress().host == argPacket->srcAddress.host && - it.second.GetAddress().port == argPacket->srcAddress.port - ) { + if (it.second.GetAddress() == argPacket->srcAddress) { it.second.ResetAttempts(); return; } diff --git a/server/clients/client_manager.hpp b/server/clients/client_manager.hpp index 320499f..9dcec7c 100644 --- a/server/clients/client_manager.hpp +++ b/server/clients/client_manager.hpp @@ -29,12 +29,13 @@ #include "SDL/SDL_net.h" #include +#include #include class ClientManager: public Singleton { public: //methods - int CheckConnections(); + std::list CheckConnections(); void HandlePong(ServerPacket* const argPacket); //common public methods diff --git a/server/linit.cpp b/server/linit.cpp index b460290..c6bd870 100644 --- a/server/linit.cpp +++ b/server/linit.cpp @@ -37,8 +37,10 @@ #include "lua.hpp" #include "entity_api.hpp" +#include "character_system_api.hpp" #include "map_system_api.hpp" #include "monster_system_api.hpp" +#include "network_api.hpp" #include "room_system_api.hpp" #include "waypoint_system_api.hpp" @@ -58,12 +60,13 @@ static const luaL_Reg loadedlibs[] = { {NULL, NULL} }; - //these libs are preloaded and must be required before used static const luaL_Reg preloadedlibs[] = { - {TORTUGA_ENTITY_API, openEntityAPI}, + {TORTUGA_ENTITY_API, openEntityAPI}, //required by derived classes + {TORTUGA_CHARACTER_SYSTEM_API, openCharacterSystemAPI}, {TORTUGA_MAP_SYSTEM_API, openMapSystemAPI}, {TORTUGA_MONSTER_SYSTEM_API, openMonsterSystemAPI}, + {TORTUGA_NETWORK_API, openNetworkAPI}, {TORTUGA_ROOM_SYSTEM_API, openRoomSystemAPI}, {TORTUGA_WAYPOINT_SYSTEM_API, openWaypointSystemAPI}, {NULL, NULL} diff --git a/server/network_api.cpp b/server/network_api.cpp new file mode 100644 index 0000000..f78bd90 --- /dev/null +++ b/server/network_api.cpp @@ -0,0 +1,37 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 "network_api.hpp" + +static int pumpCharacterUpdate(lua_State* L) { + return 0; +} + +static const luaL_Reg networkLib[] = { + {"PumpCharacterUpdate", pumpCharacterUpdate}, + {nullptr, nullptr} +}; + +LUAMOD_API int openNetworkAPI(lua_State* L) { + luaL_newlib(L, networkLib); + return 1; + +} \ No newline at end of file diff --git a/server/network_api.hpp b/server/network_api.hpp new file mode 100644 index 0000000..3f6ec7d --- /dev/null +++ b/server/network_api.hpp @@ -0,0 +1,30 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 NETWORKAPI_HPP_ +#define NETWORKAPI_HPP_ + +#include "lua.hpp" + +#define TORTUGA_NETWORK_API "network" +LUAMOD_API int openNetworkAPI(lua_State* L); + +#endif \ No newline at end of file diff --git a/server/rooms/room_api.cpp b/server/rooms/room_api.cpp index ffb9c8a..cac2651 100644 --- a/server/rooms/room_api.cpp +++ b/server/rooms/room_api.cpp @@ -65,6 +65,15 @@ static int getWaypointMgr(lua_State* L) { return 1; } +//TODO: character list + +static int setOnTick(lua_State* L) { + RoomData* room = reinterpret_cast(lua_touserdata(L, 1)); + luaL_unref(L, LUA_REGISTRYINDEX, room->GetTickReference()); + room->SetTickReference(luaL_ref(L, LUA_REGISTRYINDEX)); + return 0; +} + static int initialize(lua_State* L) { RoomData* room = static_cast(lua_touserdata(L, 1)); @@ -88,6 +97,8 @@ static const luaL_Reg roomLib[] = { {"GetMonsterMgr",getMonsterMgr}, {"GetWaypointMgr",getWaypointMgr}, + {"SetOnTick", setOnTick}, + {"Initialize", initialize}, {nullptr, nullptr} }; diff --git a/server/rooms/room_data.cpp b/server/rooms/room_data.cpp index 815f73a..14cf328 100644 --- a/server/rooms/room_data.cpp +++ b/server/rooms/room_data.cpp @@ -21,6 +21,22 @@ */ #include "room_data.hpp" +void RoomData::RunFrame() { + //get the hook + lua_rawgeti(lua, LUA_REGISTRYINDEX, tickRef); + + if (lua_isnil(lua, -1)) { + lua_pop(lua, 1); + return; + } + + //call the tick function, with this as a parameter + lua_pushlightuserdata(lua, this); + if (lua_pcall(lua, 1, 0, 0) != LUA_OK) { + throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); + } +} + std::string RoomData::SetName(std::string s) { return roomName = s; } @@ -52,3 +68,33 @@ WaypointManager* RoomData::GetWaypointMgr() { std::list* RoomData::GetCharacterList() { return &characterList; } + +lua_State* RoomData::SetLuaState(lua_State* L) { + lua = L; + pager.SetLuaState(lua); + monsterMgr.SetLuaState(lua); + waypointMgr.SetLuaState(lua); + return lua; +} + +lua_State* RoomData::GetLuaState() { + return lua; +} + +sqlite3* RoomData::SetDatabase(sqlite3* db) { + database = db; + monsterMgr.SetDatabase(database); + return database; +} + +sqlite3* RoomData::GetDatabase() { + return database; +} + +int RoomData::SetTickReference(int i) { + return tickRef = i; +} + +int RoomData::GetTickReference() { + return tickRef; +} \ No newline at end of file diff --git a/server/rooms/room_data.hpp b/server/rooms/room_data.hpp index 83720b9..1933e68 100644 --- a/server/rooms/room_data.hpp +++ b/server/rooms/room_data.hpp @@ -37,6 +37,8 @@ public: RoomData() = default; ~RoomData() = default; + void RunFrame(); + //accessors and mutators std::string SetName(std::string); std::string GetName(); @@ -49,19 +51,34 @@ public: WaypointManager* GetWaypointMgr(); std::list* GetCharacterList(); - //TODO: triggers for unload, save, per-second, player enter, player exit, etc. + //API interfaces + lua_State* SetLuaState(lua_State* L); + lua_State* GetLuaState(); + sqlite3* SetDatabase(sqlite3* db); + sqlite3* GetDatabase(); + + //hooks + int SetTickReference(int i); + int GetTickReference(); + //TODO: other triggers like player entry & exit, etc. private: - friend class RoomManager; - - //members + //metadata std::string roomName; std::string tilesetName; + //members RegionPagerLua pager; MonsterManager monsterMgr; WaypointManager waypointMgr; std::list characterList; + + //API + lua_State* lua = nullptr; + sqlite3* database = nullptr; + + //hooks + int tickRef = LUA_NOREF; }; #endif diff --git a/server/rooms/room_manager.cpp b/server/rooms/room_manager.cpp index 1ba26ca..c974413 100644 --- a/server/rooms/room_manager.cpp +++ b/server/rooms/room_manager.cpp @@ -35,29 +35,79 @@ int RoomManager::Create(std::string roomName, std::string tileset) { newRoom->SetName(roomName); newRoom->SetTileset(tileset); - newRoom->pager.SetLuaState(lua); - newRoom->monsterMgr.SetLuaState(lua); - newRoom->monsterMgr.SetDatabase(database); - newRoom->waypointMgr.SetLuaState(lua); + newRoom->SetLuaState(lua); + newRoom->SetDatabase(database); + + //get the hook + lua_rawgeti(lua, LUA_REGISTRYINDEX, createRef); + + if(!lua_isnil(lua, -1)) { + lua_pushlightuserdata(lua, newRoom); + lua_pushinteger(lua, counter); + //call the function, 2 arg, 0 return + if (lua_pcall(lua, 2, 0, 0) != LUA_OK) { + throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); + } + } //finish the routine return counter++; } void RoomManager::UnloadAll() { + //get the hook + lua_rawgeti(lua, LUA_REGISTRYINDEX, unloadRef); + + if(!lua_isnil(lua, -1)) { + //pass each room to the hook + for (auto& it : elementMap) { + lua_pushvalue(lua, -1); //copy the hook + lua_pushlightuserdata(lua, &it.second); + lua_pushinteger(lua, it.first); + //call the function, 2 arg, 0 return + if (lua_pcall(lua, 2, 0, 0) != LUA_OK) { + throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); + } + } + } + + //pop the hook or nil + lua_pop(lua, 1); + elementMap.clear(); } void RoomManager::UnloadIf(std::function)> fn) { + //get the hook + lua_rawgeti(lua, LUA_REGISTRYINDEX, unloadRef); + + //get the element std::map::iterator it = elementMap.begin(); + + //jenky pattern while (it != elementMap.end()) { if (fn(*it)) { + + if(!lua_isnil(lua, -1)) { + lua_pushvalue(lua, -1); //copy the hook + lua_pushlightuserdata(lua, &it->second); + lua_pushinteger(lua, it->first); + + //call the function, 2 arg, 0 return + if (lua_pcall(lua, 2, 0, 0) != LUA_OK) { + throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); + } + } + it = elementMap.erase(it); } else { ++it; } } + + //pop the hook or nil + lua_pop(lua, 1); } void RoomManager::PushCharacter(CharacterData* character) { @@ -71,7 +121,7 @@ void RoomManager::PushCharacter(CharacterData* character) { throw(std::runtime_error("Failed to push an character to a non-existant room")); } - room->characterList.push_back(character); + room->GetCharacterList()->push_back(character); } void RoomManager::PopCharacter(CharacterData const* character) { @@ -86,7 +136,7 @@ void RoomManager::PopCharacter(CharacterData const* character) { throw(std::runtime_error("Failed to pop an character to a non-existant room")); } - room->characterList.remove_if([character](CharacterData* ptr) { + room->GetCharacterList()->remove_if([character](CharacterData* ptr) { return character == ptr; }); } @@ -119,7 +169,11 @@ std::map* RoomManager::GetContainer() { } lua_State* RoomManager::SetLuaState(lua_State* L) { - return lua = L; + lua = L; + for (auto& it : elementMap) { + it.second.SetLuaState(lua); + } + return lua; } lua_State* RoomManager::GetLuaState() { @@ -127,9 +181,29 @@ lua_State* RoomManager::GetLuaState() { } sqlite3* RoomManager::SetDatabase(sqlite3* db) { - return database = db; + database = db; + for (auto& it : elementMap) { + it.second.SetDatabase(database); + } + return database; } sqlite3* RoomManager::GetDatabase() { return database; } + +int RoomManager::SetCreateReference(int i) { + return createRef = i; +} + +int RoomManager::SetUnloadReference(int i) { + return unloadRef = i; +} + +int RoomManager::GetCreateReference() { + return createRef; +} + +int RoomManager::GetUnloadReference() { + return unloadRef; +} diff --git a/server/rooms/room_manager.hpp b/server/rooms/room_manager.hpp index 6e21382..90e7a49 100644 --- a/server/rooms/room_manager.hpp +++ b/server/rooms/room_manager.hpp @@ -49,12 +49,19 @@ public: int GetLoadedCount(); std::map* GetContainer(); - //hooks + //API interfaces lua_State* SetLuaState(lua_State* L); lua_State* GetLuaState(); sqlite3* SetDatabase(sqlite3* db); sqlite3* GetDatabase(); + //hooks + int SetCreateReference(int i); + int SetUnloadReference(int i); + + int GetCreateReference(); + int GetUnloadReference(); + private: friend Singleton; @@ -63,9 +70,15 @@ private: //members std::map elementMap; + int counter = 0; + + //API lua_State* lua = nullptr; sqlite3* database = nullptr; - int counter = 0; + + //hooks + int createRef = LUA_NOREF; + int unloadRef = LUA_NOREF; }; #endif diff --git a/server/rooms/room_manager_api.cpp b/server/rooms/room_manager_api.cpp index 1d4ba15..c0e88a6 100644 --- a/server/rooms/room_manager_api.cpp +++ b/server/rooms/room_manager_api.cpp @@ -96,10 +96,26 @@ int getRoom(lua_State* L) { return 1; } +static int setOnCreate(lua_State* L) { + RoomManager& roomMgr = RoomManager::GetSingleton(); + luaL_unref(L, LUA_REGISTRYINDEX, roomMgr.GetCreateReference()); + roomMgr.SetCreateReference(luaL_ref(L, LUA_REGISTRYINDEX)); + return 0; +} + +static int setOnUnload(lua_State* L) { + RoomManager& roomMgr = RoomManager::GetSingleton(); + luaL_unref(L, LUA_REGISTRYINDEX, roomMgr.GetUnloadReference()); + roomMgr.SetUnloadReference(luaL_ref(L, LUA_REGISTRYINDEX)); + return 0; +} + static const luaL_Reg roomManagerLib[] = { {"CreateRoom", createRoom}, {"UnloadRoom", unloadRoom}, {"GetRoom", getRoom}, + {"SetOnCreate", setOnCreate}, + {"SetOnUnload", setOnUnload}, {nullptr, nullptr} }; diff --git a/server/server_logic.cpp b/server/server_logic.cpp index 78d5114..32bb812 100644 --- a/server/server_logic.cpp +++ b/server/server_logic.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -161,9 +162,16 @@ void ServerApplication::Init(int argc, char* argv[]) { } void ServerApplication::Proc() { + //network buffer SerialPacket* packetBuffer = reinterpret_cast(new char[MAX_PACKET_SIZE]); memset(packetBuffer, 0, MAX_PACKET_SIZE); //zero the buffer + //time system + typedef std::chrono::steady_clock Clock; + + Clock::time_point simTime = Clock::now(); + Clock::time_point realTime; + while(running) { //suck in the waiting packets & process them while(network.Receive(packetBuffer)) { @@ -173,20 +181,33 @@ void ServerApplication::Proc() { catch(std::exception& e) { std::cerr << "HandlePacket Error: " << e.what() << std::endl; } - memset(packetBuffer, 0, MAX_PACKET_SIZE); //reset the buffer - } - //update the internals - //... - - //Check connections - int disconnected = clientMgr.CheckConnections(); - if (disconnected != -1) { - FullClientUnload(disconnected); - std::cerr << "Client dropped: " << disconnected << std::endl; + //reset the buffer + memset(packetBuffer, 0, MAX_PACKET_SIZE); } - //give the machine a break - SDL_Delay(10); + //Check client connections + std::list disconnections = clientMgr.CheckConnections(); + for(auto const& it : disconnections) { + FullClientUnload(it); + std::cerr << "Client dropped: " << it << std::endl; + } + + //"tick" the internals + realTime = Clock::now(); + + if (simTime < realTime) { + while(simTime < realTime) { + for (auto& it : *roomMgr.GetContainer()) { + it.second.RunFrame(); + } + //~60 FPS + simTime += std::chrono::duration(16); + } + } + else { + //give the machine a break + SDL_Delay(10); + } } delete reinterpret_cast(packetBuffer); } diff --git a/todo.txt b/todo.txt index eca1dfc..7f22396 100644 --- a/todo.txt +++ b/todo.txt @@ -1,3 +1,6 @@ +TODO: upgrade to lua 5.3 +TODO: Split config.cfg in two, one for the server and the client + TODO: In need of script APIs (list) * Characters @@ -9,7 +12,6 @@ TODO: Account passwords (list) TODO: Features * Make sure login errors are sent to the client - * Split config.cfg in two, one for the server and the client * Add the "home" parameter to the server's config file * Waypoints, with positions and trigger zones (collision areas) for doors, monster spawns, etc. * Fix shoddy movement