Improved online performance for canvases with many components

This commit is contained in:
Yousef Khadadeh 2025-07-26 21:20:14 +03:00
parent 8a36bc4b6d
commit b6a25280e6
16 changed files with 406 additions and 183 deletions

View file

@ -13,7 +13,7 @@ template <class T> std::weak_ptr<T> make_weak_ptr(std::shared_ptr<T> 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;

View file

@ -23,7 +23,6 @@ template <typename T, typename IDType> class CollabList {
std::function<void()> updateCallback;
std::function<void(const ObjectInfoPtr& c)> clientInsertCallback;
std::function<void(const ObjectInfoPtr& c)> clientEraseCallback;
std::function<void(const std::unordered_set<ObjectInfoPtr>& c)> clientEraseSetCallback;
std::function<void(const std::vector<ObjectInfoPtr>& c)> clientInsertOrderedVectorCallback;
std::function<void(uint64_t oldPos)> clientServerLastPosShiftCallback;
@ -33,27 +32,9 @@ template <typename T, typename IDType> 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<bool(uint64_t, const std::shared_ptr<ObjectInfo>&)>& func) {
@ -63,6 +44,7 @@ template <typename T, typename IDType> 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 <typename T, typename IDType> 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 <typename T, typename IDType> 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 <typename T, typename IDType> 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 <typename T, typename IDType> 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 <typename T, typename IDType> 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<uint64_t>(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 <typename T, typename IDType> class CollabList {
clientInsertOrderedVectorCallback(objOrderedVector);
}
std::vector<bool> server_insert_ordered_vector(std::vector<ObjectInfoPtr> objOrderedVector) {
if(objOrderedVector.empty())
return std::vector<bool>();
std::vector<bool> 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<uint64_t>(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 <typename T, typename IDType> class CollabList {
newObj->id = id;
newObj->obj = item;
newObj->pos = std::min<uint64_t>(pos, clientSideList.size());
idToObjectMap.emplace(newObj->id, newObj);
item->collabListInfo = newObj;
serverSideList.insert(serverSideList.begin() + pos, newObj);
@ -207,6 +238,7 @@ template <typename T, typename IDType> 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 <typename T, typename IDType> class CollabList {
});
sync();
}
void server_erase_set(const std::unordered_set<ServerClientID>& 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<ObjectInfo>());
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 <typename T, typename IDType> class CollabList {
updateCallback();
complete_client_pos_refresh(true);
}
std::function<IDType()> getNewIDFunc;
std::vector<ObjectInfoPtr> clientSideList;
std::vector<ObjectInfoPtr> serverSideList;
std::unordered_map<IDType, ObjectInfoPtr> idToObjectMap;
};

View file

@ -8,11 +8,38 @@
#include "../SharedTypes.hpp"
#include "../Server/CommandList.hpp"
#include <chrono>
#include <cereal/types/unordered_set.hpp>
#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<CollabListType::ObjectInfo>()));
}
std::shared_ptr<DrawComponent> DrawComponent::allocate_comp_type(DrawComponentType type) {
switch(type) {
case DRAWCOMPONENT_BRUSHSTROKE:
@ -29,12 +56,20 @@ std::shared_ptr<DrawComponent> DrawComponent::allocate_comp_type(DrawComponentTy
return nullptr;
}
void DrawComponent::server_send_place_many(MainServer& server, std::vector<CollabListType::ObjectInfoPtr>& 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<ServerClientID>& 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::duration<float>>(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<ServerClientID>& 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::duration<float>>(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::duration<float>>(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<CollabListType::ObjectInfoPtr>& 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<ServerClientID>& 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) {

View file

@ -28,6 +28,15 @@ class DrawingProgramCacheBVHNode;
typedef CollabList<std::shared_ptr<DrawComponent>, ServerClientID> CollabListType;
void save(cereal::PortableBinaryOutputArchive& a, const CollabListType::ObjectInfo& o);
void load(cereal::PortableBinaryInputArchive& a, CollabListType::ObjectInfo& o);
struct SendOrderedComponentVectorOp {
std::vector<CollabListType::ObjectInfoPtr>* v;
void save(cereal::PortableBinaryOutputArchive& a) const;
void load(cereal::PortableBinaryInputArchive& a);
};
class DrawComponent {
public:
static std::shared_ptr<DrawComponent> 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<CollabListType::ObjectInfoPtr>& comps);
static void server_send_erase(MainServer& server, ServerClientID id);
static void server_send_erase_set(MainServer& server, const std::unordered_set<ServerClientID>& 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<std::chrono::steady_clock> tempServerUpdateTimer;
bool serverIsTempUpdate = false;
std::chrono::time_point<std::chrono::steady_clock> tempServerTransformTimer;
bool serverIsTempTransform = false;
std::weak_ptr<CollabListType::ObjectInfo> collabListInfo;
std::weak_ptr<DrawingProgramCacheBVHNode> parentBvhNode;
@ -77,17 +84,17 @@ class DrawComponent {
std::chrono::time_point<std::chrono::steady_clock> lastUpdateTime;
std::shared_ptr<DrawComponent> delayedUpdatePtr = nullptr;
std::shared_ptr<CoordSpaceHelper> 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<CollabListType::ObjectInfoPtr>& 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<ServerClientID>& 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<SCollision::AABB<WorldScalar>> worldAABB;

View file

@ -23,6 +23,7 @@
#include "../MainProgram.hpp"
#include <Helpers/Logger.hpp>
#include <Helpers/Parallel.hpp>
#include <cereal/types/unordered_set.hpp>
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<CollabListType::ObjectInfoPtr>& comps) {
for(auto& c : comps) {
delayedUpdateTransformComponents.erase(c->obj);
updateableComponents.erase(c->obj);
}
};
components.clientInsertOrderedVectorCallback = [&](const std::vector<CollabListType::ObjectInfoPtr>& 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<DrawComponent>* compPtr = components.get_item_by_id(id);
if(!compPtr)
auto objPtr = components.get_item_by_id(id);
if(!objPtr)
return;
std::shared_ptr<DrawComponent>& comp = *compPtr;
std::shared_ptr<DrawComponent>& comp = objPtr->obj;
float dur = std::chrono::duration_cast<std::chrono::duration<float>>(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<DrawComponent>* 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<DrawComponent>& comp = *compPtr;
float dur = std::chrono::duration_cast<std::chrono::duration<float>>(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<CoordSpaceHelper>();
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<DrawComponent>& 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<CollabListType::ObjectInfoPtr> sortedPlacedComponents;
SendOrderedComponentVectorOp a{&sortedPlacedComponents};
message(a);
std::vector<bool> 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<ServerClientID> 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<DrawComponent>& 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<ServerClientID> 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;
},

View file

@ -55,14 +55,16 @@ class DrawingProgram {
CollabListType components;
std::unordered_set<std::shared_ptr<DrawComponent>> updateableComponents;
std::unordered_set<std::shared_ptr<DrawComponent>> delayedUpdateTransformComponents;
std::unordered_set<std::shared_ptr<DrawComponent>> 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<bool> addFileInNextFrame = false;
std::pair<std::string, Vector2f> addFileInfo;

View file

@ -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<CollabListType::ObjectInfoPtr>& comps, bool force) {
if(force || check_if_rebuild_should_occur())
force_rebuild(comps);
}
void DrawingProgramCache::test_rebuild_dont_include_set(const std::vector<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& 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<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& 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<CollabListType::ObjectInfoPtr>& s, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& 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<CollabListType::ObjectInfoPtr>& s, const std::shared_ptr<DrawingProgramCacheBVHNode>& 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<CollabListType::ObjectInfoPtr> componentsToBuild, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude) {
std::erase_if(componentsToBuild, [&objsToNotInclude](auto& c) {
return objsToNotInclude.contains(c);
});
build(componentsToBuild);
}
void DrawingProgramCache::force_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& componentsToBuild) {
@ -151,7 +187,7 @@ void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCac
build_bvh_node_coords_and_resolution(*bvhNode);
if(components.size() < 100) {
if(components.size() < MAXIMUM_COMPONENTS_IN_SINGLE_NODE) {
bvhNode->components = components;
for(auto& c : bvhNode->components)
c->obj->parentBvhNode = bvhNode;

View file

@ -33,16 +33,24 @@ class DrawingProgramCache {
DrawingProgramCache(DrawingProgram& initDrawP);
~DrawingProgramCache();
static std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> 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<std::shared_ptr<DrawingProgramCacheBVHNode>> nodesWithCachedSurfaces;
bool check_if_rebuild_should_occur();
void test_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& comps, bool force = false);
void test_rebuild_dont_include_set(const std::vector<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, bool force = false);
void test_rebuild_dont_include_set_dont_include_nodes(const std::vector<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToNotInclude, bool force = false);
static void move_components_from_bvh_nodes_to_set(std::unordered_set<CollabListType::ObjectInfoPtr>& s, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& bvhNodes);
static void move_components_from_bvh_node_to_set(std::unordered_set<CollabListType::ObjectInfoPtr>& s, const std::shared_ptr<DrawingProgramCacheBVHNode>& 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<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& components)> f);
void traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& components)> f);
void force_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& 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<CollabListType::ObjectInfoPtr> componentsToBuild, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude);
void force_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& componentsToBuild);
bool traverse_bvh_erase_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& components)> f);
void build_bvh_node_coords_and_resolution(DrawingProgramCacheBVHNode& node);
void refresh_draw_cache(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const DrawData& drawData);

View file

@ -14,6 +14,10 @@ DrawingProgramSelection::DrawingProgramSelection(DrawingProgram& initDrawP):
drawP(initDrawP)
{}
const std::unordered_set<CollabListType::ObjectInfoPtr>& DrawingProgramSelection::get_selected_set() {
return selectedSet;
}
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(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<CollabListType::ObjectInfoPtr>(selectedSet.begin(), selectedSet.end()));
if(cache.check_if_rebuild_should_occur())
cache.test_rebuild(std::vector<CollabListType::ObjectInfoPtr>(selectedSet.begin(), selectedSet.end()), true);
if(drawP.world.main.input.key(InputManager::KEY_DRAW_UNSELECT).pressed)
deselect_all();

View file

@ -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<CollabListType::ObjectInfoPtr>& get_selected_set();
private:
bool mouse_collided_with_selection_aabb();
bool mouse_collided_with_scale_point();

View file

@ -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<ServerClientID> 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<CollabListType::ObjectInfoPtr> sortedObjects(erasedCompVal.begin(), erasedCompVal.end());
[&, erasedComponents = erasedComponents]() {
std::vector<CollabListType::ObjectInfoPtr> 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<ServerClientID> 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<WorldScalar>, SCollision::ColliderCollection<float>>(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<DrawingProgramCacheBVHNode>& 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;

View file

@ -12,11 +12,10 @@ class EraserTool {
void tool_update();
void reset_tool();
void draw(SkCanvas* canvas, const DrawData& drawData);
private:
std::unordered_set<CollabListType::ObjectInfoPtr> erasedComponents;
std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> erasedBVHNodes;
void move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
private:
DrawingProgram& drawP;
};

View file

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

View file

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

View file

@ -13,6 +13,7 @@
#include <cereal/types/unordered_map.hpp>
#include <cereal/types/utility.hpp>
#include <cereal/types/string.hpp>
#include <cereal/types/unordered_set.hpp>
#include <Helpers/Serializers.hpp>
#include <Helpers/Random.hpp>
#include <chrono>
@ -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<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
netServer->add_recv_callback(SERVER_PLACE_SINGLE_COMPONENT, [&](std::shared_ptr<NetServer::ClientData> 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<uint64_t>(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<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
netServer->add_recv_callback(SERVER_PLACE_MANY_COMPONENTS, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
auto& cli = clients[client->customID];
std::vector<CollabListType::ObjectInfoPtr> objOrderedVector;
SendOrderedComponentVectorOp a{&objOrderedVector};
message(a);
std::vector<std::pair<ServerClientID, std::shared_ptr<DrawComponent>>> 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<uint64_t>(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<NetServer::ClientData> 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<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
std::unordered_set<ServerClientID> 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<NetServer::ClientData> 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<DrawComponent>& comp = (*a).second;
message(idToTransform);
auto it = data.idToComponentMap.find(idToTransform);
if(it != data.idToComponentMap.end()) {
std::shared_ptr<DrawComponent>& 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<NetServer::ClientData> 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<DrawComponent>& comp = (*a).second;
auto it = data.idToComponentMap.find(idToTransform);
if(it != data.idToComponentMap.end()) {
std::shared_ptr<DrawComponent>& comp = it->second;
message(*comp);
if(isTemp)
comp->server_send_update_temp(*this, idToTransform);

View file

@ -9,6 +9,7 @@ class DrawComponent;
class ServerData {
public:
std::vector<std::pair<ServerClientID, std::shared_ptr<DrawComponent>>> components;
std::unordered_map<ServerClientID, std::shared_ptr<DrawComponent>> idToComponentMap;
std::unordered_map<ServerClientID, ResourceData> resources;
std::unordered_map<std::string, Bookmark> bookmarks;
void save(cereal::PortableBinaryOutputArchive& a) const;