From 9649135f4316bfbc5f0dd30a39cefe53e10720c4 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Tue, 9 Dec 2025 13:00:38 +0300 Subject: [PATCH] Added sync code to client ordered list --- CMakeLists.txt | 1 + include/Helpers/Networking/NetClient.cpp | 8 + include/Helpers/Networking/NetClient.hpp | 1 + include/Helpers/Networking/NetServer.cpp | 4 +- .../NetworkingObjects/NetObjManager.cpp | 6 +- .../NetworkingObjects/NetObjManager.hpp | 44 ++--- .../NetObjManagerTypeList.cpp | 7 + .../NetObjManagerTypeList.hpp | 87 +++++----- .../NetworkingObjects/NetObjOrderedList.hpp | 157 ++++++++++++------ .../NetworkingObjects/NetObjPtr.impl.hpp | 9 +- src/ConnectionManager.cpp | 4 +- src/ConnectionManager.hpp | 2 +- src/Server/CommandList.hpp | 2 + src/Server/MainServer.cpp | 5 + src/World.cpp | 35 +++- src/World.hpp | 4 +- 16 files changed, 233 insertions(+), 143 deletions(-) create mode 100644 include/Helpers/NetworkingObjects/NetObjManagerTypeList.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 530d0c0..76ebff7 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/NetObjManagerTypeList.cpp" "include/Helpers/NetworkingObjects/NetObjManager.cpp" "include/Helpers/NetworkingObjects/NetObjID.cpp" "include/Helpers/Hashes.cpp" diff --git a/include/Helpers/Networking/NetClient.cpp b/include/Helpers/Networking/NetClient.cpp index c60fd6b..5964702 100644 --- a/include/Helpers/Networking/NetClient.cpp +++ b/include/Helpers/Networking/NetClient.cpp @@ -177,3 +177,11 @@ void NetClient::send_str_as_bytes(std::shared_ptr channel, con void NetClient::add_recv_callback(MessageCommandType commandID, const NetClientRecvCallback& callback) { recvCallbacks[commandID] = callback; } + +NetClient::~NetClient() { + if(directConnectServer) { + directConnectServer->directConnectClient = nullptr; + std::erase(directConnectServer->clients, directConnectServer->directConnectClientData); + directConnectServer->directConnectClientData = nullptr; + } +} diff --git a/include/Helpers/Networking/NetClient.hpp b/include/Helpers/Networking/NetClient.hpp index f2effb8..b6cc1a1 100644 --- a/include/Helpers/Networking/NetClient.hpp +++ b/include/Helpers/Networking/NetClient.hpp @@ -32,6 +32,7 @@ class NetClient : public std::enable_shared_from_this { void add_recv_callback(MessageCommandType commandID, const NetClientRecvCallback& callback); bool is_disconnected() const; NetLibrary::DownloadProgress get_progress_into_fragmented_message(const std::string& channel) const; + ~NetClient(); private: std::string localID; diff --git a/include/Helpers/Networking/NetServer.cpp b/include/Helpers/Networking/NetServer.cpp index 4cbc7db..0cfcab9 100644 --- a/include/Helpers/Networking/NetServer.cpp +++ b/include/Helpers/Networking/NetServer.cpp @@ -261,6 +261,8 @@ const std::vector> NetServer::get_client_ } NetServer::~NetServer() { - if(directConnectClient) + if(directConnectClient) { directConnectClient->isDisconnected = true; + directConnectClient->directConnectServer = nullptr; + } } diff --git a/include/Helpers/NetworkingObjects/NetObjManager.cpp b/include/Helpers/NetworkingObjects/NetObjManager.cpp index ce870a4..ea38ec2 100644 --- a/include/Helpers/NetworkingObjects/NetObjManager.cpp +++ b/include/Helpers/NetworkingObjects/NetObjManager.cpp @@ -16,11 +16,7 @@ namespace NetworkingObjects { auto it = objectData.find(id); if(it == objectData.end()) return; - auto& netTypeIDData = typeList->netTypeIDData[it->second.netTypeID]; - if(isServer) - netTypeIDData.readUpdateFuncServer(NetObjPtr(this, id, it->second.p), a, clientReceivedFrom); - else - netTypeIDData.readUpdateFuncClient(NetObjPtr(this, id, it->second.p), a, clientReceivedFrom); + typeList->get_net_type_data(isServer, it->second.netTypeID).readUpdateFunc(NetObjPtr(this, id, it->second.p), a, clientReceivedFrom); } void NetObjManager::set_client(std::shared_ptr initClient, MessageCommandType initUpdateCommandID) { client = initClient; diff --git a/include/Helpers/NetworkingObjects/NetObjManager.hpp b/include/Helpers/NetworkingObjects/NetObjManager.hpp index e7baa29..e766c2d 100644 --- a/include/Helpers/NetworkingObjects/NetObjManager.hpp +++ b/include/Helpers/NetworkingObjects/NetObjManager.hpp @@ -25,14 +25,9 @@ namespace NetworkingObjects { bool is_server() const; template NetObjPtr read_create_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr& clientReceivedFrom) { NetObjID id; - NetTypeIDType typeID; - a(id, typeID); - auto& netTypeIDData = typeList->netTypeIDData[typeID]; - NetObjPtr newPtr = make_obj_with_id(id); - if(isServer) - netTypeIDData.readConstructorFuncServer(newPtr.template cast(), a, clientReceivedFrom); - else - netTypeIDData.readConstructorFuncClient(newPtr.template cast(), a, clientReceivedFrom); + a(id); + NetObjPtr newPtr = make_obj_with_id_might_already_exist(id); + typeList->get_type_index_data(isServer).readConstructorFunc(newPtr.template cast(), a, clientReceivedFrom); return newPtr; } template NetObjPtr read_get_obj_from_message(cereal::PortableBinaryInputArchive& a) { @@ -44,26 +39,14 @@ namespace NetworkingObjects { return NetObjPtr(this, id, std::static_pointer_cast(it->second.p)); } template NetObjPtr make_obj() { - auto sharedPtr = std::static_pointer_cast(isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].allocatorFunc() : typeList->typeIndexDataClient[std::type_index(typeid(T*))].allocatorFunc()); - NetObjID newID = NetObjID::random_gen(); - if(!objectData.emplace(newID, SingleObjectData{.netTypeID = isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].netTypeID : typeList->typeIndexDataClient[std::type_index(typeid(T*))].netTypeID, .p = std::static_pointer_cast(sharedPtr)}).second) - throw std::runtime_error("[NetObjManager::make_obj] ID Collision"); - return NetObjPtr(this, newID, sharedPtr); + return emplace_shared_ptr(NetObjID::random_gen(), std::static_pointer_cast(typeList->get_type_index_data(isServer).allocatorFunc())); } // Don't use this function unless you're sure that class T isn't a base class template NetObjPtr make_obj_direct(Args&&... items) { - auto sharedPtr = std::make_shared(items...); - NetObjID newID = NetObjID::random_gen(); - if(!objectData.emplace(newID, SingleObjectData{.netTypeID = isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].netTypeID : typeList->typeIndexDataClient[std::type_index(typeid(T*))].netTypeID, .p = std::static_pointer_cast(sharedPtr)}).second) - throw std::runtime_error("[NetObjManager::make_obj] ID Collision"); - return NetObjPtr(this, newID, sharedPtr); + return emplace_shared_ptr(NetObjID::random_gen(), std::make_shared(items...)); } template NetObjPtr obj_from_ptr(T* p) { - std::shared_ptr sharedPtr(p); - NetObjID newID = NetObjID::random_gen(); - if(!objectData.emplace(newID, SingleObjectData{.netTypeID = isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].netTypeID : typeList->typeIndexDataClient[std::type_index(typeid(T*))].netTypeID, .p = std::static_pointer_cast(sharedPtr)}).second) - throw std::runtime_error("[NetObjManager::obj_from_ptr] ID Collision"); - return NetObjPtr(this, newID, sharedPtr); + return emplace_shared_ptr(NetObjID::random_gen(), std::shared_ptr(p)); } template NetObjPtr get_obj_from_id(NetObjID id) { auto it = objectData.find(id); @@ -72,10 +55,16 @@ namespace NetworkingObjects { return NetObjPtr(this, id, std::static_pointer_cast(it->second.p)); } private: - template NetObjPtr make_obj_with_id(NetObjID id) { - auto sharedPtr = std::static_pointer_cast(isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].allocatorFunc() : typeList->typeIndexDataClient[std::type_index(typeid(T*))].allocatorFunc()); - if(!objectData.emplace(id, SingleObjectData{.netTypeID = isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].netTypeID : typeList->typeIndexDataClient[std::type_index(typeid(T*))].netTypeID, .p = std::static_pointer_cast(sharedPtr)}).second) - throw std::runtime_error("[NetObjManager::make_obj] ID Collision"); + template NetObjPtr make_obj_with_id_might_already_exist(NetObjID id) { + auto alreadyExistsIt = objectData.find(id); + if(alreadyExistsIt != objectData.end()) + return NetObjPtr(this, id, std::static_pointer_cast(alreadyExistsIt->second.p)); + return emplace_shared_ptr(id, std::static_pointer_cast(typeList->get_type_index_data(isServer).allocatorFunc())); + } + + template NetObjPtr emplace_shared_ptr(NetObjID id, const std::shared_ptr& sharedPtr) { + if(!objectData.emplace(id, SingleObjectData{.netTypeID = typeList->get_type_index_data(isServer).netTypeID, .p = std::static_pointer_cast(sharedPtr)}).second) + throw std::runtime_error("[NetObjManager::emplace_shared_ptr] ID Collision"); return NetObjPtr(this, id, sharedPtr); } @@ -85,6 +74,7 @@ namespace NetworkingObjects { NetTypeIDType netTypeID; std::shared_ptr p; }; + bool isServer; std::shared_ptr client; std::shared_ptr server; diff --git a/include/Helpers/NetworkingObjects/NetObjManagerTypeList.cpp b/include/Helpers/NetworkingObjects/NetObjManagerTypeList.cpp new file mode 100644 index 0000000..1506c5c --- /dev/null +++ b/include/Helpers/NetworkingObjects/NetObjManagerTypeList.cpp @@ -0,0 +1,7 @@ +#include "NetObjManagerTypeList.hpp" + +namespace NetworkingObjects { + const NetObjManagerTypeList::NetTypeIDFunctions& NetObjManagerTypeList::get_net_type_data(bool isServer, NetTypeIDType id) { + return isServer ? netTypeIDFunctionsServer[id] : netTypeIDFunctionsClient[id]; + } +} diff --git a/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp b/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp index 1de7213..9f47fe4 100644 --- a/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp +++ b/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp @@ -18,64 +18,71 @@ class NetObjManagerTypeList { std::function&, cereal::PortableBinaryInputArchive&, const std::shared_ptr&)> readUpdateFuncServer; }; template void register_class(const ServerClientClassFunctions& funcs) { - netTypeIDData[nextNetTypeID] = NetTypeIDContainer { - .readConstructorFuncClient = [f = funcs.readConstructorFuncClient](const NetObjPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { + typeIndexFunctionsClient[std::type_index(typeid(ClientT*))] = TypeIndexFunctions { + .readConstructorFunc = [f = funcs.readConstructorFuncClient](const NetObjPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { if(f) f(obj.cast(), message, c); }, - .readUpdateFuncClient = [f = funcs.readUpdateFuncClient](const NetObjPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { - if(f) - f(obj.cast(), message, c); - }, - .readConstructorFuncServer = [f = funcs.readConstructorFuncServer](const NetObjPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { - if(f) - f(obj.cast(), message, c); - }, - .readUpdateFuncServer = [f = funcs.readUpdateFuncServer](const NetObjPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { - if(f) - f(obj.cast(), message, c); - }, - }; - typeIndexDataClient[std::type_index(typeid(ClientT*))] = TypeIndexContainer{ - .netTypeID = nextNetTypeID, .writeConstructorFunc = [f = funcs.writeConstructorFuncClient](const NetObjPtr& obj, cereal::PortableBinaryOutputArchive& message) { f(obj.cast(), message); }, - .allocatorFunc = []() { return std::make_shared(); } + .allocatorFunc = []() { return std::make_shared(); }, + .netTypeID = netTypeID }; - typeIndexDataServer[std::type_index(typeid(ServerT*))] = TypeIndexContainer{ - .netTypeID = nextNetTypeID, + + typeIndexFunctionsServer[std::type_index(typeid(ServerT*))] = TypeIndexFunctions { + .readConstructorFunc = [f = funcs.readConstructorFuncServer](const NetObjPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { + if(f) + f(obj.cast(), message, c); + }, .writeConstructorFunc = [f = funcs.writeConstructorFuncServer](const NetObjPtr& obj, cereal::PortableBinaryOutputArchive& message) { f(obj.cast(), message); }, - .allocatorFunc = []() { return std::make_shared(); } + .allocatorFunc = []() { return std::make_shared(); }, + .netTypeID = netTypeID }; - nextNetTypeID++; - } - private: - friend class NetObjManager; - template friend class NetObjPtr; - struct TypeIndexContainer { - NetTypeIDType netTypeID; + netTypeIDFunctionsClient[netTypeID] = NetTypeIDFunctions { + .readUpdateFunc = [f = funcs.readUpdateFuncClient](const NetObjPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { + if(f) + f(obj.cast(), message, c); + } + }; + + netTypeIDFunctionsServer[netTypeID] = NetTypeIDFunctions { + .readUpdateFunc = [f = funcs.readUpdateFuncServer](const NetObjPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { + if(f) + f(obj.cast(), message, c); + } + }; + + netTypeID++; + } + + struct TypeIndexFunctions { + std::function&, cereal::PortableBinaryInputArchive&, const std::shared_ptr&)> readConstructorFunc; std::function&, cereal::PortableBinaryOutputArchive&)> writeConstructorFunc; std::function()> allocatorFunc; + NetTypeIDType netTypeID; }; + struct NetTypeIDFunctions { + std::function&, cereal::PortableBinaryInputArchive&, const std::shared_ptr&)> readUpdateFunc; + }; + + template const TypeIndexFunctions& get_type_index_data(bool isServer) { + return isServer ? typeIndexFunctionsServer[std::type_index(typeid(T*))] : typeIndexFunctionsClient[std::type_index(typeid(T*))]; + } + + const NetTypeIDFunctions& get_net_type_data(bool isServer, NetTypeIDType id); + private: // std::type_index in this case is the typeinfo for the POINTER of the type in question. This ensures that we get a mapping for the exact type that was registered, not for a child of the type in case of inheritance - std::unordered_map typeIndexDataClient; - std::unordered_map typeIndexDataServer; + std::unordered_map typeIndexFunctionsClient; + std::unordered_map typeIndexFunctionsServer; + std::unordered_map netTypeIDFunctionsClient; + std::unordered_map netTypeIDFunctionsServer; - struct NetTypeIDContainer { - std::function&, cereal::PortableBinaryInputArchive&, const std::shared_ptr&)> readConstructorFuncClient; - std::function&, cereal::PortableBinaryInputArchive&, const std::shared_ptr&)> readUpdateFuncClient; - - std::function&, cereal::PortableBinaryInputArchive&, const std::shared_ptr&)> readConstructorFuncServer; - std::function&, cereal::PortableBinaryInputArchive&, const std::shared_ptr&)> readUpdateFuncServer; - }; - std::unordered_map netTypeIDData; - - NetTypeIDType nextNetTypeID = 0; + NetTypeIDType netTypeID = 0; }; } diff --git a/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp b/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp index 8d7cbf6..28c3300 100644 --- a/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp +++ b/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp @@ -48,11 +48,11 @@ namespace NetworkingObjects { // Don't use this function unless you're sure that class T isn't a base class template NetObjPtr emplace_back_direct(const NetObjPtr>& l, Args&&... items) { NetObjPtr newObj = l.get_obj_man()->template make_obj_direct(items...); - l->push_back(l, newObj); + l->insert(l, l->size(), newObj); return newObj; } static NetObjPtr push_back_and_send_create(const NetObjPtr>& l, const NetObjPtr& newObj) { - l->push_back(l, newObj); + l->insert(l, l->size(), newObj); return newObj; } static NetObjPtr insert_and_send_create(const NetObjPtr>& l, uint32_t posToInsertAt, const NetObjPtr& newObj) { @@ -63,16 +63,14 @@ namespace NetworkingObjects { l->erase_by_index(l, indexToErase); } static void erase(const NetObjPtr>& l, const NetObjOrderedListObjectInfoPtr& p) { - l->erase_by_ptr(l, p); + l->erase_by_index(l, p->pos); } virtual uint32_t size() const = 0; virtual const std::vector>& get_data() const = 0; virtual ~NetObjOrderedList() {} protected: - virtual void push_back(const NetObjPtr>& l, const NetObjPtr& newObj) = 0; virtual void insert(const NetObjPtr>& l, uint32_t posToInsertAt, const NetObjPtr& newObj) = 0; virtual void erase_by_index(const NetObjPtr>& l, uint32_t indexToErase) = 0; - virtual void erase_by_ptr(const NetObjPtr>& l, const NetObjOrderedListObjectInfoPtr& p) = 0; virtual void write_constructor(const NetObjPtr>& l, cereal::PortableBinaryOutputArchive& a) = 0; virtual void read_update(const NetObjPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) = 0; virtual void read_constructor(const NetObjPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) = 0; @@ -99,37 +97,41 @@ namespace NetworkingObjects { return data; } protected: - virtual void push_back(const NetObjPtr>& l, const NetObjPtr& newObj) override { - auto& newObjInfoPtr = data.emplace_back(std::make_shared>(newObj, static_cast(data.size()), false)); - l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { - a(ObjPtrOrderedListCommand_StoC::INSERT_SINGLE, newObjInfoPtr->pos); - newObjInfoPtr->obj.write_create_message(a); - }); - } virtual void insert(const NetObjPtr>& l, uint32_t posToInsertAt, const NetObjPtr& newObj) override { - posToInsertAt = std::min(posToInsertAt, data.size()); - auto& newObjInfoPtr = *data.insert(data.begin() + posToInsertAt, std::make_shared>(newObj, posToInsertAt, false)); - set_positions_for_object_info_vector(data, posToInsertAt); - l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { - a(ObjPtrOrderedListCommand_StoC::INSERT_SINGLE, newObjInfoPtr->pos); + uint32_t actualPosToInsertAt = posToInsertAt = std::min(posToInsertAt, data.size()); + + // Find duplicate. If found, erase the duplicate before inserting + auto itObjAlreadyInData = idToDataMap.find(newObj.get_net_id()); + if(itObjAlreadyInData != idToDataMap.end()) { + uint32_t posToErase = itObjAlreadyInData->second->pos; + data.erase(data.begin() + posToErase); + idToDataMap.erase(itObjAlreadyInData); + if(posToErase < actualPosToInsertAt) // Shift insert position if an element was erased before the insert position + actualPosToInsertAt--; + } + + // Insert the new object + auto newObjInfoPtr = std::make_shared>(newObj, actualPosToInsertAt, false); + data.insert(data.begin() + actualPosToInsertAt, newObjInfoPtr); + idToDataMap.emplace(newObjInfoPtr->obj.get_net_id(), newObjInfoPtr); + l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr, posToInsertAt](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { + a(ObjPtrOrderedListCommand_StoC::INSERT_SINGLE, posToInsertAt); // Dont send actual pos to insert at, since client will check for duplicates on its own and shift the index if necessary newObjInfoPtr->obj.write_create_message(a); }); + set_positions_for_object_info_vector(data, actualPosToInsertAt); } virtual void erase_by_index(const NetObjPtr>& l, uint32_t indexToErase) override { + if(data.empty()) + return; + indexToErase = std::min(indexToErase, data.size() - 1); l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [indexToErase](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { a(ObjPtrOrderedListCommand_StoC::ERASE_SINGLE, indexToErase); }); + NetObjID idToErase = data[indexToErase]->obj.get_net_id(); data.erase(data.begin() + indexToErase); + idToDataMap.erase(idToErase); set_positions_for_object_info_vector(data, indexToErase); } - virtual void erase_by_ptr(const NetObjPtr>& l, const NetObjOrderedListObjectInfoPtr& p) override { - l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [indexToErase = p->pos](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { - a(ObjPtrOrderedListCommand_StoC::ERASE_SINGLE, indexToErase); - }); - uint32_t iToErase = p->pos; // We only have a reference to the pointer, so erasing it might destroy the pointer object. Save the index before erasing - data.erase(data.begin() + iToErase); - set_positions_for_object_info_vector(data, iToErase); - } virtual void write_constructor(const NetObjPtr>& l, cereal::PortableBinaryOutputArchive& a) override { a(static_cast(data.size())); for(size_t i = 0; i < data.size(); i++) @@ -156,18 +158,15 @@ namespace NetworkingObjects { case ObjPtrOrderedListCommand_CtoS::ERASE_SINGLE: NetObjID idToErase; a(idToErase); - std::erase_if(data, [&](NetObjOrderedListObjectInfoPtr& o) { - if(o->obj.get_net_id() == idToErase) { - erase_by_ptr(l, o); - return true; - } - return false; - }); + auto it = idToDataMap.find(idToErase); + if(it != idToDataMap.end()) + erase_by_index(l, it->second->pos); break; } } private: std::vector> data; + std::unordered_map> idToDataMap; }; template class NetObjOrderedListClient : public NetObjOrderedList { @@ -180,35 +179,40 @@ namespace NetworkingObjects { return clientData; } protected: - virtual void push_back(const NetObjPtr>& l, const NetObjPtr& newObj) override { - auto& newObjInfoPtr = clientData.emplace_back(std::make_shared>(newObj, static_cast(clientData.size()), true)); - l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { - a(ObjPtrOrderedListCommand_CtoS::INSERT_SINGLE, newObjInfoPtr->pos); - newObjInfoPtr->obj.write_create_message(a); - }); - } virtual void insert(const NetObjPtr>& l, uint32_t posToInsertAt, const NetObjPtr& newObj) { - posToInsertAt = std::min(posToInsertAt, clientData.size()); - auto& newObjInfoPtr = *clientData.insert(clientData.begin() + posToInsertAt, std::make_shared>(newObj, posToInsertAt, true)); - l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { - a(ObjPtrOrderedListCommand_CtoS::INSERT_SINGLE, newObjInfoPtr->pos); + uint32_t actualPosToInsertAt = posToInsertAt = std::min(posToInsertAt, clientData.size()); + + // Find duplicate. If found, erase the duplicate before inserting + auto itObjAlreadyInData = clientIdToDataMap.find(newObj.get_net_id()); + if(itObjAlreadyInData != clientIdToDataMap.end()) { + uint32_t posToErase = itObjAlreadyInData->second->pos; + clientData.erase(clientData.begin() + posToErase); + clientIdToDataMap.erase(itObjAlreadyInData); + if(posToErase < actualPosToInsertAt) // Shift insert position if an element was erased before the insert position + actualPosToInsertAt--; + } + + // Insert the new object + auto newObjInfoPtr = std::make_shared>(newObj, actualPosToInsertAt, false); + clientData.insert(clientData.begin() + actualPosToInsertAt, newObjInfoPtr); + clientIdToDataMap.emplace(newObjInfoPtr->obj.get_net_id(), newObjInfoPtr); + l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr, posToInsertAt](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { + a(ObjPtrOrderedListCommand_CtoS::INSERT_SINGLE, posToInsertAt); // Don't put actual pos to insert at, since the server will check for duplicates and might shift the index on its own newObjInfoPtr->obj.write_create_message(a); }); + set_positions_for_object_info_vector(clientData, actualPosToInsertAt); } virtual void erase_by_index(const NetObjPtr>& l, uint32_t indexToErase) override { - indexToErase = std::min(indexToErase, clientData.size()); - auto& newObjInfoPtr = clientData[indexToErase]; - l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { - a(ObjPtrOrderedListCommand_CtoS::ERASE_SINGLE, newObjInfoPtr->obj.get_net_id()); - }); - clientData.erase(clientData.begin() + indexToErase); - set_positions_for_object_info_vector(clientData, indexToErase); - } - virtual void erase_by_ptr(const NetObjPtr>& l, const NetObjOrderedListObjectInfoPtr& p) override { + if(clientData.empty()) + return; + indexToErase = std::min(indexToErase, clientData.size() - 1); + auto& p = clientData[indexToErase]; l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&p](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { a(ObjPtrOrderedListCommand_CtoS::ERASE_SINGLE, p->obj.get_net_id()); }); + p->syncIgnore = true; uint32_t iToErase = p->pos; + clientIdToDataMap.erase(p->obj.get_net_id()); clientData.erase(clientData.begin() + iToErase); set_positions_for_object_info_vector(clientData, iToErase); } @@ -230,21 +234,64 @@ namespace NetworkingObjects { ObjPtrOrderedListCommand_StoC c; a(c); switch(c) { - case ObjPtrOrderedListCommand_StoC::INSERT_SINGLE: + case ObjPtrOrderedListCommand_StoC::INSERT_SINGLE: { uint32_t newPos; a(newPos); - serverData.insert(serverData.begin() + newPos, std::make_shared>(l.get_obj_man()->template read_create_message(a, nullptr), newPos, false)); + auto toInsert = std::make_shared>(l.get_obj_man()->template read_create_message(a, nullptr), newPos, false); + auto it = serverIdToDataMap.find(toInsert->obj.get_net_id()); + if(it != serverIdToDataMap.end()) { + uint32_t posToErase = it->second->pos; + serverData.erase(serverData.begin() + posToErase); + serverIdToDataMap.erase(it); + if(posToErase < newPos) + newPos--; // Shift if there was a duplicate before this + } + serverIdToDataMap.emplace(toInsert->obj.get_net_id(), toInsert); + serverData.insert(serverData.begin() + newPos, toInsert); break; - case ObjPtrOrderedListCommand_StoC::ERASE_SINGLE: + } + case ObjPtrOrderedListCommand_StoC::ERASE_SINGLE: { uint32_t posToErase; a(posToErase); + NetObjID objID = serverData[posToErase]->obj.get_net_id(); serverData.erase(serverData.begin() + posToErase); + serverIdToDataMap.erase(objID); break; + } } + sync(); } private: + + void sync() { + std::vector>> ignoredToReinsert; + for(uint32_t i = 0; i < static_cast(clientData.size()); i++) + if(clientData[i]->syncIgnore) + ignoredToReinsert.emplace_back(std::pair>{i, clientData[i]}); + clientData = serverData; + clientIdToDataMap = serverIdToDataMap; + std::erase_if(clientData, [&](const auto& obj) { + if(obj->syncIgnore) { + clientIdToDataMap.erase(obj->obj.get_net_id()); + return true; + } + return false; + }); + for(auto& p : ignoredToReinsert) { + if(p.first > clientData.size()) + clientData.emplace_back(p.second); + else + clientData.insert(clientData.begin() + p.first, p.second); + clientIdToDataMap.emplace(p.second->obj.get_net_id(), p.second); + } + set_positions_for_object_info_vector(clientData); + } + std::vector> serverData; + std::unordered_map> serverIdToDataMap; + std::vector> clientData; + std::unordered_map> clientIdToDataMap; }; template void register_ordered_list_class(NetObjManagerTypeList& t) { diff --git a/include/Helpers/NetworkingObjects/NetObjPtr.impl.hpp b/include/Helpers/NetworkingObjects/NetObjPtr.impl.hpp index a0946b0..2d38b3f 100644 --- a/include/Helpers/NetworkingObjects/NetObjPtr.impl.hpp +++ b/include/Helpers/NetworkingObjects/NetObjPtr.impl.hpp @@ -20,7 +20,7 @@ namespace NetworkingObjects { {} template NetObjPtr& NetObjPtr::operator=(const NetObjPtr& other) { if(this != &other) { - if(sharedPtr && sharedPtr.unique()) + if(sharedPtr && sharedPtr.use_count() == 2) objMan->objectData.erase(id); objMan = other.objMan; id = other.id; @@ -29,7 +29,7 @@ namespace NetworkingObjects { return *this; } template NetObjPtr::~NetObjPtr() { - if(sharedPtr && sharedPtr.unique()) + if(sharedPtr && sharedPtr.use_count() == 2) objMan->objectData.erase(id); } @@ -90,9 +90,8 @@ namespace NetworkingObjects { } template void NetObjPtr::write_create_message(cereal::PortableBinaryOutputArchive& a) const { - auto& typeIndexData = objMan->isServer ? objMan->typeList->typeIndexDataServer[std::type_index(typeid(T*))] : objMan->typeList->typeIndexDataClient[std::type_index(typeid(T*))]; - a(id, typeIndexData.netTypeID); - typeIndexData.writeConstructorFunc(this->cast(), a); + a(id); + objMan->typeList->get_type_index_data(objMan->isServer).writeConstructorFunc(this->cast(), a); } template NetObjPtr::NetObjPtr(NetObjManager* initObjMan, NetObjID initID, const std::shared_ptr& initPtr): diff --git a/src/ConnectionManager.cpp b/src/ConnectionManager.cpp index 185ce00..5054390 100644 --- a/src/ConnectionManager.cpp +++ b/src/ConnectionManager.cpp @@ -2,6 +2,7 @@ #include "Helpers/Networking/NetLibrary.hpp" #include "Server/CommandList.hpp" #include +#include "World.hpp" void ConnectionManager::init_local_p2p(World& initWorld, const std::string& serverLocalID) { localServer = std::make_unique(initWorld, serverLocalID); @@ -9,10 +10,11 @@ void ConnectionManager::init_local_p2p(World& initWorld, const std::string& serv lastKeepAliveTime = std::chrono::steady_clock::now(); } -void ConnectionManager::connect_p2p(const std::string& serverFullID) { +void ConnectionManager::connect_p2p(World& initWorld, const std::string& serverFullID) { client = std::make_shared(serverFullID); lastKeepAliveTime = std::chrono::steady_clock::now(); NetLibrary::register_client(client); + initWorld.netObjMan.set_client(client, SERVER_UPDATE_NETWORK_OBJECT); } void ConnectionManager::update() { diff --git a/src/ConnectionManager.hpp b/src/ConnectionManager.hpp index 2f385e4..bdb2d59 100644 --- a/src/ConnectionManager.hpp +++ b/src/ConnectionManager.hpp @@ -5,7 +5,7 @@ class ConnectionManager { public: void init_local_p2p(World& initWorld, const std::string& serverLocalID); - void connect_p2p(const std::string& serverFullID); + void connect_p2p(World& initWorld, const std::string& serverFullID); void update(); void client_add_recv_callback(uint32_t commandID, const NetClientRecvCallback &callback); template void client_send_items_to_server(const std::string& channel, Args&&... items) { diff --git a/src/Server/CommandList.hpp b/src/Server/CommandList.hpp index c015fc2..2092e42 100644 --- a/src/Server/CommandList.hpp +++ b/src/Server/CommandList.hpp @@ -6,6 +6,7 @@ enum ServerCommands : MessageCommandType { SERVER_FRAGMENT = 0, SERVER_KEEP_ALIVE, SERVER_INITIAL_DATA, + SERVER_UPDATE_NETWORK_OBJECT, SERVER_MOVE_CAMERA, SERVER_MOVE_SCREEN_MOUSE, SERVER_PLACE_SINGLE_COMPONENT, @@ -29,6 +30,7 @@ enum ClientCommands : MessageCommandType { CLIENT_FRAGMENT = 0, CLIENT_KEEP_ALIVE, CLIENT_INITIAL_DATA, + CLIENT_UPDATE_NETWORK_OBJECT, CLIENT_USER_CONNECT, CLIENT_USER_DISCONNECT, CLIENT_PLACE_SINGLE_COMPONENT, diff --git a/src/Server/MainServer.cpp b/src/Server/MainServer.cpp index b5bffc9..87ae2d0 100644 --- a/src/Server/MainServer.cpp +++ b/src/Server/MainServer.cpp @@ -40,6 +40,8 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID): netServer = std::make_shared(serverLocalID); NetLibrary::register_server(netServer); + + world.netObjMan.set_server(netServer, CLIENT_UPDATE_NETWORK_OBJECT); netServer->add_recv_callback(SERVER_INITIAL_DATA, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { bool isDirectConnect; @@ -83,6 +85,9 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID): } clients.emplace(newClient.serverID, newClient); }); + netServer->add_recv_callback(SERVER_UPDATE_NETWORK_OBJECT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { + world.netObjMan.read_update_message(message, client); + }); netServer->add_recv_callback(SERVER_MOVE_CAMERA, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { auto& c = clients[client->customID]; message(c.camCoords, c.windowSize); diff --git a/src/World.cpp b/src/World.cpp index cbb542c..e2641b2 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -27,12 +27,12 @@ #endif World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): + netObjMan(initMain.netObjectTypeList, worldInfo.conType != CONNECTIONTYPE_CLIENT), main(initMain), rMan(*this), drawProg(*this), bMan(*this), - gridMan(*this), - netObjMan(initMain.netObjectTypeList, worldInfo.conType != CONNECTIONTYPE_CLIENT) + gridMan(*this) { set_canvas_background_color(main.defaultCanvasBackgroundColor); displayName = main.displayName; @@ -49,7 +49,7 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): switch(conType) { case CONNECTIONTYPE_CLIENT: { clientStillConnecting = true; - con.connect_p2p(netSource); + con.connect_p2p(*this, netSource); set_name(""); break; } @@ -75,10 +75,13 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): if(netObjMan.is_server()) { stringListTest = netObjMan.make_obj>(); stringListTest->emplace_back_direct(stringListTest, "Hello"); - stringListTest->emplace_back_direct(stringListTest, "World"); - stringListTest->emplace_back_direct(stringListTest, "Test"); - for(const NetObjOrderedListObjectInfoPtr& o : stringListTest->get_data()) - std::cout << "At position: " << o->get_pos() << " we have string: \"" << *o->get_obj() << "\" with object id " << o->get_obj().get_net_id().to_string() << std::endl; + stringListTest = netObjMan.make_obj>(); + stringListTest->emplace_back_direct(stringListTest, "World!"); + + //stringListTest->emplace_back_direct(stringListTest, "World"); + //stringListTest->emplace_back_direct(stringListTest, "Test"); + //for(const NetObjOrderedListObjectInfoPtr& o : stringListTest->get_data()) + // std::cout << "At position: " << o->get_pos() << " we have string: \"" << *o->get_obj() << "\" with object id " << o->get_obj().get_net_id().to_string() << std::endl; } } @@ -116,6 +119,14 @@ void World::init_client_callbacks() { clients[id].cursorColor = cursorColor; add_chat_message(clients[id].displayName, "joined", Toolbar::ChatMessage::Type::JOIN); }); + con.client_add_recv_callback(CLIENT_UPDATE_NETWORK_OBJECT, [&](cereal::PortableBinaryInputArchive& message) { + if(!con.host_exists()) { // Dont update a direct connected client (which is just the server) + netObjMan.read_update_message(message, nullptr); + std::cout << "Client print update: " << std::endl; + for(const NetworkingObjects::NetObjOrderedListObjectInfoPtr& o : stringListTest->get_data()) + std::cout << "At position: " << o->get_pos() << " we have string: \"" << *o->get_obj() << "\" with object id " << o->get_obj().get_net_id().to_string() << std::endl; + } + }); con.client_add_recv_callback(CLIENT_USER_DISCONNECT, [&](cereal::PortableBinaryInputArchive& message) { ServerPortionID id; message(id); @@ -163,9 +174,19 @@ void World::init_client_callbacks() { }); } +size_t testTimer = 0; +size_t strToAddNum = 0; + void World::focus_update() { con.update(); + //testTimer++; + //if(testTimer == 1000) { + // std::cout << "added: " << strToAddNum << std::endl; + // stringListTest->emplace_back_direct(stringListTest, std::to_string(strToAddNum++)); + // testTimer = 0; + //} + if(con.is_host_disconnected()) { Logger::get().log("USERINFO", "Host connection failed"); clientStillConnecting = false; diff --git a/src/World.hpp b/src/World.hpp index 41dcee2..0da4330 100644 --- a/src/World.hpp +++ b/src/World.hpp @@ -36,6 +36,9 @@ class World { World(MainProgram& initMain, OpenWorldInfo& worldInfo); + // NOTE: Keep at the very beginning so that it's destroyed last + NetworkingObjects::NetObjManager netObjMan; + void save_file(cereal::PortableBinaryOutputArchive& a) const; void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version); @@ -47,7 +50,6 @@ class World { BookmarkManager bMan; ConnectionManager con; GridManager gridMan; - NetworkingObjects::NetObjManager netObjMan; std::deque chatMessages;