infinipaint/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp
2026-01-01 08:34:49 +03:00

694 lines
39 KiB
C++

#pragma once
#include "Helpers/Networking/NetLibrary.hpp"
#include "NetObjOwnerPtr.hpp"
#include "NetObjTemporaryPtr.hpp"
#include "NetObjWeakPtr.hpp"
#include "NetObjID.hpp"
#include <cereal/archives/portable_binary.hpp>
#include <cereal/types/unordered_set.hpp>
#include <cereal/types/vector.hpp>
#include "NetObjManagerTypeList.hpp"
#include <limits>
#include <list>
namespace NetworkingObjects {
enum class ObjPtrOrderedListCommand_StoC : uint8_t {
INSERT_LIST_CONSTRUCT,
INSERT_LIST_REFERENCE,
ERASE_ID_LIST
};
enum class ObjPtrOrderedListCommand_CtoS : uint8_t {
INSERT_LIST,
ERASE_ID_LIST
};
template <typename T> struct NetObjOrderedListObjectInfo {
NetObjOrderedListObjectInfo(NetObjOwnerPtr<T> iObj, uint32_t iPos):
obj(std::move(iObj)),
pos(iPos)
{}
bool operator<(NetObjOrderedListObjectInfo& other) const {
return pos < other.pos;
}
NetObjOrderedListObjectInfo() = delete;
NetObjOwnerPtr<T> obj;
uint32_t pos;
};
template <typename T> using NetObjOrderedListIterator = std::list<NetObjOrderedListObjectInfo<T>>::iterator;
template <typename T> using NetObjOrderedListConstIterator = std::list<NetObjOrderedListObjectInfo<T>>::const_iterator;
template <typename T> void set_position_obj_info_list(NetObjOrderedListIterator<T> start, NetObjOrderedListIterator<T> end) {
if(start == end)
return;
uint32_t i = start->pos;
for(; start != end; ++start) {
start->pos = i;
++i;
}
}
template <typename T> class NetObjOrderedList {
public:
// Don't use this function unless you're sure that class T isn't a base class
template <typename ...Args> NetObjOrderedListIterator<T> emplace_back_direct(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, Args&&... items) {
std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>> newObjOwner;
newObjOwner.emplace_back(l->end(), std::move(l.get_obj_man()->template make_obj_direct<T>(items...)));
auto toRet = l->insert_ordered_list(l, nullptr, newObjOwner);
return toRet.empty() ? l->end() : toRet.back();
}
static NetObjOrderedListIterator<T> push_back_and_send_create(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, T* newObj) {
std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>> newObjOwner;
newObjOwner.emplace_back(l->end(), std::move(l.get_obj_man()->template make_obj_from_ptr<T>(newObj)));
auto toRet = l->insert_ordered_list(l, nullptr, newObjOwner);
return toRet.empty() ? l->end() : toRet.back();
}
static NetObjOrderedListIterator<T> insert_and_send_create(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const NetObjOrderedListIterator<T>& it, T* newObj) {
std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>> newObjOwner;
newObjOwner.emplace_back(it, std::move(l.get_obj_man()->template make_obj_from_ptr<T>(newObj)));
auto toRet = l->insert_ordered_list(l, nullptr, newObjOwner);
return toRet.empty() ? l->end() : toRet.back();
}
static std::vector<NetObjOrderedListIterator<T>> insert_ordered_list_and_send_create(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::vector<std::pair<NetObjOrderedListIterator<T>, T*>>& newObjs) {
if(newObjs.empty())
return {};
std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>> ownerPtrOrderedList;
for(uint32_t i = 0; i < static_cast<uint32_t>(newObjs.size()); i++)
ownerPtrOrderedList.emplace_back(newObjs[i].first, std::move(l.get_obj_man()->template make_obj_from_ptr<T>(newObjs[i].second)));
return l->insert_ordered_list(l, nullptr, ownerPtrOrderedList);
}
static std::vector<NetObjOrderedListIterator<T>> insert_ordered_list_and_send_create(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>>& newObjPtrs) {
if(newObjPtrs.empty())
return {};
return l->insert_ordered_list(l, nullptr, newObjPtrs);
}
static void erase_list(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::vector<NetObjOrderedListIterator<T>>& iterators, std::vector<NetObjOwnerPtr<T>>* erasedObjects = nullptr) {
l->erase_it_list(l, iterators, erasedObjects);
}
static void erase(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const NetObjOrderedListIterator<T>& it) {
l->erase_it_list(l, {it}, nullptr);
}
virtual void reassign_netobj_ids_call() = 0;
typedef std::function<void(const NetObjOrderedListIterator<T>& objInfo)> InsertCallback;
typedef std::function<void(const NetObjOrderedListIterator<T>& objInfo)> EraseCallback;
typedef std::function<void(const NetObjOrderedListIterator<T>& objInfo, uint32_t oldPos)> MoveCallback;
typedef NetObjOrderedListObjectInfo<T> value_type;
void set_insert_callback(const InsertCallback& func) {
insertCallback = func;
}
void set_erase_callback(const EraseCallback& func) {
eraseCallback = func;
}
void set_move_callback(const MoveCallback& func) {
moveCallback = func;
}
virtual bool contains(const NetObjID& p) const = 0;
virtual NetObjOrderedListIterator<T> begin() = 0;
virtual NetObjOrderedListIterator<T> end() = 0;
virtual NetObjOrderedListIterator<T> at(uint32_t index) = 0;
virtual NetObjOrderedListIterator<T> get(const NetObjID& id) = 0;
virtual NetObjOrderedListConstIterator<T> begin() const = 0;
virtual NetObjOrderedListConstIterator<T> end() const = 0;
virtual NetObjOrderedListConstIterator<T> at(uint32_t index) const = 0;
virtual NetObjOrderedListConstIterator<T> get(const NetObjID& id) const = 0;
virtual uint32_t size() const = 0;
virtual bool empty() const = 0;
virtual ~NetObjOrderedList() {}
protected:
virtual std::vector<NetObjOrderedListIterator<T>> insert_ordered_list(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>>& newObj) = 0;
virtual void erase_it_list(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::vector<NetObjOrderedListIterator<T>>& itList, std::vector<NetObjOwnerPtr<T>>* erasedObjects) = 0;
virtual void write_constructor(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) = 0;
virtual void read_update(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) = 0;
virtual void read_constructor(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) = 0;
// Call after inserting
void call_insert_callback(const NetObjOrderedListIterator<T>& objInfo) {
if(insertCallback)
insertCallback(objInfo);
}
void call_insert_callback_list(const std::vector<NetObjOrderedListIterator<T>>& objInfos) {
if(insertCallback) {
for(auto objInfo : objInfos)
insertCallback(objInfo);
}
}
// Call before erasing
void call_erase_callback(const NetObjOrderedListIterator<T>& objInfo) {
if(eraseCallback)
eraseCallback(objInfo);
}
void call_move_callback(const NetObjOrderedListIterator<T>& objInfo, uint32_t oldPos) {
if(moveCallback)
moveCallback(objInfo, oldPos);
}
private:
InsertCallback insertCallback;
EraseCallback eraseCallback;
MoveCallback moveCallback;
template <typename S> friend void register_ordered_list_class(NetObjManager& t);
static void write_constructor_func(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) {
l->write_constructor(l, a);
}
static void read_constructor_func(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) {
l->read_constructor(l, a, c);
}
static void read_update_func(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) {
l->read_update(l, a, c);
}
};
template <typename T> class NetObjOrderedListServer : public NetObjOrderedList<T> {
public:
NetObjOrderedListServer() {}
virtual bool contains(const NetObjID& id) const override {
return idToDataMap.contains(id);
}
virtual uint32_t size() const override {
return data.size();
}
virtual NetObjOrderedListIterator<T> begin() override {
return data.begin();
}
virtual NetObjOrderedListIterator<T> end() override {
return data.end();
}
virtual NetObjOrderedListIterator<T> at(uint32_t index) override {
if(index >= data.size())
return data.end();
return std::next(data.begin(), index);
}
virtual NetObjOrderedListIterator<T> get(const NetObjID& id) override {
auto it = idToDataMap.find(id);
if(it == idToDataMap.end())
return data.end();
return it->second;
}
virtual NetObjOrderedListConstIterator<T> begin() const override {
return data.begin();
}
virtual NetObjOrderedListConstIterator<T> end() const override {
return data.end();
}
virtual NetObjOrderedListConstIterator<T> at(uint32_t index) const override {
if(index >= data.size())
return data.end();
return std::next(data.begin(), index);
}
virtual NetObjOrderedListConstIterator<T> get(const NetObjID& id) const override {
auto it = idToDataMap.find(id);
if(it == idToDataMap.end())
return data.end();
return it->second;
}
virtual bool empty() const override {
return data.empty();
}
virtual void reassign_netobj_ids_call() override {
idToDataMap.clear();
for(auto it = data.begin(); it != data.end(); ++it) {
it->obj.reassign_ids();
idToDataMap[it->obj.get_net_id()] = it;
}
}
protected:
virtual std::vector<NetObjOrderedListIterator<T>> insert_ordered_list(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>>& newObjs) override {
if(newObjs.empty())
return {};
std::vector<NetworkingObjects::NetObjID> idPositionList;
for(auto& [itToInsertAt, newObj] : newObjs) {
if(itToInsertAt == data.begin())
idPositionList.emplace_back(NetworkingObjects::NetObjID{0, 0});
else
idPositionList.emplace_back(std::prev(itToInsertAt)->obj.get_net_id());
}
std::vector<NetObjOrderedListIterator<T>> toRet;
for(auto& [itToInsertAt, newObj] : newObjs) {
auto newObjIt = data.emplace(itToInsertAt, std::move(newObj), itToInsertAt == data.end() ? data.size() : itToInsertAt->pos); // All position values will be wrong except the first one, which is all that matters
idToDataMap.emplace(newObjIt->obj.get_net_id(), newObjIt);
toRet.emplace_back(newObjIt);
}
set_position_obj_info_list<T>(toRet[0], data.end());
l.send_server_update_to_all_clients_except(clientInserting, RELIABLE_COMMAND_CHANNEL, [&toRet, &idPositionList](const NetObjTemporaryPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_StoC::INSERT_LIST_CONSTRUCT, static_cast<uint32_t>(toRet.size()));
for(auto [newObjIt, idPosition] : std::views::zip(toRet, idPositionList)) {
a(idPosition);
newObjIt->obj.write_create_message(a);
}
});
l.send_server_update_to_client(clientInserting, RELIABLE_COMMAND_CHANNEL, [&toRet, &idPositionList](const NetObjTemporaryPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_StoC::INSERT_LIST_REFERENCE, static_cast<uint32_t>(toRet.size()));
for(auto [newObjIt, idPosition] : std::views::zip(toRet, idPositionList))
a(idPosition, newObjIt->obj.get_net_id());
});
this->call_insert_callback_list(toRet);
return toRet;
}
virtual void erase_it_list(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::vector<NetObjOrderedListIterator<T>>& itList, std::vector<NetObjOwnerPtr<T>>* erasedObjects) {
if(itList.empty())
return;
std::vector<std::pair<uint32_t, NetObjOwnerPtr<T>>> erasedObjectsWithPos;
NetObjOrderedListIterator<T> smallestPosIt = data.end();
std::vector<NetObjID> idListToSend;
for(auto& it : itList) {
if(smallestPosIt == data.end() || smallestPosIt->pos >= it->pos) {
smallestPosIt = std::next(it);
if(smallestPosIt != data.end())
smallestPosIt->pos = it->pos;
}
idListToSend.emplace_back(it->obj.get_net_id());
this->call_erase_callback(it);
idToDataMap.erase(it->obj.get_net_id());
if(erasedObjects)
erasedObjectsWithPos.emplace_back(it->pos, std::move(it->obj));
data.erase(it);
}
if(erasedObjects) {
std::sort(erasedObjectsWithPos.begin(), erasedObjectsWithPos.end(), [](auto& a, auto& b) {
return a.first < b.first;
});
for(auto& e : erasedObjectsWithPos)
erasedObjects->emplace_back(std::move(e.second));
}
l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&idListToSend](const NetObjTemporaryPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_StoC::ERASE_ID_LIST, idListToSend);
});
set_position_obj_info_list<T>(smallestPosIt, data.end());
}
virtual void write_constructor(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) override {
a(static_cast<uint32_t>(data.size()));
for(auto& c : data)
c.obj.write_create_message(a);
}
virtual void read_constructor(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) override {
uint32_t constructedSize;
a(constructedSize);
data.clear();
idToDataMap.clear();
for(uint32_t i = 0; i < constructedSize; i++) {
auto it = data.emplace(data.end(), l.get_obj_man()->template read_create_message<T>(a, c), i);
idToDataMap.emplace(data.back().obj.get_net_id(), it);
}
// Dont send the object data to all clients in the constructor, as the function that called read_create_message is also the one that will decide where to send the data of the constructed object
// Example: the INSERT_SINGLE command in read_update, if it was inserting another NetObjOrderedList, would first construct the list using read_create_message, then it will call insert() to send it to clients
}
virtual void read_update(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) override {
ObjPtrOrderedListCommand_CtoS command;
a(command);
switch(command) {
case ObjPtrOrderedListCommand_CtoS::INSERT_LIST: {
std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>> newObjs;
std::vector<uint32_t> insertIndices;
uint32_t newObjsSize;
a(newObjsSize);
for(uint32_t i = 0; i < newObjsSize; i++) {
uint32_t& insertIndex = insertIndices.emplace_back();
a(insertIndex);
insertIndex -= i;
insertIndex = std::min<uint32_t>(insertIndex, data.size());
newObjs.emplace_back(NetObjOrderedListIterator<T>(), l.get_obj_man()->template read_create_message<T>(a, c));
}
uint32_t currentIndex = data.size();
NetObjOrderedListIterator<T> currentIt = data.end();
for(std::pair<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>&, uint32_t&> elem : (std::views::zip(newObjs, insertIndices) | std::views::reverse)) {
uint32_t index = elem.second;
auto& it = elem.first.first;
while(currentIndex != index) {
--currentIndex;
--currentIt;
}
it = currentIt;
}
insert_ordered_list(l, c, newObjs);
break;
}
case ObjPtrOrderedListCommand_CtoS::ERASE_ID_LIST: {
std::vector<NetObjID> idsToErase;
a(idsToErase);
std::vector<NetObjOrderedListIterator<T>> iteratorsToErase;
for(auto& id : idsToErase) {
auto it = idToDataMap.find(id);
if(it != idToDataMap.end())
iteratorsToErase.emplace_back(it->second);
}
erase_it_list(l, iteratorsToErase, nullptr);
break;
}
}
}
private:
std::list<NetObjOrderedListObjectInfo<T>> data;
std::unordered_map<NetObjID, NetObjOrderedListIterator<T>> idToDataMap;
};
template <typename T> class NetObjOrderedListClient : public NetObjOrderedList<T> {
public:
NetObjOrderedListClient() {}
virtual bool contains(const NetObjID& id) const override {
return clientIdToDataMap.contains(id);
}
virtual uint32_t size() const override {
return clientData.size();
}
virtual NetObjOrderedListIterator<T> begin() override {
return clientData.begin();
}
virtual NetObjOrderedListIterator<T> end() override {
return clientData.end();
}
virtual NetObjOrderedListIterator<T> at(uint32_t index) override {
if(index >= clientData.size())
return clientData.end();
return std::next(clientData.begin(), index);
}
virtual NetObjOrderedListIterator<T> get(const NetObjID& id) override {
auto it = clientIdToDataMap.find(id);
if(it == clientIdToDataMap.end())
return clientData.end();
return it->second;
}
virtual NetObjOrderedListConstIterator<T> begin() const override {
return clientData.begin();
}
virtual NetObjOrderedListConstIterator<T> end() const override {
return clientData.end();
}
virtual NetObjOrderedListConstIterator<T> at(uint32_t index) const override {
if(index >= clientData.size())
return clientData.end();
return std::next(clientData.begin(), index);
}
virtual NetObjOrderedListConstIterator<T> get(const NetObjID& id) const override {
auto it = clientIdToDataMap.find(id);
if(it == clientIdToDataMap.end())
return clientData.end();
return it->second;
}
virtual bool empty() const override {
return clientData.empty();
}
virtual void reassign_netobj_ids_call() override {
serverData.clear();
serverIdToDataMap.clear();
clientIdToDataMap.clear();
for(auto it = clientData.begin(); it != clientData.end(); ++it) {
it->obj.reassign_ids();
clientIdToDataMap[it->obj.get_net_id()] = it;
auto serverIt = serverData.emplace(serverData.end(), it->obj.get_net_id());
serverIdToDataMap[it->obj.get_net_id()] = serverIt;
}
}
protected:
virtual std::vector<NetObjOrderedListIterator<T>> insert_ordered_list(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>>& newObjs) override {
if(newObjs.empty())
return {};
std::vector<NetObjOrderedListIterator<T>> toRet;
for(auto& [itToInsertAt, newObj] : newObjs) {
auto newObjIt = clientData.emplace(itToInsertAt, std::move(newObj), itToInsertAt == clientData.end() ? clientData.size() : itToInsertAt->pos); // All position values will be wrong except the first one, which is all that matters
clientIdToDataMap.emplace(newObjIt->obj.get_net_id(), newObjIt);
toRet.emplace_back(newObjIt);
}
set_position_obj_info_list<T>(toRet[0], clientData.end());
l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&toRet](const NetObjTemporaryPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_CtoS::INSERT_LIST, static_cast<uint32_t>(toRet.size()));
for(auto newObjIt : toRet) {
a(newObjIt->pos);
newObjIt->obj.write_create_message(a);
}
});
this->call_insert_callback_list(toRet);
return toRet;
}
virtual void erase_it_list(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::vector<NetObjOrderedListIterator<T>>& itList, std::vector<NetObjOwnerPtr<T>>* erasedObjects) {
if(itList.empty())
return;
std::vector<std::pair<uint32_t, NetObjOwnerPtr<T>>> erasedObjectsWithPos;
NetObjOrderedListIterator<T> smallestPosIt = clientData.end();
std::vector<NetObjID> idListToSend;
for(auto& it : itList) {
if(smallestPosIt == clientData.end() || smallestPosIt->pos >= it->pos) {
smallestPosIt = std::next(it);
if(smallestPosIt != clientData.end())
smallestPosIt->pos = it->pos;
}
idListToSend.emplace_back(it->obj.get_net_id());
this->call_erase_callback(it);
clientIdToDataMap.erase(it->obj.get_net_id());
if(erasedObjects)
erasedObjectsWithPos.emplace_back(it->pos, std::move(it->obj));
clientData.erase(it);
}
if(erasedObjects) {
std::sort(erasedObjectsWithPos.begin(), erasedObjectsWithPos.end(), [](auto& a, auto& b) {
return a.first < b.first;
});
for(auto& e : erasedObjectsWithPos)
erasedObjects->emplace_back(std::move(e.second));
}
l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&idListToSend](const NetObjTemporaryPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_CtoS::ERASE_ID_LIST, idListToSend);
});
set_position_obj_info_list<T>(smallestPosIt, clientData.end());
}
virtual void write_constructor(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) override {
a(static_cast<uint32_t>(clientData.size()));
serverData.clear();
serverIdToDataMap.clear();
for(auto& c : clientData) {
c.obj.write_create_message(a);
auto serverIt = serverData.emplace(serverData.end(), c.obj.get_net_id()); // We are the ones who made this list, so fill up
serverIdToDataMap.emplace(c.obj.get_net_id(), serverIt);
}
}
virtual void read_constructor(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) override {
uint32_t constructedSize;
a(constructedSize);
clientData.clear();
serverData.clear();
clientIdToDataMap.clear();
serverIdToDataMap.clear();
for(uint32_t i = 0; i < constructedSize; i++) {
auto clientIt = clientData.emplace(clientData.end(), l.get_obj_man()->template read_create_message<T>(a, nullptr), i);
clientIdToDataMap.emplace(clientIt->obj.get_net_id(), clientIt);
auto serverIt = serverData.emplace(serverData.end(), clientIt->obj.get_net_id());
serverIdToDataMap.emplace(clientIt->obj.get_net_id(), serverIt);
}
}
virtual void read_update(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) override {
ObjPtrOrderedListCommand_StoC c;
a(c);
switch(c) {
case ObjPtrOrderedListCommand_StoC::INSERT_LIST_CONSTRUCT: {
uint32_t newObjsSize;
a(newObjsSize);
std::vector<NetworkingObjects::NetObjID> idsBeforeElements;
std::vector<std::pair<NetObjOrderedListIterator<T>, NetObjOwnerPtr<T>>> newObjs;
for(uint32_t i = 0; i < newObjsSize; i++) {
auto& idBeforeElement = idsBeforeElements.emplace_back();
a(idBeforeElement);
newObjs.emplace_back(get_client_iterator_from_server_previous_net_obj_id(idBeforeElement), l.get_obj_man()->template read_create_message<T>(a, nullptr));
}
for(uint32_t i = 0; i < newObjsSize; i++) {
auto serverInsertAtIt = get_server_iterator_from_server_previous_net_obj_id(idsBeforeElements[i]);
auto serverIt = serverData.emplace(serverInsertAtIt, newObjs[i].second.get_net_id());
serverIdToDataMap.emplace(newObjs[i].second.get_net_id(), serverIt);
}
std::vector<NetObjOrderedListIterator<T>> insertedIterators;
auto firstInsertedIt = clientData.end();
for(uint32_t i = 0; i < newObjs.size(); i++) {
auto& [itToInsertAt, newObj] = newObjs[i];
auto newObjIt = clientData.emplace(itToInsertAt, std::move(newObj), itToInsertAt == clientData.end() ? clientData.size() : itToInsertAt->pos); // All position values will be wrong except the first one, which is all that matters
clientIdToDataMap.emplace(newObjIt->obj.get_net_id(), newObjIt);
insertedIterators.emplace_back(newObjIt);
if(i == 0)
firstInsertedIt = newObjIt;
}
set_position_obj_info_list<T>(firstInsertedIt, clientData.end());
this->call_insert_callback_list(insertedIterators);
break;
}
case ObjPtrOrderedListCommand_StoC::INSERT_LIST_REFERENCE: {
uint32_t newObjsSize;
a(newObjsSize);
struct PerElementData {
NetworkingObjects::NetObjID idBeforeElement;
NetworkingObjects::NetObjID refID;
bool foundInClient;
NetObjOrderedListIterator<T> clientObjectIt;
NetObjOrderedListIterator<T> clientInsertPosIt;
};
std::vector<PerElementData> receivedElements;
for(uint32_t i = 0; i < newObjsSize; i++) {
auto& elemData = receivedElements.emplace_back();
a(elemData.idBeforeElement, elemData.refID);
}
std::list<NetObjOrderedListObjectInfo<T>> referredToObjectData;
std::unordered_map<NetworkingObjects::NetObjID, uint32_t> referredToObjectOldPos;
auto startRefreshFrom = clientData.end();
for(auto& elemData : receivedElements) {
auto clientIdToDataMapIt = clientIdToDataMap.find(elemData.refID);
if(clientIdToDataMapIt != clientIdToDataMap.end()) {
elemData.clientObjectIt = clientIdToDataMapIt->second;
referredToObjectOldPos.emplace(elemData.clientObjectIt->obj.get_net_id(), elemData.clientObjectIt->pos);
if(startRefreshFrom == clientData.end() || startRefreshFrom == elemData.clientObjectIt) {
startRefreshFrom = std::next(elemData.clientObjectIt);
if(startRefreshFrom != clientData.end())
startRefreshFrom->pos = elemData.clientObjectIt->pos;
}
referredToObjectData.splice(referredToObjectData.end(), clientData, elemData.clientObjectIt, std::next(elemData.clientObjectIt));
clientIdToDataMap.erase(clientIdToDataMapIt);
elemData.foundInClient = true;
}
else
elemData.foundInClient = false;
}
set_position_obj_info_list<T>(startRefreshFrom, clientData.end());
for(auto& elemData : receivedElements) {
if(elemData.foundInClient)
elemData.clientInsertPosIt = get_client_iterator_from_server_previous_net_obj_id(elemData.idBeforeElement);
}
for(auto& elemData : receivedElements) {
auto serverInsertAtIt = get_server_iterator_from_server_previous_net_obj_id(elemData.idBeforeElement);
auto serverIt = serverData.emplace(serverInsertAtIt, elemData.refID);
serverIdToDataMap.emplace(elemData.refID, serverIt);
}
auto firstInsertedIt = clientData.end();
uint32_t firstInsertedActualPos = 0;
for(auto& elemData : receivedElements) {
if(!elemData.foundInClient)
continue;
if(firstInsertedIt == clientData.end()) {
firstInsertedActualPos = elemData.clientInsertPosIt->pos;
firstInsertedIt = elemData.clientObjectIt;
}
clientData.splice(elemData.clientInsertPosIt, referredToObjectData, elemData.clientObjectIt, std::next(elemData.clientObjectIt));
clientIdToDataMap.emplace(elemData.clientObjectIt->obj.get_net_id(), elemData.clientObjectIt);
}
// Instead of set_position_obj_info_list<T>
if(firstInsertedIt != clientData.end()) {
auto& it = firstInsertedIt;
auto& actualPos = firstInsertedActualPos;
for(; it != clientData.end(); ++it) {
it->pos = actualPos;
auto referredToObjectOldPosIt = referredToObjectOldPos.find(it->obj.get_net_id());
if(referredToObjectOldPosIt != referredToObjectOldPos.end()) {
uint32_t oldPos = referredToObjectOldPosIt->second;
if(oldPos != it->pos)
this->call_move_callback(it, oldPos);
}
actualPos++;
}
}
break;
}
case ObjPtrOrderedListCommand_StoC::ERASE_ID_LIST: {
std::vector<NetworkingObjects::NetObjID> idsToErase;
a(idsToErase);
for(auto& id : idsToErase) {
auto serverIDToDataMapIt = serverIdToDataMap.find(id);
serverData.erase(serverIDToDataMapIt->second);
serverIdToDataMap.erase(serverIDToDataMapIt);
}
NetObjOrderedListIterator<T> smallestPosIt = clientData.end();
for(auto& id : idsToErase) {
auto clientIt = clientIdToDataMap.find(id);
if(clientIt == clientIdToDataMap.end())
continue;
auto it = clientIt->second;
if(smallestPosIt == clientData.end() || smallestPosIt->pos >= it->pos) {
smallestPosIt = std::next(it);
if(smallestPosIt != clientData.end())
smallestPosIt->pos = it->pos;
}
this->call_erase_callback(it);
clientIdToDataMap.erase(clientIt);
clientData.erase(it);
}
set_position_obj_info_list<T>(smallestPosIt, clientData.end());
break;
}
}
}
private:
NetworkingObjects::NetObjOrderedListIterator<T> get_client_iterator_from_server_previous_net_obj_id(NetworkingObjects::NetObjID previousObjId) {
if(previousObjId == NetworkingObjects::NetObjID{0, 0})
return clientData.begin();
else {
auto serverIt = serverIdToDataMap[previousObjId];
for(;;) {
NetworkingObjects::NetObjID netObjID = *serverIt;
auto clientIt = clientIdToDataMap.find(netObjID);
if(clientIt != clientIdToDataMap.end())
return std::next(clientIt->second);
if(serverIt == serverData.begin())
return clientData.begin();
--serverIt;
}
}
return clientData.begin();
}
std::list<NetObjID>::iterator get_server_iterator_from_server_previous_net_obj_id(NetworkingObjects::NetObjID previousObjId) {
if(previousObjId == NetworkingObjects::NetObjID{0, 0})
return serverData.begin();
return std::next(serverIdToDataMap[previousObjId]);
}
std::list<NetObjID> serverData;
std::unordered_map<NetObjID, std::list<NetObjID>::iterator> serverIdToDataMap;
std::list<NetObjOrderedListObjectInfo<T>> clientData;
std::unordered_map<NetObjID, NetObjOrderedListIterator<T>> clientIdToDataMap;
};
template <typename S> void register_ordered_list_class(NetObjManager& objMan) {
objMan.register_class<NetObjOrderedList<S>, NetObjOrderedList<S>, NetObjOrderedListClient<S>, NetObjOrderedListServer<S>>({
.writeConstructorFuncClient = NetObjOrderedList<S>::write_constructor_func,
.readConstructorFuncClient = NetObjOrderedList<S>::read_constructor_func,
.readUpdateFuncClient = NetObjOrderedList<S>::read_update_func,
.writeConstructorFuncServer = NetObjOrderedList<S>::write_constructor_func,
.readConstructorFuncServer = NetObjOrderedList<S>::read_constructor_func,
.readUpdateFuncServer = NetObjOrderedList<S>::read_update_func,
});
}
}