From d266508bbfe6eb7efd34386ad86299d38f013ae3 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Wed, 10 Dec 2025 17:23:05 +0300 Subject: [PATCH] Added generic delayed update objects --- CMakeLists.txt | 1 + .../DelayUpdateSerializedClassManager.cpp | 13 +++ .../DelayUpdateSerializedClassManager.hpp | 105 ++++++++++++++++++ .../NetObjGenericSerializedClass.hpp | 35 +++--- src/BookmarkManager.cpp | 8 +- src/BookmarkManager.hpp | 1 + src/DrawingProgram/DrawingProgram.cpp | 1 - src/DrawingProgram/GridModifyTool.cpp | 2 + src/GridManager.cpp | 8 +- src/GridManager.hpp | 1 + src/MainProgram.cpp | 8 -- src/MainProgram.hpp | 3 - src/World.cpp | 28 ++++- src/World.hpp | 5 + 14 files changed, 176 insertions(+), 43 deletions(-) create mode 100644 include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.cpp create mode 100644 include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 76ebff7..80d17d4 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ set(sources "src/main.cpp" "include/Helpers/Networking/NetServer.cpp" "include/Helpers/Networking/NetClient.cpp" "include/Helpers/Networking/ByteStream.cpp" + "include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.cpp" "include/Helpers/NetworkingObjects/NetObjManagerTypeList.cpp" "include/Helpers/NetworkingObjects/NetObjManager.cpp" "include/Helpers/NetworkingObjects/NetObjID.cpp" diff --git a/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.cpp b/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.cpp new file mode 100644 index 0000000..84c42eb --- /dev/null +++ b/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.cpp @@ -0,0 +1,13 @@ +#include "DelayUpdateSerializedClassManager.hpp" + +namespace NetworkingObjects { + void DelayUpdateSerializedClassManager::update() { + std::erase_if(updatingObjs, [](auto& updatingPair) { + if((std::chrono::steady_clock::now() - updatingPair.second.lastUpdateTimePoint) >= std::chrono::seconds(3)) { + updatingPair.second.setDataAfterTimeout(); + return true; + } + return false; + }); + } +} diff --git a/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp b/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp new file mode 100644 index 0000000..3b7fa9f --- /dev/null +++ b/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp @@ -0,0 +1,105 @@ +#pragma once +#include "NetObjWeakPtr.hpp" +#include "cereal/archives/portable_binary.hpp" +#include + +namespace NetworkingObjects { + class DelayUpdateSerializedClassManager { + public: + template void register_class(NetObjManagerTypeList& t) { + t.register_class({ + .writeConstructorFuncClient = [&](const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a){client_write_func(o, a);}, + .readConstructorFuncClient = [&](const NetObjPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){delay_read_func(o, a);}, + .readUpdateFuncClient = [&](const NetObjPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){delay_read_func(o, a);}, + .writeConstructorFuncServer = [&](const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a){server_write_func(o, a);}, + .readConstructorFuncServer = [&](const NetObjPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){delay_read_func(o, a);}, + .readUpdateFuncServer = [&](const NetObjPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){server_read_update_func(o, a);} + }); + } + template void send_update_to_all(const NetworkingObjects::NetObjPtr& o) { + o.send_update_to_all(RELIABLE_COMMAND_CHANNEL, [&](const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a) { + if(o.get_obj_man()->is_server()) + server_write_func(o, a); + else + client_write_func(o, a); + }); + } + void update(); + private: + template void client_write_func(const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a) { + a(*o); + auto it = updatingObjs.find(o.get_net_id()); + if(it == updatingObjs.end()) { + UpdatingObject toAdd; + toAdd.objToUpdateCurrentData = std::static_pointer_cast(std::make_shared(*o)); + toAdd.lastUpdateTimePoint = std::chrono::steady_clock::now(); + toAdd.setDataAfterTimeout = [oWeak = NetObjWeakPtr(o), currentData = toAdd.objToUpdateCurrentData]() { + NetObjPtr oLock = oWeak.lock(); + if(oLock) + *oLock = *std::static_pointer_cast(currentData); + }; + updatingObjs.emplace(o.get_net_id(), toAdd); + } + else { + auto& [netID, updatingObjData] = *it; + updatingObjData.lastUpdateTimePoint = std::chrono::steady_clock::now(); + } + } + + template void delay_read_func(const NetObjPtr& o, cereal::PortableBinaryInputArchive& a) { + auto it = updatingObjs.find(o.get_net_id()); + if(it == updatingObjs.end()) + a(*o); + else { + auto& [netID, updatingObjData] = *it; + a(*std::static_pointer_cast(updatingObjData.objToUpdateCurrentData)); + } + } + + template void server_write_func(const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a) { + a(*o); + auto it = updatingObjs.find(o.get_net_id()); + if(it == updatingObjs.end()) { + UpdatingObject toAdd; + toAdd.objToUpdateCurrentData = std::static_pointer_cast(std::make_shared(*o)); + toAdd.lastUpdateTimePoint = std::chrono::steady_clock::now(); + toAdd.setDataAfterTimeout = [oWeak = NetObjWeakPtr(o), currentData = toAdd.objToUpdateCurrentData]() { + NetObjPtr oLock = oWeak.lock(); + if(oLock) { + *oLock = *std::static_pointer_cast(currentData); + oLock.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [](const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a) { + a(*o); + }); + } + }; + updatingObjs.emplace(o.get_net_id(), toAdd); + } + else { + auto& [netID, updatingObjData] = *it; + updatingObjData.lastUpdateTimePoint = std::chrono::steady_clock::now(); + *std::static_pointer_cast(updatingObjs[o.get_net_id()].objToUpdateCurrentData) = *o; + } + } + + template void server_read_update_func(const NetObjPtr& o, cereal::PortableBinaryInputArchive& a) { + auto it = updatingObjs.find(o.get_net_id()); + if(it == updatingObjs.end()) { + a(*o); + o.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [](const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a) { + a(*o); + }); + } + else { + auto& [netID, updatingObjData] = *it; + a(*std::static_pointer_cast(updatingObjData.objToUpdateCurrentData)); + } + } + + struct UpdatingObject { + std::shared_ptr objToUpdateCurrentData; + std::chrono::steady_clock::time_point lastUpdateTimePoint; + std::function setDataAfterTimeout; + }; + std::unordered_map updatingObjs; + }; +} diff --git a/include/Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp b/include/Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp index 9d11335..5c12b93 100644 --- a/include/Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp +++ b/include/Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp @@ -1,25 +1,24 @@ #pragma once -#include "Helpers/NetworkingObjects/NetObjPtr.decl.hpp" #include "NetObjPtr.hpp" +#include "cereal/archives/portable_binary.hpp" +#include namespace NetworkingObjects { + template void default_serialize_write_func(const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a) { + a(*o); + } + + template void default_serialize_read_func(const NetObjPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) { + a(*o); + } + template void register_generic_serialized_class(NetObjManagerTypeList& t) { t.register_class({ - .writeConstructorFuncClient = [](const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a) { - a(*o); - }, - .readConstructorFuncClient = [](const NetObjPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) { - a(*o); - }, - .readUpdateFuncClient = [](const NetObjPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) { - a(*o); - }, - .writeConstructorFuncServer = [](const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a) { - a(*o); - }, - .readConstructorFuncServer = [](const NetObjPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) { - a(*o); - }, + .writeConstructorFuncClient = default_serialize_write_func, + .readConstructorFuncClient = default_serialize_read_func, + .readUpdateFuncClient = default_serialize_read_func, + .writeConstructorFuncServer = default_serialize_write_func, + .readConstructorFuncServer = default_serialize_read_func, .readUpdateFuncServer = [](const NetObjPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) { a(*o); o.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [](const NetObjPtr& o, cereal::PortableBinaryOutputArchive& a) { @@ -30,8 +29,6 @@ namespace NetworkingObjects { } template void generic_serialized_class_send_update_to_all(const NetworkingObjects::NetObjPtr& o) { - o.send_update_to_all(RELIABLE_COMMAND_CHANNEL, [](const NetworkingObjects::NetObjPtr& o, cereal::PortableBinaryOutputArchive& a) { - a(*o); - }); + o.send_update_to_all(RELIABLE_COMMAND_CHANNEL, default_serialize_write_func); } } diff --git a/src/BookmarkManager.cpp b/src/BookmarkManager.cpp index 3635fd3..433f680 100644 --- a/src/BookmarkManager.cpp +++ b/src/BookmarkManager.cpp @@ -12,12 +12,14 @@ void Bookmark::scale_up(const WorldScalar& scaleUpAmount) { BookmarkManager::BookmarkManager(World& w): world(w) -{ +{} + +void BookmarkManager::init() { // NOTE: Should find a way for bookmarks added from the network to take into account the client's canvasScale // Have the canvasScale variable come with the ClientData object, and the grid scale commands can be part of // the client data class - if(w.netObjMan.is_server()) - bookmarks = w.netObjMan.make_obj>(); + if(world.netObjMan.is_server()) + bookmarks = world.netObjMan.make_obj>(); } void BookmarkManager::add_bookmark(const std::string& name) { diff --git a/src/BookmarkManager.hpp b/src/BookmarkManager.hpp index 0799dc8..02ebe33 100644 --- a/src/BookmarkManager.hpp +++ b/src/BookmarkManager.hpp @@ -20,6 +20,7 @@ class World; class BookmarkManager { public: BookmarkManager(World& w); + void init(); void add_bookmark(const std::string& name); void remove_bookmark(uint32_t bookmarkIndex); void jump_to_bookmark(uint32_t bookmarkIndex); diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 6c23ad7..0c9e166 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -20,7 +20,6 @@ #include "../World.hpp" #include "../MainProgram.hpp" #include "Helpers/FileDownloader.hpp" -#include "Helpers/NetworkingObjects/NetObjWeakPtr.hpp" #include "Helpers/StringHelpers.hpp" #include "LassoSelectTool.hpp" #include diff --git a/src/DrawingProgram/GridModifyTool.cpp b/src/DrawingProgram/GridModifyTool.cpp index 1d1ddab..f399e39 100644 --- a/src/DrawingProgram/GridModifyTool.cpp +++ b/src/DrawingProgram/GridModifyTool.cpp @@ -2,6 +2,7 @@ #include "DrawingProgram.hpp" #include "../MainProgram.hpp" #include "../DrawData.hpp" +#include "Helpers/Networking/NetLibrary.hpp" #include #include #include @@ -136,6 +137,7 @@ void GridModifyTool::tool_update() { selectionMode = 0; break; } + drawP.world.delayedUpdateObjectManager.send_update_to_all(grid); } else selectionMode = 0; diff --git a/src/GridManager.cpp b/src/GridManager.cpp index 616b925..11e50ad 100644 --- a/src/GridManager.cpp +++ b/src/GridManager.cpp @@ -7,9 +7,11 @@ #include GridManager::GridManager(World& w): - world(w) { - if(w.netObjMan.is_server()) - grids = w.netObjMan.make_obj>(); + world(w) {} + +void GridManager::init() { + if(world.netObjMan.is_server()) + grids = world.netObjMan.make_obj>(); } void GridManager::add_default_grid(const std::string& newName) { diff --git a/src/GridManager.hpp b/src/GridManager.hpp index fd1c704..ca8dc9f 100644 --- a/src/GridManager.hpp +++ b/src/GridManager.hpp @@ -8,6 +8,7 @@ class World; class GridManager { public: GridManager(World& w); + void init(); void add_default_grid(const std::string& newName); diff --git a/src/MainProgram.cpp b/src/MainProgram.cpp index 5bbe873..7cde893 100755 --- a/src/MainProgram.cpp +++ b/src/MainProgram.cpp @@ -45,14 +45,6 @@ MainProgram::MainProgram(): fonts(std::make_shared()), toolbar(*this) { - netObjectTypeList = std::make_shared(); - NetworkingObjects::register_generic_serialized_class(*netObjectTypeList); - NetworkingObjects::register_ordered_list_class(*netObjectTypeList); - NetworkingObjects::register_generic_serialized_class(*netObjectTypeList); - NetworkingObjects::register_ordered_list_class(*netObjectTypeList); - NetworkingObjects::register_generic_serialized_class(*netObjectTypeList); - NetworkingObjects::register_ordered_list_class(*netObjectTypeList); - displayName = Random::get().alphanumeric_str(10); Logger::get().add_log("WORLDFATAL", [&](const std::string& text) { diff --git a/src/MainProgram.hpp b/src/MainProgram.hpp index 4e5fd8f..9553176 100644 --- a/src/MainProgram.hpp +++ b/src/MainProgram.hpp @@ -9,7 +9,6 @@ #include "SharedTypes.hpp" #include #include -#include #include "Toolbar.hpp" #include "World.hpp" @@ -116,8 +115,6 @@ class MainProgram { std::string displayName; - std::shared_ptr netObjectTypeList; - ~MainProgram(); private: void new_tab_open(); diff --git a/src/World.cpp b/src/World.cpp index fd33a32..2165d61 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1,10 +1,13 @@ #include "World.hpp" #include "DrawComponents/DrawComponent.hpp" -#include "Helpers/HsvRgb.hpp" -#include "Helpers/MathExtras.hpp" -#include "Helpers/Networking/ByteStream.hpp" -#include "Helpers/NetworkingObjects/NetObjManager.hpp" -#include "Helpers/VersionNumber.hpp" +#include +#include +#include +#include +#include +#include +#include +#include #include "Server/CommandList.hpp" #include "MainProgram.hpp" #include "SharedTypes.hpp" @@ -27,13 +30,18 @@ #endif World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): - netObjMan(initMain.netObjectTypeList, worldInfo.conType != CONNECTIONTYPE_CLIENT), + netObjectTypeList(std::make_shared()), + netObjMan(netObjectTypeList, worldInfo.conType != CONNECTIONTYPE_CLIENT), main(initMain), rMan(*this), drawProg(*this), bMan(*this), gridMan(*this) { + init_net_obj_type_list(); + gridMan.init(); + bMan.init(); + set_canvas_background_color(main.defaultCanvasBackgroundColor); displayName = main.displayName; @@ -70,6 +78,13 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_INITIAL_DATA, displayName, false); } +void World::init_net_obj_type_list() { + NetworkingObjects::register_generic_serialized_class(*netObjectTypeList); + NetworkingObjects::register_ordered_list_class(*netObjectTypeList); + delayedUpdateObjectManager.register_class(*netObjectTypeList); + NetworkingObjects::register_ordered_list_class(*netObjectTypeList); +} + void World::init_client_callbacks() { con.client_add_recv_callback(CLIENT_INITIAL_DATA, [&](cereal::PortableBinaryInputArchive& message) { bool isDirectConnect; @@ -171,6 +186,7 @@ void World::focus_update() { } if(!clientStillConnecting) { + delayedUpdateObjectManager.update(); constexpr float SECONDS_TO_SEND_CAMERA_DATA = 0.5f; timeToSendCameraData.update_time_since(); if(timeToSendCameraData.get_time_since() > SECONDS_TO_SEND_CAMERA_DATA) { diff --git a/src/World.hpp b/src/World.hpp index 6fc40b0..916d6bf 100644 --- a/src/World.hpp +++ b/src/World.hpp @@ -1,4 +1,5 @@ #pragma once +#include "Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp" #include "Helpers/NetworkingObjects/NetObjOrderedList.hpp" #include "UndoManager.hpp" #include "BookmarkManager.hpp" @@ -36,6 +37,7 @@ class World { World(MainProgram& initMain, OpenWorldInfo& worldInfo); + std::shared_ptr netObjectTypeList; // NOTE: Keep at the very beginning so that it's destroyed last NetworkingObjects::NetObjManager netObjMan; @@ -106,10 +108,13 @@ class World { uint64_t canvasScale = 0; bool setToDestroy = false; + NetworkingObjects::DelayUpdateSerializedClassManager delayedUpdateObjectManager; private: + void init_net_obj_type_list(); ClientPortionID get_max_id(ServerPortionID serverID); + void init_client_callbacks(); void set_name(const std::string& n);