diff --git a/include/Helpers/SCollision.hpp b/include/Helpers/SCollision.hpp index d162e1e..9f19269 100644 --- a/include/Helpers/SCollision.hpp +++ b/include/Helpers/SCollision.hpp @@ -67,6 +67,9 @@ namespace SCollision { AABB toRet{min.template cast(), max.template cast()}; return toRet; } + template void serialize(Archive& a) { + a(min, max); + } }; template class Circle { public: diff --git a/src/BookmarkManager.hpp b/src/BookmarkManager.hpp index ecc4246..dc05f50 100644 --- a/src/BookmarkManager.hpp +++ b/src/BookmarkManager.hpp @@ -22,9 +22,16 @@ class BookmarkManager { void remove_bookmark(const std::string& name); void jump_to_bookmark(const std::string& name); const std::unordered_map& bookmark_list(); - template void serialize(Archive& a) { + + template void save(Archive& a) const { a(bookmarks); } + + template void load(Archive& a) { + a(bookmarks); + changed = true; + } + const std::vector& sorted_names(); private: bool changed = true; diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index fe1aa7d..c2f4330 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -233,9 +233,9 @@ void DrawingProgram::tool_options_gui() { } void DrawingProgram::modify_grid(ServerClientID gridToModifyID) { - switch_to_tool(DrawingProgramToolType::GRIDMODIFY); - GridModifyTool* gridModToolPtr = static_cast(drawTool.get()); - gridModToolPtr->set_grid_id(gridToModifyID); + std::unique_ptr newTool(std::make_unique(*this)); + newTool->set_grid_id(gridToModifyID); + switch_to_tool_ptr(std::move(newTool)); } void DrawingProgram::update() { diff --git a/src/DrawingProgram/GridModifyTool.cpp b/src/DrawingProgram/GridModifyTool.cpp index 0de1f98..f591d62 100644 --- a/src/DrawingProgram/GridModifyTool.cpp +++ b/src/DrawingProgram/GridModifyTool.cpp @@ -171,4 +171,5 @@ void GridModifyTool::draw(SkCanvas* canvas, const DrawData& drawData) { } void GridModifyTool::switch_tool(DrawingProgramToolType newTool) { + drawP.world.gridMan.send_grid_info(gridID); } diff --git a/src/GridManager.cpp b/src/GridManager.cpp index 5aa35f1..4db30c4 100644 --- a/src/GridManager.cpp +++ b/src/GridManager.cpp @@ -1,4 +1,5 @@ #include "GridManager.hpp" +#include "Server/CommandList.hpp" #include "SharedTypes.hpp" #include "World.hpp" #include "MainProgram.hpp" @@ -8,6 +9,19 @@ GridManager::GridManager(World& w): world(w) {} void GridManager::init_client_callbacks() { + world.con.client_add_recv_callback(CLIENT_SET_GRID, [&](cereal::PortableBinaryInputArchive& message) { + ServerClientID gID; + message(gID); + auto& g = grids[gID]; + message(g); + changed = true; + }); + world.con.client_add_recv_callback(CLIENT_REMOVE_GRID, [&](cereal::PortableBinaryInputArchive& message) { + ServerClientID gID; + message(gID); + grids.erase(gID); + changed = true; + }); } ServerClientID GridManager::add_default_grid(const std::string& newName) { @@ -22,8 +36,17 @@ ServerClientID GridManager::add_default_grid(const std::string& newName) { return newID; } +void GridManager::send_grid_info(ServerClientID gridID) { + auto gridFoundIt = grids.find(gridID); + if(gridFoundIt != grids.end()) { + WorldGrid& g = gridFoundIt->second; + world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_SET_GRID, gridID, g); + } +} + void GridManager::remove_grid(ServerClientID idToRemove) { grids.erase(idToRemove); + world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_REMOVE_GRID, idToRemove); changed = true; } @@ -43,6 +66,15 @@ const std::vector& GridManager::sorted_grid_ids() { return sortedGridIDs; } +ClientPortionID GridManager::get_max_id(ServerPortionID serverID) const { + ClientPortionID maxClientID = 0; + for(auto& p : grids) { + if(p.first.first == serverID) + maxClientID = std::max(maxClientID, p.first.second); + } + return maxClientID; +} + void GridManager::draw(SkCanvas* canvas, const DrawData& drawData) { for(auto& [str, g] : grids) g.draw(*this, canvas, drawData); diff --git a/src/GridManager.hpp b/src/GridManager.hpp index c0d4bac..e54a35a 100644 --- a/src/GridManager.hpp +++ b/src/GridManager.hpp @@ -10,6 +10,16 @@ class GridManager { ServerClientID add_default_grid(const std::string& newName); + template void save(Archive& a) const { + a(grids); + } + + template void load(Archive& a) { + a(grids); + changed = true; + } + + void send_grid_info(ServerClientID gridID); void remove_grid(ServerClientID idToRemove); void draw(SkCanvas* canvas, const DrawData& drawData); ClientPortionID get_max_id(ServerPortionID serverID) const; diff --git a/src/Server/ClientData.hpp b/src/Server/ClientData.hpp index b2b867c..1562656 100644 --- a/src/Server/ClientData.hpp +++ b/src/Server/ClientData.hpp @@ -16,7 +16,7 @@ class ClientData { Vector3f cursorColor; std::string displayName; - ServerClientID id = {0, 0}; + ServerPortionID serverID = 0; std::queue pendingResourceData; std::queue pendingResourceID; diff --git a/src/Server/CommandList.hpp b/src/Server/CommandList.hpp index 6beb9b9..2e6052c 100644 --- a/src/Server/CommandList.hpp +++ b/src/Server/CommandList.hpp @@ -18,7 +18,9 @@ enum ServerCommands : MessageCommandType { SERVER_CHAT_MESSAGE, SERVER_NEW_BOOKMARK, SERVER_REMOVE_BOOKMARK, - SERVER_CANVAS_COLOR + SERVER_CANVAS_COLOR, + SERVER_SET_GRID, + SERVER_REMOVE_GRID }; enum ClientCommands : MessageCommandType { @@ -38,11 +40,14 @@ enum ClientCommands : MessageCommandType { CLIENT_CHAT_MESSAGE, CLIENT_NEW_BOOKMARK, CLIENT_REMOVE_BOOKMARK, - CLIENT_CANVAS_COLOR + CLIENT_CANVAS_COLOR, + CLIENT_SET_GRID, + CLIENT_REMOVE_GRID }; // NOTE: Do not change savefile header length #define SAVEFILE_HEADER_LEN 12 // NOTE: Change these values whenever savefile is incompatible with previous version -#define SAVEFILE_HEADER "INFPNT000002" +#define SAVEFILE_HEADER "INFPNT000003" +#define SAVEFILE_HEADER_V2 "INFPNT000002" #define SAVEFILE_HEADER_V1 "INFPNT000001" diff --git a/src/Server/MainServer.cpp b/src/Server/MainServer.cpp index 64a2eff..e126991 100644 --- a/src/Server/MainServer.cpp +++ b/src/Server/MainServer.cpp @@ -29,7 +29,7 @@ void ServerData::save(cereal::PortableBinaryOutputArchive& a) const { a((uint64_t)components.size()); for(uint64_t i = 0; i < components.size(); i++) a(components[i]->get_type(), components[i]->id, components[i]->coords, *components[i]); - a(bookmarks); + a(bookmarks, grids); } void ServerData::load(cereal::PortableBinaryInputArchive& a) { @@ -44,20 +44,7 @@ void ServerData::load(cereal::PortableBinaryInputArchive& a) { components.emplace_back(newComp); idToComponentMap.emplace(newComp->id, newComp); } - a(bookmarks); -} - -ClientPortionID ServerData::get_max_id(ServerPortionID serverID) const { - ClientPortionID maxClientID = 0; - for(auto& p : resources) { - if(p.first.first == serverID) - maxClientID = std::max(maxClientID, p.first.second); - } - for(auto& p : components) { - if(p->id.first == serverID) - maxClientID = std::max(maxClientID, p->id.second); - } - return maxClientID; + a(bookmarks, grids); } MainServer::MainServer(World& initWorld, const std::string& serverLocalID): @@ -69,6 +56,7 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID): data.idToComponentMap.emplace(data.components.back()->id, data.components.back()); } data.bookmarks = world.bMan.bookmark_list(); + data.grids = world.gridMan.grids; data.resources = world.rMan.resource_list(); data.canvasBackColor = convert_vec3(world.canvasTheme.backColor); @@ -91,23 +79,22 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID): if(isDirectConnect) fileDisplayName = newClient.displayName; - newClient.id.first = Random::get().int_range(1, std::numeric_limits::max()); - client->customID = newClient.id.first; - newClient.id.second = data.get_max_id(newClient.id.first); - netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_USER_CONNECT, newClient.id.first, newClient.displayName, newClient.cursorColor); + newClient.serverID = Random::get().int_range(1, std::numeric_limits::max()); + client->customID = newClient.serverID; + netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_USER_CONNECT, newClient.serverID, newClient.displayName, newClient.cursorColor); if(isDirectConnect) - netServer->send_items_to_client(client, RELIABLE_COMMAND_CHANNEL, CLIENT_INITIAL_DATA, isDirectConnect, newClient.id.first, newClient.cursorColor); + netServer->send_items_to_client(client, RELIABLE_COMMAND_CHANNEL, CLIENT_INITIAL_DATA, isDirectConnect, newClient.serverID, newClient.cursorColor); else { - netServer->send_items_to_client(client, RELIABLE_COMMAND_CHANNEL, CLIENT_INITIAL_DATA, isDirectConnect, newClient.id.first, newClient.cursorColor, newClient.displayName, fileDisplayName, clients, data); + netServer->send_items_to_client(client, RELIABLE_COMMAND_CHANNEL, CLIENT_INITIAL_DATA, isDirectConnect, newClient.serverID, newClient.cursorColor, newClient.displayName, fileDisplayName, clients, data); for(auto& [id, rData] : data.resources) netServer->send_items_to_client(client, RESOURCE_COMMAND_CHANNEL, CLIENT_NEW_RESOURCE, id, rData); } - clients.emplace(newClient.id.first, newClient); + clients.emplace(newClient.serverID, newClient); }); netServer->add_recv_callback(SERVER_MOVE_MOUSE, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { auto& c = clients[client->customID]; message(c.camCoords, c.windowSize, c.cursorPos); - netServer->send_items_to_all_clients_except(client, UNRELIABLE_COMMAND_CHANNEL, CLIENT_MOVE_MOUSE, c.id.first, c.camCoords, c.windowSize, c.cursorPos); + netServer->send_items_to_all_clients_except(client, UNRELIABLE_COMMAND_CHANNEL, CLIENT_MOVE_MOUSE, c.serverID, c.camCoords, c.windowSize, c.cursorPos); }); netServer->add_recv_callback(SERVER_PLACE_SINGLE_COMPONENT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { uint64_t placement; @@ -247,6 +234,19 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID): data.bookmarks.erase(name); netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_REMOVE_BOOKMARK, name); }); + netServer->add_recv_callback(SERVER_SET_GRID, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { + ServerClientID gID; + message(gID); + WorldGrid& g = data.grids[gID]; + message(g); + netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_SET_GRID, gID, g); + }); + netServer->add_recv_callback(SERVER_REMOVE_GRID, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { + ServerClientID gID; + message(gID); + data.grids.erase(gID); + netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_REMOVE_GRID, gID); + }); netServer->add_recv_callback(SERVER_CANVAS_COLOR, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { message(data.canvasBackColor); netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_CANVAS_COLOR, data.canvasBackColor); diff --git a/src/Server/ServerData.hpp b/src/Server/ServerData.hpp index 2bf3a2f..aee936c 100644 --- a/src/Server/ServerData.hpp +++ b/src/Server/ServerData.hpp @@ -3,6 +3,7 @@ #include "../SharedTypes.hpp" #include "../BookmarkManager.hpp" #include "../ResourceManager.hpp" +#include "../WorldGrid.hpp" class DrawComponent; @@ -12,9 +13,10 @@ class ServerData { std::unordered_map> idToComponentMap; std::unordered_map resources; std::unordered_map bookmarks; + std::unordered_map grids; + Vector3f canvasBackColor; void save(cereal::PortableBinaryOutputArchive& a) const; void write_to_file(cereal::PortableBinaryOutputArchive& a) const; void load(cereal::PortableBinaryInputArchive& a); - ClientPortionID get_max_id(ServerPortionID serverID) const; }; diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 518f3ee..56e22f1 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -587,6 +587,7 @@ void Toolbar::grid_menu(bool justOpened) { if(gui.svg_icon_button("visibility eye", grid.visible ? "data/icons/eyeopen.svg" : "data/icons/eyeclose.svg", false, entryHeight, false)) { miniButtonClicked = true; grid.visible = !grid.visible; + main.world->gridMan.send_grid_info(gridID); } if(gui.svg_icon_button("edit pencil", "data/icons/pencil.svg", false, entryHeight, false)) { miniButtonClicked = true; diff --git a/src/World.cpp b/src/World.cpp index 492ad53..5055874 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -5,6 +5,7 @@ #include "Helpers/Networking/ByteStream.hpp" #include "Server/CommandList.hpp" #include "MainProgram.hpp" +#include "SharedTypes.hpp" #include "WorldGrid.hpp" #include "cereal/archives/portable_binary.hpp" #include @@ -61,6 +62,7 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): drawProg.init_client_callbacks(); rMan.init_client_callbacks(); bMan.init_client_callbacks(); + gridMan.init_client_callbacks(); init_client_callbacks(); con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_INITIAL_DATA, displayName, false); } @@ -77,9 +79,9 @@ void World::init_client_callbacks() { message(newBackColor); set_canvas_background_color(newBackColor, false); drawProg.initialize_draw_data(message); - message(bMan); + message(bMan, gridMan); } - nextClientID = std::max(rMan.get_max_id(ownID), drawProg.get_max_id(ownID)); + nextClientID = get_max_id(ownID); clientStillConnecting = false; }); con.client_add_recv_callback(CLIENT_USER_CONNECT, [&](cereal::PortableBinaryInputArchive& message) { @@ -240,6 +242,7 @@ void World::start_hosting(const std::string& initNetSource, const std::string& s drawProg.init_client_callbacks(); rMan.init_client_callbacks(); bMan.init_client_callbacks(); + gridMan.init_client_callbacks(); init_client_callbacks(); con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_INITIAL_DATA, displayName, true); conType = CONNECTIONTYPE_SERVER; @@ -302,13 +305,13 @@ void World::load_from_file(const std::filesystem::path& fileName, std::string_vi } if(buffer.size() < SAVEFILE_HEADER_LEN) - throw std::runtime_error("[World::load_from_file] File too small"); + throw std::runtime_error("[World::load_from_file] File is not an InfiniPaint canvas (File too small)"); std::string_view fileHeader = buffer.substr(0, SAVEFILE_HEADER_LEN); std::string_view uncompressedDataView; std::vector uncompressedDataVector; - if(fileHeader == SAVEFILE_HEADER) { + if(fileHeader == SAVEFILE_HEADER || fileHeader == SAVEFILE_HEADER_V2) { uncompressedDataVector.resize(ZSTD_getFrameContentSize(buffer.data() + SAVEFILE_HEADER_LEN, buffer.size() - SAVEFILE_HEADER_LEN)); size_t trueUncompressedSize = ZSTD_decompress(uncompressedDataVector.data(), uncompressedDataVector.size(), buffer.data() + SAVEFILE_HEADER_LEN, buffer.size() - SAVEFILE_HEADER_LEN); uncompressedDataView = std::string_view(uncompressedDataVector.data(), trueUncompressedSize); @@ -316,7 +319,7 @@ void World::load_from_file(const std::filesystem::path& fileName, std::string_vi else if(fileHeader == SAVEFILE_HEADER_V1) uncompressedDataView = std::string_view(buffer.data() + SAVEFILE_HEADER_LEN, buffer.size() - SAVEFILE_HEADER_LEN); else - throw std::runtime_error("[World::load_from_file] File does not have correct header"); + throw std::runtime_error("[World::load_from_file] File is not an InfiniPaint canvas (Wrong header)"); @@ -325,17 +328,22 @@ void World::load_from_file(const std::filesystem::path& fileName, std::string_vi cereal::PortableBinaryInputArchive a(f); SaveLoadFileOp op{.world = this, .versionStr = std::string(fileHeader)}; a(op); - nextClientID = std::max(rMan.get_max_id(ownID), drawProg.get_max_id(ownID)); + nextClientID = get_max_id(ownID); Logger::get().log("USERINFO", "File loaded"); set_name(filePath.stem().string()); } +ClientPortionID World::get_max_id(ServerPortionID serverID) { + return std::max(std::max(rMan.get_max_id(serverID), drawProg.get_max_id(serverID)), gridMan.get_max_id(serverID)); +} + void World::SaveLoadFileOp::save(cereal::PortableBinaryOutputArchive& a) const { a(world->drawData.cam.c, world->main.window.size.cast().eval()); a(convert_vec3(world->canvasTheme.backColor)); world->drawProg.write_to_file(a); a(world->bMan); + a(world->gridMan); world->rMan.save_strip_unused_resources(a, world->drawProg.get_used_resources()); } @@ -343,16 +351,20 @@ void World::SaveLoadFileOp::load(cereal::PortableBinaryInputArchive& a) { CoordSpaceHelper coordsToJumpTo; Vector2f windowSizeToJumpTo; a(coordsToJumpTo, windowSizeToJumpTo); - if(versionStr == SAVEFILE_HEADER) { + + if(versionStr == SAVEFILE_HEADER_V1) + world->set_canvas_background_color(DEFAULT_CANVAS_BACKGROUND_COLOR); + else { Vector3f canvasBackColor; a(canvasBackColor); world->set_canvas_background_color(canvasBackColor); } - else - world->set_canvas_background_color(DEFAULT_CANVAS_BACKGROUND_COLOR); + world->drawData.cam.smooth_move_to(*world, coordsToJumpTo, windowSizeToJumpTo, true); world->drawProg.initialize_draw_data(a); a(world->bMan); + if(versionStr == SAVEFILE_HEADER) + a(world->gridMan); a(world->rMan); } diff --git a/src/World.hpp b/src/World.hpp index de1c9f7..07eb7a9 100644 --- a/src/World.hpp +++ b/src/World.hpp @@ -100,6 +100,8 @@ class World { } canvasTheme; private: + ClientPortionID get_max_id(ServerPortionID serverID); + void init_client_callbacks(); void set_name(const std::string& n); diff --git a/src/WorldGrid.hpp b/src/WorldGrid.hpp index ea402fa..08673ad 100644 --- a/src/WorldGrid.hpp +++ b/src/WorldGrid.hpp @@ -2,20 +2,25 @@ #include #include "DrawData.hpp" #include +#include class GridManager; class WorldGrid { public: + template void serialize(Archive& a) { + a(name, gridType, size, visible, offset, bounds, color, removeDivisionsOutwards, subdivisions); + } + void draw(GridManager& gMan, SkCanvas* canvas, const DrawData& drawData); + std::string name; enum class GridType : unsigned { CIRCLE_POINTS = 0, SQUARE_POINTS, SQUARE_LINES, RULED } gridType = GridType::CIRCLE_POINTS; - std::string name; WorldScalar size{50000000}; bool visible = true; WorldVec offset{0, 0};