Merge branch 'develop'

Changes:

* "ticking" rooms
* character API
* bare-bones character manager API
* bounds checking in serial_utility.cpp

I have an idea for swapping the existing utility/singleton classes for
namesapces, as I think this would reduce the verbosity that I have to deal
with.
This commit is contained in:
Kayne Ruse
2015-02-23 01:54:46 +11:00
24 changed files with 620 additions and 157 deletions
+1 -1
View File
@@ -34,7 +34,7 @@
typedef SerialPacketBase SerialPacket; typedef SerialPacketBase SerialPacket;
//DOCS: NETWORK_VERSION is used to discern compatible servers and clients //DOCS: NETWORK_VERSION is used to discern compatible servers and clients
constexpr int NETWORK_VERSION = 20150214; constexpr int NETWORK_VERSION = 20150221;
union MaxPacket { union MaxPacket {
CharacterPacket a; CharacterPacket a;
+35 -1
View File
@@ -25,6 +25,7 @@
/* DOCS: The headers indicate what packet type is used for each message /* DOCS: The headers indicate what packet type is used for each message
* different messages under the same header will carry different amounts of * different messages under the same header will carry different amounts of
* valid data, but it will still be carried in that packet's format. * 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 { enum class SerialPacketType {
@@ -36,6 +37,8 @@ enum class SerialPacketType {
// name, player count, version // name, player count, version
//------------------------- //-------------------------
FORMAT_SERVER,
//heartbeat //heartbeat
PING, PING,
PONG, PONG,
@@ -44,11 +47,15 @@ enum class SerialPacketType {
BROADCAST_REQUEST, BROADCAST_REQUEST,
BROADCAST_RESPONSE, BROADCAST_RESPONSE,
FORMAT_END_SERVER,
//------------------------- //-------------------------
//ClientPacket //ClientPacket
// client index, account index, username // client index, account index, username
//------------------------- //-------------------------
FORMAT_CLIENT,
//Connecting to a server as a client //Connecting to a server as a client
JOIN_REQUEST, JOIN_REQUEST,
JOIN_RESPONSE, JOIN_RESPONSE,
@@ -69,15 +76,21 @@ enum class SerialPacketType {
//shut down the server //shut down the server
ADMIN_SHUTDOWN_REQUEST, ADMIN_SHUTDOWN_REQUEST,
FORMAT_END_CLIENT,
//------------------------- //-------------------------
//RegionPacket //RegionPacket
// room index, x, y, raw data // room index, x, y, raw data
//------------------------- //-------------------------
FORMAT_REGION,
//map data //map data
REGION_REQUEST, REGION_REQUEST,
REGION_CONTENT, REGION_CONTENT,
FORMAT_END_REGION,
//------------------------- //-------------------------
//CharacterPacket //CharacterPacket
// character index, // character index,
@@ -86,6 +99,11 @@ enum class SerialPacketType {
// room index, origin, motion // room index, origin, motion
//------------------------- //-------------------------
FORMAT_CHARACTER,
//full data update
CHARACTER_UPDATE,
//character management //character management
CHARACTER_CREATE, CHARACTER_CREATE,
CHARACTER_DELETE, CHARACTER_DELETE,
@@ -97,7 +115,7 @@ enum class SerialPacketType {
QUERY_CHARACTER_STATS, QUERY_CHARACTER_STATS,
QUERY_CHARACTER_LOCATION, QUERY_CHARACTER_LOCATION,
//set the info in the server //actions taken
CHARACTER_MOVEMENT, CHARACTER_MOVEMENT,
CHARACTER_ATTACK, CHARACTER_ATTACK,
CHARACTER_DAMAGE, CHARACTER_DAMAGE,
@@ -105,6 +123,8 @@ enum class SerialPacketType {
//admin control //admin control
// ADMIN_SET_CHARACTER_ORIGIN, // ADMIN_SET_CHARACTER_ORIGIN,
FORMAT_END_CHARACTER,
//------------------------- //-------------------------
//MonsterPacket //MonsterPacket
// monster index, // monster index,
@@ -113,22 +133,34 @@ enum class SerialPacketType {
// room index, origin, motion // room index, origin, motion
//------------------------- //-------------------------
FORMAT_MONSTER,
//full data update
MONSTER_UPDATE,
//character management
MONSTER_CREATE, MONSTER_CREATE,
MONSTER_DELETE, MONSTER_DELETE,
//find out info from the server
QUERY_MONSTER_EXISTS, QUERY_MONSTER_EXISTS,
QUERY_MONSTER_STATS, QUERY_MONSTER_STATS,
QUERY_MONSTER_LOCATION, QUERY_MONSTER_LOCATION,
//actions taken
MONSTER_MOVEMENT, MONSTER_MOVEMENT,
MONSTER_ATTACK, MONSTER_ATTACK,
MONSTER_DAMAGE, MONSTER_DAMAGE,
FORMAT_END_MONSTER,
//------------------------- //-------------------------
//TextPacket //TextPacket
// name, text // name, text
//------------------------- //-------------------------
FORMAT_TEXT,
//general speech //general speech
TEXT_BROADCAST, TEXT_BROADCAST,
TEXT_SPEECH, TEXT_SPEECH,
@@ -143,6 +175,8 @@ enum class SerialPacketType {
SHUTDOWN_REJECTION, SHUTDOWN_REJECTION,
QUERY_REJECTION, QUERY_REJECTION,
FORMAT_END_TEXT,
//------------------------- //-------------------------
//not used //not used
//------------------------- //-------------------------
+35 -102
View File
@@ -31,6 +31,9 @@
#include <cstring> #include <cstring>
//macros
#define BOUNDS(type, lower, upper) ((type) > (lower) && (type) < (upper))
//raw memory copy //raw memory copy
void serialCopy(void** buffer, void* data, int size) { void serialCopy(void** buffer, void* data, int size) {
memcpy(*buffer, data, size); memcpy(*buffer, data, size);
@@ -46,63 +49,28 @@ void deserialCopy(void** buffer, void* data, int size) {
//main switch functions //main switch functions
void serializePacket(void* buffer, SerialPacketBase* packet) { void serializePacket(void* buffer, SerialPacketBase* packet) {
switch(packet->type) { if (BOUNDS(packet->type, SerialPacketType::FORMAT_SERVER, SerialPacketType::FORMAT_END_SERVER)) {
case SerialPacketType::PING:
case SerialPacketType::PONG:
case SerialPacketType::BROADCAST_REQUEST:
case SerialPacketType::BROADCAST_RESPONSE:
serializeServer(buffer, static_cast<ServerPacket*>(packet)); serializeServer(buffer, static_cast<ServerPacket*>(packet));
break; }
case SerialPacketType::JOIN_REQUEST:
case SerialPacketType::JOIN_RESPONSE: if (BOUNDS(packet->type, SerialPacketType::FORMAT_CLIENT, SerialPacketType::FORMAT_END_CLIENT)) {
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<ClientPacket*>(packet)); serializeClient(buffer, static_cast<ClientPacket*>(packet));
break; }
case SerialPacketType::REGION_REQUEST:
case SerialPacketType::REGION_CONTENT: if (BOUNDS(packet->type, SerialPacketType::FORMAT_REGION, SerialPacketType::FORMAT_END_REGION)) {
serializeRegion(buffer, static_cast<RegionPacket*>(packet)); serializeRegion(buffer, static_cast<RegionPacket*>(packet));
break; }
case SerialPacketType::CHARACTER_CREATE:
case SerialPacketType::CHARACTER_DELETE: if (BOUNDS(packet->type, SerialPacketType::FORMAT_CHARACTER, SerialPacketType::FORMAT_END_CHARACTER)) {
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<CharacterPacket*>(packet)); serializeCharacter(buffer, static_cast<CharacterPacket*>(packet));
break; }
case SerialPacketType::MONSTER_CREATE:
case SerialPacketType::MONSTER_DELETE: if (BOUNDS(packet->type, SerialPacketType::FORMAT_MONSTER, SerialPacketType::FORMAT_END_MONSTER)) {
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<MonsterPacket*>(packet)); serializeMonster(buffer, static_cast<MonsterPacket*>(packet));
break; }
case SerialPacketType::TEXT_BROADCAST:
case SerialPacketType::TEXT_SPEECH: if (BOUNDS(packet->type, SerialPacketType::FORMAT_TEXT, SerialPacketType::FORMAT_END_TEXT)) {
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<TextPacket*>(packet)); serializeText(buffer, static_cast<TextPacket*>(packet));
break;
} }
} }
@@ -111,62 +79,27 @@ void deserializePacket(void* buffer, SerialPacketBase* packet) {
SerialPacketType type; SerialPacketType type;
memcpy(&type, buffer, sizeof(SerialPacketType)); memcpy(&type, buffer, sizeof(SerialPacketType));
switch(type) { if (BOUNDS(type, SerialPacketType::FORMAT_SERVER, SerialPacketType::FORMAT_END_SERVER)) {
case SerialPacketType::PING:
case SerialPacketType::PONG:
case SerialPacketType::BROADCAST_REQUEST:
case SerialPacketType::BROADCAST_RESPONSE:
deserializeServer(buffer, static_cast<ServerPacket*>(packet)); deserializeServer(buffer, static_cast<ServerPacket*>(packet));
break; }
case SerialPacketType::JOIN_REQUEST:
case SerialPacketType::JOIN_RESPONSE: if (BOUNDS(type, SerialPacketType::FORMAT_CLIENT, SerialPacketType::FORMAT_END_CLIENT)) {
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<ClientPacket*>(packet)); deserializeClient(buffer, static_cast<ClientPacket*>(packet));
break; }
case SerialPacketType::REGION_REQUEST:
case SerialPacketType::REGION_CONTENT: if (BOUNDS(type, SerialPacketType::FORMAT_REGION, SerialPacketType::FORMAT_END_REGION)) {
deserializeRegion(buffer, static_cast<RegionPacket*>(packet)); deserializeRegion(buffer, static_cast<RegionPacket*>(packet));
break; }
case SerialPacketType::CHARACTER_CREATE:
case SerialPacketType::CHARACTER_DELETE: if (BOUNDS(type, SerialPacketType::FORMAT_CHARACTER, SerialPacketType::FORMAT_END_CHARACTER)) {
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<CharacterPacket*>(packet)); deserializeCharacter(buffer, static_cast<CharacterPacket*>(packet));
break; }
case SerialPacketType::MONSTER_CREATE:
case SerialPacketType::MONSTER_DELETE: if (BOUNDS(type, SerialPacketType::FORMAT_MONSTER, SerialPacketType::FORMAT_END_MONSTER)) {
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<MonsterPacket*>(packet)); deserializeMonster(buffer, static_cast<MonsterPacket*>(packet));
break; }
case SerialPacketType::TEXT_BROADCAST:
case SerialPacketType::TEXT_SPEECH: if (BOUNDS(type, SerialPacketType::FORMAT_TEXT, SerialPacketType::FORMAT_END_TEXT)) {
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<TextPacket*>(packet)); deserializeText(buffer, static_cast<TextPacket*>(packet));
break;
} }
} }
+20
View File
@@ -3,6 +3,7 @@ print("Lua script check")
mapMaker = require("map_maker") mapMaker = require("map_maker")
mapSaver = require("map_saver") mapSaver = require("map_saver")
roomSystem = require("room_system") roomSystem = require("room_system")
characterSystem = require("character_system")
local function dumpTable(t) local function dumpTable(t)
print(t) print(t)
@@ -11,6 +12,25 @@ local function dumpTable(t)
end end
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 --NOTE: room 0 is the first that the client asks for, therefore it must exist
local overworld, uid = roomSystem.RoomManager.CreateRoom("overworld", "overworld.bmp") local overworld, uid = roomSystem.RoomManager.CreateRoom("overworld", "overworld.bmp")
roomSystem.Room.Initialize(overworld, mapSaver.Load, mapSaver.Save, mapMaker.DebugIsland, mapSaver.Save) roomSystem.Room.Initialize(overworld, mapSaver.Load, mapSaver.Save, mapMaker.DebugIsland, mapSaver.Save)
+45
View File
@@ -23,11 +23,56 @@
#include "character_data.hpp" #include "character_data.hpp"
#include "entity_api.hpp"
static int getOwner(lua_State* L) {
CharacterData* character = static_cast<CharacterData*>(lua_touserdata(L, 1));
lua_pushinteger(L, character->GetOwner());
return 1;
}
static int getHandle(lua_State* L) {
CharacterData* character = static_cast<CharacterData*>(lua_touserdata(L, 1));
lua_pushstring(L, character->GetHandle().c_str());
return 1;
}
static int getAvatar(lua_State* L) {
CharacterData* character = static_cast<CharacterData*>(lua_touserdata(L, 1));
lua_pushstring(L, character->GetAvatar().c_str());
return 1;
}
static const luaL_Reg characterLib[] = { static const luaL_Reg characterLib[] = {
// {"GetOwner", getOwner}, //unusable without account API
{"GetHandle", getHandle},
{"GetAvatar", getAvatar},
{nullptr, nullptr} {nullptr, nullptr}
}; };
LUAMOD_API int openCharacterAPI(lua_State* L) { LUAMOD_API int openCharacterAPI(lua_State* L) {
//the local table
luaL_newlib(L, characterLib); 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; return 1;
} }
-3
View File
@@ -35,9 +35,6 @@ public:
CharacterData() = default; CharacterData() = default;
~CharacterData() = default; ~CharacterData() = default;
//accessors and mutators
//...
//database stuff //database stuff
int GetOwner(); int GetOwner();
std::string GetHandle(); std::string GetHandle();
+10
View File
@@ -193,6 +193,7 @@ void CharacterManager::Unload(int uid) {
} }
void CharacterManager::Delete(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 //delete this character from the database, then remove it from memory
sqlite3_stmt* statement = nullptr; sqlite3_stmt* statement = nullptr;
@@ -252,6 +253,15 @@ CharacterData* CharacterManager::Get(int uid) {
return &it->second; return &it->second;
} }
CharacterData* CharacterManager::Get(std::string handle) {
for (std::map<int, CharacterData>::iterator it = elementMap.begin(); it != elementMap.end(); ++it) {
if (it->second.GetHandle() == handle) {
return &it->second;
}
}
return nullptr;
}
int CharacterManager::GetLoadedCount() { int CharacterManager::GetLoadedCount() {
return elementMap.size(); return elementMap.size();
} }
+5
View File
@@ -44,13 +44,18 @@ public:
//accessors and mutators //accessors and mutators
CharacterData* Get(int uid); CharacterData* Get(int uid);
CharacterData* Get(std::string handle);
int GetLoadedCount(); int GetLoadedCount();
int GetTotalCount(); int GetTotalCount();
std::map<int, CharacterData>* GetContainer(); std::map<int, CharacterData>* GetContainer();
//API interface
sqlite3* SetDatabase(sqlite3* db); sqlite3* SetDatabase(sqlite3* db);
sqlite3* GetDatabase(); sqlite3* GetDatabase();
//hooks
//TODO: character hooks
private: private:
friend Singleton<CharacterManager>; friend Singleton<CharacterManager>;
@@ -23,7 +23,70 @@
#include "character_manager.hpp" #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<void*>(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[] = { static const luaL_Reg characterManagerLib[] = {
// {"SetOnCreate", setOnCreate},
// {"SetOnLoad", setOnLoad},
// {"SetOnSave", setOnSave},
// {"SetOnUnload", setOnUnload},
// {"SetOnDelete", setOnDelete},
{"GetCharacter", getCharacter},
{"GetLoadedCount", getLoadedCount},
// {"ForEach", forEach},
{nullptr, nullptr} {nullptr, nullptr}
}; };
@@ -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;
}
@@ -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
+8 -8
View File
@@ -21,11 +21,15 @@
*/ */
#include "client_manager.hpp" #include "client_manager.hpp"
#include "ip_operators.hpp"
#include "udp_network_utility.hpp" #include "udp_network_utility.hpp"
#include <chrono> #include <chrono>
int ClientManager::CheckConnections() { std::list<int> ClientManager::CheckConnections() {
//list of clients to disconnect
std::list<int> returnList;
for (auto& it : elementMap) { for (auto& it : elementMap) {
//3 seconds between beats //3 seconds between beats
if (ClientData::Clock::now() - it.second.GetLastBeat() > std::chrono::seconds(3)) { if (ClientData::Clock::now() - it.second.GetLastBeat() > std::chrono::seconds(3)) {
@@ -38,21 +42,17 @@ int ClientManager::CheckConnections() {
for (auto& it : elementMap) { for (auto& it : elementMap) {
if (it.second.GetAttempts() > 2) { if (it.second.GetAttempts() > 2) {
int ret = it.first; returnList.push_back(it.first);
// elementMap.erase(it.first);
return ret;
} }
} }
return -1; return returnList;
} }
void ClientManager::HandlePong(ServerPacket* const argPacket) { void ClientManager::HandlePong(ServerPacket* const argPacket) {
//find and update the specified client //find and update the specified client
for (auto& it : elementMap) { for (auto& it : elementMap) {
if (it.second.GetAddress().host == argPacket->srcAddress.host && if (it.second.GetAddress() == argPacket->srcAddress) {
it.second.GetAddress().port == argPacket->srcAddress.port
) {
it.second.ResetAttempts(); it.second.ResetAttempts();
return; return;
} }
+2 -1
View File
@@ -29,12 +29,13 @@
#include "SDL/SDL_net.h" #include "SDL/SDL_net.h"
#include <functional> #include <functional>
#include <list>
#include <map> #include <map>
class ClientManager: public Singleton<ClientManager> { class ClientManager: public Singleton<ClientManager> {
public: public:
//methods //methods
int CheckConnections(); std::list<int> CheckConnections();
void HandlePong(ServerPacket* const argPacket); void HandlePong(ServerPacket* const argPacket);
//common public methods //common public methods
+5 -2
View File
@@ -37,8 +37,10 @@
#include "lua.hpp" #include "lua.hpp"
#include "entity_api.hpp" #include "entity_api.hpp"
#include "character_system_api.hpp"
#include "map_system_api.hpp" #include "map_system_api.hpp"
#include "monster_system_api.hpp" #include "monster_system_api.hpp"
#include "network_api.hpp"
#include "room_system_api.hpp" #include "room_system_api.hpp"
#include "waypoint_system_api.hpp" #include "waypoint_system_api.hpp"
@@ -58,12 +60,13 @@ static const luaL_Reg loadedlibs[] = {
{NULL, NULL} {NULL, NULL}
}; };
//these libs are preloaded and must be required before used //these libs are preloaded and must be required before used
static const luaL_Reg preloadedlibs[] = { 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_MAP_SYSTEM_API, openMapSystemAPI},
{TORTUGA_MONSTER_SYSTEM_API, openMonsterSystemAPI}, {TORTUGA_MONSTER_SYSTEM_API, openMonsterSystemAPI},
{TORTUGA_NETWORK_API, openNetworkAPI},
{TORTUGA_ROOM_SYSTEM_API, openRoomSystemAPI}, {TORTUGA_ROOM_SYSTEM_API, openRoomSystemAPI},
{TORTUGA_WAYPOINT_SYSTEM_API, openWaypointSystemAPI}, {TORTUGA_WAYPOINT_SYSTEM_API, openWaypointSystemAPI},
{NULL, NULL} {NULL, NULL}
+37
View File
@@ -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;
}
+30
View File
@@ -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
+11
View File
@@ -65,6 +65,15 @@ static int getWaypointMgr(lua_State* L) {
return 1; return 1;
} }
//TODO: character list
static int setOnTick(lua_State* L) {
RoomData* room = reinterpret_cast<RoomData*>(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) { static int initialize(lua_State* L) {
RoomData* room = static_cast<RoomData*>(lua_touserdata(L, 1)); RoomData* room = static_cast<RoomData*>(lua_touserdata(L, 1));
@@ -88,6 +97,8 @@ static const luaL_Reg roomLib[] = {
{"GetMonsterMgr",getMonsterMgr}, {"GetMonsterMgr",getMonsterMgr},
{"GetWaypointMgr",getWaypointMgr}, {"GetWaypointMgr",getWaypointMgr},
{"SetOnTick", setOnTick},
{"Initialize", initialize}, {"Initialize", initialize},
{nullptr, nullptr} {nullptr, nullptr}
}; };
+46
View File
@@ -21,6 +21,22 @@
*/ */
#include "room_data.hpp" #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) { std::string RoomData::SetName(std::string s) {
return roomName = s; return roomName = s;
} }
@@ -52,3 +68,33 @@ WaypointManager* RoomData::GetWaypointMgr() {
std::list<CharacterData*>* RoomData::GetCharacterList() { std::list<CharacterData*>* RoomData::GetCharacterList() {
return &characterList; 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;
}
+21 -4
View File
@@ -37,6 +37,8 @@ public:
RoomData() = default; RoomData() = default;
~RoomData() = default; ~RoomData() = default;
void RunFrame();
//accessors and mutators //accessors and mutators
std::string SetName(std::string); std::string SetName(std::string);
std::string GetName(); std::string GetName();
@@ -49,19 +51,34 @@ public:
WaypointManager* GetWaypointMgr(); WaypointManager* GetWaypointMgr();
std::list<CharacterData*>* GetCharacterList(); std::list<CharacterData*>* 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: private:
friend class RoomManager; //metadata
//members
std::string roomName; std::string roomName;
std::string tilesetName; std::string tilesetName;
//members
RegionPagerLua pager; RegionPagerLua pager;
MonsterManager monsterMgr; MonsterManager monsterMgr;
WaypointManager waypointMgr; WaypointManager waypointMgr;
std::list<CharacterData*> characterList; std::list<CharacterData*> characterList;
//API
lua_State* lua = nullptr;
sqlite3* database = nullptr;
//hooks
int tickRef = LUA_NOREF;
}; };
#endif #endif
+82 -8
View File
@@ -35,29 +35,79 @@ int RoomManager::Create(std::string roomName, std::string tileset) {
newRoom->SetName(roomName); newRoom->SetName(roomName);
newRoom->SetTileset(tileset); newRoom->SetTileset(tileset);
newRoom->pager.SetLuaState(lua); newRoom->SetLuaState(lua);
newRoom->monsterMgr.SetLuaState(lua); newRoom->SetDatabase(database);
newRoom->monsterMgr.SetDatabase(database);
newRoom->waypointMgr.SetLuaState(lua); //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 //finish the routine
return counter++; return counter++;
} }
void RoomManager::UnloadAll() { 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(); elementMap.clear();
} }
void RoomManager::UnloadIf(std::function<bool(std::pair<const int, RoomData const&>)> fn) { void RoomManager::UnloadIf(std::function<bool(std::pair<const int, RoomData const&>)> fn) {
//get the hook
lua_rawgeti(lua, LUA_REGISTRYINDEX, unloadRef);
//get the element
std::map<int, RoomData>::iterator it = elementMap.begin(); std::map<int, RoomData>::iterator it = elementMap.begin();
//jenky pattern
while (it != elementMap.end()) { while (it != elementMap.end()) {
if (fn(*it)) { 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); it = elementMap.erase(it);
} }
else { else {
++it; ++it;
} }
} }
//pop the hook or nil
lua_pop(lua, 1);
} }
void RoomManager::PushCharacter(CharacterData* character) { 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")); 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) { 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")); 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; return character == ptr;
}); });
} }
@@ -119,7 +169,11 @@ std::map<int, RoomData>* RoomManager::GetContainer() {
} }
lua_State* RoomManager::SetLuaState(lua_State* L) { 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() { lua_State* RoomManager::GetLuaState() {
@@ -127,9 +181,29 @@ lua_State* RoomManager::GetLuaState() {
} }
sqlite3* RoomManager::SetDatabase(sqlite3* db) { sqlite3* RoomManager::SetDatabase(sqlite3* db) {
return database = db; database = db;
for (auto& it : elementMap) {
it.second.SetDatabase(database);
}
return database;
} }
sqlite3* RoomManager::GetDatabase() { sqlite3* RoomManager::GetDatabase() {
return database; 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;
}
+15 -2
View File
@@ -49,12 +49,19 @@ public:
int GetLoadedCount(); int GetLoadedCount();
std::map<int, RoomData>* GetContainer(); std::map<int, RoomData>* GetContainer();
//hooks //API interfaces
lua_State* SetLuaState(lua_State* L); lua_State* SetLuaState(lua_State* L);
lua_State* GetLuaState(); lua_State* GetLuaState();
sqlite3* SetDatabase(sqlite3* db); sqlite3* SetDatabase(sqlite3* db);
sqlite3* GetDatabase(); sqlite3* GetDatabase();
//hooks
int SetCreateReference(int i);
int SetUnloadReference(int i);
int GetCreateReference();
int GetUnloadReference();
private: private:
friend Singleton<RoomManager>; friend Singleton<RoomManager>;
@@ -63,9 +70,15 @@ private:
//members //members
std::map<int, RoomData> elementMap; std::map<int, RoomData> elementMap;
int counter = 0;
//API
lua_State* lua = nullptr; lua_State* lua = nullptr;
sqlite3* database = nullptr; sqlite3* database = nullptr;
int counter = 0;
//hooks
int createRef = LUA_NOREF;
int unloadRef = LUA_NOREF;
}; };
#endif #endif
+16
View File
@@ -96,10 +96,26 @@ int getRoom(lua_State* L) {
return 1; 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[] = { static const luaL_Reg roomManagerLib[] = {
{"CreateRoom", createRoom}, {"CreateRoom", createRoom},
{"UnloadRoom", unloadRoom}, {"UnloadRoom", unloadRoom},
{"GetRoom", getRoom}, {"GetRoom", getRoom},
{"SetOnCreate", setOnCreate},
{"SetOnUnload", setOnUnload},
{nullptr, nullptr} {nullptr, nullptr}
}; };
+31 -10
View File
@@ -28,6 +28,7 @@
#include <stdexcept> #include <stdexcept>
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <list>
#include <sstream> #include <sstream>
#include <string> #include <string>
@@ -161,9 +162,16 @@ void ServerApplication::Init(int argc, char* argv[]) {
} }
void ServerApplication::Proc() { void ServerApplication::Proc() {
//network buffer
SerialPacket* packetBuffer = reinterpret_cast<SerialPacket*>(new char[MAX_PACKET_SIZE]); SerialPacket* packetBuffer = reinterpret_cast<SerialPacket*>(new char[MAX_PACKET_SIZE]);
memset(packetBuffer, 0, MAX_PACKET_SIZE); //zero the buffer 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) { while(running) {
//suck in the waiting packets & process them //suck in the waiting packets & process them
while(network.Receive(packetBuffer)) { while(network.Receive(packetBuffer)) {
@@ -173,21 +181,34 @@ void ServerApplication::Proc() {
catch(std::exception& e) { catch(std::exception& e) {
std::cerr << "HandlePacket Error: " << e.what() << std::endl; std::cerr << "HandlePacket Error: " << e.what() << std::endl;
} }
memset(packetBuffer, 0, MAX_PACKET_SIZE); //reset the buffer //reset the buffer
} memset(packetBuffer, 0, MAX_PACKET_SIZE);
//update the internals
//...
//Check connections
int disconnected = clientMgr.CheckConnections();
if (disconnected != -1) {
FullClientUnload(disconnected);
std::cerr << "Client dropped: " << disconnected << std::endl;
} }
//Check client connections
std::list<int> 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<int, std::milli>(16);
}
}
else {
//give the machine a break //give the machine a break
SDL_Delay(10); SDL_Delay(10);
} }
}
delete reinterpret_cast<char*>(packetBuffer); delete reinterpret_cast<char*>(packetBuffer);
} }
+3 -1
View File
@@ -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) TODO: In need of script APIs (list)
* Characters * Characters
@@ -9,7 +12,6 @@ TODO: Account passwords (list)
TODO: Features TODO: Features
* Make sure login errors are sent to the client * 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 * Add the "home" parameter to the server's config file
* Waypoints, with positions and trigger zones (collision areas) for doors, monster spawns, etc. * Waypoints, with positions and trigger zones (collision areas) for doors, monster spawns, etc.
* Fix shoddy movement * Fix shoddy movement