diff --git a/client/player_character.hpp b/client/player_character.hpp index 1f6bc7a..9dde742 100644 --- a/client/player_character.hpp +++ b/client/player_character.hpp @@ -25,6 +25,7 @@ #include "vector2.hpp" #include "sprite_sheet.hpp" +//TODO: correct the PlayerCharacter class and it's movement system class PlayerCharacter { public: enum class Direction { diff --git a/client/scenes/in_world.cpp b/client/scenes/in_world.cpp index 4f9e002..936be7d 100644 --- a/client/scenes/in_world.cpp +++ b/client/scenes/in_world.cpp @@ -404,76 +404,34 @@ void InWorld::RequestRegion(int x, int y) { //Utilities //------------------------- -int InWorld::CheckBufferDistance(Region* const region) { - /* TODO: Remove InWorld::CheckBufferDistance(), and replace it with a simpler system - * DOCUMENTATION - * This algorithm is extremely complex and involed, but initial tests show - * that it gives the right answers. The purpose is to determine how far off screen - * a certain region is, so that it can be unloaded when necessary. - * - * If the region is actually onscreen, then there's no reason to run the rest, so - * the algorithm corrects for the camera's location, before checking the bounds of - * the screen. - * - * The next part is tricky. If X or Y is negative, then it is divided by the - * graphical size of the regions, resulting in a usable integer, representing how - * far from the screen it is in "region units". If, however, X or Y is larger than - * 0, than first, the size of the screen is subtracted from that variable, before - * it is then divided by the graphical size of a region. Finally, to compensate for - * the off by one error, 1 is added at the end. - * - * Since only the magnitude of the distance in either direction is needed, this - * method returns the largest absolute value of X or Y. - * - * The final result of this algorithm is an integer representing how far, rounded - * up, a certain region is from the screen's edges in any direction, measured in - * "region units". This algorithm may be flawed, in which case, I recommend simply - * replacing it with a boolean check, to see if the region's distance from the player - * is larger than a certain value. This algorithm lacks the advantages I initially - * expected, so that may be beneficial at some point. - */ - - //locations relative to the camera - int x = region->GetX() - camera.x; - int y = region->GetY() - camera.y; - - //if the region is visible, return -1 - if (x >= -REGION_WIDTH * tileSheet.GetTileW() && x < camera.width && - y >= -REGION_HEIGHT * tileSheet.GetTileH() && y < camera.height) { - return -1; - } - - //prune the screen's area from the algorithm; get the pseudo-indexes - if (x < 0) x /= (REGION_WIDTH * tileSheet.GetTileW()); - if (y < 0) y /= (REGION_HEIGHT * tileSheet.GetTileH()); - if (x > 0) x = (x - camera.width) / (REGION_WIDTH * tileSheet.GetTileW()) + 1; - if (y > 0) y = (y - camera.height) / (REGION_HEIGHT * tileSheet.GetTileH()) + 1; - - //return the pseudo-index with the greatest magnitude - return std::max(abs(x), abs(y)); -} - -//TODO: eew ugly +//TODO: convert this into a more generic function?; using parameters for the bounds void InWorld::UpdateMap() { + //these represent the zone of regions that the client needs loaded, including the mandatory buffers (+1/-1) + int xStart = snapToBase(REGION_WIDTH, camera.x/tileSheet.GetTileW()) - REGION_WIDTH; + int xEnd = snapToBase(REGION_WIDTH, (camera.x+camera.width)/tileSheet.GetTileW()) + REGION_WIDTH; + + int yStart = snapToBase(REGION_HEIGHT, camera.y/tileSheet.GetTileH()) - REGION_HEIGHT; + int yEnd = snapToBase(REGION_HEIGHT, (camera.y+camera.height)/tileSheet.GetTileH()) + REGION_HEIGHT; + //prune distant regions for (auto it = regionPager.GetContainer()->begin(); it != regionPager.GetContainer()->end(); /* EMPTY */) { - if (CheckBufferDistance(*it) > 2) { - regionPager.UnloadRegion((*it)->GetX(), (*it)->GetY()); + //check if the region is outside off this area + if ((*it)->GetX() < xStart || (*it)->GetX() > xEnd || (*it)->GetY() < yStart || (*it)->GetY() > yEnd) { - //reset - it = regionPager.GetContainer()->begin(); + //clunky, but the alternative was time consuming + int tmpX = (*it)->GetX(); + int tmpY = (*it)->GetY(); + ++it; + + regionPager.UnloadRegion(tmpX, tmpY); continue; } ++it; } - //TODO: make the region units official? - int regionUnitX = REGION_WIDTH * tileSheet.GetTileW(); - int regionUnitY = REGION_HEIGHT * tileSheet.GetTileH(); - - //request empty regions, including buffers (-1 & +1 region unit) - for (int i = snapToBase(regionUnitX, camera.x - regionUnitX); i <= snapToBase(regionUnitX, camera.x + camera.width + regionUnitX); i += regionUnitX) { - for (int j = snapToBase(regionUnitY, camera.y - regionUnitY); j <= snapToBase(regionUnitY, camera.y + camera.height + regionUnitY); j += regionUnitY) { + //request empty regions within this zone + 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); } diff --git a/client/scenes/in_world.hpp b/client/scenes/in_world.hpp index 899ee8a..bb0cc5c 100644 --- a/client/scenes/in_world.hpp +++ b/client/scenes/in_world.hpp @@ -86,7 +86,6 @@ protected: void RequestRegion(int x, int y); //utilities - int CheckBufferDistance(Region* const); void UpdateMap(); //globals diff --git a/client/scenes/lobby_menu.cpp b/client/scenes/lobby_menu.cpp index 2e738b4..1000af4 100644 --- a/client/scenes/lobby_menu.cpp +++ b/client/scenes/lobby_menu.cpp @@ -22,6 +22,7 @@ #include "lobby_menu.hpp" #include "channels.hpp" +#include "utility.hpp" #include @@ -91,9 +92,13 @@ void LobbyMenu::FrameEnd() { } void LobbyMenu::Render(SDL_Surface* const screen) { + //TODO: I need a proper UI system for the entire client and the editor + //UI search.DrawTo(screen); join.DrawTo(screen); back.DrawTo(screen); + + //TODO: draw headers for the server list for (int i = 0; i < serverInfo.size(); i++) { //draw the selected server's highlight if (selection == &serverInfo[i]) { @@ -104,6 +109,16 @@ void LobbyMenu::Render(SDL_Surface* const screen) { //draw the server name font.DrawStringTo(serverInfo[i].name, screen, listBox.x, listBox.y + i*listBox.h); + + //draw the player count + font.DrawStringTo(to_string_custom(serverInfo[i].playerCount), screen, listBox.x + listBox.w, listBox.y + i*listBox.h); + + //compatible? + if (!serverInfo[i].compatible) { + font.DrawStringTo("?", screen, listBox.x - font.GetCharW(), listBox.y + i*listBox.h); + } + + //TODO: ping? } } @@ -139,7 +154,8 @@ void LobbyMenu::MouseButtonUp(SDL_MouseButtonEvent const& button) { selection = nullptr; } - else if (join.MouseButtonUp(button) == Button::State::HOVER && selection != nullptr) { + 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]; @@ -184,9 +200,18 @@ void LobbyMenu::KeyUp(SDL_KeyboardEvent const& key) { void LobbyMenu::HandlePacket(SerialPacket packet) { switch(packet.meta.type) { case SerialPacket::Type::BROADCAST_RESPONSE: { + //extract the data ServerInformation server; - server.name = packet.serverInfo.name; server.address = packet.meta.srcAddress; + 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; + + //push serverInfo.push_back(server); } break; diff --git a/client/scenes/lobby_menu.hpp b/client/scenes/lobby_menu.hpp index b231d16..53808bb 100644 --- a/client/scenes/lobby_menu.hpp +++ b/client/scenes/lobby_menu.hpp @@ -22,17 +22,24 @@ #ifndef LOBBYMENU_HPP_ #define LOBBYMENU_HPP_ -#include "base_scene.hpp" - +//graphics & utilities #include "image.hpp" #include "raster_font.hpp" #include "button.hpp" - #include "config_utility.hpp" + +//map +#include "region.hpp" + +//network #include "udp_network_utility.hpp" #include "serial_packet.hpp" #include "serial.hpp" +//client +#include "base_scene.hpp" + +//STL #include class LobbyMenu : public BaseScene { @@ -71,8 +78,11 @@ protected: //server list struct ServerInformation { - std::string name; IPaddress address; + //TODO: version info + std::string name; + int playerCount; + bool compatible; }; std::vector serverInfo; diff --git a/common/graphics/tile_sheet.cpp b/common/graphics/tile_sheet.cpp index e1b9ea2..bb9a4cb 100644 --- a/common/graphics/tile_sheet.cpp +++ b/common/graphics/tile_sheet.cpp @@ -53,8 +53,8 @@ void TileSheet::DrawRegionTo(SDL_Surface* const dest, Region* const region, int image.SetClipX((tile-1) % XCount * image.GetClipW()); image.SetClipY((tile-1) / XCount * image.GetClipH()); image.DrawTo(dest, - region->GetX() + i * image.GetClipW() - camX, - region->GetY() + j * image.GetClipH() - camY); + (region->GetX() + i) * image.GetClipW() - camX, + (region->GetY() + j) * image.GetClipH() - camY); } } } diff --git a/common/map/region_pager.hpp b/common/map/region_pager.hpp index 0bf035e..95864f0 100644 --- a/common/map/region_pager.hpp +++ b/common/map/region_pager.hpp @@ -46,7 +46,7 @@ public: virtual Region* SaveRegion(int x, int y) = 0; virtual Region* CreateRegion(int x, int y) = 0; virtual void UnloadRegion(int x, int y) = 0; - //TODO: delete? + //TODO: delete existing regions //accessors & mutators std::list* GetContainer() { return ®ionList; } @@ -109,10 +109,7 @@ public: for (std::list::iterator it = regionList.begin(); it != regionList.end(); /* EMPTY */) { if ((*it)->GetX() == x && (*it)->GetY() == y) { allocator.Unload(*it); - regionList.erase(it); - - //reset the loop, because of reasons - it = regionList.begin(); + it = regionList.erase(it); continue; } ++it; diff --git a/common/network/serial.cpp b/common/network/serial.cpp index 8ad8a89..ee88937 100644 --- a/common/network/serial.cpp +++ b/common/network/serial.cpp @@ -36,7 +36,20 @@ void serializeType(SerialPacket* packet, char* buffer) { void serializeServer(SerialPacket* packet, char* buffer) { memcpy(buffer, &packet->meta.type, sizeof(SerialPacket::Type)); buffer += 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)); } void serializeClient(SerialPacket* packet, char* buffer) { @@ -114,7 +127,20 @@ void deserializeType(SerialPacket* packet, char* buffer) { void deserializeServer(SerialPacket* packet, char* buffer) { memcpy(&packet->meta.type, buffer, sizeof(SerialPacket::Type)); buffer += 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)); } void deserializeClient(SerialPacket* packet, char* buffer) { diff --git a/common/network/serial.hpp b/common/network/serial.hpp index 5f002e7..b532bb8 100644 --- a/common/network/serial.hpp +++ b/common/network/serial.hpp @@ -24,7 +24,7 @@ #include "serial_packet.hpp" -/* TODO: Keep the PACKET_BUFFER_SIZE up to date +/* 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 diff --git a/common/network/serial_packet.hpp b/common/network/serial_packet.hpp index 8c023d5..35f117e 100644 --- a/common/network/serial_packet.hpp +++ b/common/network/serial_packet.hpp @@ -79,8 +79,12 @@ union SerialPacket { Metadata meta; //TODO: version info char name[PACKET_STRING_SIZE]; - //TODO: player count - //TODO: map format + int playerCount; + + //map format + int regionWidth; + int regionHeight; + int regionDepth; }serverInfo; //information about the client @@ -95,8 +99,7 @@ union SerialPacket { Metadata meta; int clientIndex; int playerIndex; - //TODO: should probably move these into the client info - //TODO: these might actually do better during the login system + //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]; Vector2 position; diff --git a/server/server_application.cpp b/server/server_application.cpp index 390a347..7317058 100644 --- a/server/server_application.cpp +++ b/server/server_application.cpp @@ -110,7 +110,7 @@ void ServerApplication::Proc() { HandlePacket(packet); } //give the computer a break - //TODO: remove this? + //TODO: remove this delay? SDL_Delay(10); } } @@ -176,10 +176,16 @@ void ServerApplication::HandlePacket(SerialPacket packet) { void ServerApplication::HandleBroadcastRequest(SerialPacket packet) { //send back the server's metadata packet.meta.type = SerialPacket::Type::BROADCAST_RESPONSE; + + //pack the data //TODO: version info snprintf(packet.serverInfo.name, PACKET_STRING_SIZE, "%s", config["server.name"].c_str()); - //TODO: player count - //TODO: map format + 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); @@ -246,7 +252,7 @@ void ServerApplication::HandleSynchronize(SerialPacket packet) { SerialPacket newPacket; char buffer[PACKET_BUFFER_SIZE]; - //TODO: map? + //TODO: syncronize the map? //players newPacket.meta.type = SerialPacket::Type::PLAYER_UPDATE; @@ -308,13 +314,15 @@ void ServerApplication::HandlePlayerNew(SerialPacket packet) { PlayerEntry::uidCounter++; } +//TODO: differentiate between delete and unload void ServerApplication::HandlePlayerDelete(SerialPacket packet) { - //TODO: remove this? //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; diff --git a/server/server_application.hpp b/server/server_application.hpp index b0b3fe6..c34172e 100644 --- a/server/server_application.hpp +++ b/server/server_application.hpp @@ -74,7 +74,7 @@ private: void HandlePlayerUpdate(SerialPacket); void HandleRegionRequest(SerialPacket); - //TODO: a function that sends to players in a certain proximity + //TODO: a function that only sends to players in a certain proximity void PumpPacket(SerialPacket); //APIs