From f649ff114813dad060a9c1dbf616f7a38cbfe045 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Tue, 29 Apr 2014 02:05:08 +1000 Subject: [PATCH] Added some refactoring documentation --- misc/refactoring fun/README.md | 3 + misc/refactoring fun/diff.png | Bin 0 -> 27280 bytes misc/refactoring fun/network.diff | 1922 ++++++++++++++++++ misc/refactoring fun/server_connections.diff | 394 ++++ misc/refactoring fun/server_internals.diff | 284 +++ 5 files changed, 2603 insertions(+) create mode 100644 misc/refactoring fun/README.md create mode 100644 misc/refactoring fun/diff.png create mode 100644 misc/refactoring fun/network.diff create mode 100644 misc/refactoring fun/server_connections.diff create mode 100644 misc/refactoring fun/server_internals.diff diff --git a/misc/refactoring fun/README.md b/misc/refactoring fun/README.md new file mode 100644 index 0000000..785918f --- /dev/null +++ b/misc/refactoring fun/README.md @@ -0,0 +1,3 @@ +This particular refactoring stage was absolute hell, mostly because I was wrestling with a severe bout of depression too. So, I've added the diffs, and a scary screenshot of the git console. + +There are three diff files because server/server_application.cpp was split into two files: server/server_internals.cpp and server/server_connections.cpp, each with it's own diff. \ No newline at end of file diff --git a/misc/refactoring fun/diff.png b/misc/refactoring fun/diff.png new file mode 100644 index 0000000000000000000000000000000000000000..c53a5f8ca3af72ed72c1241f81fe3825b108c2e6 GIT binary patch literal 27280 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 z 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 zYHG~~AdjustDirection(PlayerCharacter::Direction::WEST); +- SendState(); ++ SendPlayerUpdate(); + } + break; + + case SDLK_RIGHT: + if (localCharacter) { + localCharacter->AdjustDirection(PlayerCharacter::Direction::EAST); +- SendState(); ++ SendPlayerUpdate(); + } + break; + + case SDLK_UP: + if (localCharacter) { + localCharacter->AdjustDirection(PlayerCharacter::Direction::NORTH); +- SendState(); ++ SendPlayerUpdate(); + } + break; + + case SDLK_DOWN: + if (localCharacter) { + localCharacter->AdjustDirection(PlayerCharacter::Direction::SOUTH); +- SendState(); ++ SendPlayerUpdate(); + } + break; + } +@@ -223,28 +213,28 @@ void InWorld::KeyUp(SDL_KeyboardEvent const& key) { + case SDLK_LEFT: + if (localCharacter) { + localCharacter->AdjustDirection(PlayerCharacter::Direction::EAST); +- SendState(); ++ SendPlayerUpdate(); + } + break; + + case SDLK_RIGHT: + if (localCharacter) { + localCharacter->AdjustDirection(PlayerCharacter::Direction::WEST); +- SendState(); ++ SendPlayerUpdate(); + } + break; + + case SDLK_UP: + if (localCharacter) { + localCharacter->AdjustDirection(PlayerCharacter::Direction::SOUTH); +- SendState(); ++ SendPlayerUpdate(); + } + break; + + case SDLK_DOWN: + if (localCharacter) { + localCharacter->AdjustDirection(PlayerCharacter::Direction::NORTH); +- SendState(); ++ SendPlayerUpdate(); + } + break; + } +@@ -259,18 +249,18 @@ void InWorld::HandlePacket(SerialPacket packet) { + case SerialPacket::Type::DISCONNECT: + HandleDisconnect(packet); + break; ++ case SerialPacket::Type::REGION_CONTENT: ++ HandleRegionContent(packet); ++ break; ++ case SerialPacket::Type::PLAYER_UPDATE: ++ HandlePlayerUpdate(packet); ++ break; + case SerialPacket::Type::PLAYER_NEW: + HandlePlayerNew(packet); + break; + case SerialPacket::Type::PLAYER_DELETE: + HandlePlayerDelete(packet); + break; +- case SerialPacket::Type::PLAYER_UPDATE: +- HandlePlayerUpdate(packet); +- break; +- case SerialPacket::Type::REGION_CONTENT: +- HandleRegionContent(packet); +- break; + //handle errors + default: + throw(std::runtime_error("Unknown SerialPacket::Type encountered")); +@@ -281,23 +271,49 @@ void InWorld::HandlePacket(SerialPacket packet) { + void InWorld::HandleDisconnect(SerialPacket packet) { + network.Unbind(Channels::SERVER); + clientIndex = -1; ++ playerIndex = -1; + SetNextScene(SceneList::MAINMENU); + } + ++void InWorld::HandleRegionContent(SerialPacket packet) { ++ //replace existing regions ++ //TODO: account for map index ++ if (regionPager.FindRegion(packet.regionInfo.x, packet.regionInfo.y)) { ++ regionPager.UnloadRegion(packet.regionInfo.x, packet.regionInfo.y); ++ } ++ regionPager.PushRegion(packet.regionInfo.region); ++ packet.regionInfo.region = nullptr; ++} ++ ++void InWorld::HandlePlayerUpdate(SerialPacket packet) { ++ if (playerCharacters.find(packet.playerInfo.playerIndex) == playerCharacters.end()) { ++ HandlePlayerNew(packet); ++ return; ++ } ++ ++ //update only if the message didn't originate from here ++ if (packet.playerInfo.clientIndex != clientIndex) { ++ playerCharacters[packet.playerInfo.playerIndex].SetPosition(packet.playerInfo.position); ++ playerCharacters[packet.playerInfo.playerIndex].SetMotion(packet.playerInfo.motion); ++ } ++ playerCharacters[packet.playerInfo.playerIndex].ResetDirection(); ++} ++ + void InWorld::HandlePlayerNew(SerialPacket packet) { + if (playerCharacters.find(packet.playerInfo.playerIndex) != playerCharacters.end()) { + throw(std::runtime_error("Cannot create duplicate players")); + } + ++ //TODO: set the handle + playerCharacters[packet.playerInfo.playerIndex].GetSprite()->LoadSurface(config["dir.sprites"] + packet.playerInfo.avatar, 4, 4); + playerCharacters[packet.playerInfo.playerIndex].SetPosition(packet.playerInfo.position); + playerCharacters[packet.playerInfo.playerIndex].SetMotion(packet.playerInfo.motion); + playerCharacters[packet.playerInfo.playerIndex].ResetDirection(); + + //catch this client's player object +- if (packet.playerInfo.clientIndex == clientIndex && !localCharacter) { +- playerIndex = packet.playerInfo.playerIndex; ++ if (packet.playerInfo.playerIndex == playerIndex && !localCharacter) { + localCharacter = &playerCharacters[playerIndex]; ++ + //setup the camera + camera.width = GetScreen()->w; + camera.height = GetScreen()->h; +@@ -315,40 +331,17 @@ void InWorld::HandlePlayerDelete(SerialPacket packet) { + playerCharacters.erase(packet.playerInfo.playerIndex); + + //catch this client's player object +- if (packet.playerInfo.clientIndex == clientIndex) { ++ if (packet.playerInfo.playerIndex == playerIndex) { + playerIndex = -1; + localCharacter = nullptr; + } + } + +-void InWorld::HandlePlayerUpdate(SerialPacket packet) { +- if (playerCharacters.find(packet.playerInfo.playerIndex) == playerCharacters.end()) { +- HandlePlayerNew(packet); +- return; +- } +- +- //update only if the message didn't originate from here +- if (packet.playerInfo.clientIndex != clientIndex) { +- playerCharacters[packet.playerInfo.playerIndex].SetPosition(packet.playerInfo.position); +- playerCharacters[packet.playerInfo.playerIndex].SetMotion(packet.playerInfo.motion); +- } +- playerCharacters[packet.playerInfo.playerIndex].ResetDirection(); +-} +- +-void InWorld::HandleRegionContent(SerialPacket packet) { +- //replace existing regions +- if (regionPager.FindRegion(packet.regionInfo.x, packet.regionInfo.y)) { +- regionPager.UnloadRegion(packet.regionInfo.x, packet.regionInfo.y); +- } +- regionPager.PushRegion(packet.regionInfo.region); +- packet.regionInfo.region = nullptr; +-} +- + //------------------------- + //Server control + //------------------------- + +-void InWorld::SendState() { ++void InWorld::SendPlayerUpdate() { + SerialPacket packet; + char buffer[PACKET_BUFFER_SIZE]; + +@@ -369,7 +362,7 @@ void InWorld::RequestDisconnect() { + + //send a disconnect request + packet.meta.type = SerialPacket::Type::DISCONNECT; +- packet.clientInfo.index = clientIndex; ++ packet.clientInfo.clientIndex = clientIndex; + serialize(&packet, buffer); + network.Send(Channels::SERVER, buffer, PACKET_BUFFER_SIZE); + } +@@ -380,17 +373,18 @@ void InWorld::RequestShutDown() { + + //send a shutdown request + packet.meta.type = SerialPacket::Type::SHUTDOWN; +- packet.clientInfo.index = clientIndex; ++ packet.clientInfo.clientIndex = clientIndex; + serialize(&packet, buffer); + network.Send(Channels::SERVER, buffer, PACKET_BUFFER_SIZE); + } + +-void InWorld::RequestRegion(int x, int y) { ++void InWorld::RequestRegion(int mapIndex, int x, int y) { + SerialPacket packet; + char buffer[PACKET_BUFFER_SIZE]; + + //pack the region's data + packet.meta.type = SerialPacket::Type::REGION_REQUEST; ++ packet.regionInfo.mapIndex = mapIndex; + packet.regionInfo.x = x; + packet.regionInfo.y = y; + serialize(&packet, buffer); +@@ -430,7 +424,7 @@ void InWorld::UpdateMap() { + for (int i = xStart; i <= xEnd; i += REGION_WIDTH) { + for (int j = yStart; j <= yEnd; j += REGION_HEIGHT) { + if (!regionPager.FindRegion(i, j)) { +- RequestRegion(i, j); ++ RequestRegion(0, i, j); + } + } + } +diff --git a/client/scenes/in_world.hpp b/client/scenes/in_world.hpp +index bb0cc5c..00dcfa2 100644 +--- a/client/scenes/in_world.hpp ++++ b/client/scenes/in_world.hpp +@@ -52,7 +52,7 @@ + class InWorld : public BaseScene { + public: + //Public access members +- InWorld(ConfigUtility* const, UDPNetworkUtility* const, int* const); ++ InWorld(ConfigUtility* const, UDPNetworkUtility* const, int* const, int* const); + ~InWorld(); + + protected: +@@ -80,19 +80,19 @@ protected: + void HandleRegionContent(SerialPacket); + + //Server control +- void SendState(); ++ void SendPlayerUpdate(); + void RequestDisconnect(); + void RequestShutDown(); +- void RequestRegion(int x, int y); ++ void RequestRegion(int mapIndex, int x, int y); + + //utilities + void UpdateMap(); + +- //globals ++ //shared parameters + ConfigUtility& config; +- FrameRate fps; + UDPNetworkUtility& network; + int& clientIndex; ++ int& playerIndex; + + //graphics + Image buttonImage; +@@ -111,11 +111,11 @@ protected: + int width = 0, height = 0; + int marginX = 0, marginY = 0; + } camera; ++ FrameRate fps; + + //game + std::map playerCharacters; + PlayerCharacter* localCharacter = nullptr; +- int playerIndex = -1; + }; + + #endif +diff --git a/client/scenes/lobby_menu.cpp b/client/scenes/lobby_menu.cpp +index 1000af4..d6900da 100644 +--- a/client/scenes/lobby_menu.cpp ++++ b/client/scenes/lobby_menu.cpp +@@ -30,10 +30,11 @@ + //Public access members + //------------------------- + +-LobbyMenu::LobbyMenu(ConfigUtility* const argConfig, UDPNetworkUtility* const argNetwork, int* const argClientIndex): ++LobbyMenu::LobbyMenu(ConfigUtility* const argConfig, UDPNetworkUtility* const argNetwork, int* const argClientIndex, int* const argPlayerIndex): + config(*argConfig), + network(*argNetwork), +- clientIndex(*argClientIndex) ++ clientIndex(*argClientIndex), ++ playerIndex(*argPlayerIndex) + { + //setup the utility objects + image.LoadSurface(config["dir.interface"] + "button_menu.bmp"); +@@ -155,13 +156,17 @@ void LobbyMenu::MouseButtonUp(SDL_MouseButtonEvent const& button) { + } + + else if (join.MouseButtonUp(button) == Button::State::HOVER && selection != nullptr && selection->compatible) { +- //TODO: The player login information should be collected by the lobby screen + //the vars + SerialPacket packet; + char buffer[PACKET_BUFFER_SIZE]; + +- //join the selected server ++ //pack the packet + packet.meta.type = SerialPacket::Type::JOIN_REQUEST; ++ strncpy(packet.clientInfo.player, config["client.player"].c_str(), PACKET_STRING_SIZE); ++ strncpy(packet.clientInfo.handle, config["client.handle"].c_str(), PACKET_STRING_SIZE); ++ strncpy(packet.clientInfo.avatar, config["client.avatar"].c_str(), PACKET_STRING_SIZE); ++ ++ //join the selected server + serialize(&packet, buffer); + network.Send(&selection->address, buffer, PACKET_BUFFER_SIZE); + selection = nullptr; +@@ -203,20 +208,20 @@ void LobbyMenu::HandlePacket(SerialPacket packet) { + //extract the data + ServerInformation server; + server.address = packet.meta.srcAddress; ++ server.networkVersion = packet.serverInfo.networkVersion; + server.name = packet.serverInfo.name; + server.playerCount = packet.serverInfo.playerCount; + + //NOTE: Check compatibility here +- server.compatible = packet.serverInfo.regionWidth == REGION_WIDTH && +- packet.serverInfo.regionHeight == REGION_HEIGHT && +- packet.serverInfo.regionDepth == REGION_DEPTH; ++ server.compatible = server.networkVersion == NETWORK_VERSION; + + //push + serverInfo.push_back(server); + } + break; + case SerialPacket::Type::JOIN_RESPONSE: +- clientIndex = packet.clientInfo.index; ++ clientIndex = packet.clientInfo.clientIndex; ++ playerIndex = packet.clientInfo.playerIndex; + network.Bind(&packet.meta.srcAddress, Channels::SERVER); + SetNextScene(SceneList::INWORLD); + break; +diff --git a/client/scenes/lobby_menu.hpp b/client/scenes/lobby_menu.hpp +index 53808bb..4273400 100644 +--- a/client/scenes/lobby_menu.hpp ++++ b/client/scenes/lobby_menu.hpp +@@ -28,9 +28,6 @@ + #include "button.hpp" + #include "config_utility.hpp" + +-//map +-#include "region.hpp" +- + //network + #include "udp_network_utility.hpp" + #include "serial_packet.hpp" +@@ -45,7 +42,7 @@ + class LobbyMenu : public BaseScene { + public: + //Public access members +- LobbyMenu(ConfigUtility* const, UDPNetworkUtility* const, int* const); ++ LobbyMenu(ConfigUtility* const, UDPNetworkUtility* const, int* const, int* const); + ~LobbyMenu(); + + protected: +@@ -64,10 +61,11 @@ protected: + + void HandlePacket(SerialPacket); + +- //global ++ //shared parameters + ConfigUtility& config; + UDPNetworkUtility& network; + int& clientIndex; ++ int& playerIndex; + + //members + Image image; +@@ -79,7 +77,7 @@ protected: + //server list + struct ServerInformation { + IPaddress address; +- //TODO: version info ++ int networkVersion; + std::string name; + int playerCount; + bool compatible; +diff --git a/client/scenes/main_menu.hpp b/client/scenes/main_menu.hpp +index 5aba53d..9c06a0d 100644 +--- a/client/scenes/main_menu.hpp ++++ b/client/scenes/main_menu.hpp +@@ -49,7 +49,7 @@ protected: + void KeyDown(SDL_KeyboardEvent const&); + void KeyUp(SDL_KeyboardEvent const&); + +- //globals ++ //shared parameters + ConfigUtility& config; + + //members +diff --git a/client/scenes/options_menu.hpp b/client/scenes/options_menu.hpp +index 6e99965..bc05e1f 100644 +--- a/client/scenes/options_menu.hpp ++++ b/client/scenes/options_menu.hpp +@@ -49,7 +49,7 @@ protected: + void KeyDown(SDL_KeyboardEvent const&); + void KeyUp(SDL_KeyboardEvent const&); + +- //globals ++ //shared parameters + ConfigUtility& config; + + //members +diff --git a/client/scenes/splash_screen.hpp b/client/scenes/splash_screen.hpp +index f332cbe..f24a1c6 100644 +--- a/client/scenes/splash_screen.hpp ++++ b/client/scenes/splash_screen.hpp +@@ -40,7 +40,7 @@ protected: + void Update(double delta); + void Render(SDL_Surface* const); + +- //globals ++ //shared parameters + ConfigUtility& config; + + //members +diff --git a/common/network/serial.cpp b/common/network/serial.cpp +index ee88937..6f2f069 100644 +--- a/common/network/serial.cpp ++++ b/common/network/serial.cpp +@@ -26,84 +26,58 @@ + #include + + //------------------------- ++//Convenience Macros ++//------------------------- ++ ++#define SERIALIZE(buffer, data, size) memcpy(buffer, data, size); buffer += size; ++#define DESERIALIZE(buffer, data, size) memcpy(data, buffer, size); buffer += size; ++ ++//------------------------- + //internal serialization functions + //------------------------- + + void serializeType(SerialPacket* packet, char* buffer) { +- memcpy(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); ++ SERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); + } + + void serializeServer(SerialPacket* packet, char* buffer) { +- memcpy(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); +- buffer += sizeof(SerialPacket::Type); ++ SERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); + + //server info +- //Note: version info serialization goes here +- memcpy(buffer, packet->serverInfo.name, PACKET_STRING_SIZE); +- buffer += PACKET_STRING_SIZE; +- memcpy(buffer, &packet->serverInfo.playerCount, sizeof(int)); +- buffer += sizeof(int); +- +- //map format +- memcpy(buffer, &packet->serverInfo.regionWidth, sizeof(int)); +- buffer += sizeof(int); +- memcpy(buffer, &packet->serverInfo.regionHeight, sizeof(int)); +- buffer += sizeof(int); +- memcpy(buffer, &packet->serverInfo.regionDepth, sizeof(int)); ++ SERIALIZE(buffer, &packet->serverInfo.networkVersion, sizeof(int)); ++ SERIALIZE(buffer, packet->serverInfo.name, PACKET_STRING_SIZE); ++ SERIALIZE(buffer, &packet->serverInfo.playerCount, sizeof(int)); + } + + void serializeClient(SerialPacket* packet, char* buffer) { +- memcpy(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); +- buffer += sizeof(SerialPacket::Type); +- memcpy(buffer, &packet->clientInfo.index, sizeof(int)); +-} +- +-void serializePlayer(SerialPacket* packet, char* buffer) { +- memcpy(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); +- buffer += sizeof(SerialPacket::Type); ++ SERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); + + //indexes +- memcpy(buffer, &packet->playerInfo.clientIndex, sizeof(int)); +- buffer += sizeof(int); +- memcpy(buffer, &packet->playerInfo.playerIndex, sizeof(int)); +- buffer += sizeof(int); +- +- //text +- memcpy(buffer, packet->playerInfo.handle, PACKET_STRING_SIZE); +- buffer += PACKET_STRING_SIZE; +- memcpy(buffer, packet->playerInfo.avatar, PACKET_STRING_SIZE); +- buffer += PACKET_STRING_SIZE; ++ SERIALIZE(buffer, &packet->clientInfo.clientIndex, sizeof(int)); ++ SERIALIZE(buffer, &packet->clientInfo.playerIndex, sizeof(int)); + +- //vectors +- memcpy(buffer, &packet->playerInfo.position.x, sizeof(double)); +- buffer += sizeof(double); +- memcpy(buffer, &packet->playerInfo.position.y, sizeof(double)); +- buffer += sizeof(double); +- memcpy(buffer, &packet->playerInfo.motion.x, sizeof(double)); +- buffer += sizeof(double); +- memcpy(buffer, &packet->playerInfo.motion.y, sizeof(double)); ++ //texts ++ SERIALIZE(buffer, packet->clientInfo.player, PACKET_STRING_SIZE); ++ SERIALIZE(buffer, packet->clientInfo.handle, PACKET_STRING_SIZE); ++ SERIALIZE(buffer, packet->clientInfo.avatar, PACKET_STRING_SIZE); + } + + void serializeRegionFormat(SerialPacket* packet, char* buffer) { +- memcpy(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); +- buffer += sizeof(SerialPacket::Type); ++ SERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); + +- //x & y +- memcpy(buffer, &packet->regionInfo.x, sizeof(int)); +- buffer += sizeof(int); +- memcpy(buffer, &packet->regionInfo.y, sizeof(int)); ++ //format ++ SERIALIZE(buffer, &packet->regionInfo.mapIndex, sizeof(int)); ++ SERIALIZE(buffer, &packet->regionInfo.x, sizeof(int)); ++ SERIALIZE(buffer, &packet->regionInfo.y, sizeof(int)); + } + + void serializeRegionContent(SerialPacket* packet, char* buffer) { +- //format +- memcpy(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); +- buffer += sizeof(SerialPacket::Type); ++ SERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); + +- //x & y +- *reinterpret_cast(buffer) = packet->regionInfo.region->GetX(); +- buffer += sizeof(int); +- *reinterpret_cast(buffer) = packet->regionInfo.region->GetY(); +- buffer += sizeof(int); ++ //format ++ SERIALIZE(buffer, &packet->regionInfo.mapIndex, sizeof(int)); ++ SERIALIZE(buffer, &packet->regionInfo.x, sizeof(int)); ++ SERIALIZE(buffer, &packet->regionInfo.y, sizeof(int)); + + //content + for (register int i = 0; i < REGION_WIDTH; i++) { +@@ -116,92 +90,79 @@ void serializeRegionContent(SerialPacket* packet, char* buffer) { + } + } + ++void serializePlayer(SerialPacket* packet, char* buffer) { ++ SERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); ++ ++ //indexes ++ SERIALIZE(buffer, &packet->playerInfo.clientIndex, sizeof(int)); ++ SERIALIZE(buffer, &packet->playerInfo.playerIndex, sizeof(int)); ++ ++ //texts ++ SERIALIZE(buffer, packet->clientInfo.handle, PACKET_STRING_SIZE); ++ SERIALIZE(buffer, packet->clientInfo.avatar, PACKET_STRING_SIZE); ++ ++ //vectors ++ SERIALIZE(buffer, &packet->playerInfo.position.x, sizeof(double)); ++ SERIALIZE(buffer, &packet->playerInfo.position.y, sizeof(double)); ++ SERIALIZE(buffer, &packet->playerInfo.motion.x, sizeof(double)); ++ SERIALIZE(buffer, &packet->playerInfo.motion.y, sizeof(double)); ++} ++ + //------------------------- + //internal deserialization functions + //------------------------- + + void deserializeType(SerialPacket* packet, char* buffer) { +- memcpy(&packet->meta.type, buffer, sizeof(SerialPacket::Type)); ++ DESERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); + } + + void deserializeServer(SerialPacket* packet, char* buffer) { +- memcpy(&packet->meta.type, buffer, sizeof(SerialPacket::Type)); +- buffer += sizeof(SerialPacket::Type); ++ DESERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); + + //server info +- //Note: version info deserialization goes here +- memcpy(packet->serverInfo.name, buffer, PACKET_STRING_SIZE); +- buffer += PACKET_STRING_SIZE; +- memcpy(&packet->serverInfo.playerCount, buffer, sizeof(int)); +- buffer += sizeof(int); +- +- //map format +- memcpy(&packet->serverInfo.regionWidth, buffer, sizeof(int)); +- buffer += sizeof(int); +- memcpy(&packet->serverInfo.regionHeight, buffer, sizeof(int)); +- buffer += sizeof(int); +- memcpy(&packet->serverInfo.regionDepth, buffer, sizeof(int)); ++ DESERIALIZE(buffer, &packet->serverInfo.networkVersion, sizeof(int)); ++ DESERIALIZE(buffer, packet->serverInfo.name, PACKET_STRING_SIZE); ++ DESERIALIZE(buffer, &packet->serverInfo.playerCount, sizeof(int)); + } + + void deserializeClient(SerialPacket* packet, char* buffer) { +- memcpy(&packet->meta.type, buffer, sizeof(SerialPacket::Type)); +- buffer += sizeof(SerialPacket::Type); +- memcpy(&packet->clientInfo.index, buffer, sizeof(int)); +-} +- +-void deserializePlayer(SerialPacket* packet, char* buffer) { +- memcpy(&packet->meta.type, buffer, sizeof(SerialPacket::Type)); +- buffer += sizeof(SerialPacket::Type); ++ DESERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); + + //indexes +- memcpy(&packet->playerInfo.clientIndex, buffer, sizeof(int)); +- buffer += sizeof(int); +- memcpy(&packet->playerInfo.playerIndex, buffer, sizeof(int)); +- buffer += sizeof(int); +- +- //text +- memcpy(packet->playerInfo.handle, buffer, PACKET_STRING_SIZE); +- buffer += PACKET_STRING_SIZE; +- memcpy(packet->playerInfo.avatar, buffer, PACKET_STRING_SIZE); +- buffer += PACKET_STRING_SIZE; ++ DESERIALIZE(buffer, &packet->clientInfo.clientIndex, sizeof(int)); ++ DESERIALIZE(buffer, &packet->clientInfo.playerIndex, sizeof(int)); + +- //vectors +- memcpy(&packet->playerInfo.position.x, buffer, sizeof(double)); +- buffer += sizeof(double); +- memcpy(&packet->playerInfo.position.y, buffer, sizeof(double)); +- buffer += sizeof(double); +- memcpy(&packet->playerInfo.motion.x, buffer, sizeof(double)); +- buffer += sizeof(double); +- memcpy(&packet->playerInfo.motion.y, buffer, sizeof(double)); ++ //texts ++ DESERIALIZE(buffer, packet->clientInfo.player, PACKET_STRING_SIZE); ++ DESERIALIZE(buffer, packet->clientInfo.handle, PACKET_STRING_SIZE); ++ DESERIALIZE(buffer, packet->clientInfo.avatar, PACKET_STRING_SIZE); + } + + void deserializeRegionFormat(SerialPacket* packet, char* buffer) { +- memcpy(&packet->meta.type, buffer, sizeof(SerialPacket::Type)); +- buffer += sizeof(SerialPacket::Type); ++ DESERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); + +- //x & y +- memcpy(&packet->regionInfo.x, buffer, sizeof(int)); +- buffer += sizeof(int); +- memcpy(&packet->regionInfo.y, buffer, sizeof(int)); ++ //format ++ DESERIALIZE(buffer, &packet->regionInfo.mapIndex, sizeof(int)); ++ DESERIALIZE(buffer, &packet->regionInfo.x, sizeof(int)); ++ DESERIALIZE(buffer, &packet->regionInfo.y, sizeof(int)); + } + + void deserializeRegionContent(SerialPacket* packet, char* buffer) { +- memcpy(&packet->meta.type, buffer, sizeof(SerialPacket::Type)); +- buffer += sizeof(SerialPacket::Type); ++ DESERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); + +- //x & y +- memcpy(&packet->regionInfo.x, buffer, sizeof(int)); +- buffer += sizeof(int); +- memcpy(&packet->regionInfo.y, buffer, sizeof(int)); +- buffer += sizeof(int); ++ //format ++ DESERIALIZE(buffer, &packet->regionInfo.mapIndex, sizeof(int)); ++ DESERIALIZE(buffer, &packet->regionInfo.x, sizeof(int)); ++ DESERIALIZE(buffer, &packet->regionInfo.y, sizeof(int)); + +- //content ++ //an object to work on + BlankAllocator().Create( + &packet->regionInfo.region, + packet->regionInfo.x, + packet->regionInfo.y + ); + ++ //content + for (register int i = 0; i < REGION_WIDTH; i++) { + for (register int j = 0; j < REGION_HEIGHT; j++) { + for (register int k = 0; k < REGION_DEPTH; k++) { +@@ -212,6 +173,24 @@ void deserializeRegionContent(SerialPacket* packet, char* buffer) { + } + } + ++void deserializePlayer(SerialPacket* packet, char* buffer) { ++ DESERIALIZE(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); ++ ++ //indexes ++ DESERIALIZE(buffer, &packet->playerInfo.clientIndex, sizeof(int)); ++ DESERIALIZE(buffer, &packet->playerInfo.playerIndex, sizeof(int)); ++ ++ //texts ++ DESERIALIZE(buffer, packet->clientInfo.handle, PACKET_STRING_SIZE); ++ DESERIALIZE(buffer, packet->clientInfo.avatar, PACKET_STRING_SIZE); ++ ++ //vectors ++ DESERIALIZE(buffer, &packet->playerInfo.position.x, sizeof(double)); ++ DESERIALIZE(buffer, &packet->playerInfo.position.y, sizeof(double)); ++ DESERIALIZE(buffer, &packet->playerInfo.motion.x, sizeof(double)); ++ DESERIALIZE(buffer, &packet->playerInfo.motion.y, sizeof(double)); ++} ++ + //------------------------- + //the interface functions + //------------------------- +@@ -223,8 +202,6 @@ void serialize(SerialPacket* packet, void* buffer) { + case SerialPacket::Type::PING: + case SerialPacket::Type::PONG: + case SerialPacket::Type::BROADCAST_REQUEST: +- case SerialPacket::Type::JOIN_REQUEST: +- case SerialPacket::Type::SYNCHRONIZE: + serializeType(packet, reinterpret_cast(buffer)); + break; + +@@ -234,19 +211,14 @@ void serialize(SerialPacket* packet, void* buffer) { + break; + + //Client info ++ case SerialPacket::Type::JOIN_REQUEST: + case SerialPacket::Type::JOIN_RESPONSE: ++ case SerialPacket::Type::SYNCHRONIZE: + case SerialPacket::Type::DISCONNECT: + case SerialPacket::Type::SHUTDOWN: + serializeClient(packet, reinterpret_cast(buffer)); + break; + +- //Player info +- case SerialPacket::Type::PLAYER_NEW: +- case SerialPacket::Type::PLAYER_DELETE: +- case SerialPacket::Type::PLAYER_UPDATE: +- serializePlayer(packet, reinterpret_cast(buffer)); +- break; +- + //region info + case SerialPacket::Type::REGION_REQUEST: + serializeRegionFormat(packet, reinterpret_cast(buffer)); +@@ -255,6 +227,13 @@ void serialize(SerialPacket* packet, void* buffer) { + case SerialPacket::Type::REGION_CONTENT: + serializeRegionContent(packet, reinterpret_cast(buffer)); + break; ++ ++ //Player info ++ case SerialPacket::Type::PLAYER_NEW: ++ case SerialPacket::Type::PLAYER_DELETE: ++ case SerialPacket::Type::PLAYER_UPDATE: ++ serializePlayer(packet, reinterpret_cast(buffer)); ++ break; + } + } + +@@ -267,8 +246,6 @@ void deserialize(SerialPacket* packet, void* buffer) { + case SerialPacket::Type::PING: + case SerialPacket::Type::PONG: + case SerialPacket::Type::BROADCAST_REQUEST: +- case SerialPacket::Type::JOIN_REQUEST: +- case SerialPacket::Type::SYNCHRONIZE: + //NOTHING + break; + +@@ -278,19 +255,14 @@ void deserialize(SerialPacket* packet, void* buffer) { + break; + + //Client info ++ case SerialPacket::Type::JOIN_REQUEST: + case SerialPacket::Type::JOIN_RESPONSE: ++ case SerialPacket::Type::SYNCHRONIZE: + case SerialPacket::Type::DISCONNECT: + case SerialPacket::Type::SHUTDOWN: + deserializeClient(packet, reinterpret_cast(buffer)); + break; + +- //Player info +- case SerialPacket::Type::PLAYER_NEW: +- case SerialPacket::Type::PLAYER_DELETE: +- case SerialPacket::Type::PLAYER_UPDATE: +- deserializePlayer(packet, reinterpret_cast(buffer)); +- break; +- + //region info + case SerialPacket::Type::REGION_REQUEST: + deserializeRegionFormat(packet, reinterpret_cast(buffer)); +@@ -299,5 +271,12 @@ void deserialize(SerialPacket* packet, void* buffer) { + case SerialPacket::Type::REGION_CONTENT: + deserializeRegionContent(packet, reinterpret_cast(buffer)); + break; ++ ++ //Player info ++ case SerialPacket::Type::PLAYER_NEW: ++ case SerialPacket::Type::PLAYER_DELETE: ++ case SerialPacket::Type::PLAYER_UPDATE: ++ deserializePlayer(packet, reinterpret_cast(buffer)); ++ break; + } + } +\ No newline at end of file +diff --git a/common/network/serial.hpp b/common/network/serial.hpp +index b532bb8..609b1f3 100644 +--- a/common/network/serial.hpp ++++ b/common/network/serial.hpp +@@ -27,10 +27,10 @@ + /* NOTE: Keep the PACKET_BUFFER_SIZE up to date + * NOTE: REGION_CONTENT is currently the largest type of packet + * map content: REGION_WIDTH * REGION_HEIGHT * REGION_DEPTH * sizoeof(region::type_t) +- * map format: sizeof(int) * 2 ++ * map format: sizeof(int) * 3 + * metadata: sizeof(metadata) + */ +-#define PACKET_BUFFER_SIZE REGION_WIDTH * REGION_HEIGHT * REGION_DEPTH * sizeof(Region::type_t) + sizeof(int) * 2 + sizeof(SerialPacket::Metadata) ++#define PACKET_BUFFER_SIZE REGION_WIDTH * REGION_HEIGHT * REGION_DEPTH * sizeof(Region::type_t) + sizeof(int) * 3 + sizeof(SerialPacket::Metadata) + + void serialize(SerialPacket* const, void*); + void deserialize(SerialPacket* const, void*); +diff --git a/common/network/serial_packet.hpp b/common/network/serial_packet.hpp +index 86c9b2a..12574f7 100644 +--- a/common/network/serial_packet.hpp ++++ b/common/network/serial_packet.hpp +@@ -27,6 +27,7 @@ + + #include "SDL/SDL_net.h" + ++#define NETWORK_VERSION 20140426 + #define PACKET_STRING_SIZE 100 + + #pragma pack(push, 0) +@@ -49,23 +50,23 @@ union SerialPacket { + JOIN_REQUEST = 5, + JOIN_RESPONSE = 6, + +- //disconnect from the server +- DISCONNECT = 7, +- + //mass update +- SYNCHRONIZE = 8, ++ SYNCHRONIZE = 7, ++ ++ //disconnect from the server ++ DISCONNECT = 8, + + //shut down the server + SHUTDOWN = 9, + +- //Player movement, etc. +- PLAYER_NEW = 10, +- PLAYER_DELETE = 11, +- PLAYER_UPDATE = 12, +- + //map data +- REGION_REQUEST = 13, +- REGION_CONTENT = 14, ++ REGION_REQUEST = 10, ++ REGION_CONTENT = 11, ++ ++ //Player movement, etc. ++ PLAYER_NEW = 12, ++ PLAYER_DELETE = 13, ++ PLAYER_UPDATE = 14, + + //TODO: combat packets + }; +@@ -79,42 +80,41 @@ union SerialPacket { + //information about the server + struct ServerInformation { + Metadata meta; +- //TODO: version info ++ int networkVersion; + char name[PACKET_STRING_SIZE]; + int playerCount; +- +- //map format +- int regionWidth; +- int regionHeight; +- int regionDepth; + }serverInfo; + + //information about the client +- //TODO: login credentials + struct ClientInformation { + Metadata meta; +- int index; ++ int clientIndex; ++ int playerIndex; ++ char player[PACKET_STRING_SIZE]; ++ char handle[PACKET_STRING_SIZE]; ++ char avatar[PACKET_STRING_SIZE]; + }clientInfo; + ++ //map data ++ struct RegionInformation { ++ Metadata meta; ++ int mapIndex; ++ int x, y; ++ Region* region; ++ }regionInfo; ++ + //information about a player + struct PlayerInformation { + Metadata meta; + int clientIndex; + int playerIndex; +- //TODO: should move handle/avatar into clientInfo; these might actually do better during the login system + char handle[PACKET_STRING_SIZE]; + char avatar[PACKET_STRING_SIZE]; ++ int mapIndex; + Vector2 position; + Vector2 motion; + }playerInfo; + +- //map data +- struct RegionInformation { +- Metadata meta; +- int x, y; +- Region* region; +- }regionInfo; +- + //defaults + SerialPacket() { + meta.type = Type::NONE; +diff --git a/rsc/config.cfg b/rsc/config.cfg +index 0b5fec0..37fbd5e 100644 +--- a/rsc/config.cfg ++++ b/rsc/config.cfg +@@ -23,7 +23,8 @@ map.pager.height = 20 + map.pager.depth = 3 + + #player options +-player.handle = username +-player.avatar = elliot2.bmp ++client.player = Kayne Ruse ++client.handle = Ratstail91 ++client.avatar = elliot2.bmp + + #debugging +diff --git a/server/client_entry.cpp b/server/client_entry.cpp +index 7708423..88e2ab9 100644 +--- a/server/client_entry.cpp ++++ b/server/client_entry.cpp +@@ -21,4 +21,4 @@ + */ + #include "client_entry.hpp" + +-unsigned int ClientEntry::uidCounter; ++int ClientEntry::uidCounter = 0; +diff --git a/server/client_entry.hpp b/server/client_entry.hpp +index 42225b8..6e6b6a3 100644 +--- a/server/client_entry.hpp ++++ b/server/client_entry.hpp +@@ -25,8 +25,8 @@ + #include "SDL/SDL_net.h" + + struct ClientEntry { +- IPaddress address; +- static unsigned int uidCounter; ++ IPaddress address = {0,0}; ++ static int uidCounter; + }; + + #endif +diff --git a/server/player_entry.cpp b/server/player_entry.cpp +index 11cfdfc..3eb8777 100644 +--- a/server/player_entry.cpp ++++ b/server/player_entry.cpp +@@ -21,4 +21,4 @@ + */ + #include "player_entry.hpp" + +-unsigned int PlayerEntry::uidCounter; ++int PlayerEntry::uidCounter = 0; +diff --git a/server/player_entry.hpp b/server/player_entry.hpp +index 17dca99..a55c603 100644 +--- a/server/player_entry.hpp ++++ b/server/player_entry.hpp +@@ -31,32 +31,33 @@ + struct PlayerEntry { + //metadata + int clientIndex; ++ std::string player; + std::string handle; + std::string avatar; + + //world position +- int mapIndex; +- Vector2 position; +- Vector2 motion; +- BBox bbox; ++ int mapIndex = 0; ++ Vector2 position = {0.0,0.0}; ++ Vector2 motion = {0.0,0.0}; ++ BBox bbox = {0,0,0,0}; + + //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; ++ int level = 0; ++ int exp = 0; ++ int maxHP = 0; ++ int health = 0; ++ int maxMP = 0; ++ int mana = 0; ++ int attack = 0; ++ int defence = 0; ++ int intelligence = 0; ++ int resistance = 0; ++ float accuracy = 0.0; ++ float evasion = 0.0; ++ float luck = 0.0; + + //uid +- static unsigned int uidCounter; ++ static int uidCounter; + }; + + #endif +diff --git a/server/server_application.cpp b/server/server_application.cpp +deleted file mode 100644 +index 1a96d5b..0000000 +--- a/server/server_application.cpp ++++ /dev/null +@@ -1,368 +0,0 @@ +-/* Copyright: (c) Kayne Ruse 2013, 2014 +- * +- * This software is provided 'as-is', without any express or implied +- * warranty. In no event will the authors be held liable for any damages +- * arising from the use of this software. +- * +- * Permission is granted to anyone to use this software for any purpose, +- * including commercial applications, and to alter it and redistribute it +- * freely, subject to the following restrictions: +- * +- * 1. The origin of this software must not be misrepresented; you must not +- * claim that you wrote the original software. If you use this software +- * in a product, an acknowledgment in the product documentation would be +- * appreciated but is not required. +- * +- * 2. Altered source versions must be plainly marked as such, and must not be +- * misrepresented as being the original software. +- * +- * 3. This notice may not be removed or altered from any source +- * distribution. +-*/ +-#include "server_application.hpp" +- +-#include "utility.hpp" +- +-#include +-#include +-#include +- +-//------------------------- +-//Define the public members +-//------------------------- +- +-void ServerApplication::Init(int argc, char** argv) { +- //NOTE: I might need to rearrange the init process so that lua & SQL can interact with the map system as needed. +- std::cout << "Beginning startup" << std::endl; +- +- //initial setup +- ClientEntry::uidCounter = 0; +- PlayerEntry::uidCounter = 0; +- config.Load("rsc\\config.cfg"); +- +- //Init SDL +- if (SDL_Init(0)) { +- throw(std::runtime_error("Failed to initialize SDL")); +- } +- std::cout << "Initialized SDL" << std::endl; +- +- //Init SDL_net +- if (SDLNet_Init()) { +- throw(std::runtime_error("Failed to initialize SDL_net")); +- } +- network.Open(config.Int("server.port"), PACKET_BUFFER_SIZE); +- std::cout << "Initialized SDL_net" << std::endl; +- +- //Init SQL +- int ret = sqlite3_open_v2(config["server.dbname"].c_str(), &database, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, nullptr); +- if (ret != SQLITE_OK || !database) { +- throw(std::runtime_error(std::string() + "Failed to initialize SQL: " + sqlite3_errmsg(database) )); +- } +- std::cout << "Initialized SQL" << std::endl; +- +- //setup the database +- if (runSQLScript(database, config["dir.scripts"] + "setup_server.sql")) { +- throw(std::runtime_error("Failed to initialize SQL's setup script")); +- } +- std::cout << "Initialized SQL's setup script" << std::endl; +- +- //lua +- luaState = luaL_newstate(); +- if (!luaState) { +- throw(std::runtime_error("Failed to initialize lua")); +- } +- luaL_openlibs(luaState); +- std::cout << "Initialized lua" << std::endl; +- +- //run the startup script +- if (luaL_dofile(luaState, (config["dir.scripts"] + "setup_server.lua").c_str())) { +- throw(std::runtime_error(std::string() + "Failed to initialize lua's setup script: " + lua_tostring(luaState, -1) )); +- } +- std::cout << "Initialized lua's setup script" << std::endl; +- +- //setup the map object +- regionPager.GetAllocator()->SetLuaState(luaState); +- regionPager.GetFormat()->SetLuaState(luaState); +- //TODO: config parameter +- regionPager.GetFormat()->SetSaveDir("save/mapname/"); +- +- std::cout << "Initialized the map system" << std::endl; +- std::cout << "\tsizeof(SerialPacket): " << sizeof(SerialPacket) << std::endl; +- std::cout << "\tPACKET_BUFFER_SIZE: " << PACKET_BUFFER_SIZE << std::endl; +- +- //finalize the startup +- std::cout << "Startup completed successfully" << std::endl; +- +- //debugging +- // +-} +- +-void ServerApplication::Proc() { +- SerialPacket packet; +- while(running) { +- //suck in the waiting packets & process them +- while(network.Receive()) { +- //get the packet +- deserialize(&packet, network.GetInData()); +- //cache the source address +- packet.meta.srcAddress = network.GetInPacket()->address; +- //we need to go deeper +- HandlePacket(packet); +- } +- //give the computer a break +- SDL_Delay(10); +- } +-} +- +-void ServerApplication::Quit() { +- std::cout << "Shutting down" << std::endl; +- //empty the members +- regionPager.UnloadAll(); +- +- //APIs +- lua_close(luaState); +- sqlite3_close_v2(database); +- network.Close(); +- SDLNet_Quit(); +- SDL_Quit(); +- std::cout << "Shutdown finished" << std::endl; +-} +- +-//------------------------- +-//Define the uber switch +-//------------------------- +- +-void ServerApplication::HandlePacket(SerialPacket packet) { +- switch(packet.meta.type) { +- case SerialPacket::Type::BROADCAST_REQUEST: +- HandleBroadcastRequest(packet); +- break; +- case SerialPacket::Type::JOIN_REQUEST: +- HandleJoinRequest(packet); +- break; +- case SerialPacket::Type::DISCONNECT: +- HandleDisconnect(packet); +- break; +- case SerialPacket::Type::SYNCHRONIZE: +- HandleSynchronize(packet); +- break; +- case SerialPacket::Type::SHUTDOWN: +- HandleShutdown(packet); +- break; +- case SerialPacket::Type::PLAYER_NEW: +- HandlePlayerNew(packet); +- break; +- case SerialPacket::Type::PLAYER_DELETE: +- HandlePlayerDelete(packet); +- break; +- case SerialPacket::Type::PLAYER_UPDATE: +- HandlePlayerUpdate(packet); +- break; +- case SerialPacket::Type::REGION_REQUEST: +- HandleRegionRequest(packet); +- break; +- //handle errors +- default: +- throw(std::runtime_error("Unknown SerialPacket::Type encountered")); +- break; +- } +-} +- +-//------------------------- +-//Handle various network input +-//------------------------- +- +-void ServerApplication::HandleBroadcastRequest(SerialPacket packet) { +- //send back the server's metadata +- packet.meta.type = SerialPacket::Type::BROADCAST_RESPONSE; +- +- //pack the data +- snprintf(packet.serverInfo.name, PACKET_STRING_SIZE, "%s", config["server.name"].c_str()); +- packet.serverInfo.playerCount = playerMap.size(); +- packet.serverInfo.regionWidth = REGION_WIDTH; +- packet.serverInfo.regionHeight = REGION_HEIGHT; +- packet.serverInfo.regionDepth = REGION_DEPTH; +- +- //send the data +- char buffer[PACKET_BUFFER_SIZE]; +- serialize(&packet, buffer); +- network.Send(&packet.meta.srcAddress, buffer, PACKET_BUFFER_SIZE); +-} +- +-void ServerApplication::HandleJoinRequest(SerialPacket packet) { +- //register the new client +- ClientEntry newClient; +- newClient.address = packet.meta.srcAddress; +- clientMap[ClientEntry::uidCounter] = newClient; +- +- //send the client their index +- char buffer[PACKET_BUFFER_SIZE]; +- packet.meta.type = SerialPacket::Type::JOIN_RESPONSE; +- packet.clientInfo.index = ClientEntry::uidCounter; +- serialize(&packet, buffer); +- +- //bounce this packet +- network.Send(&newClient.address, buffer, PACKET_BUFFER_SIZE); +- +- //finished this routine +- ClientEntry::uidCounter++; +- std::cout << "Connect, total: " << clientMap.size() << std::endl; +-} +- +-void ServerApplication::HandleDisconnect(SerialPacket packet) { +- //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); +- +- //prep the delete packet +- SerialPacket delPacket; +- delPacket.meta.type = SerialPacket::Type::PLAYER_DELETE; +- +- //TODO: can this use DeletePlayer() instead? +- //delete server and client side players +- erase_if(playerMap, [&](std::pair it) -> bool { +- //find the internal players to delete +- if (it.second.clientIndex == packet.clientInfo.index) { +- //send the delete player command to all clients +- delPacket.playerInfo.playerIndex = it.first; +- PumpPacket(delPacket); +- +- //delete this player object +- return true; +- } +- +- //don't delete this player object +- return false; +- }); +- +- //finished this routine +- std::cout << "Disconnect, total: " << clientMap.size() << std::endl; +-} +- +-void ServerApplication::HandleSynchronize(SerialPacket packet) { +- //TODO: compensate for large distances +- +- //send all the server's data to this client +- SerialPacket newPacket; +- char buffer[PACKET_BUFFER_SIZE]; +- +- //players +- newPacket.meta.type = SerialPacket::Type::PLAYER_UPDATE; +- for (auto& it : playerMap) { +- //TODO: update this for the expanded PlayerEntry structure +- 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; +- serialize(&newPacket, buffer); +- network.Send(&clientMap[packet.clientInfo.index].address, buffer, PACKET_BUFFER_SIZE); +- } +-} +- +-void ServerApplication::HandleShutdown(SerialPacket packet) { +- //end the server +- running = false; +- +- //disconnect all clients +- packet.meta.type = SerialPacket::Type::DISCONNECT; +- PumpPacket(packet); +- +- //finished this routine +- std::cout << "Shutdown signal accepted" << std::endl; +-} +- +-void ServerApplication::HandlePlayerNew(SerialPacket packet) { +- //register the new PlayerEntry +- //NOTE: assigning each field one-by-one so adding or moving a field doesn't break this code +- PlayerEntry newPlayer; +- +- //metadata +- newPlayer.clientIndex = packet.playerInfo.clientIndex; +- newPlayer.handle = packet.playerInfo.handle; +- newPlayer.avatar = packet.playerInfo.avatar; +- +- //position +- newPlayer.mapIndex = 0; +- newPlayer.position = {0,0}; +- newPlayer.motion = {0,0}; +- newPlayer.bbox = {0, 0, 0, 0}; +- +- //TODO: Add the statistic creation code here +- +- //push this player +- playerMap[PlayerEntry::uidCounter] = newPlayer; +- +- //send the client their info +- packet.playerInfo.playerIndex = PlayerEntry::uidCounter; +- packet.playerInfo.position = newPlayer.position; +- packet.playerInfo.motion = newPlayer.motion; +- +- //actually send to everyone +- PumpPacket(packet); +- +- //finish this routine +- PlayerEntry::uidCounter++; +-} +- +-//TODO: differentiate between delete and unload +-void ServerApplication::HandlePlayerDelete(SerialPacket packet) { +- //TODO: authenticate who is deleting this player +- if (playerMap.find(packet.playerInfo.playerIndex) == playerMap.end()) { +- throw(std::runtime_error("Cannot delete a non-existant player")); +- } +- +- //TODO: remove the deleted player from the database? +- +- //prep the delete packet +- SerialPacket delPacket; +- delPacket.meta.type = SerialPacket::Type::PLAYER_DELETE; +- +- //delete the specified playerEntry +- erase_if(playerMap, [&](std::pair it) -> bool { +- //find the specified PlayerEntry +- if (it.first == packet.playerInfo.playerIndex) { +- //send to all +- delPacket.playerInfo.playerIndex = it.first; +- PumpPacket(delPacket); +- +- //delete this player +- return true; +- } +- //skip this player +- return false; +- }); +-} +- +-void ServerApplication::HandlePlayerUpdate(SerialPacket packet) { +- if (playerMap.find(packet.playerInfo.playerIndex) == playerMap.end()) { +- throw(std::runtime_error("Cannot update a non-existant player")); +- } +- +- //TODO: the server needs it's own movement system too +- playerMap[packet.playerInfo.playerIndex].position = packet.playerInfo.position; +- playerMap[packet.playerInfo.playerIndex].motion = packet.playerInfo.motion; +- +- PumpPacket(packet); +-} +- +-void ServerApplication::HandleRegionRequest(SerialPacket packet) { +- char buffer[PACKET_BUFFER_SIZE]; +- packet.meta.type = SerialPacket::Type::REGION_CONTENT; +- packet.regionInfo.region = regionPager.GetRegion(packet.regionInfo.x, packet.regionInfo.y); +- serialize(&packet, buffer); +- network.Send(&packet.meta.srcAddress, buffer, PACKET_BUFFER_SIZE); +-} +- +-void ServerApplication::PumpPacket(SerialPacket packet) { +- //NOTE: I don't really like this, but it'll do for now +- char buffer[PACKET_BUFFER_SIZE]; +- serialize(&packet, buffer); +- for (auto& it : clientMap) { +- network.Send(&it.second.address, buffer, PACKET_BUFFER_SIZE); +- } +-} +diff --git a/server/server_application.hpp b/server/server_application.hpp +index 2da5de1..cd6d4ac 100644 +--- a/server/server_application.hpp ++++ b/server/server_application.hpp +@@ -23,7 +23,6 @@ + #define SERVERAPPLICATION_HPP_ + + //server specific stuff +-#include "server_utility.hpp" + #include "client_entry.hpp" + #include "player_entry.hpp" + +@@ -63,28 +62,29 @@ public: + private: + void HandlePacket(SerialPacket); + +- //high cohesion utility functions ++ //handle incoming traffic + void HandleBroadcastRequest(SerialPacket); + void HandleJoinRequest(SerialPacket); +- void HandleDisconnect(SerialPacket); + void HandleSynchronize(SerialPacket); ++ void HandleDisconnect(SerialPacket); + void HandleShutdown(SerialPacket); +- void HandlePlayerNew(SerialPacket); +- void HandlePlayerDelete(SerialPacket); + void HandlePlayerUpdate(SerialPacket); + void HandleRegionRequest(SerialPacket); + + //TODO: a function that only sends to players in a certain proximity + void PumpPacket(SerialPacket); + ++ //TODO: manage the database ++ //TODO: combat systems ++ + //APIs + UDPNetworkUtility network; + sqlite3* database = nullptr; + lua_State* luaState = nullptr; + + //server tables +- std::map clientMap; +- std::map playerMap; ++ std::map clientMap; ++ std::map playerMap; + + //maps + //TODO: I need to handle multiple map objects +diff --git a/server/server_connections.cpp b/server/server_connections.cpp +new file mode 100644 +index 0000000..2f35566 +--- /dev/null ++++ b/server/server_connections.cpp +@@ -0,0 +1,186 @@ ++/* Copyright: (c) Kayne Ruse 2013, 2014 ++ * ++ * This software is provided 'as-is', without any express or implied ++ * warranty. In no event will the authors be held liable for any damages ++ * arising from the use of this software. ++ * ++ * Permission is granted to anyone to use this software for any purpose, ++ * including commercial applications, and to alter it and redistribute it ++ * freely, subject to the following restrictions: ++ * ++ * 1. The origin of this software must not be misrepresented; you must not ++ * claim that you wrote the original software. If you use this software ++ * in a product, an acknowledgment in the product documentation would be ++ * appreciated but is not required. ++ * ++ * 2. Altered source versions must be plainly marked as such, and must not be ++ * misrepresented as being the original software. ++ * ++ * 3. This notice may not be removed or altered from any source ++ * distribution. ++*/ ++#include "server_application.hpp" ++ ++#include ++#include ++ ++//------------------------- ++//Handle various network input ++//------------------------- ++ ++void ServerApplication::HandleBroadcastRequest(SerialPacket packet) { ++ //pack the server's data ++ packet.meta.type = SerialPacket::Type::BROADCAST_RESPONSE; ++ packet.serverInfo.networkVersion = NETWORK_VERSION; ++ snprintf(packet.serverInfo.name, PACKET_STRING_SIZE, "%s", config["server.name"].c_str()); ++ packet.serverInfo.playerCount = playerMap.size(); ++ ++ //bounce this packet ++ char buffer[PACKET_BUFFER_SIZE]; ++ serialize(&packet, buffer); ++ network.Send(&packet.meta.srcAddress, buffer, PACKET_BUFFER_SIZE); ++} ++ ++void ServerApplication::HandleJoinRequest(SerialPacket packet) { ++ //create the new client ++ ClientEntry newClient; ++ newClient.address = packet.meta.srcAddress; ++ ++ //TODO: move this into the player management code ++ //create the new player ++ PlayerEntry newPlayer; ++ newPlayer.clientIndex = ClientEntry::uidCounter; ++ newPlayer.player = packet.clientInfo.player; ++ newPlayer.handle = packet.clientInfo.handle; ++ newPlayer.avatar = packet.clientInfo.avatar; ++ ++ //send the client their info ++ packet.meta.type = SerialPacket::Type::JOIN_RESPONSE; ++ packet.clientInfo.clientIndex = ClientEntry::uidCounter; ++ packet.clientInfo.playerIndex = PlayerEntry::uidCounter; ++ ++ //bounce this packet ++ char buffer[PACKET_BUFFER_SIZE]; ++ serialize(&packet, buffer); ++ network.Send(&newClient.address, buffer, PACKET_BUFFER_SIZE); ++ ++ //send the new player to all clients ++ packet.meta.type = SerialPacket::Type::PLAYER_NEW; ++ packet.playerInfo.playerIndex = PlayerEntry::uidCounter; ++ strncpy(packet.playerInfo.handle, newPlayer.handle.c_str(), PACKET_STRING_SIZE); ++ strncpy(packet.playerInfo.avatar, newPlayer.avatar.c_str(), PACKET_STRING_SIZE); ++ packet.playerInfo.position = newPlayer.position; ++ packet.playerInfo.motion = newPlayer.motion; ++ PumpPacket(packet); ++ ++ //finished this routine ++ clientMap[ClientEntry::uidCounter] = newClient; ++ playerMap[PlayerEntry::uidCounter] = newPlayer; ++ ClientEntry::uidCounter++; ++ PlayerEntry::uidCounter++; ++ std::cout << "Connect, total: " << clientMap.size() << std::endl; ++} ++ ++void ServerApplication::HandleSynchronize(SerialPacket packet) { ++ //TODO: compensate for large distances ++ ++ //send all the server's data to this client ++ SerialPacket newPacket; ++ char buffer[PACKET_BUFFER_SIZE]; ++ ++ //players ++ newPacket.meta.type = SerialPacket::Type::PLAYER_UPDATE; ++ for (auto& it : playerMap) { ++ //TODO: update this for the expanded PlayerEntry structure ++ 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.mapIndex = it.second.mapIndex; ++ newPacket.playerInfo.position = it.second.position; ++ newPacket.playerInfo.motion = it.second.motion; ++ serialize(&newPacket, buffer); ++ network.Send(&clientMap[packet.clientInfo.clientIndex].address, buffer, PACKET_BUFFER_SIZE); ++ } ++} ++ ++void ServerApplication::HandleDisconnect(SerialPacket packet) { ++ //TODO: authenticate who is disconnecting/kicking ++ //TODO: define the difference between unloading and deletng a player ++ ++ //disconnect the specified client ++ char buffer[PACKET_BUFFER_SIZE]; ++ serialize(&packet, buffer); ++ network.Send(&clientMap[packet.clientInfo.clientIndex].address, buffer, PACKET_BUFFER_SIZE); ++ clientMap.erase(packet.clientInfo.clientIndex); ++ ++ //prep the delete packet ++ SerialPacket delPacket; ++ delPacket.meta.type = SerialPacket::Type::PLAYER_DELETE; ++ ++ //delete server and client side players ++ erase_if(playerMap, [&](std::pair it) -> bool { ++ //find the internal players to delete ++ if (it.second.clientIndex == packet.clientInfo.clientIndex) { ++ //send the delete player command to all clients ++ delPacket.playerInfo.playerIndex = it.first; ++ PumpPacket(delPacket); ++ ++ //delete this player object ++ return true; ++ } ++ ++ //don't delete this player object ++ return false; ++ }); ++ ++ //finished this routine ++ std::cout << "Disconnect, total: " << clientMap.size() << std::endl; ++} ++ ++void ServerApplication::HandleShutdown(SerialPacket packet) { ++ //TODO: authenticate who is shutting the server down ++ ++ //end the server ++ running = false; ++ ++ //disconnect all clients ++ packet.meta.type = SerialPacket::Type::DISCONNECT; ++ packet.clientInfo.clientIndex = -1; ++ PumpPacket(packet); ++ ++ //finished this routine ++ std::cout << "Shutdown signal accepted" << std::endl; ++} ++ ++void ServerApplication::HandlePlayerUpdate(SerialPacket packet) { ++ //TODO: this should be moved elsewhere ++ if (playerMap.find(packet.playerInfo.playerIndex) == playerMap.end()) { ++ throw(std::runtime_error("Cannot update a non-existant player")); ++ } ++ ++ //TODO: the server needs it's own movement system too ++ playerMap[packet.playerInfo.playerIndex].position = packet.playerInfo.position; ++ playerMap[packet.playerInfo.playerIndex].motion = packet.playerInfo.motion; ++ ++ PumpPacket(packet); ++} ++ ++void ServerApplication::HandleRegionRequest(SerialPacket packet) { ++ //TODO: this should be moved elsewhere ++ packet.meta.type = SerialPacket::Type::REGION_CONTENT; ++ packet.regionInfo.region = regionPager.GetRegion(packet.regionInfo.x, packet.regionInfo.y); ++ ++ //send the content ++ char buffer[PACKET_BUFFER_SIZE]; ++ serialize(&packet, buffer); ++ network.Send(&packet.meta.srcAddress, buffer, PACKET_BUFFER_SIZE); ++} ++ ++void ServerApplication::PumpPacket(SerialPacket packet) { ++ //NOTE: I don't really like this, but it'll do for now ++ char buffer[PACKET_BUFFER_SIZE]; ++ serialize(&packet, buffer); ++ for (auto& it : clientMap) { ++ network.Send(&it.second.address, buffer, PACKET_BUFFER_SIZE); ++ } ++} +diff --git a/server/server_internals.cpp b/server/server_internals.cpp +new file mode 100644 +index 0000000..e1a9cb9 +--- /dev/null ++++ b/server/server_internals.cpp +@@ -0,0 +1,168 @@ ++/* Copyright: (c) Kayne Ruse 2014 ++ * ++ * This software is provided 'as-is', without any express or implied ++ * warranty. In no event will the authors be held liable for any damages ++ * arising from the use of this software. ++ * ++ * Permission is granted to anyone to use this software for any purpose, ++ * including commercial applications, and to alter it and redistribute it ++ * freely, subject to the following restrictions: ++ * ++ * 1. The origin of this software must not be misrepresented; you must not ++ * claim that you wrote the original software. If you use this software ++ * in a product, an acknowledgment in the product documentation would be ++ * appreciated but is not required. ++ * ++ * 2. Altered source versions must be plainly marked as such, and must not be ++ * misrepresented as being the original software. ++ * ++ * 3. This notice may not be removed or altered from any source ++ * distribution. ++*/ ++#include "server_application.hpp" ++ ++#include "server_utility.hpp" ++ ++#include ++#include ++#include ++ ++//------------------------- ++//Define the public members ++//------------------------- ++ ++void ServerApplication::Init(int argc, char** argv) { ++ //NOTE: I might need to rearrange the init process so that lua & SQL can interact with the map system as needed. ++ std::cout << "Beginning startup" << std::endl; ++ ++ //initial setup ++ config.Load("rsc\\config.cfg"); ++ ++ //Init SDL ++ if (SDL_Init(0)) { ++ throw(std::runtime_error("Failed to initialize SDL")); ++ } ++ std::cout << "Initialized SDL" << std::endl; ++ ++ //Init SDL_net ++ if (SDLNet_Init()) { ++ throw(std::runtime_error("Failed to initialize SDL_net")); ++ } ++ network.Open(config.Int("server.port"), PACKET_BUFFER_SIZE); ++ std::cout << "Initialized SDL_net" << std::endl; ++ ++ //Init SQL ++ int ret = sqlite3_open_v2(config["server.dbname"].c_str(), &database, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, nullptr); ++ if (ret != SQLITE_OK || !database) { ++ throw(std::runtime_error(std::string() + "Failed to initialize SQL: " + sqlite3_errmsg(database) )); ++ } ++ std::cout << "Initialized SQL" << std::endl; ++ ++ //setup the database ++ if (runSQLScript(database, config["dir.scripts"] + "setup_server.sql")) { ++ throw(std::runtime_error("Failed to initialize SQL's setup script")); ++ } ++ std::cout << "Initialized SQL's setup script" << std::endl; ++ ++ //lua ++ luaState = luaL_newstate(); ++ if (!luaState) { ++ throw(std::runtime_error("Failed to initialize lua")); ++ } ++ luaL_openlibs(luaState); ++ std::cout << "Initialized lua" << std::endl; ++ ++ //run the startup script ++ if (luaL_dofile(luaState, (config["dir.scripts"] + "setup_server.lua").c_str())) { ++ throw(std::runtime_error(std::string() + "Failed to initialize lua's setup script: " + lua_tostring(luaState, -1) )); ++ } ++ std::cout << "Initialized lua's setup script" << std::endl; ++ ++ //setup the map object ++ regionPager.GetAllocator()->SetLuaState(luaState); ++ regionPager.GetFormat()->SetLuaState(luaState); ++ //TODO: config parameter ++ regionPager.GetFormat()->SetSaveDir("save/mapname/"); ++ ++ std::cout << "Initialized the map system" << std::endl; ++ std::cout << "\tsizeof(SerialPacket): " << sizeof(SerialPacket) << std::endl; ++ std::cout << "\tPACKET_BUFFER_SIZE: " << PACKET_BUFFER_SIZE << std::endl; ++ ++ //finalize the startup ++ std::cout << "Startup completed successfully" << std::endl; ++ ++ //debugging ++ // ++} ++ ++void ServerApplication::Proc() { ++ SerialPacket packet; ++ while(running) { ++ //suck in the waiting packets & process them ++ while(network.Receive()) { ++ //get the packet ++ deserialize(&packet, network.GetInData()); ++ //cache the source address ++ packet.meta.srcAddress = network.GetInPacket()->address; ++ //we need to go deeper ++ HandlePacket(packet); ++ } ++ //update the internals ++ //TODO: update the internals i.e. player positions ++ //give the computer a break ++ SDL_Delay(10); ++ } ++} ++ ++void ServerApplication::Quit() { ++ std::cout << "Shutting down" << std::endl; ++ ++ //save the server state ++ //TODO: save the existing players ++ ++ //empty the members ++ regionPager.UnloadAll(); ++ ++ //APIs ++ lua_close(luaState); ++ sqlite3_close_v2(database); ++ network.Close(); ++ SDLNet_Quit(); ++ SDL_Quit(); ++ ++ std::cout << "Shutdown finished" << std::endl; ++} ++ ++//------------------------- ++//Define the uber switch ++//------------------------- ++ ++void ServerApplication::HandlePacket(SerialPacket packet) { ++ switch(packet.meta.type) { ++ case SerialPacket::Type::BROADCAST_REQUEST: ++ HandleBroadcastRequest(packet); ++ break; ++ case SerialPacket::Type::JOIN_REQUEST: ++ HandleJoinRequest(packet); ++ break; ++ case SerialPacket::Type::SYNCHRONIZE: ++ HandleSynchronize(packet); ++ break; ++ case SerialPacket::Type::DISCONNECT: ++ HandleDisconnect(packet); ++ break; ++ case SerialPacket::Type::SHUTDOWN: ++ HandleShutdown(packet); ++ break; ++ case SerialPacket::Type::PLAYER_UPDATE: ++ HandlePlayerUpdate(packet); ++ break; ++ case SerialPacket::Type::REGION_REQUEST: ++ HandleRegionRequest(packet); ++ break; ++ //handle errors ++ default: ++ throw(std::runtime_error("Unknown SerialPacket::Type encountered")); ++ break; ++ } ++} +diff --git a/todo.txt b/todo.txt +new file mode 100644 +index 0000000..8f42fcd +--- /dev/null ++++ b/todo.txt +@@ -0,0 +1,30 @@ ++Please note that due to modificatons, the indicated line may be incorrect. ++ ++## TODO ++server_application.hpp:74 a function that only sends to players in a certain proximity ++server_application.hpp:94 I need to handle multiple map objects ++server_application.hpp:95 Unload regions that are distant from any players ++server_internals.cpp:84 config parameter ++server_internals.cpp:111 update the internals i.e. player positions ++server_internals.cpp:121 save the existing players ++server_networking.cpp:68 finish the player's initialization ++server_networking.cpp:77 compensate for large distances ++server_networking.cpp:86 update this for the expanded PlayerEntry structure ++server_networking.cpp:99 authenticate who is disconnecting/kicking ++server_networking.cpp:100 define the difference between unloading and deletng a player ++server_networking.cpp:133 authenticate who is shitting the server down ++server_networking.cpp:152 the server needs it's own movement system too ++player_character.hpp:28 correct the PlayerCharacter class and it's movement system ++in_world.cpp:62 add the tilesheet to the map system? ++in_world.cpp:104 sort the players and entities by Y position ++in_world.cpp:280 account for map index ++in_world.cpp:307 set the handle ++in_world.cpp:398 convert this into a more generic function?; using parameters for the bounds ++in_world.hpp:108 Fix the camera ++lobby_menu.cpp:96 I need a proper UI system for the entire client and the editor ++lobby_menu.cpp:102 draw headers for the server list ++lobby_menu.cpp:122 ping? ++bbox.hpp:29 This is supposed to interact with the vector ++region_pager.hpp:49 delete existing regions ++serial_packet.hpp:71 combat packets ++setup_server.sql:1 The SQL startup script needs revising diff --git a/misc/refactoring fun/server_connections.diff b/misc/refactoring fun/server_connections.diff new file mode 100644 index 0000000..d68326e --- /dev/null +++ b/misc/refactoring fun/server_connections.diff @@ -0,0 +1,394 @@ +diff --git a/server/server_connections.cpp b/server/server_connections.cpp +index 1a96d5b..2f35566 100644 +--- a/server/server_connections.cpp ++++ b/server/server_connections.cpp +@@ -21,212 +21,106 @@ + */ + #include "server_application.hpp" + +-#include "utility.hpp" +- + #include + #include +-#include +- +-//------------------------- +-//Define the public members +-//------------------------- +- +-void ServerApplication::Init(int argc, char** argv) { +- //NOTE: I might need to rearrange the init process so that lua & SQL can interact with the map system as needed. +- std::cout << "Beginning startup" << std::endl; +- +- //initial setup +- ClientEntry::uidCounter = 0; +- PlayerEntry::uidCounter = 0; +- config.Load("rsc\\config.cfg"); +- +- //Init SDL +- if (SDL_Init(0)) { +- throw(std::runtime_error("Failed to initialize SDL")); +- } +- std::cout << "Initialized SDL" << std::endl; +- +- //Init SDL_net +- if (SDLNet_Init()) { +- throw(std::runtime_error("Failed to initialize SDL_net")); +- } +- network.Open(config.Int("server.port"), PACKET_BUFFER_SIZE); +- std::cout << "Initialized SDL_net" << std::endl; +- +- //Init SQL +- int ret = sqlite3_open_v2(config["server.dbname"].c_str(), &database, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, nullptr); +- if (ret != SQLITE_OK || !database) { +- throw(std::runtime_error(std::string() + "Failed to initialize SQL: " + sqlite3_errmsg(database) )); +- } +- std::cout << "Initialized SQL" << std::endl; +- +- //setup the database +- if (runSQLScript(database, config["dir.scripts"] + "setup_server.sql")) { +- throw(std::runtime_error("Failed to initialize SQL's setup script")); +- } +- std::cout << "Initialized SQL's setup script" << std::endl; +- +- //lua +- luaState = luaL_newstate(); +- if (!luaState) { +- throw(std::runtime_error("Failed to initialize lua")); +- } +- luaL_openlibs(luaState); +- std::cout << "Initialized lua" << std::endl; +- +- //run the startup script +- if (luaL_dofile(luaState, (config["dir.scripts"] + "setup_server.lua").c_str())) { +- throw(std::runtime_error(std::string() + "Failed to initialize lua's setup script: " + lua_tostring(luaState, -1) )); +- } +- std::cout << "Initialized lua's setup script" << std::endl; +- +- //setup the map object +- regionPager.GetAllocator()->SetLuaState(luaState); +- regionPager.GetFormat()->SetLuaState(luaState); +- //TODO: config parameter +- regionPager.GetFormat()->SetSaveDir("save/mapname/"); +- +- std::cout << "Initialized the map system" << std::endl; +- std::cout << "\tsizeof(SerialPacket): " << sizeof(SerialPacket) << std::endl; +- std::cout << "\tPACKET_BUFFER_SIZE: " << PACKET_BUFFER_SIZE << std::endl; +- +- //finalize the startup +- std::cout << "Startup completed successfully" << std::endl; +- +- //debugging +- // +-} +- +-void ServerApplication::Proc() { +- SerialPacket packet; +- while(running) { +- //suck in the waiting packets & process them +- while(network.Receive()) { +- //get the packet +- deserialize(&packet, network.GetInData()); +- //cache the source address +- packet.meta.srcAddress = network.GetInPacket()->address; +- //we need to go deeper +- HandlePacket(packet); +- } +- //give the computer a break +- SDL_Delay(10); +- } +-} +- +-void ServerApplication::Quit() { +- std::cout << "Shutting down" << std::endl; +- //empty the members +- regionPager.UnloadAll(); +- +- //APIs +- lua_close(luaState); +- sqlite3_close_v2(database); +- network.Close(); +- SDLNet_Quit(); +- SDL_Quit(); +- std::cout << "Shutdown finished" << std::endl; +-} +- +-//------------------------- +-//Define the uber switch +-//------------------------- +- +-void ServerApplication::HandlePacket(SerialPacket packet) { +- switch(packet.meta.type) { +- case SerialPacket::Type::BROADCAST_REQUEST: +- HandleBroadcastRequest(packet); +- break; +- case SerialPacket::Type::JOIN_REQUEST: +- HandleJoinRequest(packet); +- break; +- case SerialPacket::Type::DISCONNECT: +- HandleDisconnect(packet); +- break; +- case SerialPacket::Type::SYNCHRONIZE: +- HandleSynchronize(packet); +- break; +- case SerialPacket::Type::SHUTDOWN: +- HandleShutdown(packet); +- break; +- case SerialPacket::Type::PLAYER_NEW: +- HandlePlayerNew(packet); +- break; +- case SerialPacket::Type::PLAYER_DELETE: +- HandlePlayerDelete(packet); +- break; +- case SerialPacket::Type::PLAYER_UPDATE: +- HandlePlayerUpdate(packet); +- break; +- case SerialPacket::Type::REGION_REQUEST: +- HandleRegionRequest(packet); +- break; +- //handle errors +- default: +- throw(std::runtime_error("Unknown SerialPacket::Type encountered")); +- break; +- } +-} + + //------------------------- + //Handle various network input + //------------------------- + + void ServerApplication::HandleBroadcastRequest(SerialPacket packet) { +- //send back the server's metadata ++ //pack the server's data + packet.meta.type = SerialPacket::Type::BROADCAST_RESPONSE; +- +- //pack the data ++ packet.serverInfo.networkVersion = NETWORK_VERSION; + snprintf(packet.serverInfo.name, PACKET_STRING_SIZE, "%s", config["server.name"].c_str()); + packet.serverInfo.playerCount = playerMap.size(); +- packet.serverInfo.regionWidth = REGION_WIDTH; +- packet.serverInfo.regionHeight = REGION_HEIGHT; +- packet.serverInfo.regionDepth = REGION_DEPTH; + +- //send the data ++ //bounce this packet + char buffer[PACKET_BUFFER_SIZE]; + serialize(&packet, buffer); + network.Send(&packet.meta.srcAddress, buffer, PACKET_BUFFER_SIZE); + } + + void ServerApplication::HandleJoinRequest(SerialPacket packet) { +- //register the new client ++ //create the new client + ClientEntry newClient; + newClient.address = packet.meta.srcAddress; +- clientMap[ClientEntry::uidCounter] = newClient; + +- //send the client their index +- char buffer[PACKET_BUFFER_SIZE]; ++ //TODO: move this into the player management code ++ //create the new player ++ PlayerEntry newPlayer; ++ newPlayer.clientIndex = ClientEntry::uidCounter; ++ newPlayer.player = packet.clientInfo.player; ++ newPlayer.handle = packet.clientInfo.handle; ++ newPlayer.avatar = packet.clientInfo.avatar; ++ ++ //send the client their info + packet.meta.type = SerialPacket::Type::JOIN_RESPONSE; +- packet.clientInfo.index = ClientEntry::uidCounter; +- serialize(&packet, buffer); ++ packet.clientInfo.clientIndex = ClientEntry::uidCounter; ++ packet.clientInfo.playerIndex = PlayerEntry::uidCounter; + + //bounce this packet ++ char buffer[PACKET_BUFFER_SIZE]; ++ serialize(&packet, buffer); + network.Send(&newClient.address, buffer, PACKET_BUFFER_SIZE); + ++ //send the new player to all clients ++ packet.meta.type = SerialPacket::Type::PLAYER_NEW; ++ packet.playerInfo.playerIndex = PlayerEntry::uidCounter; ++ strncpy(packet.playerInfo.handle, newPlayer.handle.c_str(), PACKET_STRING_SIZE); ++ strncpy(packet.playerInfo.avatar, newPlayer.avatar.c_str(), PACKET_STRING_SIZE); ++ packet.playerInfo.position = newPlayer.position; ++ packet.playerInfo.motion = newPlayer.motion; ++ PumpPacket(packet); ++ + //finished this routine ++ clientMap[ClientEntry::uidCounter] = newClient; ++ playerMap[PlayerEntry::uidCounter] = newPlayer; + ClientEntry::uidCounter++; ++ PlayerEntry::uidCounter++; + std::cout << "Connect, total: " << clientMap.size() << std::endl; + } + ++void ServerApplication::HandleSynchronize(SerialPacket packet) { ++ //TODO: compensate for large distances ++ ++ //send all the server's data to this client ++ SerialPacket newPacket; ++ char buffer[PACKET_BUFFER_SIZE]; ++ ++ //players ++ newPacket.meta.type = SerialPacket::Type::PLAYER_UPDATE; ++ for (auto& it : playerMap) { ++ //TODO: update this for the expanded PlayerEntry structure ++ 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.mapIndex = it.second.mapIndex; ++ newPacket.playerInfo.position = it.second.position; ++ newPacket.playerInfo.motion = it.second.motion; ++ serialize(&newPacket, buffer); ++ network.Send(&clientMap[packet.clientInfo.clientIndex].address, buffer, PACKET_BUFFER_SIZE); ++ } ++} ++ + void ServerApplication::HandleDisconnect(SerialPacket packet) { + //TODO: authenticate who is disconnecting/kicking ++ //TODO: define the difference between unloading and deletng a player + + //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); ++ network.Send(&clientMap[packet.clientInfo.clientIndex].address, buffer, PACKET_BUFFER_SIZE); ++ clientMap.erase(packet.clientInfo.clientIndex); + + //prep the delete packet + SerialPacket delPacket; + delPacket.meta.type = SerialPacket::Type::PLAYER_DELETE; + +- //TODO: can this use DeletePlayer() instead? + //delete server and client side players +- erase_if(playerMap, [&](std::pair it) -> bool { ++ erase_if(playerMap, [&](std::pair it) -> bool { + //find the internal players to delete +- if (it.second.clientIndex == packet.clientInfo.index) { ++ if (it.second.clientIndex == packet.clientInfo.clientIndex) { + //send the delete player command to all clients + delPacket.playerInfo.playerIndex = it.first; + PumpPacket(delPacket); +@@ -243,102 +137,23 @@ void ServerApplication::HandleDisconnect(SerialPacket packet) { + std::cout << "Disconnect, total: " << clientMap.size() << std::endl; + } + +-void ServerApplication::HandleSynchronize(SerialPacket packet) { +- //TODO: compensate for large distances +- +- //send all the server's data to this client +- SerialPacket newPacket; +- char buffer[PACKET_BUFFER_SIZE]; +- +- //players +- newPacket.meta.type = SerialPacket::Type::PLAYER_UPDATE; +- for (auto& it : playerMap) { +- //TODO: update this for the expanded PlayerEntry structure +- 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; +- serialize(&newPacket, buffer); +- network.Send(&clientMap[packet.clientInfo.index].address, buffer, PACKET_BUFFER_SIZE); +- } +-} +- + void ServerApplication::HandleShutdown(SerialPacket packet) { ++ //TODO: authenticate who is shutting the server down ++ + //end the server + running = false; + + //disconnect all clients + packet.meta.type = SerialPacket::Type::DISCONNECT; ++ packet.clientInfo.clientIndex = -1; + PumpPacket(packet); + + //finished this routine + std::cout << "Shutdown signal accepted" << std::endl; + } + +-void ServerApplication::HandlePlayerNew(SerialPacket packet) { +- //register the new PlayerEntry +- //NOTE: assigning each field one-by-one so adding or moving a field doesn't break this code +- PlayerEntry newPlayer; +- +- //metadata +- newPlayer.clientIndex = packet.playerInfo.clientIndex; +- newPlayer.handle = packet.playerInfo.handle; +- newPlayer.avatar = packet.playerInfo.avatar; +- +- //position +- newPlayer.mapIndex = 0; +- newPlayer.position = {0,0}; +- newPlayer.motion = {0,0}; +- newPlayer.bbox = {0, 0, 0, 0}; +- +- //TODO: Add the statistic creation code here +- +- //push this player +- playerMap[PlayerEntry::uidCounter] = newPlayer; +- +- //send the client their info +- packet.playerInfo.playerIndex = PlayerEntry::uidCounter; +- packet.playerInfo.position = newPlayer.position; +- packet.playerInfo.motion = newPlayer.motion; +- +- //actually send to everyone +- PumpPacket(packet); +- +- //finish this routine +- PlayerEntry::uidCounter++; +-} +- +-//TODO: differentiate between delete and unload +-void ServerApplication::HandlePlayerDelete(SerialPacket packet) { +- //TODO: authenticate who is deleting this player +- if (playerMap.find(packet.playerInfo.playerIndex) == playerMap.end()) { +- throw(std::runtime_error("Cannot delete a non-existant player")); +- } +- +- //TODO: remove the deleted player from the database? +- +- //prep the delete packet +- SerialPacket delPacket; +- delPacket.meta.type = SerialPacket::Type::PLAYER_DELETE; +- +- //delete the specified playerEntry +- erase_if(playerMap, [&](std::pair it) -> bool { +- //find the specified PlayerEntry +- if (it.first == packet.playerInfo.playerIndex) { +- //send to all +- delPacket.playerInfo.playerIndex = it.first; +- PumpPacket(delPacket); +- +- //delete this player +- return true; +- } +- //skip this player +- return false; +- }); +-} +- + void ServerApplication::HandlePlayerUpdate(SerialPacket packet) { ++ //TODO: this should be moved elsewhere + if (playerMap.find(packet.playerInfo.playerIndex) == playerMap.end()) { + throw(std::runtime_error("Cannot update a non-existant player")); + } +@@ -351,9 +166,12 @@ void ServerApplication::HandlePlayerUpdate(SerialPacket packet) { + } + + void ServerApplication::HandleRegionRequest(SerialPacket packet) { +- char buffer[PACKET_BUFFER_SIZE]; ++ //TODO: this should be moved elsewhere + packet.meta.type = SerialPacket::Type::REGION_CONTENT; + packet.regionInfo.region = regionPager.GetRegion(packet.regionInfo.x, packet.regionInfo.y); ++ ++ //send the content ++ char buffer[PACKET_BUFFER_SIZE]; + serialize(&packet, buffer); + network.Send(&packet.meta.srcAddress, buffer, PACKET_BUFFER_SIZE); + } diff --git a/misc/refactoring fun/server_internals.diff b/misc/refactoring fun/server_internals.diff new file mode 100644 index 0000000..f7836fb --- /dev/null +++ b/misc/refactoring fun/server_internals.diff @@ -0,0 +1,284 @@ +diff --git a/server/server_internals.cpp b/server/server_internals.cpp +index 1a96d5b..e1a9cb9 100644 +--- a/server/server_internals.cpp ++++ b/server/server_internals.cpp +@@ -1,4 +1,4 @@ +-/* Copyright: (c) Kayne Ruse 2013, 2014 ++/* 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 +@@ -21,7 +21,7 @@ + */ + #include "server_application.hpp" + +-#include "utility.hpp" ++#include "server_utility.hpp" + + #include + #include +@@ -36,8 +36,6 @@ void ServerApplication::Init(int argc, char** argv) { + std::cout << "Beginning startup" << std::endl; + + //initial setup +- ClientEntry::uidCounter = 0; +- PlayerEntry::uidCounter = 0; + config.Load("rsc\\config.cfg"); + + //Init SDL +@@ -109,6 +107,8 @@ void ServerApplication::Proc() { + //we need to go deeper + HandlePacket(packet); + } ++ //update the internals ++ //TODO: update the internals i.e. player positions + //give the computer a break + SDL_Delay(10); + } +@@ -116,6 +116,10 @@ void ServerApplication::Proc() { + + void ServerApplication::Quit() { + std::cout << "Shutting down" << std::endl; ++ ++ //save the server state ++ //TODO: save the existing players ++ + //empty the members + regionPager.UnloadAll(); + +@@ -125,6 +129,7 @@ void ServerApplication::Quit() { + network.Close(); + SDLNet_Quit(); + SDL_Quit(); ++ + std::cout << "Shutdown finished" << std::endl; + } + +@@ -140,21 +145,15 @@ void ServerApplication::HandlePacket(SerialPacket packet) { + case SerialPacket::Type::JOIN_REQUEST: + HandleJoinRequest(packet); + break; +- case SerialPacket::Type::DISCONNECT: +- HandleDisconnect(packet); +- break; + case SerialPacket::Type::SYNCHRONIZE: + HandleSynchronize(packet); + break; ++ case SerialPacket::Type::DISCONNECT: ++ HandleDisconnect(packet); ++ break; + case SerialPacket::Type::SHUTDOWN: + HandleShutdown(packet); + break; +- case SerialPacket::Type::PLAYER_NEW: +- HandlePlayerNew(packet); +- break; +- case SerialPacket::Type::PLAYER_DELETE: +- HandlePlayerDelete(packet); +- break; + case SerialPacket::Type::PLAYER_UPDATE: + HandlePlayerUpdate(packet); + break; +@@ -167,202 +166,3 @@ void ServerApplication::HandlePacket(SerialPacket packet) { + break; + } + } +- +-//------------------------- +-//Handle various network input +-//------------------------- +- +-void ServerApplication::HandleBroadcastRequest(SerialPacket packet) { +- //send back the server's metadata +- packet.meta.type = SerialPacket::Type::BROADCAST_RESPONSE; +- +- //pack the data +- snprintf(packet.serverInfo.name, PACKET_STRING_SIZE, "%s", config["server.name"].c_str()); +- packet.serverInfo.playerCount = playerMap.size(); +- packet.serverInfo.regionWidth = REGION_WIDTH; +- packet.serverInfo.regionHeight = REGION_HEIGHT; +- packet.serverInfo.regionDepth = REGION_DEPTH; +- +- //send the data +- char buffer[PACKET_BUFFER_SIZE]; +- serialize(&packet, buffer); +- network.Send(&packet.meta.srcAddress, buffer, PACKET_BUFFER_SIZE); +-} +- +-void ServerApplication::HandleJoinRequest(SerialPacket packet) { +- //register the new client +- ClientEntry newClient; +- newClient.address = packet.meta.srcAddress; +- clientMap[ClientEntry::uidCounter] = newClient; +- +- //send the client their index +- char buffer[PACKET_BUFFER_SIZE]; +- packet.meta.type = SerialPacket::Type::JOIN_RESPONSE; +- packet.clientInfo.index = ClientEntry::uidCounter; +- serialize(&packet, buffer); +- +- //bounce this packet +- network.Send(&newClient.address, buffer, PACKET_BUFFER_SIZE); +- +- //finished this routine +- ClientEntry::uidCounter++; +- std::cout << "Connect, total: " << clientMap.size() << std::endl; +-} +- +-void ServerApplication::HandleDisconnect(SerialPacket packet) { +- //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); +- +- //prep the delete packet +- SerialPacket delPacket; +- delPacket.meta.type = SerialPacket::Type::PLAYER_DELETE; +- +- //TODO: can this use DeletePlayer() instead? +- //delete server and client side players +- erase_if(playerMap, [&](std::pair it) -> bool { +- //find the internal players to delete +- if (it.second.clientIndex == packet.clientInfo.index) { +- //send the delete player command to all clients +- delPacket.playerInfo.playerIndex = it.first; +- PumpPacket(delPacket); +- +- //delete this player object +- return true; +- } +- +- //don't delete this player object +- return false; +- }); +- +- //finished this routine +- std::cout << "Disconnect, total: " << clientMap.size() << std::endl; +-} +- +-void ServerApplication::HandleSynchronize(SerialPacket packet) { +- //TODO: compensate for large distances +- +- //send all the server's data to this client +- SerialPacket newPacket; +- char buffer[PACKET_BUFFER_SIZE]; +- +- //players +- newPacket.meta.type = SerialPacket::Type::PLAYER_UPDATE; +- for (auto& it : playerMap) { +- //TODO: update this for the expanded PlayerEntry structure +- 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; +- serialize(&newPacket, buffer); +- network.Send(&clientMap[packet.clientInfo.index].address, buffer, PACKET_BUFFER_SIZE); +- } +-} +- +-void ServerApplication::HandleShutdown(SerialPacket packet) { +- //end the server +- running = false; +- +- //disconnect all clients +- packet.meta.type = SerialPacket::Type::DISCONNECT; +- PumpPacket(packet); +- +- //finished this routine +- std::cout << "Shutdown signal accepted" << std::endl; +-} +- +-void ServerApplication::HandlePlayerNew(SerialPacket packet) { +- //register the new PlayerEntry +- //NOTE: assigning each field one-by-one so adding or moving a field doesn't break this code +- PlayerEntry newPlayer; +- +- //metadata +- newPlayer.clientIndex = packet.playerInfo.clientIndex; +- newPlayer.handle = packet.playerInfo.handle; +- newPlayer.avatar = packet.playerInfo.avatar; +- +- //position +- newPlayer.mapIndex = 0; +- newPlayer.position = {0,0}; +- newPlayer.motion = {0,0}; +- newPlayer.bbox = {0, 0, 0, 0}; +- +- //TODO: Add the statistic creation code here +- +- //push this player +- playerMap[PlayerEntry::uidCounter] = newPlayer; +- +- //send the client their info +- packet.playerInfo.playerIndex = PlayerEntry::uidCounter; +- packet.playerInfo.position = newPlayer.position; +- packet.playerInfo.motion = newPlayer.motion; +- +- //actually send to everyone +- PumpPacket(packet); +- +- //finish this routine +- PlayerEntry::uidCounter++; +-} +- +-//TODO: differentiate between delete and unload +-void ServerApplication::HandlePlayerDelete(SerialPacket packet) { +- //TODO: authenticate who is deleting this player +- if (playerMap.find(packet.playerInfo.playerIndex) == playerMap.end()) { +- throw(std::runtime_error("Cannot delete a non-existant player")); +- } +- +- //TODO: remove the deleted player from the database? +- +- //prep the delete packet +- SerialPacket delPacket; +- delPacket.meta.type = SerialPacket::Type::PLAYER_DELETE; +- +- //delete the specified playerEntry +- erase_if(playerMap, [&](std::pair it) -> bool { +- //find the specified PlayerEntry +- if (it.first == packet.playerInfo.playerIndex) { +- //send to all +- delPacket.playerInfo.playerIndex = it.first; +- PumpPacket(delPacket); +- +- //delete this player +- return true; +- } +- //skip this player +- return false; +- }); +-} +- +-void ServerApplication::HandlePlayerUpdate(SerialPacket packet) { +- if (playerMap.find(packet.playerInfo.playerIndex) == playerMap.end()) { +- throw(std::runtime_error("Cannot update a non-existant player")); +- } +- +- //TODO: the server needs it's own movement system too +- playerMap[packet.playerInfo.playerIndex].position = packet.playerInfo.position; +- playerMap[packet.playerInfo.playerIndex].motion = packet.playerInfo.motion; +- +- PumpPacket(packet); +-} +- +-void ServerApplication::HandleRegionRequest(SerialPacket packet) { +- char buffer[PACKET_BUFFER_SIZE]; +- packet.meta.type = SerialPacket::Type::REGION_CONTENT; +- packet.regionInfo.region = regionPager.GetRegion(packet.regionInfo.x, packet.regionInfo.y); +- serialize(&packet, buffer); +- network.Send(&packet.meta.srcAddress, buffer, PACKET_BUFFER_SIZE); +-} +- +-void ServerApplication::PumpPacket(SerialPacket packet) { +- //NOTE: I don't really like this, but it'll do for now +- char buffer[PACKET_BUFFER_SIZE]; +- serialize(&packet, buffer); +- for (auto& it : clientMap) { +- network.Send(&it.second.address, buffer, PACKET_BUFFER_SIZE); +- } +-}