Added sync code to client ordered list

This commit is contained in:
Yousef Khadadeh 2025-12-09 13:00:38 +03:00
parent c51b7e7eb1
commit 9649135f43
16 changed files with 233 additions and 143 deletions

View file

@ -177,3 +177,11 @@ void NetClient::send_str_as_bytes(std::shared_ptr<rtc::DataChannel> channel, con
void NetClient::add_recv_callback(MessageCommandType commandID, const NetClientRecvCallback& callback) {
recvCallbacks[commandID] = callback;
}
NetClient::~NetClient() {
if(directConnectServer) {
directConnectServer->directConnectClient = nullptr;
std::erase(directConnectServer->clients, directConnectServer->directConnectClientData);
directConnectServer->directConnectClientData = nullptr;
}
}

View file

@ -32,6 +32,7 @@ class NetClient : public std::enable_shared_from_this<NetClient> {
void add_recv_callback(MessageCommandType commandID, const NetClientRecvCallback& callback);
bool is_disconnected() const;
NetLibrary::DownloadProgress get_progress_into_fragmented_message(const std::string& channel) const;
~NetClient();
private:
std::string localID;

View file

@ -261,6 +261,8 @@ const std::vector<std::shared_ptr<NetServer::ClientData>> NetServer::get_client_
}
NetServer::~NetServer() {
if(directConnectClient)
if(directConnectClient) {
directConnectClient->isDisconnected = true;
directConnectClient->directConnectServer = nullptr;
}
}

View file

@ -16,11 +16,7 @@ namespace NetworkingObjects {
auto it = objectData.find(id);
if(it == objectData.end())
return;
auto& netTypeIDData = typeList->netTypeIDData[it->second.netTypeID];
if(isServer)
netTypeIDData.readUpdateFuncServer(NetObjPtr<void>(this, id, it->second.p), a, clientReceivedFrom);
else
netTypeIDData.readUpdateFuncClient(NetObjPtr<void>(this, id, it->second.p), a, clientReceivedFrom);
typeList->get_net_type_data(isServer, it->second.netTypeID).readUpdateFunc(NetObjPtr<void>(this, id, it->second.p), a, clientReceivedFrom);
}
void NetObjManager::set_client(std::shared_ptr<NetClient> initClient, MessageCommandType initUpdateCommandID) {
client = initClient;

View file

@ -25,14 +25,9 @@ namespace NetworkingObjects {
bool is_server() const;
template <typename T> NetObjPtr<T> read_create_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& clientReceivedFrom) {
NetObjID id;
NetTypeIDType typeID;
a(id, typeID);
auto& netTypeIDData = typeList->netTypeIDData[typeID];
NetObjPtr<T> newPtr = make_obj_with_id<T>(id);
if(isServer)
netTypeIDData.readConstructorFuncServer(newPtr.template cast<void>(), a, clientReceivedFrom);
else
netTypeIDData.readConstructorFuncClient(newPtr.template cast<void>(), a, clientReceivedFrom);
a(id);
NetObjPtr<T> newPtr = make_obj_with_id_might_already_exist<T>(id);
typeList->get_type_index_data<T>(isServer).readConstructorFunc(newPtr.template cast<void>(), a, clientReceivedFrom);
return newPtr;
}
template <typename T> NetObjPtr<T> read_get_obj_from_message(cereal::PortableBinaryInputArchive& a) {
@ -44,26 +39,14 @@ namespace NetworkingObjects {
return NetObjPtr<T>(this, id, std::static_pointer_cast<T>(it->second.p));
}
template <typename T> NetObjPtr<T> make_obj() {
auto sharedPtr = std::static_pointer_cast<T>(isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].allocatorFunc() : typeList->typeIndexDataClient[std::type_index(typeid(T*))].allocatorFunc());
NetObjID newID = NetObjID::random_gen();
if(!objectData.emplace(newID, SingleObjectData{.netTypeID = isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].netTypeID : typeList->typeIndexDataClient[std::type_index(typeid(T*))].netTypeID, .p = std::static_pointer_cast<void>(sharedPtr)}).second)
throw std::runtime_error("[NetObjManager::make_obj] ID Collision");
return NetObjPtr<T>(this, newID, sharedPtr);
return emplace_shared_ptr<T>(NetObjID::random_gen(), std::static_pointer_cast<T>(typeList->get_type_index_data<T>(isServer).allocatorFunc()));
}
// Don't use this function unless you're sure that class T isn't a base class
template <typename T, typename... Args> NetObjPtr<T> make_obj_direct(Args&&... items) {
auto sharedPtr = std::make_shared<T>(items...);
NetObjID newID = NetObjID::random_gen();
if(!objectData.emplace(newID, SingleObjectData{.netTypeID = isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].netTypeID : typeList->typeIndexDataClient[std::type_index(typeid(T*))].netTypeID, .p = std::static_pointer_cast<void>(sharedPtr)}).second)
throw std::runtime_error("[NetObjManager::make_obj] ID Collision");
return NetObjPtr<T>(this, newID, sharedPtr);
return emplace_shared_ptr<T>(NetObjID::random_gen(), std::make_shared<T>(items...));
}
template <typename T> NetObjPtr<T> obj_from_ptr(T* p) {
std::shared_ptr<T> sharedPtr(p);
NetObjID newID = NetObjID::random_gen();
if(!objectData.emplace(newID, SingleObjectData{.netTypeID = isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].netTypeID : typeList->typeIndexDataClient[std::type_index(typeid(T*))].netTypeID, .p = std::static_pointer_cast<void>(sharedPtr)}).second)
throw std::runtime_error("[NetObjManager::obj_from_ptr] ID Collision");
return NetObjPtr<T>(this, newID, sharedPtr);
return emplace_shared_ptr<T>(NetObjID::random_gen(), std::shared_ptr<T>(p));
}
template <typename T> NetObjPtr<T> get_obj_from_id(NetObjID id) {
auto it = objectData.find(id);
@ -72,10 +55,16 @@ namespace NetworkingObjects {
return NetObjPtr<T>(this, id, std::static_pointer_cast<T>(it->second.p));
}
private:
template <typename T> NetObjPtr<T> make_obj_with_id(NetObjID id) {
auto sharedPtr = std::static_pointer_cast<T>(isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].allocatorFunc() : typeList->typeIndexDataClient[std::type_index(typeid(T*))].allocatorFunc());
if(!objectData.emplace(id, SingleObjectData{.netTypeID = isServer ? typeList->typeIndexDataServer[std::type_index(typeid(T*))].netTypeID : typeList->typeIndexDataClient[std::type_index(typeid(T*))].netTypeID, .p = std::static_pointer_cast<void>(sharedPtr)}).second)
throw std::runtime_error("[NetObjManager::make_obj] ID Collision");
template <typename T> NetObjPtr<T> make_obj_with_id_might_already_exist(NetObjID id) {
auto alreadyExistsIt = objectData.find(id);
if(alreadyExistsIt != objectData.end())
return NetObjPtr<T>(this, id, std::static_pointer_cast<T>(alreadyExistsIt->second.p));
return emplace_shared_ptr<T>(id, std::static_pointer_cast<T>(typeList->get_type_index_data<T>(isServer).allocatorFunc()));
}
template <typename T> NetObjPtr<T> emplace_shared_ptr(NetObjID id, const std::shared_ptr<T>& sharedPtr) {
if(!objectData.emplace(id, SingleObjectData{.netTypeID = typeList->get_type_index_data<T>(isServer).netTypeID, .p = std::static_pointer_cast<void>(sharedPtr)}).second)
throw std::runtime_error("[NetObjManager::emplace_shared_ptr] ID Collision");
return NetObjPtr<T>(this, id, sharedPtr);
}
@ -85,6 +74,7 @@ namespace NetworkingObjects {
NetTypeIDType netTypeID;
std::shared_ptr<void> p;
};
bool isServer;
std::shared_ptr<NetClient> client;
std::shared_ptr<NetServer> server;

View file

@ -0,0 +1,7 @@
#include "NetObjManagerTypeList.hpp"
namespace NetworkingObjects {
const NetObjManagerTypeList::NetTypeIDFunctions& NetObjManagerTypeList::get_net_type_data(bool isServer, NetTypeIDType id) {
return isServer ? netTypeIDFunctionsServer[id] : netTypeIDFunctionsClient[id];
}
}

View file

@ -18,64 +18,71 @@ class NetObjManagerTypeList {
std::function<void(const NetObjPtr<ServerT>&, cereal::PortableBinaryInputArchive&, const std::shared_ptr<NetServer::ClientData>&)> readUpdateFuncServer;
};
template <typename ClientT, typename ServerT, typename ClientAllocatedType, typename ServerAllocatedType> void register_class(const ServerClientClassFunctions<ClientT, ServerT>& funcs) {
netTypeIDData[nextNetTypeID] = NetTypeIDContainer {
.readConstructorFuncClient = [f = funcs.readConstructorFuncClient](const NetObjPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
typeIndexFunctionsClient[std::type_index(typeid(ClientT*))] = TypeIndexFunctions {
.readConstructorFunc = [f = funcs.readConstructorFuncClient](const NetObjPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
if(f)
f(obj.cast<ClientT>(), message, c);
},
.readUpdateFuncClient = [f = funcs.readUpdateFuncClient](const NetObjPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
if(f)
f(obj.cast<ClientT>(), message, c);
},
.readConstructorFuncServer = [f = funcs.readConstructorFuncServer](const NetObjPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
if(f)
f(obj.cast<ServerT>(), message, c);
},
.readUpdateFuncServer = [f = funcs.readUpdateFuncServer](const NetObjPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
if(f)
f(obj.cast<ServerT>(), message, c);
},
};
typeIndexDataClient[std::type_index(typeid(ClientT*))] = TypeIndexContainer{
.netTypeID = nextNetTypeID,
.writeConstructorFunc = [f = funcs.writeConstructorFuncClient](const NetObjPtr<void>& obj, cereal::PortableBinaryOutputArchive& message) {
f(obj.cast<ClientT>(), message);
},
.allocatorFunc = []() { return std::make_shared<ClientAllocatedType>(); }
.allocatorFunc = []() { return std::make_shared<ClientAllocatedType>(); },
.netTypeID = netTypeID
};
typeIndexDataServer[std::type_index(typeid(ServerT*))] = TypeIndexContainer{
.netTypeID = nextNetTypeID,
typeIndexFunctionsServer[std::type_index(typeid(ServerT*))] = TypeIndexFunctions {
.readConstructorFunc = [f = funcs.readConstructorFuncServer](const NetObjPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
if(f)
f(obj.cast<ServerT>(), message, c);
},
.writeConstructorFunc = [f = funcs.writeConstructorFuncServer](const NetObjPtr<void>& obj, cereal::PortableBinaryOutputArchive& message) {
f(obj.cast<ServerT>(), message);
},
.allocatorFunc = []() { return std::make_shared<ServerAllocatedType>(); }
.allocatorFunc = []() { return std::make_shared<ServerAllocatedType>(); },
.netTypeID = netTypeID
};
nextNetTypeID++;
}
private:
friend class NetObjManager;
template <typename T> friend class NetObjPtr;
struct TypeIndexContainer {
NetTypeIDType netTypeID;
netTypeIDFunctionsClient[netTypeID] = NetTypeIDFunctions {
.readUpdateFunc = [f = funcs.readUpdateFuncClient](const NetObjPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
if(f)
f(obj.cast<ClientT>(), message, c);
}
};
netTypeIDFunctionsServer[netTypeID] = NetTypeIDFunctions {
.readUpdateFunc = [f = funcs.readUpdateFuncServer](const NetObjPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
if(f)
f(obj.cast<ServerT>(), message, c);
}
};
netTypeID++;
}
struct TypeIndexFunctions {
std::function<void(const NetObjPtr<void>&, cereal::PortableBinaryInputArchive&, const std::shared_ptr<NetServer::ClientData>&)> readConstructorFunc;
std::function<void(const NetObjPtr<void>&, cereal::PortableBinaryOutputArchive&)> writeConstructorFunc;
std::function<std::shared_ptr<void>()> allocatorFunc;
NetTypeIDType netTypeID;
};
struct NetTypeIDFunctions {
std::function<void(const NetObjPtr<void>&, cereal::PortableBinaryInputArchive&, const std::shared_ptr<NetServer::ClientData>&)> readUpdateFunc;
};
template <typename T> const TypeIndexFunctions& get_type_index_data(bool isServer) {
return isServer ? typeIndexFunctionsServer[std::type_index(typeid(T*))] : typeIndexFunctionsClient[std::type_index(typeid(T*))];
}
const NetTypeIDFunctions& get_net_type_data(bool isServer, NetTypeIDType id);
private:
// std::type_index in this case is the typeinfo for the POINTER of the type in question. This ensures that we get a mapping for the exact type that was registered, not for a child of the type in case of inheritance
std::unordered_map<std::type_index, TypeIndexContainer> typeIndexDataClient;
std::unordered_map<std::type_index, TypeIndexContainer> typeIndexDataServer;
std::unordered_map<std::type_index, TypeIndexFunctions> typeIndexFunctionsClient;
std::unordered_map<std::type_index, TypeIndexFunctions> typeIndexFunctionsServer;
std::unordered_map<NetTypeIDType, NetTypeIDFunctions> netTypeIDFunctionsClient;
std::unordered_map<NetTypeIDType, NetTypeIDFunctions> netTypeIDFunctionsServer;
struct NetTypeIDContainer {
std::function<void(const NetObjPtr<void>&, cereal::PortableBinaryInputArchive&, const std::shared_ptr<NetServer::ClientData>&)> readConstructorFuncClient;
std::function<void(const NetObjPtr<void>&, cereal::PortableBinaryInputArchive&, const std::shared_ptr<NetServer::ClientData>&)> readUpdateFuncClient;
std::function<void(const NetObjPtr<void>&, cereal::PortableBinaryInputArchive&, const std::shared_ptr<NetServer::ClientData>&)> readConstructorFuncServer;
std::function<void(const NetObjPtr<void>&, cereal::PortableBinaryInputArchive&, const std::shared_ptr<NetServer::ClientData>&)> readUpdateFuncServer;
};
std::unordered_map<NetTypeIDType, NetTypeIDContainer> netTypeIDData;
NetTypeIDType nextNetTypeID = 0;
NetTypeIDType netTypeID = 0;
};
}

View file

@ -48,11 +48,11 @@ namespace NetworkingObjects {
// Don't use this function unless you're sure that class T isn't a base class
template <typename ...Args> NetObjPtr<T> emplace_back_direct(const NetObjPtr<NetObjOrderedList<T>>& l, Args&&... items) {
NetObjPtr<T> newObj = l.get_obj_man()->template make_obj_direct<T>(items...);
l->push_back(l, newObj);
l->insert(l, l->size(), newObj);
return newObj;
}
static NetObjPtr<T> push_back_and_send_create(const NetObjPtr<NetObjOrderedList<T>>& l, const NetObjPtr<T>& newObj) {
l->push_back(l, newObj);
l->insert(l, l->size(), newObj);
return newObj;
}
static NetObjPtr<T> insert_and_send_create(const NetObjPtr<NetObjOrderedList<T>>& l, uint32_t posToInsertAt, const NetObjPtr<T>& newObj) {
@ -63,16 +63,14 @@ namespace NetworkingObjects {
l->erase_by_index(l, indexToErase);
}
static void erase(const NetObjPtr<NetObjOrderedList<T>>& l, const NetObjOrderedListObjectInfoPtr<T>& p) {
l->erase_by_ptr(l, p);
l->erase_by_index(l, p->pos);
}
virtual uint32_t size() const = 0;
virtual const std::vector<NetObjOrderedListObjectInfoPtr<T>>& get_data() const = 0;
virtual ~NetObjOrderedList() {}
protected:
virtual void push_back(const NetObjPtr<NetObjOrderedList<T>>& l, const NetObjPtr<T>& newObj) = 0;
virtual void insert(const NetObjPtr<NetObjOrderedList<T>>& l, uint32_t posToInsertAt, const NetObjPtr<T>& newObj) = 0;
virtual void erase_by_index(const NetObjPtr<NetObjOrderedList<T>>& l, uint32_t indexToErase) = 0;
virtual void erase_by_ptr(const NetObjPtr<NetObjOrderedList<T>>& l, const NetObjOrderedListObjectInfoPtr<T>& p) = 0;
virtual void write_constructor(const NetObjPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) = 0;
virtual void read_update(const NetObjPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) = 0;
virtual void read_constructor(const NetObjPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) = 0;
@ -99,37 +97,41 @@ namespace NetworkingObjects {
return data;
}
protected:
virtual void push_back(const NetObjPtr<NetObjOrderedList<T>>& l, const NetObjPtr<T>& newObj) override {
auto& newObjInfoPtr = data.emplace_back(std::make_shared<NetObjOrderedListObjectInfo<T>>(newObj, static_cast<uint32_t>(data.size()), false));
l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_StoC::INSERT_SINGLE, newObjInfoPtr->pos);
newObjInfoPtr->obj.write_create_message(a);
});
}
virtual void insert(const NetObjPtr<NetObjOrderedList<T>>& l, uint32_t posToInsertAt, const NetObjPtr<T>& newObj) override {
posToInsertAt = std::min<uint32_t>(posToInsertAt, data.size());
auto& newObjInfoPtr = *data.insert(data.begin() + posToInsertAt, std::make_shared<NetObjOrderedListObjectInfo<T>>(newObj, posToInsertAt, false));
set_positions_for_object_info_vector<T>(data, posToInsertAt);
l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_StoC::INSERT_SINGLE, newObjInfoPtr->pos);
uint32_t actualPosToInsertAt = posToInsertAt = std::min<uint32_t>(posToInsertAt, data.size());
// Find duplicate. If found, erase the duplicate before inserting
auto itObjAlreadyInData = idToDataMap.find(newObj.get_net_id());
if(itObjAlreadyInData != idToDataMap.end()) {
uint32_t posToErase = itObjAlreadyInData->second->pos;
data.erase(data.begin() + posToErase);
idToDataMap.erase(itObjAlreadyInData);
if(posToErase < actualPosToInsertAt) // Shift insert position if an element was erased before the insert position
actualPosToInsertAt--;
}
// Insert the new object
auto newObjInfoPtr = std::make_shared<NetObjOrderedListObjectInfo<T>>(newObj, actualPosToInsertAt, false);
data.insert(data.begin() + actualPosToInsertAt, newObjInfoPtr);
idToDataMap.emplace(newObjInfoPtr->obj.get_net_id(), newObjInfoPtr);
l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr, posToInsertAt](const NetObjPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_StoC::INSERT_SINGLE, posToInsertAt); // Dont send actual pos to insert at, since client will check for duplicates on its own and shift the index if necessary
newObjInfoPtr->obj.write_create_message(a);
});
set_positions_for_object_info_vector<T>(data, actualPosToInsertAt);
}
virtual void erase_by_index(const NetObjPtr<NetObjOrderedList<T>>& l, uint32_t indexToErase) override {
if(data.empty())
return;
indexToErase = std::min<uint32_t>(indexToErase, data.size() - 1);
l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [indexToErase](const NetObjPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_StoC::ERASE_SINGLE, indexToErase);
});
NetObjID idToErase = data[indexToErase]->obj.get_net_id();
data.erase(data.begin() + indexToErase);
idToDataMap.erase(idToErase);
set_positions_for_object_info_vector<T>(data, indexToErase);
}
virtual void erase_by_ptr(const NetObjPtr<NetObjOrderedList<T>>& l, const NetObjOrderedListObjectInfoPtr<T>& p) override {
l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [indexToErase = p->pos](const NetObjPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_StoC::ERASE_SINGLE, indexToErase);
});
uint32_t iToErase = p->pos; // We only have a reference to the pointer, so erasing it might destroy the pointer object. Save the index before erasing
data.erase(data.begin() + iToErase);
set_positions_for_object_info_vector<T>(data, iToErase);
}
virtual void write_constructor(const NetObjPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) override {
a(static_cast<uint32_t>(data.size()));
for(size_t i = 0; i < data.size(); i++)
@ -156,18 +158,15 @@ namespace NetworkingObjects {
case ObjPtrOrderedListCommand_CtoS::ERASE_SINGLE:
NetObjID idToErase;
a(idToErase);
std::erase_if(data, [&](NetObjOrderedListObjectInfoPtr<T>& o) {
if(o->obj.get_net_id() == idToErase) {
erase_by_ptr(l, o);
return true;
}
return false;
});
auto it = idToDataMap.find(idToErase);
if(it != idToDataMap.end())
erase_by_index(l, it->second->pos);
break;
}
}
private:
std::vector<NetObjOrderedListObjectInfoPtr<T>> data;
std::unordered_map<NetObjID, NetObjOrderedListObjectInfoPtr<T>> idToDataMap;
};
template <typename T> class NetObjOrderedListClient : public NetObjOrderedList<T> {
@ -180,35 +179,40 @@ namespace NetworkingObjects {
return clientData;
}
protected:
virtual void push_back(const NetObjPtr<NetObjOrderedList<T>>& l, const NetObjPtr<T>& newObj) override {
auto& newObjInfoPtr = clientData.emplace_back(std::make_shared<NetObjOrderedListObjectInfo<T>>(newObj, static_cast<uint32_t>(clientData.size()), true));
l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_CtoS::INSERT_SINGLE, newObjInfoPtr->pos);
newObjInfoPtr->obj.write_create_message(a);
});
}
virtual void insert(const NetObjPtr<NetObjOrderedList<T>>& l, uint32_t posToInsertAt, const NetObjPtr<T>& newObj) {
posToInsertAt = std::min<uint32_t>(posToInsertAt, clientData.size());
auto& newObjInfoPtr = *clientData.insert(clientData.begin() + posToInsertAt, std::make_shared<NetObjOrderedListObjectInfo<T>>(newObj, posToInsertAt, true));
l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_CtoS::INSERT_SINGLE, newObjInfoPtr->pos);
uint32_t actualPosToInsertAt = posToInsertAt = std::min<uint32_t>(posToInsertAt, clientData.size());
// Find duplicate. If found, erase the duplicate before inserting
auto itObjAlreadyInData = clientIdToDataMap.find(newObj.get_net_id());
if(itObjAlreadyInData != clientIdToDataMap.end()) {
uint32_t posToErase = itObjAlreadyInData->second->pos;
clientData.erase(clientData.begin() + posToErase);
clientIdToDataMap.erase(itObjAlreadyInData);
if(posToErase < actualPosToInsertAt) // Shift insert position if an element was erased before the insert position
actualPosToInsertAt--;
}
// Insert the new object
auto newObjInfoPtr = std::make_shared<NetObjOrderedListObjectInfo<T>>(newObj, actualPosToInsertAt, false);
clientData.insert(clientData.begin() + actualPosToInsertAt, newObjInfoPtr);
clientIdToDataMap.emplace(newObjInfoPtr->obj.get_net_id(), newObjInfoPtr);
l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr, posToInsertAt](const NetObjPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_CtoS::INSERT_SINGLE, posToInsertAt); // Don't put actual pos to insert at, since the server will check for duplicates and might shift the index on its own
newObjInfoPtr->obj.write_create_message(a);
});
set_positions_for_object_info_vector<T>(clientData, actualPosToInsertAt);
}
virtual void erase_by_index(const NetObjPtr<NetObjOrderedList<T>>& l, uint32_t indexToErase) override {
indexToErase = std::min<uint32_t>(indexToErase, clientData.size());
auto& newObjInfoPtr = clientData[indexToErase];
l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_CtoS::ERASE_SINGLE, newObjInfoPtr->obj.get_net_id());
});
clientData.erase(clientData.begin() + indexToErase);
set_positions_for_object_info_vector<T>(clientData, indexToErase);
}
virtual void erase_by_ptr(const NetObjPtr<NetObjOrderedList<T>>& l, const NetObjOrderedListObjectInfoPtr<T>& p) override {
if(clientData.empty())
return;
indexToErase = std::min<uint32_t>(indexToErase, clientData.size() - 1);
auto& p = clientData[indexToErase];
l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&p](const NetObjPtr<NetObjOrderedList<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrOrderedListCommand_CtoS::ERASE_SINGLE, p->obj.get_net_id());
});
p->syncIgnore = true;
uint32_t iToErase = p->pos;
clientIdToDataMap.erase(p->obj.get_net_id());
clientData.erase(clientData.begin() + iToErase);
set_positions_for_object_info_vector<T>(clientData, iToErase);
}
@ -230,21 +234,64 @@ namespace NetworkingObjects {
ObjPtrOrderedListCommand_StoC c;
a(c);
switch(c) {
case ObjPtrOrderedListCommand_StoC::INSERT_SINGLE:
case ObjPtrOrderedListCommand_StoC::INSERT_SINGLE: {
uint32_t newPos;
a(newPos);
serverData.insert(serverData.begin() + newPos, std::make_shared<NetObjOrderedListObjectInfo<T>>(l.get_obj_man()->template read_create_message<T>(a, nullptr), newPos, false));
auto toInsert = std::make_shared<NetObjOrderedListObjectInfo<T>>(l.get_obj_man()->template read_create_message<T>(a, nullptr), newPos, false);
auto it = serverIdToDataMap.find(toInsert->obj.get_net_id());
if(it != serverIdToDataMap.end()) {
uint32_t posToErase = it->second->pos;
serverData.erase(serverData.begin() + posToErase);
serverIdToDataMap.erase(it);
if(posToErase < newPos)
newPos--; // Shift if there was a duplicate before this
}
serverIdToDataMap.emplace(toInsert->obj.get_net_id(), toInsert);
serverData.insert(serverData.begin() + newPos, toInsert);
break;
case ObjPtrOrderedListCommand_StoC::ERASE_SINGLE:
}
case ObjPtrOrderedListCommand_StoC::ERASE_SINGLE: {
uint32_t posToErase;
a(posToErase);
NetObjID objID = serverData[posToErase]->obj.get_net_id();
serverData.erase(serverData.begin() + posToErase);
serverIdToDataMap.erase(objID);
break;
}
}
sync();
}
private:
void sync() {
std::vector<std::pair<uint32_t, NetObjOrderedListObjectInfoPtr<T>>> ignoredToReinsert;
for(uint32_t i = 0; i < static_cast<uint32_t>(clientData.size()); i++)
if(clientData[i]->syncIgnore)
ignoredToReinsert.emplace_back(std::pair<uint32_t, NetObjOrderedListObjectInfoPtr<T>>{i, clientData[i]});
clientData = serverData;
clientIdToDataMap = serverIdToDataMap;
std::erase_if(clientData, [&](const auto& obj) {
if(obj->syncIgnore) {
clientIdToDataMap.erase(obj->obj.get_net_id());
return true;
}
return false;
});
for(auto& p : ignoredToReinsert) {
if(p.first > clientData.size())
clientData.emplace_back(p.second);
else
clientData.insert(clientData.begin() + p.first, p.second);
clientIdToDataMap.emplace(p.second->obj.get_net_id(), p.second);
}
set_positions_for_object_info_vector<T>(clientData);
}
std::vector<NetObjOrderedListObjectInfoPtr<T>> serverData;
std::unordered_map<NetObjID, NetObjOrderedListObjectInfoPtr<T>> serverIdToDataMap;
std::vector<NetObjOrderedListObjectInfoPtr<T>> clientData;
std::unordered_map<NetObjID, NetObjOrderedListObjectInfoPtr<T>> clientIdToDataMap;
};
template <typename S> void register_ordered_list_class(NetObjManagerTypeList& t) {

View file

@ -20,7 +20,7 @@ namespace NetworkingObjects {
{}
template <typename T> NetObjPtr<T>& NetObjPtr<T>::operator=(const NetObjPtr<T>& other) {
if(this != &other) {
if(sharedPtr && sharedPtr.unique())
if(sharedPtr && sharedPtr.use_count() == 2)
objMan->objectData.erase(id);
objMan = other.objMan;
id = other.id;
@ -29,7 +29,7 @@ namespace NetworkingObjects {
return *this;
}
template <typename T> NetObjPtr<T>::~NetObjPtr() {
if(sharedPtr && sharedPtr.unique())
if(sharedPtr && sharedPtr.use_count() == 2)
objMan->objectData.erase(id);
}
@ -90,9 +90,8 @@ namespace NetworkingObjects {
}
template <typename T> void NetObjPtr<T>::write_create_message(cereal::PortableBinaryOutputArchive& a) const {
auto& typeIndexData = objMan->isServer ? objMan->typeList->typeIndexDataServer[std::type_index(typeid(T*))] : objMan->typeList->typeIndexDataClient[std::type_index(typeid(T*))];
a(id, typeIndexData.netTypeID);
typeIndexData.writeConstructorFunc(this->cast<void>(), a);
a(id);
objMan->typeList->get_type_index_data<T>(objMan->isServer).writeConstructorFunc(this->cast<void>(), a);
}
template <typename T> NetObjPtr<T>::NetObjPtr(NetObjManager* initObjMan, NetObjID initID, const std::shared_ptr<T>& initPtr):