diff --git a/include/Helpers/Networking/NetClient.cpp b/include/Helpers/Networking/NetClient.cpp index 5964702..9da7d61 100644 --- a/include/Helpers/Networking/NetClient.cpp +++ b/include/Helpers/Networking/NetClient.cpp @@ -45,12 +45,22 @@ void NetClient::update() { if(isDisconnected) return; if(std::chrono::steady_clock::now() - lastMessageTime >= NetLibrary::TIMEOUT_DURATION) { - Logger::get().log("INFO", "Connection timed out"); + Logger::get().log("INFO", "[NetClient::update] Connection timed out"); isDisconnected = true; return; } - parse_received_messages(); - send_queued_messages(); + try { + parse_received_messages(); + send_queued_messages(); + } + catch(const std::exception& e) { + Logger::get().log("INFO", "[NetClient::update] Exception thrown while parsing and sending messages: " + std::string(e.what())); + isDisconnected = true; + } + catch(...) { + Logger::get().log("INFO", "[NetClient::update] Unknown exception thrown while parsing and sending messages."); + isDisconnected = true; + } } void NetClient::parse_received_messages() { diff --git a/include/Helpers/Networking/NetServer.cpp b/include/Helpers/Networking/NetServer.cpp index 0cfcab9..4dfab29 100644 --- a/include/Helpers/Networking/NetServer.cpp +++ b/include/Helpers/Networking/NetServer.cpp @@ -26,8 +26,20 @@ void NetServer::update() { if(client->setToDisconnect) disconnectCallback(client); else { - client->parse_received_messages(*this); - client->send_queued_messages(*this); + try { + client->parse_received_messages(*this); + client->send_queued_messages(*this); + } + 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())); + client->setToDisconnect = true; + disconnectCallback(client); + } + catch(...) { + Logger::get().log("INFO", "[NetServer::update] Unknown exception thrown while parsing and sending messages for a client."); + client->setToDisconnect = true; + disconnectCallback(client); + } } } } diff --git a/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp b/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp index 28c3300..a06c142 100644 --- a/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp +++ b/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp @@ -101,19 +101,24 @@ namespace NetworkingObjects { uint32_t actualPosToInsertAt = posToInsertAt = std::min(posToInsertAt, data.size()); // Find duplicate. If found, erase the duplicate before inserting + std::shared_ptr> newObjInfoPtr; auto itObjAlreadyInData = idToDataMap.find(newObj.get_net_id()); if(itObjAlreadyInData != idToDataMap.end()) { uint32_t posToErase = itObjAlreadyInData->second->pos; + newObjInfoPtr = data[posToErase]; data.erase(data.begin() + posToErase); - idToDataMap.erase(itObjAlreadyInData); if(posToErase < actualPosToInsertAt) // Shift insert position if an element was erased before the insert position actualPosToInsertAt--; + newObjInfoPtr->pos = actualPosToInsertAt; + data.insert(data.begin() + actualPosToInsertAt, newObjInfoPtr); + } + else { + // Insert the new object if no duplicate found + newObjInfoPtr = std::make_shared>(newObj, 0, false); + data.insert(data.begin() + actualPosToInsertAt, newObjInfoPtr); + idToDataMap.emplace(newObjInfoPtr->obj.get_net_id(), newObjInfoPtr); } - // 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); @@ -182,20 +187,28 @@ namespace NetworkingObjects { virtual void insert(const NetObjPtr>& l, uint32_t posToInsertAt, const NetObjPtr& newObj) { uint32_t actualPosToInsertAt = posToInsertAt = std::min(posToInsertAt, clientData.size()); - // Find duplicate. If found, erase the duplicate before inserting + NetObjOrderedListObjectInfoPtr newObjInfoPtr; auto itObjAlreadyInData = clientIdToDataMap.find(newObj.get_net_id()); if(itObjAlreadyInData != clientIdToDataMap.end()) { uint32_t posToErase = itObjAlreadyInData->second->pos; + newObjInfoPtr = clientData[posToErase]; clientData.erase(clientData.begin() + posToErase); - clientIdToDataMap.erase(itObjAlreadyInData); if(posToErase < actualPosToInsertAt) // Shift insert position if an element was erased before the insert position actualPosToInsertAt--; + clientData.insert(clientData.begin() + actualPosToInsertAt, newObjInfoPtr); + } + else { + auto serverIt = serverIdToDataMap.find(newObj.get_net_id()); // No duplicate in client data, but check if it's already in server data + if(serverIt != serverIdToDataMap.end()) + newObjInfoPtr = serverIt->second; + else + newObjInfoPtr = std::make_shared>(newObj, 0, true); + clientData.insert(clientData.begin() + actualPosToInsertAt, newObjInfoPtr); + clientIdToDataMap.emplace(newObjInfoPtr->obj.get_net_id(), newObjInfoPtr); } - // 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); + newObjInfoPtr->syncIgnore = true; + 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); @@ -235,26 +248,43 @@ namespace NetworkingObjects { a(c); switch(c) { case ObjPtrOrderedListCommand_StoC::INSERT_SINGLE: { - uint32_t newPos; - a(newPos); - 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()); + uint32_t newServerPos; + a(newServerPos); + auto objToInsert = l.get_obj_man()->template read_create_message(a, nullptr); + auto it = serverIdToDataMap.find(objToInsert.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 + uint32_t serverPosErased = std::numeric_limits::max(); + std::erase_if(serverData, [&](auto& objInfo) { + if(objToInsert.get_net_id() == objInfo->obj.get_net_id()) { + serverPosErased = &objInfo - &serverData[0]; + return true; + } + return false; + }); + assert(serverPosErased != std::numeric_limits::max()); + if(serverPosErased < newServerPos) + newServerPos--; // Shift if there was a duplicate before this + //it->second->pos = newServerPos; // NOTE: DO NOT set the pos variable here, as pos is reserved for the position in the client vector + serverData.insert(serverData.begin() + newServerPos, it->second); + } + else { + NetObjOrderedListObjectInfoPtr objInfoToInsert; + auto clientIt = clientIdToDataMap.find(objToInsert.get_net_id()); + if(clientIt != clientIdToDataMap.end()) + objInfoToInsert = clientIt->second; + else + objInfoToInsert = std::make_shared>(objToInsert, 0, false); + objInfoToInsert->syncIgnore = false; + serverData.insert(serverData.begin() + newServerPos, objInfoToInsert); + serverIdToDataMap.emplace(objInfoToInsert->obj.get_net_id(), objInfoToInsert); } - serverIdToDataMap.emplace(toInsert->obj.get_net_id(), toInsert); - serverData.insert(serverData.begin() + newPos, toInsert); break; } case ObjPtrOrderedListCommand_StoC::ERASE_SINGLE: { - uint32_t posToErase; - a(posToErase); - NetObjID objID = serverData[posToErase]->obj.get_net_id(); - serverData.erase(serverData.begin() + posToErase); + uint32_t serverPosToErase; + a(serverPosToErase); + NetObjID objID = serverData[serverPosToErase]->obj.get_net_id(); + serverData.erase(serverData.begin() + serverPosToErase); serverIdToDataMap.erase(objID); break; } diff --git a/src/ConnectionManager.cpp b/src/ConnectionManager.cpp index 5054390..10d6d74 100644 --- a/src/ConnectionManager.cpp +++ b/src/ConnectionManager.cpp @@ -3,6 +3,7 @@ #include "Server/CommandList.hpp" #include #include "World.hpp" +#include void ConnectionManager::init_local_p2p(World& initWorld, const std::string& serverLocalID) { localServer = std::make_unique(initWorld, serverLocalID); @@ -18,8 +19,9 @@ void ConnectionManager::connect_p2p(World& initWorld, const std::string& serverF } void ConnectionManager::update() { - if(localServer) + if(localServer) { localServer->update(); + } if(client) { client->update(); if(std::chrono::steady_clock::now() - lastKeepAliveTime > std::chrono::seconds(2)) { diff --git a/src/MainProgram.cpp b/src/MainProgram.cpp index dc80f31..377332e 100755 --- a/src/MainProgram.cpp +++ b/src/MainProgram.cpp @@ -96,6 +96,10 @@ void MainProgram::update() { setTabToClose.reset(); } + std::erase_if(worlds, [&](const std::shared_ptr& w) { + return w->setToDestroy; + }); + if(worldIndex >= worlds.size()) worldIndex = worlds.size() - 1; if(worlds.size() == 0) diff --git a/src/World.cpp b/src/World.cpp index e2641b2..eab077b 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -77,11 +77,6 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): stringListTest->emplace_back_direct(stringListTest, "Hello"); 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; } } @@ -103,9 +98,10 @@ void World::init_client_callbacks() { drawProg.initialize_draw_data(message); message(bMan, gridMan); stringListTest = netObjMan.read_create_message>(message, nullptr); - std::cout << "Client print: " << 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; + //std::cout << "============================================" << std::endl; + //std::cout << "Client print: " << 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; } nextClientID = get_max_id(ownID); clientStillConnecting = false; @@ -120,11 +116,13 @@ void World::init_client_callbacks() { 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) + if(!con.host_exists() && !clientStillConnecting) { // Dont update a direct connected client (which is just the server), and dont process these until the CLIENT_INITIAL_DATA message is received first 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; + //std::cout << "============================================\n"; + //std::cout << "Client print update: \n"; + //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() << '\n'; + //std::cout << std::endl; } }); con.client_add_recv_callback(CLIENT_USER_DISCONNECT, [&](cereal::PortableBinaryInputArchive& message) { @@ -180,13 +178,6 @@ 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; @@ -198,11 +189,8 @@ void World::focus_update() { if(con.is_client_disconnected()) { Logger::get().log("USERINFO", "Client connection failed"); - clientStillConnecting = false; - clients.clear(); - netSource.clear(); - conType = CONNECTIONTYPE_LOCAL; - con = ConnectionManager(); + setToDestroy = true; + return; } if(!clientStillConnecting) { @@ -220,6 +208,17 @@ void World::focus_update() { } con.client_send_items_to_server(UNRELIABLE_COMMAND_CHANNEL, SERVER_MOVE_SCREEN_MOUSE, main.input.mouse.pos); drawProg.update(); + + testTimer++; + if(testTimer == 1) { + std::string strToAdd = (netObjMan.is_server() ? "Server " : "Client ") + std::to_string(strToAddNum++); + //std::cout << "============================================\n"; + //std::cout << "Added: " << strToAdd << std::endl; + stringListTest->emplace_back_direct(stringListTest, strToAdd); + //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; + testTimer = 0; + } } drawData.cam.update_main(*this); @@ -247,6 +246,10 @@ void World::redo_with_checks() { void World::unfocus_update() { con.update(); + if(con.is_client_disconnected()) { + Logger::get().log("USERINFO", "Client connection failed"); + setToDestroy = true; + } } void World::set_canvas_background_color(const Vector3f& newBackColor, bool sendChangeOverNetwork) { diff --git a/src/World.hpp b/src/World.hpp index 0da4330..86d8874 100644 --- a/src/World.hpp +++ b/src/World.hpp @@ -106,6 +106,8 @@ class World { void scale_up_step(); uint64_t canvasScale = 0; + + bool setToDestroy = false; private: ClientPortionID get_max_id(ServerPortionID serverID);