diff --git a/include/Helpers/Networking/NetLibrary.hpp b/include/Helpers/Networking/NetLibrary.hpp index 172aeba..7acd55a 100644 --- a/include/Helpers/Networking/NetLibrary.hpp +++ b/include/Helpers/Networking/NetLibrary.hpp @@ -13,7 +13,7 @@ template std::weak_ptr make_weak_ptr(std::shared_ptr ptr) { retu #define RELIABLE_COMMAND_CHANNEL "reliableCommand" #define UNRELIABLE_COMMAND_CHANNEL "unreliableCommand" #define RESOURCE_COMMAND_CHANNEL "resourceCommand" -#define MAIN_NETLIBRARY_CHANNEL "main00001" +#define MAIN_NETLIBRARY_CHANNEL "main00002" // NOTE: Change this value whenever netcode is incompatible with the previous version typedef uint8_t MessageCommandType; diff --git a/src/CollabList.hpp b/src/CollabList.hpp index dd910fb..2068430 100644 --- a/src/CollabList.hpp +++ b/src/CollabList.hpp @@ -23,7 +23,6 @@ template class CollabList { std::function updateCallback; std::function clientInsertCallback; std::function clientEraseCallback; - std::function& c)> clientEraseSetCallback; std::function& c)> clientInsertOrderedVectorCallback; std::function clientServerLastPosShiftCallback; @@ -33,27 +32,9 @@ template class CollabList { obj->syncIgnore = false; } - T* get_item_by_id(IDType id) { - auto clientFound = std::find_if(clientSideList.begin(), clientSideList.end(), [&](const auto& obj) { - return (obj->id == id); - }); - if(clientFound != clientSideList.end()) - return &((*clientFound)->obj); - auto serverFound = std::find_if(serverSideList.begin(), serverSideList.end(), [&](const auto& obj) { - return (obj->id == id); - }); - if(serverFound != serverSideList.end()) - return &((*serverFound)->obj); - return nullptr; - } - - IDType get_id(const T& item) { - auto clientFound = std::find_if(clientSideList.begin(), clientSideList.end(), [&](const auto& obj) { - return (obj->obj == item); - }); - if(clientFound != clientSideList.end()) - return (*clientFound)->id; - return {0, 0}; + ObjectInfoPtr get_item_by_id(IDType id) { + auto it = idToObjectMap.find(id); + return (idToObjectMap.end() == it) ? nullptr : it->second; } void client_erase_if(const std::function&)>& func) { @@ -63,6 +44,7 @@ template class CollabList { if(clientEraseCallback) clientEraseCallback(clientSideList[i]); clientSideList[i]->obj->collabListInfo.reset(); + idToObjectMap.erase(clientSideList[i]->id); clientSideList.erase(clientSideList.begin() + i); i--; if(updateCallback) @@ -83,6 +65,7 @@ template class CollabList { item->collabListInfo = newObj; clientSideList.insert(clientSideList.begin() + newObj->pos, newObj); + idToObjectMap.emplace(newObj->id, newObj); client_pos_refresh_after_insert(newObj->pos); @@ -100,6 +83,7 @@ template class CollabList { if(clientEraseCallback) clientEraseCallback(obj); erasedID = obj->id; + idToObjectMap.erase(erasedID); obj->syncIgnore = true; obj->obj->collabListInfo.reset(); erasedSomething = true; @@ -120,6 +104,9 @@ template class CollabList { if(objSet.contains(obj)) { obj->syncIgnore = true; obj->obj->collabListInfo.reset(); + idToObjectMap.erase(obj->id); + if(clientEraseCallback) + clientEraseCallback(obj); erasedSomething = true; return true; } @@ -129,8 +116,6 @@ template class CollabList { complete_client_pos_refresh(false); if(updateCallback) updateCallback(); - if(clientEraseSetCallback) - clientEraseSetCallback(objSet); for(auto& o : objSet) o->obj->collabListInfo.reset(); } @@ -145,19 +130,21 @@ template class CollabList { objOrderedVector.front()->syncIgnore = true; objOrderedVector.front()->id = getNewIDFunc(); objOrderedVector.front()->obj->collabListInfo = objOrderedVector.front(); + idToObjectMap.emplace(objOrderedVector.front()->id, objOrderedVector.front()); for(uint64_t i = 1; i < static_cast(objOrderedVector.size()); i++) { auto& obj = objOrderedVector[i]; obj->syncIgnore = true; obj->id = getNewIDFunc(); obj->obj->collabListInfo = obj; + idToObjectMap.emplace(obj->id, obj); if(obj->pos - objOrderedVector[startPoint]->pos != (i - startPoint)) { - uint64_t insertPosition = std::max(clientSideList.size(), objOrderedVector[startPoint]->pos); + uint64_t insertPosition = std::min(clientSideList.size(), objOrderedVector[startPoint]->pos); clientSideList.insert(clientSideList.begin() + insertPosition, objOrderedVector.begin() + startPoint, objOrderedVector.begin() + i); startPoint = i; } } - clientSideList.insert(clientSideList.begin() + objOrderedVector[startPoint]->pos, objOrderedVector.begin() + startPoint, objOrderedVector.end()); + clientSideList.insert(clientSideList.begin() + std::min(clientSideList.size(), objOrderedVector[startPoint]->pos), objOrderedVector.begin() + startPoint, objOrderedVector.end()); complete_client_pos_refresh(false); if(updateCallback) @@ -166,12 +153,55 @@ template class CollabList { clientInsertOrderedVectorCallback(objOrderedVector); } + std::vector server_insert_ordered_vector(std::vector objOrderedVector) { + if(objOrderedVector.empty()) + return std::vector(); + + std::vector didntExistPreviously(objOrderedVector.size(), true); + + uint64_t startPoint = 0; + auto foundInClient1 = idToObjectMap.emplace(objOrderedVector.front()->id, objOrderedVector.front()); + if(!foundInClient1.second) { // object already existed + objOrderedVector.front() = foundInClient1.first->second; + didntExistPreviously[0] = false; + } + objOrderedVector.front()->syncIgnore = false; + objOrderedVector.front()->obj->collabListInfo = objOrderedVector.front(); + + for(uint64_t i = 1; i < static_cast(objOrderedVector.size()); i++) { + auto& obj = objOrderedVector[i]; + + auto foundInClient = idToObjectMap.emplace(obj->id, obj); + if(!foundInClient.second) { // object already existed + obj = foundInClient.first->second; + didntExistPreviously[i] = false; + } + obj->syncIgnore = false; + obj->obj->collabListInfo = obj; + + if(obj->pos - objOrderedVector[startPoint]->pos != (i - startPoint)) { + uint64_t insertPosition = std::min(clientSideList.size(), objOrderedVector[startPoint]->pos); + clientSideList.insert(clientSideList.begin() + insertPosition, objOrderedVector.begin() + startPoint, objOrderedVector.begin() + i); + uint64_t insertPositionServer = std::min(serverSideList.size(), objOrderedVector[startPoint]->pos); + serverSideList.insert(serverSideList.begin() + insertPositionServer, objOrderedVector.begin() + startPoint, objOrderedVector.begin() + i); + startPoint = i; + } + } + clientSideList.insert(clientSideList.begin() + std::min(clientSideList.size(), objOrderedVector[startPoint]->pos), objOrderedVector.begin() + startPoint, objOrderedVector.end()); + serverSideList.insert(serverSideList.begin() + std::min(serverSideList.size(), objOrderedVector[startPoint]->pos), objOrderedVector.begin() + startPoint, objOrderedVector.end()); + + if(clientInsertOrderedVectorCallback) + clientInsertOrderedVectorCallback(objOrderedVector); + + sync(); + + return didntExistPreviously; + } + bool server_insert(uint64_t pos, IDType id, const T& item) { - auto foundInClient = std::find_if(clientSideList.begin(), clientSideList.end(), [&](const auto& clientObj) { - return clientObj->id == id; - }); - if(foundInClient != clientSideList.end()) { - ObjectInfoPtr& o = *foundInClient; + auto foundInClient = idToObjectMap.find(id); + if(foundInClient != idToObjectMap.end()) { + ObjectInfoPtr& o = foundInClient->second; serverSideList.insert(serverSideList.begin() + pos, o); o->syncIgnore = false; sync(); @@ -182,6 +212,7 @@ template class CollabList { newObj->id = id; newObj->obj = item; newObj->pos = std::min(pos, clientSideList.size()); + idToObjectMap.emplace(newObj->id, newObj); item->collabListInfo = newObj; serverSideList.insert(serverSideList.begin() + pos, newObj); @@ -207,6 +238,7 @@ template class CollabList { std::erase_if(clientSideList, [&](const auto& obj) { if(obj->id == id) { obj->syncIgnore = false; + idToObjectMap.erase(obj->id); if(clientEraseCallback) clientEraseCallback(obj); obj->obj->collabListInfo.reset(); @@ -216,12 +248,36 @@ template class CollabList { }); sync(); } + + void server_erase_set(const std::unordered_set& idsToErase) { + std::erase_if(serverSideList, [&](const auto& obj) { + if(idsToErase.contains(obj->id)) { + obj->syncIgnore = false; + return true; + } + return false; + }); + std::erase_if(clientSideList, [&](const auto& obj) { + if(idsToErase.contains(obj->id)) { + obj->syncIgnore = false; + if(clientEraseCallback) + clientEraseCallback(obj); + idToObjectMap.erase(obj->id); + obj->obj->collabListInfo.reset(); + return true; + } + return false; + }); + sync(); + } + void init_emplace_back(IDType id, const T& item) { auto newObj(std::make_shared()); newObj->syncIgnore = false; newObj->id = id; newObj->obj = item; item->collabListInfo = newObj; + idToObjectMap.emplace(newObj->id, newObj); serverSideList.emplace_back(newObj); clientSideList.emplace_back(newObj); @@ -274,7 +330,9 @@ template class CollabList { updateCallback(); complete_client_pos_refresh(true); } + std::function getNewIDFunc; std::vector clientSideList; std::vector serverSideList; + std::unordered_map idToObjectMap; }; diff --git a/src/DrawComponents/DrawComponent.cpp b/src/DrawComponents/DrawComponent.cpp index 65f0daa..9a0a720 100644 --- a/src/DrawComponents/DrawComponent.cpp +++ b/src/DrawComponents/DrawComponent.cpp @@ -8,11 +8,38 @@ #include "../SharedTypes.hpp" #include "../Server/CommandList.hpp" #include +#include #ifndef IS_SERVER #include "../MainProgram.hpp" #endif +void save(cereal::PortableBinaryOutputArchive& a, const CollabListType::ObjectInfo& o) { + // Client will send ID, but server should ignore ID and generate its own + a(o.pos, o.obj->get_type(), o.id, o.obj->coords, *o.obj); +} + +void load(cereal::PortableBinaryInputArchive& a, CollabListType::ObjectInfo& o) { + DrawComponentType t; + a(o.pos, t, o.id); + o.obj = DrawComponent::allocate_comp_type(t); + a(o.obj->coords, *o.obj); +} + +void SendOrderedComponentVectorOp::save(cereal::PortableBinaryOutputArchive& a) const { + a((uint64_t)v->size()); + for(auto& c : *v) + a(*c); +} + +void SendOrderedComponentVectorOp::load(cereal::PortableBinaryInputArchive& a) { + v->clear(); + uint64_t vSize; + a(vSize); + for(uint64_t i = 0; i < vSize; i++) + a(*v->emplace_back(std::make_shared())); +} + std::shared_ptr DrawComponent::allocate_comp_type(DrawComponentType type) { switch(type) { case DRAWCOMPONENT_BRUSHSTROKE: @@ -29,12 +56,20 @@ std::shared_ptr DrawComponent::allocate_comp_type(DrawComponentTy return nullptr; } +void DrawComponent::server_send_place_many(MainServer& server, std::vector& comps) { + server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_PLACE_MANY_COMPONENTS, SendOrderedComponentVectorOp{&comps}); +} + void DrawComponent::server_send_place(MainServer& server, ServerClientID id, uint64_t placement) { - server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_PLACE_COMPONENT, placement, get_type(), id, this->coords, *this); + server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_PLACE_SINGLE_COMPONENT, placement, get_type(), id, this->coords, *this); } void DrawComponent::server_send_erase(MainServer& server, ServerClientID id) { - server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_ERASE_COMPONENT, id); + server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_ERASE_SINGLE_COMPONENT, id); +} + +void DrawComponent::server_send_erase_set(MainServer& server, const std::unordered_set& ids) { + server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_ERASE_MANY_COMPONENTS, ids); } void DrawComponent::server_send_update_temp(MainServer& server, ServerClientID id) { @@ -48,15 +83,8 @@ void DrawComponent::server_send_update_final(MainServer& server, ServerClientID serverIsTempUpdate = false; } -void DrawComponent::server_send_transform_temp(MainServer& server, ServerClientID id) { - server.netServer->send_items_to_all_clients(UNRELIABLE_COMMAND_CHANNEL, CLIENT_TRANSFORM_COMPONENT, true, id, this->coords); - tempServerUpdateTimer = std::chrono::steady_clock::now(); - serverIsTempUpdate = true; -} - -void DrawComponent::server_send_transform_final(MainServer& server, ServerClientID id) { - server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_TRANSFORM_COMPONENT, false, id, this->coords); - serverIsTempUpdate = false; +void DrawComponent::server_send_transform(MainServer& server, ServerClientID id) { + server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_TRANSFORM_COMPONENT, id, this->coords); } void DrawComponent::server_update(MainServer& server, ServerClientID id) { @@ -67,13 +95,6 @@ void DrawComponent::server_update(MainServer& server, ServerClientID id) { serverIsTempUpdate = false; } } - if(serverIsTempTransform) { - float timeSince = std::chrono::duration_cast>(std::chrono::steady_clock::now() - tempServerTransformTimer).count(); - if(timeSince >= 1.0f) { - server_send_transform_final(server, id); - serverIsTempTransform = false; - } - } } void DrawComponent::get_used_resources(std::unordered_set& v) const { @@ -90,14 +111,6 @@ bool DrawComponent::bounds_draw_check(const DrawData& drawData) const { } void DrawComponent::check_timers(DrawingProgram& drawP) { - if(delayedCoordinateSpace) { - float transformDur = std::chrono::duration_cast>(std::chrono::steady_clock::now() - lastTransformTime).count(); - if(transformDur > CLIENT_DRAWCOMP_DELAY_TIMER_DURATION) { - coords = *delayedCoordinateSpace; - delayedCoordinateSpace = nullptr; - finalize_update(drawP); - } - } if(delayedUpdatePtr) { float updateDur = std::chrono::duration_cast>(std::chrono::steady_clock::now() - lastUpdateTime).count(); if(updateDur > CLIENT_DRAWCOMP_DELAY_TIMER_DURATION) { @@ -124,14 +137,13 @@ void DrawComponent::final_update(DrawingProgram& drawP, bool invalidateCache) { finalize_update(drawP, invalidateCache); } -void DrawComponent::client_send_place(DrawingProgram& drawP) { - //if(collabListInfo.lock()) If this fails, then there's a real issue - drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_PLACE_COMPONENT, collabListInfo.lock()->pos, get_type(), this->coords, *this); +void DrawComponent::client_send_place_many(DrawingProgram& drawP, std::vector& comps) { + drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_PLACE_MANY_COMPONENTS, SendOrderedComponentVectorOp{&comps}); } -void DrawComponent::client_send_erase(DrawingProgram& drawP) { - if(collabListInfo.lock()) - drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_ERASE_COMPONENT, collabListInfo.lock()->id); +void DrawComponent::client_send_place(DrawingProgram& drawP) { + //if(collabListInfo.lock()) If this fails, then there's a real issue + drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_PLACE_SINGLE_COMPONENT, collabListInfo.lock()->pos, get_type(), this->coords, *this); } void DrawComponent::client_send_update_temp(DrawingProgram& drawP) { @@ -144,14 +156,18 @@ void DrawComponent::client_send_update_final(DrawingProgram& drawP) { drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_UPDATE_COMPONENT, false, collabListInfo.lock()->id, *this); } -void DrawComponent::client_send_transform_temp(DrawingProgram& drawP) { +void DrawComponent::client_send_transform(DrawingProgram& drawP) { if(collabListInfo.lock()) - drawP.world.con.client_send_items_to_server(UNRELIABLE_COMMAND_CHANNEL, SERVER_TRANSFORM_COMPONENT, true, collabListInfo.lock()->id, this->coords); + drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_TRANSFORM_COMPONENT, collabListInfo.lock()->id, this->coords); } -void DrawComponent::client_send_transform_final(DrawingProgram& drawP) { +void DrawComponent::client_send_erase(DrawingProgram& drawP) { if(collabListInfo.lock()) - drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_TRANSFORM_COMPONENT, false, collabListInfo.lock()->id, this->coords); + drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_ERASE_SINGLE_COMPONENT, collabListInfo.lock()->id); +} + +void DrawComponent::client_send_erase_set(DrawingProgram& drawP, const std::unordered_set& ids) { + drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_ERASE_MANY_COMPONENTS, ids); } void DrawComponent::finalize_update(DrawingProgram& drawP, bool invalidateCache) { diff --git a/src/DrawComponents/DrawComponent.hpp b/src/DrawComponents/DrawComponent.hpp index 78baab8..48a8336 100644 --- a/src/DrawComponents/DrawComponent.hpp +++ b/src/DrawComponents/DrawComponent.hpp @@ -28,6 +28,15 @@ class DrawingProgramCacheBVHNode; typedef CollabList, ServerClientID> CollabListType; +void save(cereal::PortableBinaryOutputArchive& a, const CollabListType::ObjectInfo& o); +void load(cereal::PortableBinaryInputArchive& a, CollabListType::ObjectInfo& o); + +struct SendOrderedComponentVectorOp { + std::vector* v; + void save(cereal::PortableBinaryOutputArchive& a) const; + void load(cereal::PortableBinaryInputArchive& a); +}; + class DrawComponent { public: static std::shared_ptr allocate_comp_type(DrawComponentType type); @@ -48,25 +57,23 @@ class DrawComponent { float scale; } transformData; bool shouldDraw = false; - unsigned mipmapLevel = 0; + uint8_t mipmapLevel = 0; } drawSetupData; void canvas_do_calculated_transform(SkCanvas* canvas); void calculate_draw_transform(const DrawData& drawData); void server_send_place(MainServer& server, ServerClientID id, uint64_t placement); + static void server_send_place_many(MainServer& server, std::vector& comps); static void server_send_erase(MainServer& server, ServerClientID id); + static void server_send_erase_set(MainServer& server, const std::unordered_set& ids); void server_send_update_temp(MainServer& server, ServerClientID id); void server_send_update_final(MainServer& server, ServerClientID id); - void server_send_transform_temp(MainServer& server, ServerClientID id); - void server_send_transform_final(MainServer& server, ServerClientID id); + void server_send_transform(MainServer& server, ServerClientID id); std::chrono::time_point tempServerUpdateTimer; bool serverIsTempUpdate = false; - std::chrono::time_point tempServerTransformTimer; - bool serverIsTempTransform = false; - std::weak_ptr collabListInfo; std::weak_ptr parentBvhNode; @@ -77,17 +84,17 @@ class DrawComponent { std::chrono::time_point lastUpdateTime; std::shared_ptr delayedUpdatePtr = nullptr; - std::shared_ptr delayedCoordinateSpace = nullptr; bool bounds_draw_check(const DrawData& drawData) const; void check_timers(DrawingProgram& drawP); + static void client_send_place_many(DrawingProgram& drawP, std::vector& comps); void client_send_place(DrawingProgram& drawP); void client_send_erase(DrawingProgram& drawP); + static void client_send_erase_set(DrawingProgram& drawP, const std::unordered_set& ids); void client_send_update_temp(DrawingProgram& drawP); void client_send_update_final(DrawingProgram& drawP); - void client_send_transform_temp(DrawingProgram& drawP); - void client_send_transform_final(DrawingProgram& drawP); + void client_send_transform(DrawingProgram& drawP); std::optional> worldAABB; diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 26b9837..a803fb2 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -23,6 +23,7 @@ #include "../MainProgram.hpp" #include #include +#include DrawingProgram::DrawingProgram(World& initWorld): world(initWorld), @@ -52,15 +53,9 @@ DrawingProgram::DrawingProgram(World& initWorld): }; components.clientEraseCallback = [&](const CollabListType::ObjectInfoPtr& c) { compCache.erase_component(c); - delayedUpdateTransformComponents.erase(c->obj); + delayedUpdateComponents.erase(c->obj); updateableComponents.erase(c->obj); }; - components.clientEraseSetCallback = [&](const std::unordered_set& comps) { - for(auto& c : comps) { - delayedUpdateTransformComponents.erase(c->obj); - updateableComponents.erase(c->obj); - } - }; components.clientInsertOrderedVectorCallback = [&](const std::vector& comps) { for(auto& c : comps) components.clientInsertCallback(c); @@ -75,10 +70,10 @@ void DrawingProgram::init_client_callbacks() { bool isTemp; ServerClientID id; message(isTemp, id); - std::shared_ptr* compPtr = components.get_item_by_id(id); - if(!compPtr) + auto objPtr = components.get_item_by_id(id); + if(!objPtr) return; - std::shared_ptr& comp = *compPtr; + std::shared_ptr& comp = objPtr->obj; float dur = std::chrono::duration_cast>(std::chrono::steady_clock::now() - comp->lastUpdateTime).count(); if(dur > CLIENT_DRAWCOMP_DELAY_TIMER_DURATION){ comp->delayedUpdatePtr = nullptr; @@ -91,31 +86,22 @@ void DrawingProgram::init_client_callbacks() { else { comp->delayedUpdatePtr = DrawComponent::allocate_comp_type(comp->get_type()); message(*comp->delayedUpdatePtr); - delayedUpdateTransformComponents.emplace(comp); + delayedUpdateComponents.emplace(comp); } }); world.con.client_add_recv_callback(CLIENT_TRANSFORM_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) { - bool isTemp; ServerClientID id; - message(isTemp, id); - std::shared_ptr* compPtr = components.get_item_by_id(id); - if(!compPtr) + message(id); + auto objPtr = components.get_item_by_id(id); + if(!objPtr) return; - std::shared_ptr& comp = *compPtr; - float dur = std::chrono::duration_cast>(std::chrono::steady_clock::now() - comp->lastTransformTime).count(); - if(dur > CLIENT_DRAWCOMP_DELAY_TIMER_DURATION){ - comp->delayedCoordinateSpace = nullptr; - message(comp->coords); - if(!isTemp) - comp->finalize_update(*this); // No need to update draw data during transformation - } - else { - comp->delayedCoordinateSpace = std::make_shared(); - message(*comp->delayedCoordinateSpace); - delayedUpdateTransformComponents.emplace(comp); - } + if(selection.is_selected(objPtr)) // Whatever transformation we're doing right now will overwrite this transformation, so we can ignore this message + return; + std::shared_ptr& comp = objPtr->obj; + message(comp->coords); + comp->finalize_update(*this); // No need to update draw data during transformation }); - world.con.client_add_recv_callback(CLIENT_PLACE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) { + world.con.client_add_recv_callback(CLIENT_PLACE_SINGLE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) { uint64_t insertPosition; DrawComponentType type; message(insertPosition, type); @@ -126,17 +112,42 @@ void DrawingProgram::init_client_callbacks() { if(didntExistPreviously) newObj->final_update(*this); }); - world.con.client_add_recv_callback(CLIENT_ERASE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) { - ServerClientID cToRemove; - message(cToRemove); - components.server_erase(cToRemove); + world.con.client_add_recv_callback(CLIENT_PLACE_MANY_COMPONENTS, [&](cereal::PortableBinaryInputArchive& message) { + std::vector sortedPlacedComponents; + + SendOrderedComponentVectorOp a{&sortedPlacedComponents}; + message(a); + + std::vector didntExistPreviously = components.server_insert_ordered_vector(sortedPlacedComponents); + if(sortedPlacedComponents.size() >= DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD) { + parallel_loop_container(sortedPlacedComponents, [&](const auto& c){ + c->obj->final_update(*this, false); + }); + force_rebuild_cache(); + } + else { + for(size_t i = 0; i < sortedPlacedComponents.size(); i++) { + if(didntExistPreviously[i]) + sortedPlacedComponents[i]->obj->final_update(*this); + } + } + }); + world.con.client_add_recv_callback(CLIENT_ERASE_SINGLE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) { + ServerClientID idToErase; + message(idToErase); + components.server_erase(idToErase); + }); + world.con.client_add_recv_callback(CLIENT_ERASE_MANY_COMPONENTS, [&](cereal::PortableBinaryInputArchive& message) { + std::unordered_set idsToErase; + message(idsToErase); + components.server_erase_set(idsToErase); }); } -void DrawingProgram::check_delayed_update_transform_timers() { - std::erase_if(delayedUpdateTransformComponents, [&](auto& comp) { +void DrawingProgram::check_delayed_update_timers() { + std::erase_if(delayedUpdateComponents, [&](auto& comp) { comp->check_timers(*this); - return !comp->delayedUpdatePtr && !comp->delayedCoordinateSpace; + return !comp->delayedUpdatePtr; }); } @@ -352,11 +363,26 @@ void DrawingProgram::update() { if(controls.leftClickReleased) controls.leftClickReleased = false; - check_delayed_update_transform_timers(); + check_delayed_update_timers(); check_updateable_components(); - compCache.update(); selection.update(); + + if(controls.selectedTool == TOOL_ERASER) + compCache.test_rebuild_dont_include_set_dont_include_nodes(components.client_list(), eraserTool.erasedComponents, eraserTool.erasedBVHNodes); + else if(controls.selectedTool == TOOL_RECTSELECT) + compCache.test_rebuild_dont_include_set(components.client_list(), selection.get_selected_set()); + else + compCache.test_rebuild(components.client_list()); +} + +void DrawingProgram::force_rebuild_cache() { + if(controls.selectedTool == TOOL_ERASER) + compCache.test_rebuild_dont_include_set_dont_include_nodes(components.client_list(), eraserTool.erasedComponents, eraserTool.erasedBVHNodes, true); + else if(controls.selectedTool == TOOL_RECTSELECT) + compCache.test_rebuild_dont_include_set(components.client_list(), selection.get_selected_set(), true); + else + compCache.test_rebuild(components.client_list(), true); } void DrawingProgram::drag_drop_update() { @@ -448,7 +474,7 @@ void DrawingProgram::initialize_draw_data(cereal::PortableBinaryInputArchive& a) parallel_loop_all_components([&](const auto& c){ c->obj->final_update(*this, false); }); - compCache.force_rebuild(components.client_list()); + compCache.test_rebuild(components.client_list(), true); } void DrawingProgram::add_undo_place_component(uint64_t placement, const std::shared_ptr& comp) { @@ -476,15 +502,17 @@ void DrawingProgram::add_undo_place_components(uint64_t placement, const std::ve world.undo.push(UndoManager::UndoRedoPair{ [&, comps]() { bool toRet = true; + std::unordered_set idsToErase; for(auto& c : comps) { if(!c->collabListInfo.lock()) toRet = false; else { + idsToErase.emplace(c->collabListInfo.lock()->id); ServerClientID compID; - c->client_send_erase(*this); components.client_erase(c, compID); } } + DrawComponent::client_send_erase_set(*this, idsToErase); reset_tools(); return toRet; }, diff --git a/src/DrawingProgram/DrawingProgram.hpp b/src/DrawingProgram/DrawingProgram.hpp index 3b8df69..da6277b 100644 --- a/src/DrawingProgram/DrawingProgram.hpp +++ b/src/DrawingProgram/DrawingProgram.hpp @@ -55,14 +55,16 @@ class DrawingProgram { CollabListType components; std::unordered_set> updateableComponents; - std::unordered_set> delayedUpdateTransformComponents; + std::unordered_set> delayedUpdateComponents; Vector4f* get_foreground_color_ptr(); private: - void check_delayed_update_transform_timers(); + void check_delayed_update_timers(); void check_updateable_components(); + void force_rebuild_cache(); + std::atomic addFileInNextFrame = false; std::pair addFileInfo; diff --git a/src/DrawingProgram/DrawingProgramCache.cpp b/src/DrawingProgram/DrawingProgramCache.cpp index da0507a..468e0ba 100644 --- a/src/DrawingProgram/DrawingProgramCache.cpp +++ b/src/DrawingProgram/DrawingProgramCache.cpp @@ -49,9 +49,45 @@ void DrawingProgramCache::clear_own_cached_surfaces() { }); } -void DrawingProgramCache::update() { - if(!disableRefresh && unsortedComponents.size() >= 1000 && (std::chrono::steady_clock::now() - lastBvhBuildTime) >= std::chrono::seconds(5)) - force_rebuild(drawP.components.client_list()); +bool DrawingProgramCache::check_if_rebuild_should_occur() { + return (unsortedComponents.size() >= MINIMUM_COMPONENTS_TO_START_REBUILD && (std::chrono::steady_clock::now() - lastBvhBuildTime) >= std::chrono::seconds(5)); +} + +void DrawingProgramCache::test_rebuild(const std::vector& comps, bool force) { + if(force || check_if_rebuild_should_occur()) + force_rebuild(comps); +} + +void DrawingProgramCache::test_rebuild_dont_include_set(const std::vector& comps, const std::unordered_set& objsToNotInclude, bool force) { + if(force || check_if_rebuild_should_occur()) + force_rebuild_dont_include_objs(comps, objsToNotInclude); +} + +void DrawingProgramCache::test_rebuild_dont_include_set_dont_include_nodes(const std::vector& comps, const std::unordered_set& objsToNotInclude, const std::unordered_set>& nodesToNotInclude, bool force) { + if(force || check_if_rebuild_should_occur()) { + auto objsToNotIncludeNew = objsToNotInclude; + move_components_from_bvh_nodes_to_set(objsToNotIncludeNew, nodesToNotInclude); + force_rebuild_dont_include_objs(comps, objsToNotIncludeNew); + } +} + +void DrawingProgramCache::move_components_from_bvh_nodes_to_set(std::unordered_set& s, const std::unordered_set>& bvhNodes) { + for(auto& b : bvhNodes) + move_components_from_bvh_node_to_set(s, b); +} + +void DrawingProgramCache::move_components_from_bvh_node_to_set(std::unordered_set& s, const std::shared_ptr& bvhNode) { + for(auto& c : bvhNode->components) + s.emplace(c); + for(auto& p : bvhNode->children) + move_components_from_bvh_node_to_set(s, p); +} + +void DrawingProgramCache::force_rebuild_dont_include_objs(std::vector componentsToBuild, const std::unordered_set& objsToNotInclude) { + std::erase_if(componentsToBuild, [&objsToNotInclude](auto& c) { + return objsToNotInclude.contains(c); + }); + build(componentsToBuild); } void DrawingProgramCache::force_rebuild(const std::vector& componentsToBuild) { @@ -151,7 +187,7 @@ void DrawingProgramCache::build_bvh_node(const std::shared_ptrcomponents = components; for(auto& c : bvhNode->components) c->obj->parentBvhNode = bvhNode; diff --git a/src/DrawingProgram/DrawingProgramCache.hpp b/src/DrawingProgram/DrawingProgramCache.hpp index bf9e23d..bf05794 100644 --- a/src/DrawingProgram/DrawingProgramCache.hpp +++ b/src/DrawingProgram/DrawingProgramCache.hpp @@ -33,16 +33,24 @@ class DrawingProgramCache { DrawingProgramCache(DrawingProgram& initDrawP); ~DrawingProgramCache(); - static std::unordered_set> nodesWithCachedSurfaces; - bool disableRefresh = false; + static constexpr size_t MINIMUM_COMPONENTS_TO_START_REBUILD = 1000; + static constexpr size_t MAXIMUM_COMPONENTS_IN_SINGLE_NODE = 100; + + static std::unordered_set> nodesWithCachedSurfaces; + + bool check_if_rebuild_should_occur(); + void test_rebuild(const std::vector& comps, bool force = false); + void test_rebuild_dont_include_set(const std::vector& comps, const std::unordered_set& objsToNotInclude, bool force = false); + void test_rebuild_dont_include_set_dont_include_nodes(const std::vector& comps, const std::unordered_set& objsToNotInclude, const std::unordered_set>& nodesToNotInclude, bool force = false); + + static void move_components_from_bvh_nodes_to_set(std::unordered_set& s, const std::unordered_set>& bvhNodes); + static void move_components_from_bvh_node_to_set(std::unordered_set& s, const std::shared_ptr& bvhNode); - void update(); void erase_component(const CollabListType::ObjectInfoPtr& c); void add_component(const CollabListType::ObjectInfoPtr& c); void refresh_all_draw_cache(const DrawData& drawData); void traverse_bvh_run_function(const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f); void traverse_bvh_erase_function(const SCollision::AABB& aabb, std::function& node, std::vector& components)> f); - void force_rebuild(const std::vector& componentsToBuild); // Run before actually updating the component void preupdate_component(const CollabListType::ObjectInfoPtr& c); void invalidate_cache_before_pos(uint64_t placementToInvalidateAt); @@ -53,6 +61,9 @@ class DrawingProgramCache { const std::chrono::steady_clock::time_point& get_last_bvh_build_time() const; void clear_own_cached_surfaces(); private: + void force_rebuild_dont_include_objs(std::vector componentsToBuild, const std::unordered_set& objsToNotInclude); + void force_rebuild(const std::vector& componentsToBuild); + bool traverse_bvh_erase_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, std::vector& components)> f); void build_bvh_node_coords_and_resolution(DrawingProgramCacheBVHNode& node); void refresh_draw_cache(const std::shared_ptr& bvhNode, const DrawData& drawData); diff --git a/src/DrawingProgram/DrawingProgramSelection.cpp b/src/DrawingProgram/DrawingProgramSelection.cpp index 5563cd2..88c6a87 100644 --- a/src/DrawingProgram/DrawingProgramSelection.cpp +++ b/src/DrawingProgram/DrawingProgramSelection.cpp @@ -14,6 +14,10 @@ DrawingProgramSelection::DrawingProgramSelection(DrawingProgram& initDrawP): drawP(initDrawP) {} +const std::unordered_set& DrawingProgramSelection::get_selected_set() { + return selectedSet; +} + void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC) { auto cCWorld = drawP.world.drawData.cam.c.collider_to_world, SCollision::ColliderCollection>(cC); @@ -73,19 +77,26 @@ void DrawingProgramSelection::deselect_all() { parallel_loop_container(a, [&](auto& obj) { obj->obj->coords = selectionTransformCoords.other_coord_space_from_this_space(obj->obj->coords); obj->obj->final_update(drawP, false); + obj->obj->client_send_transform(drawP); }); selectedSet.clear(); cache.clear(); selectionTransformCoords = CoordSpaceHelper(); - drawP.compCache.force_rebuild(drawP.components.client_list()); + drawP.force_rebuild_cache(); transformOpHappening = TransformOperation::NONE; } } +bool DrawingProgramSelection::is_selected(const CollabListType::ObjectInfoPtr& objToCheck) { + return selectedSet.contains(objToCheck); +} + void DrawingProgramSelection::update() { if(is_something_selected()) { - if(cache.get_unsorted_component_list().size() >= 1000 && (std::chrono::steady_clock::now() - cache.get_last_bvh_build_time()) >= std::chrono::seconds(5)) - cache.force_rebuild(std::vector(selectedSet.begin(), selectedSet.end())); + + if(cache.check_if_rebuild_should_occur()) + cache.test_rebuild(std::vector(selectedSet.begin(), selectedSet.end()), true); + if(drawP.world.main.input.key(InputManager::KEY_DRAW_UNSELECT).pressed) deselect_all(); diff --git a/src/DrawingProgram/DrawingProgramSelection.hpp b/src/DrawingProgram/DrawingProgramSelection.hpp index 52ac1ec..288c7d0 100644 --- a/src/DrawingProgram/DrawingProgramSelection.hpp +++ b/src/DrawingProgram/DrawingProgramSelection.hpp @@ -13,6 +13,8 @@ class DrawingProgramSelection { void draw_gui(SkCanvas* canvas, const DrawData& drawData); bool is_something_selected(); bool mouse_collided_with_selection(); + bool is_selected(const CollabListType::ObjectInfoPtr& objToCheck); + const std::unordered_set& get_selected_set(); private: bool mouse_collided_with_selection_aabb(); bool mouse_collided_with_scale_point(); diff --git a/src/DrawingProgram/EraserTool.cpp b/src/DrawingProgram/EraserTool.cpp index 23d1b94..f2cde6b 100644 --- a/src/DrawingProgram/EraserTool.cpp +++ b/src/DrawingProgram/EraserTool.cpp @@ -22,16 +22,17 @@ void EraserTool::gui_toolbox() { void EraserTool::reset_tool() { if(!erasedComponents.empty()) { - for(auto& bvhNode : erasedBVHNodes) - move_erased_components_from_bvh_nodes_recursive(bvhNode); + DrawingProgramCache::move_components_from_bvh_nodes_to_set(erasedComponents, erasedBVHNodes); + std::unordered_set idsToErase; for(auto& c : erasedComponents) - c->obj->client_send_erase(drawP); + idsToErase.emplace(c->id); + DrawComponent::client_send_erase_set(drawP, idsToErase); drawP.components.client_erase_set(erasedComponents); drawP.world.undo.push(UndoManager::UndoRedoPair{ - [&, erasedCompVal = erasedComponents]() { - std::vector sortedObjects(erasedCompVal.begin(), erasedCompVal.end()); + [&, erasedComponents = erasedComponents]() { + std::vector sortedObjects(erasedComponents.begin(), erasedComponents.end()); std::sort(sortedObjects.begin(), sortedObjects.end(), [](auto& a, auto& b) { return a->pos < b->pos; }); @@ -41,27 +42,27 @@ void EraserTool::reset_tool() { return false; drawP.components.client_insert_ordered_vector(sortedObjects); - - for(auto& comp : sortedObjects) - comp->obj->client_send_place(drawP); + DrawComponent::client_send_place_many(drawP, sortedObjects); drawP.reset_tools(); return true; }, - [&, erasedCompVal = erasedComponents]() { - for(auto& comp : erasedCompVal) + [&, erasedComponents = erasedComponents]() { + for(auto& comp : erasedComponents) if(!comp->obj->collabListInfo.lock()) return false; - for(auto& comp : erasedCompVal) - comp->obj->client_send_erase(drawP); + std::unordered_set idsToErase; + for(auto& c : erasedComponents) + idsToErase.emplace(c->id); - drawP.components.client_erase_set(erasedCompVal); + DrawComponent::client_send_erase_set(drawP, idsToErase); + drawP.components.client_erase_set(erasedComponents); drawP.reset_tools(); - drawP.compCache.force_rebuild(drawP.components.client_list()); + drawP.force_rebuild_cache(); return true; } @@ -69,7 +70,6 @@ void EraserTool::reset_tool() { } erasedComponents.clear(); erasedBVHNodes.clear(); - drawP.compCache.disableRefresh = false; } void EraserTool::tool_update() { @@ -82,8 +82,6 @@ void EraserTool::tool_update() { SCollision::generate_wide_line(cC, prevMousePos, drawP.world.main.input.mouse.pos, drawP.controls.relativeWidth * 2.0f, true); auto cCWorld = drawP.world.drawData.cam.c.collider_to_world, SCollision::ColliderCollection>(cC); - drawP.compCache.disableRefresh = true; - drawP.compCache.traverse_bvh_erase_function(cCWorld.bounds, [&](const auto& bvhNode, auto& comps) { if(bvhNode && SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min)) && @@ -109,13 +107,6 @@ void EraserTool::tool_update() { reset_tool(); } -void EraserTool::move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr& bvhNode) { - for(auto& c : bvhNode->components) - erasedComponents.emplace(c); - for(auto& p : bvhNode->children) - move_erased_components_from_bvh_nodes_recursive(p); -} - void EraserTool::draw(SkCanvas* canvas, const DrawData& drawData) { if(!drawData.main->toolbar.io->hoverObstructed) { SkColor4f c = drawP.world.main.canvasTheme.toolFrontColor; diff --git a/src/DrawingProgram/EraserTool.hpp b/src/DrawingProgram/EraserTool.hpp index 7f6bc10..e5c5c39 100644 --- a/src/DrawingProgram/EraserTool.hpp +++ b/src/DrawingProgram/EraserTool.hpp @@ -12,11 +12,10 @@ class EraserTool { void tool_update(); void reset_tool(); void draw(SkCanvas* canvas, const DrawData& drawData); - private: + std::unordered_set erasedComponents; std::unordered_set> erasedBVHNodes; - - void move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr& bvhNode); + private: DrawingProgram& drawP; }; diff --git a/src/DrawingProgram/RectSelectTool.cpp b/src/DrawingProgram/RectSelectTool.cpp index 6304e70..72628e3 100644 --- a/src/DrawingProgram/RectSelectTool.cpp +++ b/src/DrawingProgram/RectSelectTool.cpp @@ -35,13 +35,11 @@ void RectSelectTool::gui_toolbox() { void RectSelectTool::reset_tool() { drawP.selection.deselect_all(); controls = RectSelectControls(); - drawP.compCache.disableRefresh = false; } void RectSelectTool::tool_update() { switch(controls.selectionMode) { case 0: { - drawP.compCache.disableRefresh = false; if(drawP.controls.leftClick) { if(drawP.selection.is_something_selected()) { if(!drawP.selection.mouse_collided_with_selection()) diff --git a/src/Server/CommandList.hpp b/src/Server/CommandList.hpp index 847785a..76239bf 100644 --- a/src/Server/CommandList.hpp +++ b/src/Server/CommandList.hpp @@ -7,9 +7,11 @@ enum ServerCommands : MessageCommandType { SERVER_KEEP_ALIVE, SERVER_INITIAL_DATA, SERVER_MOVE_MOUSE, - SERVER_PLACE_COMPONENT, + SERVER_PLACE_SINGLE_COMPONENT, + SERVER_PLACE_MANY_COMPONENTS, SERVER_TRANSFORM_COMPONENT, - SERVER_ERASE_COMPONENT, + SERVER_ERASE_SINGLE_COMPONENT, + SERVER_ERASE_MANY_COMPONENTS, SERVER_UPDATE_COMPONENT, SERVER_RESOURCE_INIT, SERVER_RESOURCE_DATA, @@ -24,9 +26,11 @@ enum ClientCommands : MessageCommandType { CLIENT_INITIAL_DATA, CLIENT_USER_CONNECT, CLIENT_USER_DISCONNECT, - CLIENT_PLACE_COMPONENT, + CLIENT_PLACE_SINGLE_COMPONENT, + CLIENT_PLACE_MANY_COMPONENTS, CLIENT_TRANSFORM_COMPONENT, - CLIENT_ERASE_COMPONENT, + CLIENT_ERASE_SINGLE_COMPONENT, + CLIENT_ERASE_MANY_COMPONENTS, CLIENT_UPDATE_COMPONENT, CLIENT_MOVE_MOUSE, CLIENT_NEW_RESOURCE, @@ -35,5 +39,6 @@ enum ClientCommands : MessageCommandType { CLIENT_REMOVE_BOOKMARK }; +// NOTE: Change these values whenever savefile is incompatible with previous version #define SAVEFILE_HEADER_LEN 12 #define SAVEFILE_HEADER "INFPNT000001" diff --git a/src/Server/MainServer.cpp b/src/Server/MainServer.cpp index d2acc83..81e757c 100644 --- a/src/Server/MainServer.cpp +++ b/src/Server/MainServer.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ void ServerData::load(cereal::PortableBinaryInputArchive& a) { auto newComp = DrawComponent::allocate_comp_type(t); a(newComp->coords, *newComp); components.emplace_back(id, newComp); + idToComponentMap.emplace(id, newComp); } a(bookmarks); } @@ -60,8 +62,10 @@ ClientPortionID ServerData::get_max_id(ServerPortionID serverID) const { MainServer::MainServer(World& initWorld, const std::string& serverLocalID): world(initWorld) { - for(auto& c : world.drawProg.components.client_list()) + for(auto& c : world.drawProg.components.client_list()) { data.components.emplace_back(c->id, c->obj->copy()); + data.idToComponentMap.emplace(data.components.back().first, data.components.back().second); + } data.bookmarks = world.bMan.bookmark_list(); data.resources = world.rMan.resource_list(); @@ -102,7 +106,7 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID): message(c.camCoords, c.windowSize, c.cursorPos); netServer->send_items_to_all_clients_except(client, UNRELIABLE_COMMAND_CHANNEL, CLIENT_MOVE_MOUSE, c.id.first, c.camCoords, c.windowSize, c.cursorPos); }); - netServer->add_recv_callback(SERVER_PLACE_COMPONENT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { + netServer->add_recv_callback(SERVER_PLACE_SINGLE_COMPONENT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { uint64_t placement; DrawComponentType type; message(placement, type); @@ -111,39 +115,93 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID): ServerClientID id = clients[client->customID].get_next_id(); placement = std::min(placement, data.components.size()); data.components.insert(data.components.begin() + placement, {id, newComp}); + data.idToComponentMap.emplace(id, newComp); newComp->server_send_place(*this, id, placement); }); - netServer->add_recv_callback(SERVER_ERASE_COMPONENT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { + netServer->add_recv_callback(SERVER_PLACE_MANY_COMPONENTS, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { + auto& cli = clients[client->customID]; + + std::vector objOrderedVector; + SendOrderedComponentVectorOp a{&objOrderedVector}; + message(a); + + std::vector>> objOrderedComponentPairs; + for(auto& o : objOrderedVector) { + o->id = cli.get_next_id(); + objOrderedComponentPairs.emplace_back(o->id, o->obj); + } + + uint64_t startPoint = 0; + + if(objOrderedVector.empty()) + return; + + data.idToComponentMap.emplace(objOrderedVector.front()->id, objOrderedVector.front()->obj); + + for(uint64_t i = 1; i < static_cast(objOrderedVector.size()); i++) { + auto& obj = objOrderedVector[i]; + data.idToComponentMap.emplace(obj->id, obj->obj); + if(obj->pos - objOrderedVector[startPoint]->pos != (i - startPoint)) { + uint64_t insertPosition = std::min(data.components.size(), objOrderedVector[startPoint]->pos); + data.components.insert(data.components.begin() + insertPosition, objOrderedComponentPairs.begin() + startPoint, objOrderedComponentPairs.begin() + i); + startPoint = i; + } + } + + data.components.insert(data.components.begin() + std::min(data.components.size(), objOrderedVector[startPoint]->pos), objOrderedComponentPairs.begin() + startPoint, objOrderedComponentPairs.end()); + + startPoint = 0; + + for(uint64_t i = 0; i < data.components.size(); i++) { + if(startPoint >= objOrderedVector.size()) + break; + auto& [id, c] = data.components[i]; + auto& o = objOrderedVector[startPoint]; + if(c == o->obj) { + o->id = id; + o->pos = i; + startPoint++; + } + } + + DrawComponent::server_send_place_many(*this, objOrderedVector); + }); + netServer->add_recv_callback(SERVER_ERASE_SINGLE_COMPONENT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { ServerClientID compToRemove; message(compToRemove); - std::erase_if(data.components, [&](auto& c){ return c.first == compToRemove; }); DrawComponent::server_send_erase(*this, compToRemove); + data.idToComponentMap.erase(compToRemove); + std::erase_if(data.components, [&](auto& p) { + return p.first == compToRemove; + }); + }); + netServer->add_recv_callback(SERVER_ERASE_MANY_COMPONENTS, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { + std::unordered_set idsToRemove; + message(idsToRemove); + DrawComponent::server_send_erase_set(*this, idsToRemove); + for(auto& id : idsToRemove) + data.idToComponentMap.erase(id); + std::erase_if(data.components, [&](auto& p) { + return idsToRemove.contains(p.first); + }); }); netServer->add_recv_callback(SERVER_TRANSFORM_COMPONENT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { - bool isTemp; ServerClientID idToTransform; - message(isTemp, idToTransform); - auto a = std::find_if(data.components.begin(), data.components.end(), [&](auto& c) { - return (c.first == idToTransform); - }); - if(a != data.components.end()) { - std::shared_ptr& comp = (*a).second; + message(idToTransform); + auto it = data.idToComponentMap.find(idToTransform); + if(it != data.idToComponentMap.end()) { + std::shared_ptr& comp = it->second; message(comp->coords); - if(isTemp) - comp->server_send_transform_temp(*this, idToTransform); - else - comp->server_send_transform_final(*this, idToTransform); + comp->server_send_transform(*this, idToTransform); } }); netServer->add_recv_callback(SERVER_UPDATE_COMPONENT, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { bool isTemp; ServerClientID idToTransform; message(isTemp, idToTransform); - auto a = std::find_if(data.components.begin(), data.components.end(), [&](auto& c) { - return (c.first == idToTransform); - }); - if(a != data.components.end()) { - std::shared_ptr& comp = (*a).second; + auto it = data.idToComponentMap.find(idToTransform); + if(it != data.idToComponentMap.end()) { + std::shared_ptr& comp = it->second; message(*comp); if(isTemp) comp->server_send_update_temp(*this, idToTransform); diff --git a/src/Server/ServerData.hpp b/src/Server/ServerData.hpp index 37dc39c..dae96b7 100644 --- a/src/Server/ServerData.hpp +++ b/src/Server/ServerData.hpp @@ -9,6 +9,7 @@ class DrawComponent; class ServerData { public: std::vector>> components; + std::unordered_map> idToComponentMap; std::unordered_map resources; std::unordered_map bookmarks; void save(cereal::PortableBinaryOutputArchive& a) const;