diff --git a/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.cpp b/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.cpp index 9cf99dc..09586df 100644 --- a/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.cpp +++ b/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.cpp @@ -22,13 +22,14 @@ namespace NetworkingObjects { void DelayUpdateSerializedClassManager::update(NetworkingObjects::NetObjManager& netObjMan) { for(auto& [objID, updatingObj] : updatingObjs) { - if(updatingObj.lastTemporaryUpdateTimePoint.has_value() && (std::chrono::steady_clock::now() - updatingObj.lastTemporaryUpdateTimePoint.value()) >= std::chrono::milliseconds(300)) { + if(!updatingObj.updateLock && updatingObj.lastTemporaryUpdateTimePoint.has_value() && (std::chrono::steady_clock::now() - updatingObj.lastTemporaryUpdateTimePoint.value()) >= std::chrono::milliseconds(300)) { updatingObj.sendFinalUpdate(); updatingObj.lastTemporaryUpdateTimePoint = std::nullopt; } } std::erase_if(updatingObjs, [](auto& updatingPair) { - if((std::chrono::steady_clock::now() - updatingPair.second.lastUpdateTimePoint) >= std::chrono::seconds(3) && !updatingPair.second.lastTemporaryUpdateTimePoint.has_value()) { + constexpr auto TIME_TO_DISPLAY_NETWORK_UPDATES = std::chrono::seconds(5); + if((std::chrono::steady_clock::now() - updatingPair.second.lastUpdateTimePoint) >= TIME_TO_DISPLAY_NETWORK_UPDATES && !updatingPair.second.lastTemporaryUpdateTimePoint.has_value() && !updatingPair.second.updateLock) { updatingPair.second.setDataAfterTimeout(); return true; } diff --git a/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp b/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp index 3a0daa6..16e9eb7 100644 --- a/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp +++ b/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp @@ -24,6 +24,16 @@ namespace NetworkingObjects { class DelayUpdateSerializedClassManager { + private: + struct UpdatingObject { + std::shared_ptr objToUpdateCurrentData; + std::shared_ptr doLastAssignment; + std::chrono::steady_clock::time_point lastUpdateTimePoint; + std::function setDataAfterTimeout; + std::optional lastTemporaryUpdateTimePoint; + std::function sendFinalUpdate; + bool updateLock = false; + }; public: template struct CustomConstructors { std::function writeConstructor = [](const T& o, cereal::PortableBinaryOutputArchive& a) { @@ -83,13 +93,27 @@ namespace NetworkingObjects { client_write_update_func(o, a, finalUpdate); }); } + // NOTE: Locked objects will not be erased from the DelayUpdateSerializedClassManager map. Make sure you unlock objects before they're erased + template void set_object_update_lock(const NetworkingObjects::NetObjTemporaryPtr& o, bool lock) { + if(o.get_obj_man()->is_connected()) { + UpdatingObject* updatingObj; + if(o.get_obj_man()->is_server()) + updatingObj = server_create_updating_obj_entry(o, false); + else + updatingObj = client_create_updating_obj_entry(o, false); + updatingObj->updateLock = lock; + } + } void update(NetworkingObjects::NetObjManager& netObjMan); private: template void client_write_update_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a, bool finalUpdate) { a(finalUpdate); typeIndexFuncs[std::type_index(typeid(T))].writeUpdateFunc(static_cast(o.get()), a); - auto it = updatingObjs.find(o.get_net_id()); + client_create_updating_obj_entry(o, finalUpdate); + } + template UpdatingObject* client_create_updating_obj_entry(const NetObjTemporaryPtr& o, bool finalUpdate) { + auto it = updatingObjs.find(o.get_net_id()); if(it == updatingObjs.end()) { UpdatingObject toAdd; toAdd.objToUpdateCurrentData = typeIndexFuncs[std::type_index(typeid(T))].allocateCopyFunc(static_cast(o.get())); @@ -112,13 +136,14 @@ namespace NetworkingObjects { } }; toAdd.lastTemporaryUpdateTimePoint = finalUpdate ? std::nullopt : std::optional(std::chrono::steady_clock::now()); - updatingObjs.emplace(o.get_net_id(), toAdd); + it = updatingObjs.emplace(o.get_net_id(), toAdd).first; } else { auto& [netID, updatingObjData] = *it; updatingObjData.lastUpdateTimePoint = std::chrono::steady_clock::now(); updatingObjData.lastTemporaryUpdateTimePoint = finalUpdate ? std::nullopt : std::optional(std::chrono::steady_clock::now()); } + return &it->second; } template void client_read_update_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::function&)>& readUpdate) { @@ -136,6 +161,10 @@ namespace NetworkingObjects { template void server_write_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a, bool finalUpdate) { typeIndexFuncs[std::type_index(typeid(T))].writeUpdateFunc(static_cast(o.get()), a); + server_create_updating_obj_entry(o, finalUpdate); + } + + template UpdatingObject* server_create_updating_obj_entry(const NetObjTemporaryPtr& o, bool finalUpdate) { auto it = updatingObjs.find(o.get_net_id()); if(it == updatingObjs.end()) { @@ -162,7 +191,7 @@ namespace NetworkingObjects { } }; toAdd.lastTemporaryUpdateTimePoint = finalUpdate ? std::nullopt : std::optional(std::chrono::steady_clock::now()); - updatingObjs.emplace(o.get_net_id(), toAdd); + it = updatingObjs.emplace(o.get_net_id(), toAdd).first; } else { auto& [netID, updatingObjData] = *it; @@ -171,6 +200,8 @@ namespace NetworkingObjects { typeIndexFuncs[std::type_index(typeid(T))].assignmentFunc(static_cast(updatingObjs[o.get_net_id()].objToUpdateCurrentData.get()), static_cast(o.get())); *updatingObjData.doLastAssignment = false; } + + return &it->second; } template void server_read_update_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::function&)>& readUpdate, const std::shared_ptr& c) { @@ -192,14 +223,6 @@ namespace NetworkingObjects { } } - struct UpdatingObject { - std::shared_ptr objToUpdateCurrentData; - std::shared_ptr doLastAssignment; - std::chrono::steady_clock::time_point lastUpdateTimePoint; - std::function setDataAfterTimeout; - std::optional lastTemporaryUpdateTimePoint; - std::function sendFinalUpdate; - }; std::unordered_map updatingObjs; struct ExtraFunctions { diff --git a/src/CanvasComponents/CanvasComponentContainer.cpp b/src/CanvasComponents/CanvasComponentContainer.cpp index 71cedb5..f8e7aec 100644 --- a/src/CanvasComponents/CanvasComponentContainer.cpp +++ b/src/CanvasComponents/CanvasComponentContainer.cpp @@ -79,6 +79,10 @@ void CanvasComponentContainer::send_comp_update(DrawingProgram& drawP, bool fina drawP.world.delayedUpdateObjectManager.send_update_to_all(compAllocator, finalUpdate); } +void CanvasComponentContainer::set_object_update_lock(DrawingProgram& drawP, bool lockSet) { + drawP.world.delayedUpdateObjectManager.set_object_update_lock(compAllocator, lockSet); +} + void CanvasComponentContainer::write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a) { a(o->coords); o->compAllocator.write_create_message(a); diff --git a/src/CanvasComponents/CanvasComponentContainer.hpp b/src/CanvasComponents/CanvasComponentContainer.hpp index e38390d..6af5598 100644 --- a/src/CanvasComponents/CanvasComponentContainer.hpp +++ b/src/CanvasComponents/CanvasComponentContainer.hpp @@ -96,6 +96,7 @@ class CanvasComponentContainer { void send_comp_update(DrawingProgram& drawP, bool finalUpdate); void scale_up(const WorldScalar& scaleUpAmount); void normalize_object_coordinates(); + void set_object_update_lock(DrawingProgram& drawP, bool lockSet); std::weak_ptr cacheParentBvhNode; DrawingProgramLayerListItem* parentLayer = nullptr; diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 02527ea..7ebb3aa 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -300,11 +300,11 @@ void DrawingProgram::server_init_no_file() { void DrawingProgram::scale_up(const WorldScalar& scaleUpAmount) { selection.deselect_all(); + switch_to_tool(drawTool->get_type() == DrawingProgramToolType::GRIDMODIFY ? DrawingProgramToolType::EDIT : drawTool->get_type(), true); layerMan.scale_up(scaleUpAmount); if(controls.lockedCameraScale.has_value()) controls.lockedCameraScale.value() *= scaleUpAmount; rebuild_cache(); - switch_to_tool(drawTool->get_type() == DrawingProgramToolType::GRIDMODIFY ? DrawingProgramToolType::EDIT : drawTool->get_type(), true); } void DrawingProgram::write_components_server(cereal::PortableBinaryOutputArchive& a) { diff --git a/src/DrawingProgram/EditCanvasComponentWorldUndoAction.cpp b/src/DrawingProgram/EditCanvasComponentWorldUndoAction.cpp index 3b287f1..fc31fd0 100644 --- a/src/DrawingProgram/EditCanvasComponentWorldUndoAction.cpp +++ b/src/DrawingProgram/EditCanvasComponentWorldUndoAction.cpp @@ -88,4 +88,8 @@ bool EditTransformCanvasComponentWorldUndoAction::undo_redo(WorldUndoManager& un return true; } +void EditTransformCanvasComponentWorldUndoAction::scale_up(const WorldScalar& scaleAmount) { + coords.scale_about(WorldVec{0, 0}, scaleAmount, true); +} + EditTransformCanvasComponentWorldUndoAction::~EditTransformCanvasComponentWorldUndoAction() {} diff --git a/src/DrawingProgram/EditCanvasComponentWorldUndoAction.hpp b/src/DrawingProgram/EditCanvasComponentWorldUndoAction.hpp index a764a79..6b6e8b3 100644 --- a/src/DrawingProgram/EditCanvasComponentWorldUndoAction.hpp +++ b/src/DrawingProgram/EditCanvasComponentWorldUndoAction.hpp @@ -39,6 +39,7 @@ class EditTransformCanvasComponentWorldUndoAction : public WorldUndoAction { bool undo(WorldUndoManager& undoMan) override; bool redo(WorldUndoManager& undoMan) override; bool undo_redo(WorldUndoManager& undoMan); + void scale_up(const WorldScalar& scaleAmount) override; ~EditTransformCanvasComponentWorldUndoAction(); std::unique_ptr data; diff --git a/src/DrawingProgram/Tools/EraserTool.cpp b/src/DrawingProgram/Tools/EraserTool.cpp index fad9ae9..80c1a7e 100644 --- a/src/DrawingProgram/Tools/EraserTool.cpp +++ b/src/DrawingProgram/Tools/EraserTool.cpp @@ -141,6 +141,7 @@ void EraserTool::erase_on_path() { auto it = updatedComponents.find(c); if(it != updatedComponents.end()) { c->obj->get_comp().set_data_from(*it->second.copyData->obj); + c->obj->set_object_update_lock(drawP, false); updatedComponents.erase(it); } erasedComponents.emplace(c); @@ -161,8 +162,10 @@ void EraserTool::erase_on_path() { case CanvasComponentEraseDetailResult::NO_CHANGE: return false; case CanvasComponentEraseDetailResult::CHANGED: { - if(!updatedComponents.contains(c)) + if(!updatedComponents.contains(c)) { + c->obj->set_object_update_lock(drawP, true); updatedComponents.emplace(c, std::move(dataCopy)); + } // NOTE: commit_update is what should be run here, but invalidate_cache is being run instead. This is for a few reasons: // - Mesh initialize_draw_data does nothing, so commit_update isnt necessary // - worldAABB only shrinks, which means that invalidate_cache_at_optional_aabb will invalidate a "good enough" space even if worldAABB isn't updated (which commit_update does) @@ -171,17 +174,20 @@ void EraserTool::erase_on_path() { drawP.drawCache.invalidate_cache_at_optional_aabb(c->obj->get_world_bounds()); return false; } - case CanvasComponentEraseDetailResult::REMOVED: + case CanvasComponentEraseDetailResult::REMOVED: { auto it = updatedComponents.find(c); if(it != updatedComponents.end()) { c->obj->get_comp().set_data_from(*it->second.copyData->obj); + c->obj->set_object_update_lock(drawP, false); updatedComponents.erase(it); } else c->obj->get_comp().set_data_from(*dataCopy->obj); + erasedComponents.emplace(c); drawP.drawCache.invalidate_cache_at_optional_aabb(c->obj->get_world_bounds()); return true; + } } } } @@ -211,6 +217,7 @@ void EraserTool::reset_erasing_stroke() { void EraserTool::erase_component(CanvasComponentContainer::ObjInfo* erasedComp) { erasedComponents.erase(erasedComp); updatedComponents.erase(erasedComp); + erasedComp->obj->set_object_update_lock(drawP, false); } void EraserTool::commit_erase() { @@ -223,6 +230,7 @@ void EraserTool::commit_erase() { std::erase_if(updatedComponents, [&] (auto& p) { auto& [comp, oldData] = p; + comp->obj->set_object_update_lock(drawP, false); if(!oldData.splitComps.empty()) { for(CanvasComponentContainer* c : oldData.splitComps) newObjectsToPlace[comp->obj->parentLayer].emplace_back(std::next(comp->obj->objInfo), c); diff --git a/src/World.cpp b/src/World.cpp index d408ee8..d911a80 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -628,13 +628,13 @@ void World::scale_up_step() { void World::scale_up(const WorldScalar& scaleUpAmount) { Logger::get().log(Logger::LogType::DESKTOP_USERINFO, "Canvas scaled up"); + // drawProg will be sending info on committed objects/modified grids. These objects will NOT be scaled up. + // This means that the scale up message must be sent AFTER the World::scale_up function is called on our end + // if this client is the one responsible for the scale up + drawProg.scale_up(scaleUpAmount); bMan.scale_up(scaleUpAmount); gridMan.scale_up(scaleUpAmount); drawData.cam.scale_up(*this, scaleUpAmount); - // drawProg will be sending info on committed objects/modified grids. These objects will be scaled up already. - // This means that the scale up message must be send BEFORE the World::scale_up function is called on our end - // if this client is the one responsible for the scale up - drawProg.scale_up(scaleUpAmount); undo.scale_up(scaleUpAmount); }