From cd4d2572f894327d9379d3b0d895f4959e0a3f3c Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Tue, 23 Dec 2025 14:10:33 +0300 Subject: [PATCH] Add multi update message --- include/Helpers/Networking/NetClient.hpp | 2 +- include/Helpers/Networking/NetServer.hpp | 2 +- .../NetworkingObjects/NetObjManager.cpp | 54 +++++++++++- .../NetworkingObjects/NetObjManager.hpp | 85 ++++++++++++------- src/Bookmarks/BookmarkManager.cpp | 46 +++++----- src/CommandList.hpp | 2 + src/ConnectionManager.cpp | 2 +- src/World.cpp | 8 +- 8 files changed, 140 insertions(+), 61 deletions(-) diff --git a/include/Helpers/Networking/NetClient.hpp b/include/Helpers/Networking/NetClient.hpp index b6cc1a1..63d1488 100644 --- a/include/Helpers/Networking/NetClient.hpp +++ b/include/Helpers/Networking/NetClient.hpp @@ -20,7 +20,7 @@ class NetClient : public std::enable_shared_from_this { template void send_items_to_server(const std::string& channel, Args&&... items) { if(isDisconnected) return; - auto ss(std::make_shared()); + auto ss(std::make_shared(std::ios::binary | std::ios::out)); { cereal::PortableBinaryOutputArchive m(*ss); (m(items), ...); diff --git a/include/Helpers/Networking/NetServer.hpp b/include/Helpers/Networking/NetServer.hpp index 67173e6..e2c6a49 100644 --- a/include/Helpers/Networking/NetServer.hpp +++ b/include/Helpers/Networking/NetServer.hpp @@ -67,7 +67,7 @@ class NetServer : public std::enable_shared_from_this { send_string_stream_to_client_if(clientChecker, channel, get_string_stream_from_items(items...)); } template std::shared_ptr get_string_stream_from_items(Args&&... items) { - auto ss(std::make_shared()); + auto ss(std::make_shared(std::ios::binary | std::ios::out)); { cereal::PortableBinaryOutputArchive m(*ss); (m(items), ...); diff --git a/include/Helpers/NetworkingObjects/NetObjManager.cpp b/include/Helpers/NetworkingObjects/NetObjManager.cpp index 176282e..0d774a0 100644 --- a/include/Helpers/NetworkingObjects/NetObjManager.cpp +++ b/include/Helpers/NetworkingObjects/NetObjManager.cpp @@ -17,16 +17,66 @@ namespace NetworkingObjects { return; typeList.get_net_type_data(isServer, it->second.netTypeID).readUpdateFunc(NetObjTemporaryPtr(this, id, it->second.p), a, clientReceivedFrom); } - void NetObjManager::set_client(std::shared_ptr initClient, MessageCommandType initUpdateCommandID) { + void NetObjManager::read_many_update_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr& clientReceivedFrom) { + for(;;) { + bool objExists; + a(objExists); + if(!objExists) + break; + read_update_message(a, clientReceivedFrom); + } + } + void NetObjManager::set_client(std::shared_ptr initClient, MessageCommandType initUpdateCommandID, MessageCommandType initMultiUpdateCommandID) { client = initClient; updateCommandID = initUpdateCommandID; + multiUpdateCommandID = initMultiUpdateCommandID; } - void NetObjManager::set_server(std::shared_ptr initServer, MessageCommandType initUpdateCommandID) { + void NetObjManager::set_server(std::shared_ptr initServer, MessageCommandType initUpdateCommandID, MessageCommandType initMultiUpdateCommandID) { server = initServer; updateCommandID = initUpdateCommandID; + multiUpdateCommandID = initMultiUpdateCommandID; } void NetObjManager::disconnect() { server = nullptr; client = nullptr; } + bool NetObjManager::is_connected() const { + return (server && !server->is_disconnected()) || (client && !client->is_disconnected()); + } + void NetObjManager::send_multi_update_messsage(std::function captureSendBlock, SendUpdateType updateType, const std::shared_ptr& specificClient) { + if(is_connected()) { + std::shared_ptr d = std::make_shared(); + d->ss = std::make_shared(std::ios::binary | std::ios::out); + d->outArchive = std::make_unique(*d->ss); + (*d->outArchive)(multiUpdateCommandID); + multiUpdateData = d; // Using weak pointer here ensures that object will be freed + captureSendBlock(); + if(!d->channelToSendTo.empty()) { + (*d->outArchive)(false); + d->outArchive = nullptr; // Destroy output archive to finalize serialization + send_update_data_by_type(d->channelToSendTo, d->ss, updateType, specificClient); + } + } + else + captureSendBlock(); + } + + void NetObjManager::send_update_data_by_type(const std::string& channel, const std::shared_ptr& ss, SendUpdateType updateType, const std::shared_ptr& specificClient) { + if(client && updateType != SendUpdateType::SEND_TO_ALL) + throw std::runtime_error("[NetObjManager::send_update_data_by_type] Client can only send to all (to server)"); + switch(updateType) { + case SendUpdateType::SEND_TO_ALL: + if(client) + client->send_string_stream_to_server(channel, ss); + else + server->send_string_stream_to_all_clients(channel, ss); + break; + case SendUpdateType::SEND_TO_ALL_EXCEPT: + server->send_string_stream_to_all_clients_except(specificClient, channel, ss); + break; + case SendUpdateType::SEND_TO_SPECIFIC_CLIENT: + server->send_string_stream_to_client(specificClient, channel, ss); + break; + } + } } diff --git a/include/Helpers/NetworkingObjects/NetObjManager.hpp b/include/Helpers/NetworkingObjects/NetObjManager.hpp index 805bb73..c96dbfb 100644 --- a/include/Helpers/NetworkingObjects/NetObjManager.hpp +++ b/include/Helpers/NetworkingObjects/NetObjManager.hpp @@ -21,10 +21,12 @@ namespace NetworkingObjects { public: NetObjManager(bool initIsServer); void read_update_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr& clientReceivedFrom); - void set_client(std::shared_ptr initClient, MessageCommandType initUpdateCommandID); - void set_server(std::shared_ptr initServer, MessageCommandType initUpdateCommandID); + void read_many_update_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr& clientReceivedFrom); + void set_client(std::shared_ptr initClient, MessageCommandType initUpdateCommandID, MessageCommandType initMultiUpdateCommandID); + void set_server(std::shared_ptr initServer, MessageCommandType initUpdateCommandID, MessageCommandType initMultiUpdateCommandID); void disconnect(); bool is_server() const; + bool is_connected() const; template NetObjOwnerPtr read_create_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr& clientReceivedFrom) { NetObjID id; a(id); @@ -44,6 +46,13 @@ namespace NetworkingObjects { return NetObjTemporaryPtr(this, id, std::static_pointer_cast(it->second.p)); } + enum class SendUpdateType { + SEND_TO_ALL, + SEND_TO_ALL_EXCEPT, + SEND_TO_SPECIFIC_CLIENT + }; + void send_multi_update_messsage(std::function captureSendBlock, SendUpdateType updateType, const std::shared_ptr& specificClient); + // NOTE: DO NOT USE THESE FUNCTIONS UNLESS SITUATION ISN'T NORMAL, EVERY OBJECT SHOULD HAVE A UNIQUE ID AND IDS SHOULDNT BE REUSED UNDER ANY CIRCUMSTANCES template NetObjOwnerPtr make_obj_with_specific_id(const NetObjID& id) { return emplace_raw_ptr(id, static_cast(typeList.get_type_index_data(isServer).allocatorFunc())); @@ -72,6 +81,8 @@ namespace NetworkingObjects { typeList.register_class(funcs); } private: + void send_update_data_by_type(const std::string& channel, const std::shared_ptr& ss, SendUpdateType updateType, const std::shared_ptr& specificClient); + template NetObjOwnerPtr emplace_raw_ptr(NetObjID id, T* rawPtr) { if(!objectData.emplace(id, SingleObjectData{.netTypeID = typeList.get_type_index_data(isServer).netTypeID, .p = rawPtr}).second) throw std::runtime_error("[NetObjManager::emplace_raw_ptr] ID Collision"); @@ -86,51 +97,51 @@ namespace NetworkingObjects { } template static void send_client_update(const NetObjTemporaryPtr& ptr, const std::string& channel, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { - if(ptr.get_obj_man()->client && !ptr.get_obj_man()->client->is_disconnected()) { - auto ss(std::make_shared()); - { - cereal::PortableBinaryOutputArchive m(*ss); - m(ptr.get_obj_man()->updateCommandID, ptr.get_net_id()); - sendUpdateFunc(ptr, m); - } + auto ss = send_update_general_check_for_multi_update(channel, ptr, sendUpdateFunc); + if(ss) ptr.get_obj_man()->client->send_string_stream_to_server(channel, ss); - } } template static void send_server_update_to_client(const NetObjTemporaryPtr& ptr, const std::shared_ptr& clientToSendTo, const std::string& channel, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { - if(ptr.get_obj_man()->server && !ptr.get_obj_man()->server->is_disconnected()) { - auto ss(std::make_shared()); - { - cereal::PortableBinaryOutputArchive m(*ss); - m(ptr.get_obj_man()->updateCommandID, ptr.get_net_id()); - sendUpdateFunc(ptr, m); - } - ptr.get_obj_man()->server->send_string_stream_to_client(clientToSendTo, channel, ss); + if(clientToSendTo) { + auto ss = send_update_general_check_for_multi_update(channel, ptr, sendUpdateFunc); + if(ss) + ptr.get_obj_man()->server->send_string_stream_to_client(clientToSendTo, channel, ss); } } template static void send_server_update_to_all_clients_except(const NetObjTemporaryPtr& ptr, const std::shared_ptr& clientToNotSendTo, const std::string& channel, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { - if(ptr.get_obj_man()->server && !ptr.get_obj_man()->server->is_disconnected()) { - auto ss(std::make_shared()); - { - cereal::PortableBinaryOutputArchive m(*ss); - m(ptr.get_obj_man()->updateCommandID, ptr.get_net_id()); - sendUpdateFunc(ptr, m); - } + auto ss = send_update_general_check_for_multi_update(channel, ptr, sendUpdateFunc); + if(ss) ptr.get_obj_man()->server->send_string_stream_to_all_clients_except(clientToNotSendTo, channel, ss); - } } template static void send_server_update_to_all_clients(const NetObjTemporaryPtr& ptr, const std::string& channel, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { - if(ptr.get_obj_man()->server && !ptr.get_obj_man()->server->is_disconnected()) { - auto ss(std::make_shared()); - { - cereal::PortableBinaryOutputArchive m(*ss); - m(ptr.get_obj_man()->updateCommandID, ptr.get_net_id()); - sendUpdateFunc(ptr, m); - } + auto ss = send_update_general_check_for_multi_update(channel, ptr, sendUpdateFunc); + if(ss) ptr.get_obj_man()->server->send_string_stream_to_all_clients(channel, ss); + } + + template static std::shared_ptr send_update_general_check_for_multi_update(const std::string& channel, const NetObjTemporaryPtr& ptr, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { + if(ptr.get_obj_man()->is_connected()) { + std::shared_ptr mData = ptr.get_obj_man()->multiUpdateData.lock(); + if(mData) { + mData->channelToSendTo = channel; + (*mData->outArchive)(true, ptr.get_net_id()); + sendUpdateFunc(ptr, *mData->outArchive); + return nullptr; + } + else { + auto ss(std::make_shared(std::ios::binary | std::ios::out)); + { + cereal::PortableBinaryOutputArchive m(*ss); + m(ptr.get_obj_man()->updateCommandID, ptr.get_net_id()); + sendUpdateFunc(ptr, m); + } + return ss; + } } + return nullptr; } template static void write_create_message(const NetObjTemporaryPtr& ptr, cereal::PortableBinaryOutputArchive& a) { @@ -147,10 +158,18 @@ namespace NetworkingObjects { void* p; }; + struct MultiUpdateData { + std::string channelToSendTo; + std::shared_ptr ss; + std::unique_ptr outArchive; + }; + std::weak_ptr multiUpdateData; + bool isServer; std::shared_ptr client; std::shared_ptr server; MessageCommandType updateCommandID; + MessageCommandType multiUpdateCommandID; std::unordered_map objectData; NetObjManagerTypeList typeList; NetTypeIDType nextTypeID; diff --git a/src/Bookmarks/BookmarkManager.cpp b/src/Bookmarks/BookmarkManager.cpp index 64ae8df..b530f52 100644 --- a/src/Bookmarks/BookmarkManager.cpp +++ b/src/Bookmarks/BookmarkManager.cpp @@ -103,31 +103,33 @@ void BookmarkManager::setup_list_gui(const std::string& id) { return false; }, .moveObjectsToListAtIndex = [&](NetObjID listObj, size_t index, const std::vector& objsToInsert) { - std::unordered_map> toEraseMap; - uint32_t newIndex = index; - std::vector> toInsertObjPtrs; - auto& listPtr = world.netObjMan.get_obj_temporary_ref_from_id(listObj)->get_folder_list(); + world.netObjMan.send_multi_update_messsage([&]() { + std::unordered_map> toEraseMap; + uint32_t newIndex = index; + std::vector> toInsertObjPtrs; + auto& listPtr = world.netObjMan.get_obj_temporary_ref_from_id(listObj)->get_folder_list(); - for(size_t i = 0; i < objsToInsert.size(); i++) { - toEraseMap[objsToInsert[i].parent].emplace(objsToInsert[i].object); - if(newIndex != 0 && objsToInsert[i].parent == listObj && listPtr->get(objsToInsert[i].object)->pos <= index) - newIndex--; - } + for(size_t i = 0; i < objsToInsert.size(); i++) { + toEraseMap[objsToInsert[i].parent].emplace(objsToInsert[i].object); + if(newIndex != 0 && objsToInsert[i].parent == listObj && listPtr->get(objsToInsert[i].object)->pos <= index) + newIndex--; + } - for(auto& [listToEraseFrom, setToErase] : toEraseMap) { - auto& listToEraseFromPtr = world.netObjMan.get_obj_temporary_ref_from_id(listToEraseFrom)->get_folder_list(); - listToEraseFromPtr->erase_unordered_set(listToEraseFromPtr, setToErase, &toInsertObjPtrs); - } + for(auto& [listToEraseFrom, setToErase] : toEraseMap) { + auto& listToEraseFromPtr = world.netObjMan.get_obj_temporary_ref_from_id(listToEraseFrom)->get_folder_list(); + listToEraseFromPtr->erase_unordered_set(listToEraseFromPtr, setToErase, &toInsertObjPtrs); + } - std::vector>> toInsert; - for(uint32_t i = 0; i < objsToInsert.size(); i++) { - auto it = std::find_if(toInsertObjPtrs.begin(), toInsertObjPtrs.end(), [id = objsToInsert[i].object](NetObjOwnerPtr& objPtr) { - return objPtr.get_net_id() == id; - }); - toInsert.emplace_back(newIndex + i, std::move(*it)); - toInsert.back().second.reassign_ids(); - } - listPtr->insert_ordered_list_and_send_create(listPtr, toInsert); + std::vector>> toInsert; + for(uint32_t i = 0; i < objsToInsert.size(); i++) { + auto it = std::find_if(toInsertObjPtrs.begin(), toInsertObjPtrs.end(), [id = objsToInsert[i].object](NetObjOwnerPtr& objPtr) { + return objPtr.get_net_id() == id; + }); + toInsert.emplace_back(newIndex + i, std::move(*it)); + toInsert.back().second.reassign_ids(); + } + listPtr->insert_ordered_list_and_send_create(listPtr, toInsert); + }, NetObjManager::SendUpdateType::SEND_TO_ALL, nullptr); } }); if(toDeleteObject.has_value()) { diff --git a/src/CommandList.hpp b/src/CommandList.hpp index cb4222a..b8401d4 100644 --- a/src/CommandList.hpp +++ b/src/CommandList.hpp @@ -7,6 +7,7 @@ enum ServerCommands : MessageCommandType { SERVER_KEEP_ALIVE, SERVER_INITIAL_DATA, SERVER_UPDATE_NETWORK_OBJECT, + SERVER_UPDATE_MANY_NETWORK_OBJECTS, SERVER_NEW_RESOURCE_ID, SERVER_NEW_RESOURCE_DATA }; @@ -16,6 +17,7 @@ enum ClientCommands : MessageCommandType { CLIENT_KEEP_ALIVE, CLIENT_INITIAL_DATA, CLIENT_UPDATE_NETWORK_OBJECT, + CLIENT_UPDATE_MANY_NETWORK_OBJECTS, CLIENT_NEW_RESOURCE_ID, CLIENT_NEW_RESOURCE_DATA }; diff --git a/src/ConnectionManager.cpp b/src/ConnectionManager.cpp index c170bfd..35bfe0d 100644 --- a/src/ConnectionManager.cpp +++ b/src/ConnectionManager.cpp @@ -9,7 +9,7 @@ void ConnectionManager::connect_p2p(World& initWorld, const std::string& serverF client = std::make_shared(serverFullID); lastKeepAliveTime = std::chrono::steady_clock::now(); NetLibrary::register_client(client); - initWorld.netObjMan.set_client(client, SERVER_UPDATE_NETWORK_OBJECT); + initWorld.netObjMan.set_client(client, SERVER_UPDATE_NETWORK_OBJECT, SERVER_UPDATE_MANY_NETWORK_OBJECTS); } void ConnectionManager::update() { diff --git a/src/World.cpp b/src/World.cpp index 91671ab..6021b14 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -154,6 +154,9 @@ void World::init_server_callbacks() { netServer->add_recv_callback(SERVER_UPDATE_NETWORK_OBJECT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { netObjMan.read_update_message(message, client); }); + netServer->add_recv_callback(SERVER_UPDATE_MANY_NETWORK_OBJECTS, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { + netObjMan.read_many_update_message(message, client); + }); netServer->add_recv_callback(SERVER_KEEP_ALIVE, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { }); netServer->add_disconnect_callback([&](std::shared_ptr client) { @@ -184,6 +187,9 @@ void World::init_client_callbacks() { con.client_add_recv_callback(CLIENT_UPDATE_NETWORK_OBJECT, [&](cereal::PortableBinaryInputArchive& message) { netObjMan.read_update_message(message, nullptr); }); + con.client_add_recv_callback(CLIENT_UPDATE_MANY_NETWORK_OBJECTS, [&](cereal::PortableBinaryInputArchive& message) { + netObjMan.read_many_update_message(message, nullptr); + }); con.client_add_recv_callback(CLIENT_KEEP_ALIVE, [&](cereal::PortableBinaryInputArchive& message) { }); } @@ -347,7 +353,7 @@ void World::start_hosting(const std::string& initNetSource, const std::string& s main.init_net_library(); netServer = std::make_shared(serverLocalID); NetLibrary::register_server(netServer); - netObjMan.set_server(netServer, CLIENT_UPDATE_NETWORK_OBJECT); + netObjMan.set_server(netServer, CLIENT_UPDATE_NETWORK_OBJECT, CLIENT_UPDATE_MANY_NETWORK_OBJECTS); conType = CONNECTIONTYPE_SERVER; netSource = initNetSource; init_server_callbacks();