Added generic delayed update objects

This commit is contained in:
Yousef Khadadeh 2025-12-10 17:23:05 +03:00
parent ceafbf8bb6
commit d266508bbf
14 changed files with 176 additions and 43 deletions

View file

@ -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"

View file

@ -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;
});
}
}

View file

@ -0,0 +1,105 @@
#pragma once
#include "NetObjWeakPtr.hpp"
#include "cereal/archives/portable_binary.hpp"
#include <chrono>
namespace NetworkingObjects {
class DelayUpdateSerializedClassManager {
public:
template <typename T> void register_class(NetObjManagerTypeList& t) {
t.register_class<T, T, T, T>({
.writeConstructorFuncClient = [&](const NetObjPtr<T>& o, cereal::PortableBinaryOutputArchive& a){client_write_func<T>(o, a);},
.readConstructorFuncClient = [&](const NetObjPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){delay_read_func<T>(o, a);},
.readUpdateFuncClient = [&](const NetObjPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){delay_read_func<T>(o, a);},
.writeConstructorFuncServer = [&](const NetObjPtr<T>& o, cereal::PortableBinaryOutputArchive& a){server_write_func<T>(o, a);},
.readConstructorFuncServer = [&](const NetObjPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){delay_read_func<T>(o, a);},
.readUpdateFuncServer = [&](const NetObjPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){server_read_update_func<T>(o, a);}
});
}
template <typename T> void send_update_to_all(const NetworkingObjects::NetObjPtr<T>& o) {
o.send_update_to_all(RELIABLE_COMMAND_CHANNEL, [&](const NetObjPtr<T>& 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 <typename T> void client_write_func(const NetObjPtr<T>& 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<void>(std::make_shared<T>(*o));
toAdd.lastUpdateTimePoint = std::chrono::steady_clock::now();
toAdd.setDataAfterTimeout = [oWeak = NetObjWeakPtr<T>(o), currentData = toAdd.objToUpdateCurrentData]() {
NetObjPtr<T> oLock = oWeak.lock();
if(oLock)
*oLock = *std::static_pointer_cast<T>(currentData);
};
updatingObjs.emplace(o.get_net_id(), toAdd);
}
else {
auto& [netID, updatingObjData] = *it;
updatingObjData.lastUpdateTimePoint = std::chrono::steady_clock::now();
}
}
template <typename T> void delay_read_func(const NetObjPtr<T>& 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<T>(updatingObjData.objToUpdateCurrentData));
}
}
template <typename T> void server_write_func(const NetObjPtr<T>& 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<void>(std::make_shared<T>(*o));
toAdd.lastUpdateTimePoint = std::chrono::steady_clock::now();
toAdd.setDataAfterTimeout = [oWeak = NetObjWeakPtr<T>(o), currentData = toAdd.objToUpdateCurrentData]() {
NetObjPtr<T> oLock = oWeak.lock();
if(oLock) {
*oLock = *std::static_pointer_cast<T>(currentData);
oLock.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [](const NetObjPtr<T>& 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<T>(updatingObjs[o.get_net_id()].objToUpdateCurrentData) = *o;
}
}
template <typename T> void server_read_update_func(const NetObjPtr<T>& 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<T>& o, cereal::PortableBinaryOutputArchive& a) {
a(*o);
});
}
else {
auto& [netID, updatingObjData] = *it;
a(*std::static_pointer_cast<T>(updatingObjData.objToUpdateCurrentData));
}
}
struct UpdatingObject {
std::shared_ptr<void> objToUpdateCurrentData;
std::chrono::steady_clock::time_point lastUpdateTimePoint;
std::function<void()> setDataAfterTimeout;
};
std::unordered_map<NetObjID, UpdatingObject> updatingObjs;
};
}

View file

@ -1,25 +1,24 @@
#pragma once
#include "Helpers/NetworkingObjects/NetObjPtr.decl.hpp"
#include "NetObjPtr.hpp"
#include "cereal/archives/portable_binary.hpp"
#include <chrono>
namespace NetworkingObjects {
template <typename T> void default_serialize_write_func(const NetObjPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
a(*o);
}
template <typename T> void default_serialize_read_func(const NetObjPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) {
a(*o);
}
template <typename T> void register_generic_serialized_class(NetObjManagerTypeList& t) {
t.register_class<T, T, T, T>({
.writeConstructorFuncClient = [](const NetObjPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
a(*o);
},
.readConstructorFuncClient = [](const NetObjPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) {
a(*o);
},
.readUpdateFuncClient = [](const NetObjPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) {
a(*o);
},
.writeConstructorFuncServer = [](const NetObjPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
a(*o);
},
.readConstructorFuncServer = [](const NetObjPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) {
a(*o);
},
.writeConstructorFuncClient = default_serialize_write_func<T>,
.readConstructorFuncClient = default_serialize_read_func<T>,
.readUpdateFuncClient = default_serialize_read_func<T>,
.writeConstructorFuncServer = default_serialize_write_func<T>,
.readConstructorFuncServer = default_serialize_read_func<T>,
.readUpdateFuncServer = [](const NetObjPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) {
a(*o);
o.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [](const NetObjPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
@ -30,8 +29,6 @@ namespace NetworkingObjects {
}
template <typename T> void generic_serialized_class_send_update_to_all(const NetworkingObjects::NetObjPtr<T>& o) {
o.send_update_to_all(RELIABLE_COMMAND_CHANNEL, [](const NetworkingObjects::NetObjPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
a(*o);
});
o.send_update_to_all(RELIABLE_COMMAND_CHANNEL, default_serialize_write_func<T>);
}
}

View file

@ -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<NetworkingObjects::NetObjOrderedList<Bookmark>>();
if(world.netObjMan.is_server())
bookmarks = world.netObjMan.make_obj<NetworkingObjects::NetObjOrderedList<Bookmark>>();
}
void BookmarkManager::add_bookmark(const std::string& name) {

View file

@ -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);

View file

@ -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 <Helpers/Logger.hpp>

View file

@ -2,6 +2,7 @@
#include "DrawingProgram.hpp"
#include "../MainProgram.hpp"
#include "../DrawData.hpp"
#include "Helpers/Networking/NetLibrary.hpp"
#include <Helpers/NetworkingObjects/NetObjWeakPtr.hpp>
#include <Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp>
#include <cstddef>
@ -136,6 +137,7 @@ void GridModifyTool::tool_update() {
selectionMode = 0;
break;
}
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(grid);
}
else
selectionMode = 0;

View file

@ -7,9 +7,11 @@
#include <algorithm>
GridManager::GridManager(World& w):
world(w) {
if(w.netObjMan.is_server())
grids = w.netObjMan.make_obj<NetworkingObjects::NetObjOrderedList<WorldGrid>>();
world(w) {}
void GridManager::init() {
if(world.netObjMan.is_server())
grids = world.netObjMan.make_obj<NetworkingObjects::NetObjOrderedList<WorldGrid>>();
}
void GridManager::add_default_grid(const std::string& newName) {

View file

@ -8,6 +8,7 @@ class World;
class GridManager {
public:
GridManager(World& w);
void init();
void add_default_grid(const std::string& newName);

View file

@ -45,14 +45,6 @@ MainProgram::MainProgram():
fonts(std::make_shared<FontData>()),
toolbar(*this)
{
netObjectTypeList = std::make_shared<NetworkingObjects::NetObjManagerTypeList>();
NetworkingObjects::register_generic_serialized_class<std::string>(*netObjectTypeList);
NetworkingObjects::register_ordered_list_class<std::string>(*netObjectTypeList);
NetworkingObjects::register_generic_serialized_class<Bookmark>(*netObjectTypeList);
NetworkingObjects::register_ordered_list_class<Bookmark>(*netObjectTypeList);
NetworkingObjects::register_generic_serialized_class<WorldGrid>(*netObjectTypeList);
NetworkingObjects::register_ordered_list_class<WorldGrid>(*netObjectTypeList);
displayName = Random::get().alphanumeric_str(10);
Logger::get().add_log("WORLDFATAL", [&](const std::string& text) {

View file

@ -9,7 +9,6 @@
#include "SharedTypes.hpp"
#include <Eigen/Dense>
#include <include/core/SkSurfaceProps.h>
#include <Helpers/NetworkingObjects/NetObjManagerTypeList.hpp>
#include "Toolbar.hpp"
#include "World.hpp"
@ -116,8 +115,6 @@ class MainProgram {
std::string displayName;
std::shared_ptr<NetworkingObjects::NetObjManagerTypeList> netObjectTypeList;
~MainProgram();
private:
void new_tab_open();

View file

@ -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 <Helpers/HsvRgb.hpp>
#include <Helpers/MathExtras.hpp>
#include <Helpers/Networking/ByteStream.hpp>
#include <Helpers/NetworkingObjects/NetObjManager.hpp>
#include <Helpers/NetworkingObjects/NetObjManagerTypeList.hpp>
#include <Helpers/VersionNumber.hpp>
#include <Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp>
#include <Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp>
#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<NetworkingObjects::NetObjManagerTypeList>()),
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<Bookmark>(*netObjectTypeList);
NetworkingObjects::register_ordered_list_class<Bookmark>(*netObjectTypeList);
delayedUpdateObjectManager.register_class<WorldGrid>(*netObjectTypeList);
NetworkingObjects::register_ordered_list_class<WorldGrid>(*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) {

View file

@ -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<NetworkingObjects::NetObjManagerTypeList> 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);