From 7e69688ca6b9dec3e2ad9c39bc510d1bec45882c Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sun, 27 Jul 2025 11:56:34 +0300 Subject: [PATCH] Transformations over network work again, with undo and redo --- include/Helpers/Parallel.hpp | 2 +- src/CoordSpaceHelper.cpp | 8 +++ src/CoordSpaceHelper.hpp | 4 +- src/DrawComponents/DrawComponent.cpp | 9 ++- src/DrawComponents/DrawComponent.hpp | 4 +- src/DrawingProgram/DrawingProgram.cpp | 43 ++++++++---- src/DrawingProgram/DrawingProgramCache.cpp | 3 +- src/DrawingProgram/DrawingProgramCache.hpp | 3 +- .../DrawingProgramSelection.cpp | 65 ++++++++++++++++++- src/Server/CommandList.hpp | 4 +- src/Server/MainServer.cpp | 16 ++--- 11 files changed, 128 insertions(+), 33 deletions(-) diff --git a/include/Helpers/Parallel.hpp b/include/Helpers/Parallel.hpp index ee78dfd..8b178b9 100644 --- a/include/Helpers/Parallel.hpp +++ b/include/Helpers/Parallel.hpp @@ -5,7 +5,7 @@ #include #endif -template void parallel_loop_container(ContainerType& c, std::function func) { +template void parallel_loop_container(const ContainerType& c, std::function func) { #ifdef __EMSCRIPTEN__ std::for_each(c.begin(), c.end(), func); #else diff --git a/src/CoordSpaceHelper.cpp b/src/CoordSpaceHelper.cpp index 00d9c64..db78613 100644 --- a/src/CoordSpaceHelper.cpp +++ b/src/CoordSpaceHelper.cpp @@ -153,6 +153,14 @@ CoordSpaceHelper CoordSpaceHelper::other_coord_space_to_this_space(const CoordSp return toRet; } +bool CoordSpaceHelper::operator==(const CoordSpaceHelper& otherCoords) const { + return rotation == otherCoords.rotation && pos == otherCoords.pos && inverseScale == otherCoords.inverseScale; +} + +bool CoordSpaceHelper::operator!=(const CoordSpaceHelper& otherCoords) const { + return rotation != otherCoords.rotation || pos != otherCoords.pos || inverseScale != otherCoords.inverseScale; +} + CoordSpaceHelper CoordSpaceHelper::other_coord_space_from_this_space(const CoordSpaceHelper& other) const { CoordSpaceHelper toRet; toRet.pos = from_space_world(other.pos); diff --git a/src/CoordSpaceHelper.hpp b/src/CoordSpaceHelper.hpp index 24b4cd6..45deaaa 100644 --- a/src/CoordSpaceHelper.hpp +++ b/src/CoordSpaceHelper.hpp @@ -24,8 +24,10 @@ class CoordSpaceHelper { CoordSpaceHelper(); CoordSpaceHelper(const WorldVec& initPos, const WorldScalar& initInverseScale, double initRotation); - Vector2f to_space(const WorldVec& coord) const; + bool operator==(const CoordSpaceHelper& otherCoords) const; + bool operator!=(const CoordSpaceHelper& otherCoords) const; + Vector2f to_space(const WorldVec& coord) const; WorldVec from_space(Vector2f coord) const; diff --git a/src/DrawComponents/DrawComponent.cpp b/src/DrawComponents/DrawComponent.cpp index 9a0a720..ea85319 100644 --- a/src/DrawComponents/DrawComponent.cpp +++ b/src/DrawComponents/DrawComponent.cpp @@ -83,8 +83,8 @@ void DrawComponent::server_send_update_final(MainServer& server, ServerClientID serverIsTempUpdate = false; } -void DrawComponent::server_send_transform(MainServer& server, ServerClientID id) { - server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_TRANSFORM_COMPONENT, id, this->coords); +void DrawComponent::server_send_transform_many(MainServer& server, const std::vector>& transforms) { + server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_TRANSFORM_MANY_COMPONENTS, transforms); } void DrawComponent::server_update(MainServer& server, ServerClientID id) { @@ -156,9 +156,8 @@ void DrawComponent::client_send_update_final(DrawingProgram& drawP) { drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_UPDATE_COMPONENT, false, collabListInfo.lock()->id, *this); } -void DrawComponent::client_send_transform(DrawingProgram& drawP) { - if(collabListInfo.lock()) - drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_TRANSFORM_COMPONENT, collabListInfo.lock()->id, this->coords); +void DrawComponent::client_send_transform_many(DrawingProgram& drawP, const std::vector>& transforms) { + drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_TRANSFORM_MANY_COMPONENTS, transforms); } void DrawComponent::client_send_erase(DrawingProgram& drawP) { diff --git a/src/DrawComponents/DrawComponent.hpp b/src/DrawComponents/DrawComponent.hpp index 48a8336..b78c031 100644 --- a/src/DrawComponents/DrawComponent.hpp +++ b/src/DrawComponents/DrawComponent.hpp @@ -69,7 +69,7 @@ class DrawComponent { static void server_send_erase_set(MainServer& server, const std::unordered_set& ids); void server_send_update_temp(MainServer& server, ServerClientID id); void server_send_update_final(MainServer& server, ServerClientID id); - void server_send_transform(MainServer& server, ServerClientID id); + static void server_send_transform_many(MainServer& server, const std::vector>& transforms); std::chrono::time_point tempServerUpdateTimer; bool serverIsTempUpdate = false; @@ -94,7 +94,7 @@ class DrawComponent { static void client_send_erase_set(DrawingProgram& drawP, const std::unordered_set& ids); void client_send_update_temp(DrawingProgram& drawP); void client_send_update_final(DrawingProgram& drawP); - void client_send_transform(DrawingProgram& drawP); + static void client_send_transform_many(DrawingProgram& drawP, const std::vector>& transforms); std::optional> worldAABB; diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index a803fb2..daf430e 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -89,17 +89,38 @@ void DrawingProgram::init_client_callbacks() { delayedUpdateComponents.emplace(comp); } }); - world.con.client_add_recv_callback(CLIENT_TRANSFORM_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) { - ServerClientID id; - message(id); - auto objPtr = components.get_item_by_id(id); - if(!objPtr) - return; - if(selection.is_selected(objPtr)) // Whatever transformation we're doing right now will overwrite this transformation, so we can ignore this message - return; - std::shared_ptr& comp = objPtr->obj; - message(comp->coords); - comp->finalize_update(*this); // No need to update draw data during transformation + world.con.client_add_recv_callback(CLIENT_TRANSFORM_MANY_COMPONENTS, [&](cereal::PortableBinaryInputArchive& message) { + std::vector> transforms; + message(transforms); + if(transforms.size() >= DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD) { + std::atomic objsActuallyTransformed = 0; + parallel_loop_container(transforms, [&](auto& p) { + auto& [id, coords] = p; + auto objPtr = components.get_item_by_id(id); + if(!objPtr || selection.is_selected(objPtr)) // Whatever transformation we're doing right now will overwrite this transformation, so we can ignore this message + return; + std::shared_ptr& comp = objPtr->obj; + if(comp->coords == coords) + return; + comp->coords = coords; + objsActuallyTransformed++; + comp->finalize_update(*this, false); + }); + if(objsActuallyTransformed != 0) + force_rebuild_cache(); + } + else { + for(auto& [id, coords] : transforms) { + auto objPtr = components.get_item_by_id(id); + if(!objPtr || selection.is_selected(objPtr)) // Whatever transformation we're doing right now will overwrite this transformation, so we can ignore this message + continue; + std::shared_ptr& comp = objPtr->obj; + if(comp->coords == coords) + return; + comp->coords = coords; + comp->finalize_update(*this); // No need to update draw data during transformation + } + } }); world.con.client_add_recv_callback(CLIENT_PLACE_SINGLE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) { uint64_t insertPosition; diff --git a/src/DrawingProgram/DrawingProgramCache.cpp b/src/DrawingProgram/DrawingProgramCache.cpp index 468e0ba..82105d8 100644 --- a/src/DrawingProgram/DrawingProgramCache.cpp +++ b/src/DrawingProgram/DrawingProgramCache.cpp @@ -2,6 +2,7 @@ #include "DrawingProgram.hpp" #include "../World.hpp" #include "../MainProgram.hpp" +#include "Helpers/Parallel.hpp" #include #ifdef USE_SKIA_BACKEND_GRAPHITE @@ -177,7 +178,7 @@ void DrawingProgramCache::preupdate_component(const CollabListType::ObjectInfoPt invalidate_cache_before_pos(c->pos); } -void DrawingProgramCache::build_bvh_node(const std::shared_ptr& bvhNode, std::vector components) { +void DrawingProgramCache::build_bvh_node(const std::shared_ptr& bvhNode, const std::vector& components) { if(components.empty()) return; diff --git a/src/DrawingProgram/DrawingProgramCache.hpp b/src/DrawingProgram/DrawingProgramCache.hpp index bf05794..62caa63 100644 --- a/src/DrawingProgram/DrawingProgramCache.hpp +++ b/src/DrawingProgram/DrawingProgramCache.hpp @@ -70,7 +70,8 @@ class DrawingProgramCache { void build(std::vector components); void traverse_bvh_run_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f); std::pair, std::vector::iterator> get_bvh_node_fully_containing_recursive(const std::shared_ptr& bvhNode, DrawComponent* c); - void build_bvh_node(const std::shared_ptr& bvhNode, std::vector components); + + void build_bvh_node(const std::shared_ptr& bvhNode, const std::vector& components); std::chrono::steady_clock::time_point lastBvhBuildTime; std::shared_ptr bvhRoot; diff --git a/src/DrawingProgram/DrawingProgramSelection.cpp b/src/DrawingProgram/DrawingProgramSelection.cpp index 88c6a87..db9a5ae 100644 --- a/src/DrawingProgram/DrawingProgramSelection.cpp +++ b/src/DrawingProgram/DrawingProgramSelection.cpp @@ -74,11 +74,74 @@ void DrawingProgramSelection::calculate_aabb() { void DrawingProgramSelection::deselect_all() { if(is_something_selected()) { std::vector a(selectedSet.begin(), selectedSet.end()); + std::vector> transformsFrom; + for(auto& transformedObj : a) + transformsFrom.emplace_back(transformedObj, transformedObj->obj->coords); + parallel_loop_container(a, [&](auto& obj) { obj->obj->coords = selectionTransformCoords.other_coord_space_from_this_space(obj->obj->coords); obj->obj->final_update(drawP, false); - obj->obj->client_send_transform(drawP); }); + std::vector> transformsTo; + std::vector> transformsToSend; + for(auto& transformedObj : a) { + transformsToSend.emplace_back(transformedObj->id, transformedObj->obj->coords); + transformsTo.emplace_back(transformedObj, transformedObj->obj->coords); + } + DrawComponent::client_send_transform_many(drawP, transformsToSend); + + drawP.world.undo.push(UndoManager::UndoRedoPair{ + [&, transformsFrom = transformsFrom]() { + std::vector> transformsToSend; + for(auto& [comp, coords] : transformsFrom) { + auto lockObjInfo = comp->obj->collabListInfo.lock(); + if(lockObjInfo) + transformsToSend.emplace_back(lockObjInfo->id, coords); + else + return false; + } + + parallel_loop_container(transformsFrom, [&](auto& p) { + auto& [comp, coords] = p; + comp->obj->coords = coords; + comp->obj->finalize_update(drawP, false); + }); + + DrawComponent::client_send_transform_many(drawP, transformsToSend); + + drawP.reset_tools(); + + drawP.force_rebuild_cache(); + + return true; + }, + [&, transformsTo = transformsTo]() { + std::vector> transformsToSend; + for(auto& [comp, coords] : transformsTo) { + auto lockObjInfo = comp->obj->collabListInfo.lock(); + if(lockObjInfo) + transformsToSend.emplace_back(lockObjInfo->id, coords); + else + return false; + } + + parallel_loop_container(transformsTo, [&](auto& p) { + auto& [comp, coords] = p; + comp->obj->coords = coords; + comp->obj->finalize_update(drawP, false); + }); + + DrawComponent::client_send_transform_many(drawP, transformsToSend); + + drawP.reset_tools(); + + drawP.force_rebuild_cache(); + + return true; + } + }); + + selectedSet.clear(); cache.clear(); selectionTransformCoords = CoordSpaceHelper(); diff --git a/src/Server/CommandList.hpp b/src/Server/CommandList.hpp index 76239bf..b18b39e 100644 --- a/src/Server/CommandList.hpp +++ b/src/Server/CommandList.hpp @@ -9,7 +9,7 @@ enum ServerCommands : MessageCommandType { SERVER_MOVE_MOUSE, SERVER_PLACE_SINGLE_COMPONENT, SERVER_PLACE_MANY_COMPONENTS, - SERVER_TRANSFORM_COMPONENT, + SERVER_TRANSFORM_MANY_COMPONENTS, SERVER_ERASE_SINGLE_COMPONENT, SERVER_ERASE_MANY_COMPONENTS, SERVER_UPDATE_COMPONENT, @@ -28,7 +28,7 @@ enum ClientCommands : MessageCommandType { CLIENT_USER_DISCONNECT, CLIENT_PLACE_SINGLE_COMPONENT, CLIENT_PLACE_MANY_COMPONENTS, - CLIENT_TRANSFORM_COMPONENT, + CLIENT_TRANSFORM_MANY_COMPONENTS, CLIENT_ERASE_SINGLE_COMPONENT, CLIENT_ERASE_MANY_COMPONENTS, CLIENT_UPDATE_COMPONENT, diff --git a/src/Server/MainServer.cpp b/src/Server/MainServer.cpp index 81e757c..040d72c 100644 --- a/src/Server/MainServer.cpp +++ b/src/Server/MainServer.cpp @@ -185,15 +185,15 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID): return idsToRemove.contains(p.first); }); }); - netServer->add_recv_callback(SERVER_TRANSFORM_COMPONENT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { - ServerClientID idToTransform; - message(idToTransform); - auto it = data.idToComponentMap.find(idToTransform); - if(it != data.idToComponentMap.end()) { - std::shared_ptr& comp = it->second; - message(comp->coords); - comp->server_send_transform(*this, idToTransform); + netServer->add_recv_callback(SERVER_TRANSFORM_MANY_COMPONENTS, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { + std::vector> transformsToSend; + message(transformsToSend); + for(auto& t : transformsToSend) { + auto it = data.idToComponentMap.find(t.first); + if(it != data.idToComponentMap.end()) + it->second->coords = t.second; } + DrawComponent::server_send_transform_many(*this, transformsToSend); }); netServer->add_recv_callback(SERVER_UPDATE_COMPONENT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { bool isTemp;