diff --git a/common/bbox.hpp b/common/bbox.hpp new file mode 100644 index 0000000..5b29944 --- /dev/null +++ b/common/bbox.hpp @@ -0,0 +1,75 @@ +/* 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. +*/ +#ifndef BBOX_HPP_ +#define BBOX_HPP_ + +#include +#include +#include + +//TODO: This is supposed to interact with the vector +class BBox { +public: + double x, y; + double w, h; + + BBox() = default; + BBox(double i, double j, double k, double l): x(i), y(j), w(k), h(l) {}; + ~BBox() = default; + BBox& operator=(BBox const&) = default; + + double Size() { + return std::max(w*h,0.0); + } + + bool IsCollision(BBox rhs) { + return not ( + x >= rhs.x + rhs.w || + y >= rhs.y + rhs.h || + rhs.x >= x + w || + rhs.y >= y + h + ); + } + + BBox Intersection(BBox rhs) { + if (!IsCollision(rhs)) { + return {0, 0, 0, 0}; + } + BBox ret; + ret.x = std::max(x, rhs.x); + ret.y = std::max(y, rhs.y); + ret.w = std::min(x+w, rhs.x+rhs.w) - ret.x; + ret.h = std::min(y+h, rhs.y+rhs.h) - ret.y; + return ret; + } + + double operator[](size_t i) { + if (i >= 4) + throw(std::domain_error("Out of range")); + return *(&x+i); + } +}; + +//This is explicitly a POD +static_assert(std::is_pod::value, "BBox is not a POD"); + +#endif \ No newline at end of file diff --git a/common/network/network_packet.hpp b/common/network/network_packet.hpp index f9b4894..4933591 100644 --- a/common/network/network_packet.hpp +++ b/common/network/network_packet.hpp @@ -31,6 +31,7 @@ #pragma pack(push, 0) +//TODO: update the code here to match the entity code union NetworkPacket { //types of packets enum class Type { diff --git a/common/vector2.hpp b/common/vector2.hpp index a7a721d..c898c02 100644 --- a/common/vector2.hpp +++ b/common/vector2.hpp @@ -22,6 +22,7 @@ #ifndef VECTOR2_HPP_ #define VECTOR2_HPP_ +#include #include #include @@ -29,7 +30,6 @@ class Vector2 { public: double x, y; - //This is explicitly a POD Vector2() = default; Vector2(double i, double j): x(i), y(j) {}; ~Vector2() = default; @@ -112,4 +112,7 @@ template Vector2 operator/(T t, Vector2 v) { return v / t; } template bool operator==(T t, Vector2 v) { return v == t; } template bool operator!=(T t, Vector2 v) { return v != t; } +//This is explicitly a POD +static_assert(std::is_pod::value, "Vector2 is not a POD"); + #endif diff --git a/server/client.cpp b/server/client.cpp new file mode 100644 index 0000000..7321482 --- /dev/null +++ b/server/client.cpp @@ -0,0 +1,29 @@ +/* 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 "client.hpp" + +#include + +//This is explicitly a POD +static_assert(std::is_pod::value, "Client is not a POD"); + +unsigned int Client::uidCounter; diff --git a/server/client.hpp b/server/client.hpp new file mode 100644 index 0000000..cf4105e --- /dev/null +++ b/server/client.hpp @@ -0,0 +1,32 @@ +/* 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 CLIENT_HPP_ +#define CLIENT_HPP_ + +#include "SDL/SDL_net.h" + +struct Client { + IPaddress address; + static unsigned int uidCounter; +}; + +#endif diff --git a/server/entity.cpp b/server/entity.cpp new file mode 100644 index 0000000..bfc6061 --- /dev/null +++ b/server/entity.cpp @@ -0,0 +1,29 @@ +/* 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 "entity.hpp" + +#include + +//This is explicitly a POD +static_assert(std::is_pod::value, "Entity is not a POD"); + +unsigned int Entity::uidCounter; diff --git a/server/entity.hpp b/server/entity.hpp new file mode 100644 index 0000000..59296ba --- /dev/null +++ b/server/entity.hpp @@ -0,0 +1,47 @@ +/* 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 ENTITY_HPP_ +#define ENTITY_HPP_ + +//POD members +#include "vector2.hpp" +#include "bbox.hpp" + +struct Entity { + enum Type { + PLAYER, + PORTAL, + ITEMS, + CHEST, + DOOR, + }; + + Type type; + int mapIndex; + Vector2 position; + Vector2 motion; + BBox bbox; + unsigned int externalID; + static unsigned int uidCounter; +}; + +#endif diff --git a/server/player_entity.cpp b/server/player_entity.cpp new file mode 100644 index 0000000..ccc49b3 --- /dev/null +++ b/server/player_entity.cpp @@ -0,0 +1,24 @@ +/* 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 "player_entity.hpp" + +unsigned int PlayerEntity::uidCounter; diff --git a/server/player_entity.hpp b/server/player_entity.hpp new file mode 100644 index 0000000..2a5a6e6 --- /dev/null +++ b/server/player_entity.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 PLAYERENTITY_HPP_ +#define PLAYERENTITY_HPP_ + +#include + +struct PlayerEntity { + //metadata + int clientIndex; + std::string handle; + std::string avatar; + + //statistics + int level; + int exp; + int maxHP; + int health; + int maxMP; + int mana; + int attack; + int defence; + int intelligence; + int resistance; + float accuracy; + float evasion; + float luck; + + //uid + static unsigned int uidCounter; +}; + +#endif diff --git a/server/server_application.cpp b/server/server_application.cpp index 890809d..f90fffe 100644 --- a/server/server_application.cpp +++ b/server/server_application.cpp @@ -51,9 +51,11 @@ int runSQLScript(sqlite3* db, std::string fname) { void ServerApplication::Init(int argc, char** argv) { cout << "Beginning startup" << endl; - int ret = 0; - //load config + //initial setup + Client::uidCounter = 0; + Entity::uidCounter = 0; + PlayerEntity::uidCounter = 0; config.Load("rsc\\config.cfg"); //Init SDL @@ -70,7 +72,7 @@ void ServerApplication::Init(int argc, char** argv) { cout << "Initialized SDL_net" << endl; //Init SQL - ret = sqlite3_open_v2(config["server.dbname"].c_str(), &database, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, nullptr); + int ret = sqlite3_open_v2(config["server.dbname"].c_str(), &database, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, nullptr); if (ret != SQLITE_OK || !database) { throw(runtime_error(string() + "Failed to initialize SQL: " + sqlite3_errmsg(database) )); } @@ -97,12 +99,13 @@ void ServerApplication::Init(int argc, char** argv) { cout << "Initialized lua's setup script" << endl; //setup the map object - mapPager.SetRegionWidth(REGION_WIDTH); - mapPager.SetRegionHeight(REGION_HEIGHT); - mapPager.SetRegionDepth(REGION_DEPTH); - mapPager.GetGenerator()->SetLuaState(luaState); - mapPager.GetFormat()->SetLuaState(luaState); - mapPager.GetFormat()->SetSaveDir("save/mapname/"); + regionPager.SetRegionWidth(REGION_WIDTH); + regionPager.SetRegionHeight(REGION_HEIGHT); + regionPager.SetRegionDepth(REGION_DEPTH); + regionPager.GetGenerator()->SetLuaState(luaState); + regionPager.GetFormat()->SetLuaState(luaState); + //TODO: config parameter + regionPager.GetFormat()->SetSaveDir("save/mapname/"); //TODO: pass args to the generator & format as needed //NOTE: I might need to rearrange the init process so that lua & SQL can interact // with the map system as needed. @@ -130,6 +133,7 @@ void ServerApplication::Loop() { HandlePacket(packet); } //give the computer a break + //TODO: remove this? SDL_Delay(10); } } @@ -137,9 +141,7 @@ void ServerApplication::Loop() { void ServerApplication::Quit() { cout << "Shutting down" << endl; //empty the members - mapPager.UnloadAll(); - //TODO: player manager - //TODO: client manager + regionPager.UnloadAll(); //APIs lua_close(luaState); @@ -207,44 +209,54 @@ void ServerApplication::HandleBroadcastRequest(NetworkPacket packet) { void ServerApplication::HandleJoinRequest(NetworkPacket packet) { //register the new client - ClientEntry c; - c.address = packet.meta.srcAddress; - clientMap[clientCounter] = c; + clientMap[Client::uidCounter] = {packet.meta.srcAddress}; - //send the client their info + //send the client their index char buffer[PACKET_BUFFER_SIZE]; - packet.meta.type = NetworkPacket::Type::JOIN_RESPONSE; - packet.clientInfo.index = clientCounter; + packet.clientInfo.index = Client::uidCounter; serialize(&packet, buffer); - network.Send(&clientMap[clientCounter].address, buffer, PACKET_BUFFER_SIZE); + //bounce this packet + network.Send(&packet.meta.srcAddress, buffer, PACKET_BUFFER_SIZE); //finished this routine - clientCounter++; + Client::uidCounter++; cout << "Connect, total: " << clientMap.size() << endl; } void ServerApplication::HandleDisconnect(NetworkPacket packet) { - //disconnect the specified client //TODO: authenticate who is disconnecting/kicking + + //disconnect the specified client char buffer[PACKET_BUFFER_SIZE]; serialize(&packet, buffer); network.Send(&clientMap[packet.clientInfo.index].address, buffer, PACKET_BUFFER_SIZE); clientMap.erase(packet.clientInfo.index); - //delete players from all clients + //prep the delete packet NetworkPacket delPacket; delPacket.meta.type = NetworkPacket::Type::PLAYER_DELETE; - erase_if(playerMap, [&](std::pair it) -> bool { + //TODO: can this use DeletePlayer() instead? + //delete PlayerEntity, Entity, and client side players + erase_if(playerMap, [&](std::pair playerIter) -> bool { //find the internal players to delete - if (it.second.clientIndex == packet.clientInfo.index) { - delPacket.playerInfo.playerIndex = it.first; + if (playerIter.second.clientIndex == packet.clientInfo.index) { //send the delete player command to all clients + delPacket.playerInfo.playerIndex = playerIter.first; PumpPacket(delPacket); + + //erase the corresponding Entity + erase_if(entityMap, [&](std::pair entityIter) -> bool { + return entityIter.second.type == Entity::Type::PLAYER && entityIter.second.externalID == playerIter.first; + }); + + //delete this player object return true; } + + //don't delete this player object return false; }); @@ -253,19 +265,40 @@ void ServerApplication::HandleDisconnect(NetworkPacket packet) { } void ServerApplication::HandleSynchronize(NetworkPacket packet) { - //send all the server's data to this client //TODO: compensate for large distances + + //send all the server's data to this client NetworkPacket newPacket; char buffer[PACKET_BUFFER_SIZE]; - //players - newPacket.meta.type = NetworkPacket::Type::PLAYER_UPDATE; - for (auto& it : playerMap) { - newPacket.playerInfo.playerIndex = it.first; - snprintf(newPacket.playerInfo.handle, PACKET_STRING_SIZE, "%s", it.second.handle.c_str()); - snprintf(newPacket.playerInfo.avatar, PACKET_STRING_SIZE, "%s", it.second.avatar.c_str()); - newPacket.playerInfo.position = it.second.position; - newPacket.playerInfo.motion = it.second.motion; + //TODO: map? + + //entities + for (auto& it : entityMap) { + //what are we sending? + switch(it.second.type) { + case Entity::Type::PLAYER: + //TODO: update the network code to match the entity code + newPacket.meta.type = NetworkPacket::Type::PLAYER_UPDATE; + newPacket.playerInfo.playerIndex = it.first; + snprintf(newPacket.playerInfo.handle, PACKET_STRING_SIZE, "%s", playerMap[it.second.externalID].handle.c_str()); + snprintf(newPacket.playerInfo.avatar, PACKET_STRING_SIZE, "%s", playerMap[it.second.externalID].avatar.c_str()); + newPacket.playerInfo.position = it.second.position; + newPacket.playerInfo.motion = it.second.motion; + break; + case Entity::Type::PORTAL: + //TODO + break; + case Entity::Type::ITEMS: + //TODO + break; + case Entity::Type::CHEST: + //TODO + break; + case Entity::Type::DOOR: + //TODO + break; + } serialize(&newPacket, buffer); network.Send(&clientMap[packet.clientInfo.index].address, buffer, PACKET_BUFFER_SIZE); } @@ -284,47 +317,70 @@ void ServerApplication::HandleShutdown(NetworkPacket packet) { } void ServerApplication::HandlePlayerNew(NetworkPacket packet) { - //create the new player object - PlayerEntry newPlayer; - newPlayer.clientIndex = packet.playerInfo.clientIndex; - newPlayer.mapIndex = 0; - newPlayer.handle = packet.playerInfo.handle; - newPlayer.avatar = packet.playerInfo.avatar; - newPlayer.position = {0,0}; - newPlayer.motion = {0,0}; + //register the new Entity + entityMap[Entity::uidCounter] = { + Entity::Type::PLAYER, + 0, + {0, 0}, + {0, 0}, + {0, 0, 0, 0}, + PlayerEntity::uidCounter + }; - //push this player - playerMap[playerCounter] = newPlayer; + //register the new PlayerEntity + playerMap[PlayerEntity::uidCounter] = { + packet.playerInfo.clientIndex, + packet.playerInfo.handle, + packet.playerInfo.avatar, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0.0, + 0.0, + 0.0 + }; //send the client their info - packet.playerInfo.playerIndex = playerCounter; - packet.playerInfo.position = playerMap[playerCounter].position; - packet.playerInfo.motion = playerMap[playerCounter].motion; + packet.playerInfo.playerIndex = PlayerEntity::uidCounter; + packet.playerInfo.position = entityMap[Entity::uidCounter].position; + packet.playerInfo.motion = entityMap[Entity::uidCounter].motion; //actually send to everyone PumpPacket(packet); //finish this routine - playerCounter++; + Entity::uidCounter++; + PlayerEntity::uidCounter++; } void ServerApplication::HandlePlayerDelete(NetworkPacket packet) { - if (playerMap.find(packet.playerInfo.playerIndex) == playerMap.end()) { + //TODO: remove this? + if (entityMap.find(packet.playerInfo.playerIndex) == entityMap.end()) { throw(std::runtime_error("Cannot delete a non-existant player")); } - //delete players - erase_if(playerMap, [&](pair it) -> bool { - if (it.first == packet.playerInfo.playerIndex) { - NetworkPacket delPacket; - - //data to delete one specific player - delPacket.meta.type = NetworkPacket::Type::PLAYER_DELETE; - delPacket.playerInfo.playerIndex = it.first; + //prep the delete packet + NetworkPacket delPacket; + delPacket.meta.type = NetworkPacket::Type::PLAYER_DELETE; + //delete the specified Entity, PlayerEntity + erase_if(entityMap, [&](std::pair entityIter) -> bool { + //find the specified Entity + if (entityIter.first == packet.playerInfo.playerIndex) { //send to all + delPacket.playerInfo.playerIndex = entityIter.first; PumpPacket(delPacket); - + //erase matching PlayerEntity + erase_if(playerMap, [&](std::pair playerIter) -> bool { + return playerIter.first == entityIter.second.externalID; + }); return true; } return false; @@ -332,13 +388,14 @@ void ServerApplication::HandlePlayerDelete(NetworkPacket packet) { } void ServerApplication::HandlePlayerUpdate(NetworkPacket packet) { - if (playerMap.find(packet.playerInfo.playerIndex) == playerMap.end()) { + //TODO: Lookup the reference once, and operate on that instead of looking it up 3 times + if (entityMap.find(packet.playerInfo.playerIndex) == entityMap.end()) { throw(std::runtime_error("Cannot update a non-existant player")); } - //server is the slave to the clients, but only for now - playerMap[packet.playerInfo.playerIndex].position = packet.playerInfo.position; - playerMap[packet.playerInfo.playerIndex].motion = packet.playerInfo.motion; + //TODO: the server needs it's own movement system too + entityMap[packet.playerInfo.playerIndex].position = packet.playerInfo.position; + entityMap[packet.playerInfo.playerIndex].motion = packet.playerInfo.motion; PumpPacket(packet); } @@ -346,7 +403,7 @@ void ServerApplication::HandlePlayerUpdate(NetworkPacket packet) { void ServerApplication::HandleRegionRequest(NetworkPacket packet) { char buffer[PACKET_BUFFER_SIZE]; packet.meta.type = NetworkPacket::Type::REGION_CONTENT; - packet.regionInfo.region = mapPager.GetRegion(packet.regionInfo.x, packet.regionInfo.y); + packet.regionInfo.region = regionPager.GetRegion(packet.regionInfo.x, packet.regionInfo.y); serialize(&packet, buffer); network.Send(&packet.meta.srcAddress, buffer, PACKET_BUFFER_SIZE); } diff --git a/server/server_application.hpp b/server/server_application.hpp index 97610cf..4b19910 100644 --- a/server/server_application.hpp +++ b/server/server_application.hpp @@ -22,6 +22,11 @@ #ifndef SERVERAPPLICATION_HPP_ #define SERVERAPPLICATION_HPP_ +//server specific stuff +#include "client.hpp" +#include "entity.hpp" +#include "player_entity.hpp" + //maps #include "map_generator.hpp" #include "map_file_format.hpp" @@ -45,19 +50,6 @@ #include #include -struct ClientEntry { - IPaddress address; -}; - -struct PlayerEntry { - int clientIndex; - int mapIndex; - std::string handle; - std::string avatar; - Vector2 position; - Vector2 motion; -}; - //The main application class class ServerApplication { public: @@ -83,29 +75,26 @@ private: void HandlePlayerUpdate(NetworkPacket); void HandleRegionRequest(NetworkPacket); + //TODO: a function that sends to players in a certain proximity void PumpPacket(NetworkPacket); - //maps - RegionPager mapPager; - - //networking + //APIs UDPNetworkUtility network; - - //database sqlite3* database = nullptr; - - //lua lua_State* luaState = nullptr; + //server tables + std::map clientMap; + std::map entityMap; + std::map playerMap; + + //maps + //TODO: I need to handle multiple map objects + RegionPager regionPager; + //misc bool running = true; ConfigUtility config; - - std::map clientMap; - std::map playerMap; - - int clientCounter = 0; - int playerCounter = 0; }; #endif