Added net ordered list test, and fixed ordering issues with list

This commit is contained in:
Yousef Khadadeh 2026-01-05 12:43:40 +03:00
parent 8611a2e6bb
commit 6e13c5742e
5 changed files with 137 additions and 17 deletions

View file

@ -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() {

View file

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

View file

@ -11,6 +11,14 @@
#include <limits>
#include <list>
//#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 <typename T> void netobj_ordered_list_debug_check(const std::list<NetObjOrderedListObjectInfo<T>>& l, const std::unordered_map<NetObjID, NetObjOrderedListIterator<T>>& 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 <typename T> void refresh_positions_complete(std::list<NetObjOrderedListObjectInfo<T>>& l) {
uint32_t i = 0;
for(auto& c : l) {
c.pos = i;
++i;
}
}
template <typename T> 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<NetObjOrderedList<T>>& l, const std::vector<NetObjOrderedListIterator<T>>& itList, std::vector<NetObjOwnerPtr<T>>* erasedObjects) {
@ -338,11 +378,13 @@ namespace NetworkingObjects {
});
set_position_obj_info_list<T>(smallestPosIt, data.end());
NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(data, idToDataMap);
}
virtual void write_constructor(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) override {
a(static_cast<uint32_t>(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<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& 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<T>(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<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) override {
@ -373,17 +416,9 @@ namespace NetworkingObjects {
newObjs.emplace_back(NetObjOrderedListIterator<T>(), l.get_obj_man()->template read_create_message<T>(a, c));
}
uint32_t currentIndex = data.size();
NetObjOrderedListIterator<T> currentIt = data.end();
for(std::pair<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>&, 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<NetObjOrderedListIterator<T>> 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<NetObjOrderedList<T>>& l, const std::vector<NetObjOrderedListIterator<T>>& itList, std::vector<NetObjOwnerPtr<T>>* erasedObjects) {
@ -481,6 +517,7 @@ namespace NetworkingObjects {
});
set_position_obj_info_list<T>(smallestPosIt, clientData.end());
NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(clientData, clientIdToDataMap);
}
virtual void write_constructor(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) override {
a(static_cast<uint32_t>(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<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) 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<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) override {
ObjPtrOrderedListCommand_StoC c;
@ -515,17 +554,18 @@ namespace NetworkingObjects {
a(newObjsSize);
std::vector<NetworkingObjects::NetObjID> idsBeforeElements;
std::vector<std::list<NetObjID>::iterator> serverInsertIts;
std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>> 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<T>(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<T>(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<NetObjID>::iterator serverInsertIt;
bool foundInClient;
NetObjOrderedListIterator<T> clientObjectIt;
NetObjOrderedListIterator<T> 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<NetObjOrderedListObjectInfo<T>> 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<T>(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<T>(smallestPosIt, clientData.end());
NETOBJ_ORDERED_LIST_VERBOSE_DEBUG(clientData, clientIdToDataMap);
break;
}
}

View file

@ -9,6 +9,7 @@
#include <Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp>
#include <cereal/types/unordered_map.hpp>
#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 <cereal/archives/portable_binary.hpp>
#include <chrono>
#include <fstream>
#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<NetworkingObjects::NetObjOrderedList<uint16_t>>();
#endif
}
}
@ -108,6 +113,11 @@ void World::init_net_obj_type_list() {
canvasTheme.register_class();
ClientData::register_class(*this);
NetworkingObjects::register_unordered_set_class<ClientData>(netObjMan);
#ifdef ENABLE_ORDERED_LIST_TEST
NetworkingObjects::register_generic_serialized_class<uint16_t>(netObjMan);
NetworkingObjects::register_ordered_list_class<uint16_t>(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<ClientData>(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<NetworkingObjects::NetObjOrderedList<uint16_t>>(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<std::pair<NetObjOrderedListIterator<uint16_t>, NetObjOwnerPtr<uint16_t>>> toInsert;
std::vector<uint32_t> 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<uint32_t>(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<uint16_t>(Random::get().int_range<uint32_t>(10, 100)));
listDebugTest->insert_ordered_list_and_send_create(listDebugTest, toInsert);
}
else {
std::vector<NetObjOrderedListIterator<uint16_t>> 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)

View file

@ -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<NetworkingObjects::NetObjOrderedList<uint16_t>> listDebugTest;
void list_debug_test_update();
std::chrono::steady_clock::time_point listDebugTestTimeStart = std::chrono::steady_clock::now();
#endif
};