From 6e13c5742e2d87d6f89532f44fd26d11942452b1 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Mon, 5 Jan 2026 12:43:40 +0300 Subject: [PATCH] Added net ordered list test, and fixed ordering issues with list --- include/Helpers/Networking/NetClient.cpp | 4 + include/Helpers/Networking/NetServer.cpp | 4 + .../NetworkingObjects/NetObjOrderedList.hpp | 80 +++++++++++++++---- src/World.cpp | 58 ++++++++++++++ src/World.hpp | 8 ++ 5 files changed, 137 insertions(+), 17 deletions(-) diff --git a/include/Helpers/Networking/NetClient.cpp b/include/Helpers/Networking/NetClient.cpp index 9da7d61..6ed5903 100644 --- a/include/Helpers/Networking/NetClient.cpp +++ b/include/Helpers/Networking/NetClient.cpp @@ -49,9 +49,12 @@ void NetClient::update() { isDisconnected = true; return; } +#ifdef NDEBUG try { +#endif parse_received_messages(); send_queued_messages(); +#ifdef NDEBUG } catch(const std::exception& e) { Logger::get().log("INFO", "[NetClient::update] Exception thrown while parsing and sending messages: " + std::string(e.what())); @@ -61,6 +64,7 @@ void NetClient::update() { Logger::get().log("INFO", "[NetClient::update] Unknown exception thrown while parsing and sending messages."); isDisconnected = true; } +#endif } void NetClient::parse_received_messages() { diff --git a/include/Helpers/Networking/NetServer.cpp b/include/Helpers/Networking/NetServer.cpp index 0f8a2df..f9e910e 100644 --- a/include/Helpers/Networking/NetServer.cpp +++ b/include/Helpers/Networking/NetServer.cpp @@ -26,9 +26,12 @@ void NetServer::update() { if(client->setToDisconnect) disconnectCallback(client); else { + #ifdef NDEBUG try { + #endif client->parse_received_messages(*this); client->send_queued_messages(*this); + #ifdef NDEBUG } catch(const std::exception& e) { Logger::get().log("INFO", "[NetServer::update] Exception thrown while parsing and sending messages for a client: " + std::string(e.what())); @@ -40,6 +43,7 @@ void NetServer::update() { client->setToDisconnect = true; disconnectCallback(client); } + #endif } } } diff --git a/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp b/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp index 56be6f0..5fb4b19 100644 --- a/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp +++ b/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp @@ -11,6 +11,14 @@ #include #include +//#define ENABLE_NETOBJ_ORDERED_LIST_VERBOSE_DEBUG + +#ifdef ENABLE_NETOBJ_ORDERED_LIST_VERBOSE_DEBUG + #define NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(data, idMap) netobj_ordered_list_debug_check(data, idMap) +#else + #define NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(data, idMap) +#endif + namespace NetworkingObjects { enum class ObjPtrOrderedListCommand_StoC : uint8_t { INSERT_LIST_CONSTRUCT, @@ -49,6 +57,37 @@ namespace NetworkingObjects { } } +#ifdef ENABLE_NETOBJ_ORDERED_LIST_VERBOSE_DEBUG + template void netobj_ordered_list_debug_check(const std::list>& l, const std::unordered_map>& idMap) { + uint32_t i = 0; + std::cout << "[netobj_ordered_list_debug_check] Debug print start\n"; + for(auto& o : l) { + std::cout << "Pos " << o.pos << " with id " << o.obj.get_net_id().to_string() << '\n'; + if(o.pos != i) + throw std::runtime_error("[netobj_ordered_list_debug_check] Position values are incorrect!"); + ++i; + auto it = idMap.find(o.obj.get_net_id()); + if(it == idMap.end()) + throw std::runtime_error("[netobj_ordered_list_debug_check] Could not find element in ID Map!"); + if(&(*it->second) != &o) + throw std::runtime_error("[netobj_ordered_list_debug_check] ID Map and list point to different objects!"); + } + if(l.size() != idMap.size()) { + std::cout << "ID map size: " << idMap.size() << " list size: " << l.size() << '\n'; + throw std::runtime_error("[netobj_ordered_list_debug_check] ID map and list have different sizes!"); + } + std::cout << "[netobj_ordered_list_debug_check] Debug print end" << std::endl; + } +#endif + + template void refresh_positions_complete(std::list>& l) { + uint32_t i = 0; + for(auto& c : l) { + c.pos = i; + ++i; + } + } + template class NetObjOrderedList { public: // Don't use this function unless you're sure that class T isn't a base class @@ -301,6 +340,7 @@ namespace NetworkingObjects { a(idPosition, newObjIt->obj.get_net_id()); }); this->call_insert_callback_list(toRet); + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(data, idToDataMap); return toRet; } virtual void erase_it_list(const NetObjTemporaryPtr>& l, const std::vector>& itList, std::vector>* erasedObjects) { @@ -338,11 +378,13 @@ namespace NetworkingObjects { }); set_position_obj_info_list(smallestPosIt, data.end()); + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(data, idToDataMap); } virtual void write_constructor(const NetObjTemporaryPtr>& l, cereal::PortableBinaryOutputArchive& a) override { a(static_cast(data.size())); for(auto& c : data) c.obj.write_create_message(a); + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(data, idToDataMap); } virtual void read_constructor(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) override { uint32_t constructedSize; @@ -353,6 +395,7 @@ namespace NetworkingObjects { auto it = data.emplace(data.end(), l.get_obj_man()->template read_create_message(a, c), i); idToDataMap.emplace(data.back().obj.get_net_id(), it); } + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(data, idToDataMap); // Dont send the object data to all clients in the constructor, as the function that called read_create_message is also the one that will decide where to send the data of the constructed object } virtual void read_update(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) override { @@ -373,17 +416,9 @@ namespace NetworkingObjects { newObjs.emplace_back(NetObjOrderedListIterator(), l.get_obj_man()->template read_create_message(a, c)); } - uint32_t currentIndex = data.size(); - NetObjOrderedListIterator currentIt = data.end(); - for(std::pair, NetObjOwnerPtr>&, uint32_t&> elem : (std::views::zip(newObjs, insertIndices) | std::views::reverse)) { - uint32_t index = elem.second; - auto& it = elem.first.first; - while(currentIndex != index) { - --currentIndex; - --currentIt; - } - it = currentIt; - } + std::vector> itList = this->at_ordered_indices(insertIndices); + for(size_t i = 0; i < itList.size(); i++) + newObjs[i].first = itList[i]; insert_ordered_list(l, c, newObjs); break; @@ -445,6 +480,7 @@ namespace NetworkingObjects { } }); this->call_insert_callback_list(toRet); + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(clientData, clientIdToDataMap); return toRet; } virtual void erase_it_list(const NetObjTemporaryPtr>& l, const std::vector>& itList, std::vector>* erasedObjects) { @@ -481,6 +517,7 @@ namespace NetworkingObjects { }); set_position_obj_info_list(smallestPosIt, clientData.end()); + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(clientData, clientIdToDataMap); } virtual void write_constructor(const NetObjTemporaryPtr>& l, cereal::PortableBinaryOutputArchive& a) override { a(static_cast(clientData.size())); @@ -491,6 +528,7 @@ namespace NetworkingObjects { auto serverIt = serverData.emplace(serverData.end(), c.obj.get_net_id()); // We are the ones who made this list, so fill up serverIdToDataMap.emplace(c.obj.get_net_id(), serverIt); } + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(clientData, clientIdToDataMap); } virtual void read_constructor(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) override { uint32_t constructedSize; @@ -505,6 +543,7 @@ namespace NetworkingObjects { auto serverIt = serverData.emplace(serverData.end(), clientIt->obj.get_net_id()); serverIdToDataMap.emplace(clientIt->obj.get_net_id(), serverIt); } + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(clientData, clientIdToDataMap); } virtual void read_update(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) override { ObjPtrOrderedListCommand_StoC c; @@ -515,17 +554,18 @@ namespace NetworkingObjects { a(newObjsSize); std::vector idsBeforeElements; + std::vector::iterator> serverInsertIts; std::vector, NetObjOwnerPtr>> newObjs; for(uint32_t i = 0; i < newObjsSize; i++) { auto& idBeforeElement = idsBeforeElements.emplace_back(); a(idBeforeElement); newObjs.emplace_back(get_client_iterator_from_server_previous_net_obj_id(idBeforeElement), l.get_obj_man()->template read_create_message(a, nullptr)); + serverInsertIts.emplace_back(get_server_iterator_from_server_previous_net_obj_id(idBeforeElement)); } for(uint32_t i = 0; i < newObjsSize; i++) { - auto serverInsertAtIt = get_server_iterator_from_server_previous_net_obj_id(idsBeforeElements[i]); - auto serverIt = serverData.emplace(serverInsertAtIt, newObjs[i].second.get_net_id()); + auto serverIt = serverData.emplace(serverInsertIts[i], newObjs[i].second.get_net_id()); serverIdToDataMap.emplace(newObjs[i].second.get_net_id(), serverIt); } @@ -539,9 +579,11 @@ namespace NetworkingObjects { if(i == 0) firstInsertedIt = newObjIt; } + set_position_obj_info_list(firstInsertedIt, clientData.end()); this->call_insert_callback_list(insertedIterators); + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(clientData, clientIdToDataMap); break; } case ObjPtrOrderedListCommand_StoC::INSERT_LIST_REFERENCE: { @@ -551,6 +593,7 @@ namespace NetworkingObjects { struct PerElementData { NetworkingObjects::NetObjID idBeforeElement; NetworkingObjects::NetObjID refID; + std::list::iterator serverInsertIt; bool foundInClient; NetObjOrderedListIterator clientObjectIt; NetObjOrderedListIterator clientInsertPosIt; @@ -560,6 +603,7 @@ namespace NetworkingObjects { for(uint32_t i = 0; i < newObjsSize; i++) { auto& elemData = receivedElements.emplace_back(); a(elemData.idBeforeElement, elemData.refID); + elemData.serverInsertIt = get_server_iterator_from_server_previous_net_obj_id(elemData.idBeforeElement); } std::list> referredToObjectData; @@ -577,7 +621,7 @@ namespace NetworkingObjects { if(startRefreshFrom != clientData.end()) startRefreshFrom->pos = elemData.clientObjectIt->pos; } - referredToObjectData.splice(referredToObjectData.end(), clientData, elemData.clientObjectIt, std::next(elemData.clientObjectIt)); + referredToObjectData.splice(referredToObjectData.end(), clientData, elemData.clientObjectIt); clientIdToDataMap.erase(clientIdToDataMapIt); elemData.foundInClient = true; } @@ -586,6 +630,7 @@ namespace NetworkingObjects { } set_position_obj_info_list(startRefreshFrom, clientData.end()); + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(clientData, clientIdToDataMap); for(auto& elemData : receivedElements) { if(elemData.foundInClient) @@ -593,8 +638,7 @@ namespace NetworkingObjects { } for(auto& elemData : receivedElements) { - auto serverInsertAtIt = get_server_iterator_from_server_previous_net_obj_id(elemData.idBeforeElement); - auto serverIt = serverData.emplace(serverInsertAtIt, elemData.refID); + auto serverIt = serverData.emplace(elemData.serverInsertIt, elemData.refID); serverIdToDataMap.emplace(elemData.refID, serverIt); } @@ -607,7 +651,7 @@ namespace NetworkingObjects { firstInsertedActualPos = elemData.clientInsertPosIt->pos; firstInsertedIt = elemData.clientObjectIt; } - clientData.splice(elemData.clientInsertPosIt, referredToObjectData, elemData.clientObjectIt, std::next(elemData.clientObjectIt)); + clientData.splice(elemData.clientInsertPosIt, referredToObjectData, elemData.clientObjectIt); clientIdToDataMap.emplace(elemData.clientObjectIt->obj.get_net_id(), elemData.clientObjectIt); } @@ -626,6 +670,7 @@ namespace NetworkingObjects { actualPos++; } } + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(clientData, clientIdToDataMap); break; } case ObjPtrOrderedListCommand_StoC::ERASE_ID_LIST: { @@ -655,6 +700,7 @@ namespace NetworkingObjects { } set_position_obj_info_list(smallestPosIt, clientData.end()); + NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(clientData, clientIdToDataMap); break; } } diff --git a/src/World.cpp b/src/World.cpp index 43af720..70e792d 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -9,6 +9,7 @@ #include #include #include "DrawingProgram/Layers/DrawingProgramLayerListItem.hpp" +#include "Helpers/NetworkingObjects/NetObjOrderedList.hpp" #include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp" #include "Helpers/NetworkingObjects/NetObjUnorderedSet.hpp" #include "CommandList.hpp" @@ -18,6 +19,7 @@ #include "VersionConstants.hpp" #include "WorldGrid.hpp" #include +#include #include #include "FileHelpers.hpp" #include "FontData.hpp" @@ -67,6 +69,9 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): else load_empty_canvas(); set_name(name); + #ifdef ENABLE_ORDERED_LIST_TEST + listDebugTest = netObjMan.make_obj>(); + #endif } } @@ -108,6 +113,11 @@ void World::init_net_obj_type_list() { canvasTheme.register_class(); ClientData::register_class(*this); NetworkingObjects::register_unordered_set_class(netObjMan); + +#ifdef ENABLE_ORDERED_LIST_TEST + NetworkingObjects::register_generic_serialized_class(netObjMan); + NetworkingObjects::register_ordered_list_class(netObjMan); +#endif } void World::init_client(const std::string& serverFullID) { @@ -135,6 +145,10 @@ void World::init_client(const std::string& serverFullID) { ownClientData = netObjMan.get_obj_temporary_ref_from_id(clientDataObjID); drawData.cam.smooth_move_to(*main.world, ownClientData->get_cam_coords(), ownClientData->get_window_size(), true); + #ifdef ENABLE_ORDERED_LIST_TEST + listDebugTest = netObjMan.read_create_message>(message, nullptr); + #endif + clientStillConnecting = false; }); netClient->add_recv_callback(CLIENT_UPDATE_NETWORK_OBJECT, [&](cereal::PortableBinaryInputArchive& message) { @@ -165,6 +179,9 @@ void World::focus_update() { } ownClientData->set_cursor_pos(ownClientData, main.input.mouse.pos); drawProg.update(); + #ifdef ENABLE_ORDERED_LIST_TEST + list_debug_test_update(); + #endif } drawData.cam.update_main(*this); @@ -326,6 +343,9 @@ void World::start_hosting(const std::string& initNetSource, const std::string& s drawProg.write_components_server(a); canvasTheme.write_create_message(a); clients.write_create_message(a); + #ifdef ENABLE_ORDERED_LIST_TEST + listDebugTest.write_create_message(a); + #endif } netServer->send_string_stream_to_client(client, RELIABLE_COMMAND_CHANNEL, ss); for(auto& r : rMan.resource_list()) { @@ -484,6 +504,44 @@ void World::scale_up(const WorldScalar& scaleUpAmount) { undo.scale_up(scaleUpAmount); } +#ifdef ENABLE_ORDERED_LIST_TEST +void World::list_debug_test_update() { + if(std::chrono::steady_clock::now() < (listDebugTestTimeStart + std::chrono::minutes(1))) { + bool isInsert; + if(listDebugTest->size() > 70) + isInsert = false; + else if(listDebugTest->size() < 20) + isInsert = true; + else + isInsert = Random::get().real_range(0.0f, 1.0f) > 0.5f; + + using namespace NetworkingObjects; + if(isInsert) { + std::vector, NetObjOwnerPtr>> toInsert; + std::vector randomIndices; + uint32_t insertAmount = Random::get().int_range(5, 25); + for(uint32_t i = 0; i < insertAmount; i++) + randomIndices.emplace_back(Random::get().int_range(0, listDebugTest->size() + 10)); + randomIndices[1] = randomIndices[0]; + std::sort(randomIndices.begin(), randomIndices.end()); + for(uint32_t index : randomIndices) + toInsert.emplace_back(listDebugTest->at(index), netObjMan.make_obj_direct(Random::get().int_range(10, 100))); + listDebugTest->insert_ordered_list_and_send_create(listDebugTest, toInsert); + } + else { + std::vector> toErase; + for(uint32_t i = 0; i < listDebugTest->size(); i++) { + if(Random::get().real_range(0.0f, 1.0f) > 0.8f) + toErase.emplace_back(listDebugTest->at(i)); + } + listDebugTest->erase_list(listDebugTest, toErase); + } + } + if(netServer && netServer->get_client_list().empty()) + listDebugTestTimeStart = std::chrono::steady_clock::now(); +} +#endif + void World::draw(SkCanvas* canvas) { drawData.refresh_draw_optimizing_values(); if(drawData.drawGrids) diff --git a/src/World.hpp b/src/World.hpp index e706cbd..ee58b1b 100644 --- a/src/World.hpp +++ b/src/World.hpp @@ -17,6 +17,8 @@ class MainProgram; +//#define ENABLE_ORDERED_LIST_TEST + #define DEFAULT_CANVAS_BACKGROUND_COLOR Vector3f{0.07f, 0.07f, 0.07f} class World { @@ -113,4 +115,10 @@ class World { WorldVec mouseWorldMove = {0, 0}; std::chrono::steady_clock::time_point lastKeepAliveSent; + +#ifdef ENABLE_ORDERED_LIST_TEST + NetworkingObjects::NetObjOwnerPtr> listDebugTest; + void list_debug_test_update(); + std::chrono::steady_clock::time_point listDebugTestTimeStart = std::chrono::steady_clock::now(); +#endif };