#pragma once #include "Helpers/Networking/NetLibrary.hpp" #include "NetObjPtr.hpp" #include "cereal/archives/portable_binary.hpp" #include "NetObjManagerTypeList.hpp" namespace NetworkingObjects { enum class ObjPtrOrderedListCommand_StoC : uint8_t { INSERT_SINGLE, ERASE_SINGLE }; enum class ObjPtrOrderedListCommand_CtoS : uint8_t { INSERT_SINGLE, ERASE_SINGLE }; template class NetObjOrderedListObjectInfo { public: NetObjOrderedListObjectInfo(const NetObjPtr& initObj, uint32_t initPos, bool initSyncIgnore): obj(initObj), pos(initPos), syncIgnore(initSyncIgnore) {} uint32_t get_pos() const { return pos; } const NetObjPtr& get_obj() const { return obj; } private: NetObjPtr obj; uint32_t pos; bool syncIgnore; template friend class NetObjOrderedList; template friend class NetObjOrderedListServer; template friend class NetObjOrderedListClient; template friend void set_positions_for_object_info_vector(std::vector>>& v, uint32_t i); }; template using NetObjOrderedListObjectInfoPtr = std::shared_ptr>; template void set_positions_for_object_info_vector(std::vector>& v, uint32_t i = 0) { uint32_t vSize = static_cast(v.size()); for(; i < vSize; i++) v[i]->pos = i; } template class NetObjOrderedList { public: template static NetObjPtr emplace_back(const NetObjPtr>& l, Args&&... args) { NetObjPtr newObj = l.get_obj_man()->template make_obj(args...); return push_back_and_send_create(l, newObj); } static NetObjPtr push_back_and_send_create(const NetObjPtr>& l, const NetObjPtr& newObj) { l->push_back(l, newObj); return newObj; } static NetObjPtr insert_and_send_create(const NetObjPtr>& l, uint32_t posToInsertAt, const NetObjPtr& newObj) { l->insert(l, posToInsertAt, newObj); return newObj; } static void erase(const NetObjPtr>& l, uint32_t indexToErase) { l->erase_by_index(l, indexToErase); } static void erase(const NetObjPtr>& l, const NetObjOrderedListObjectInfoPtr& p) { l->erase_by_ptr(l, p); } virtual uint32_t size() const = 0; virtual const std::vector>& get_data() const = 0; protected: virtual void push_back(const NetObjPtr>& l, const NetObjPtr& newObj) = 0; virtual void insert(const NetObjPtr>& l, uint32_t posToInsertAt, const NetObjPtr& newObj) = 0; virtual void erase_by_index(const NetObjPtr>& l, uint32_t indexToErase) = 0; virtual void erase_by_ptr(const NetObjPtr>& l, const NetObjOrderedListObjectInfoPtr& p) = 0; virtual void write_constructor(const NetObjPtr>& l, cereal::PortableBinaryOutputArchive& a) = 0; virtual void read_update(const NetObjPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) = 0; virtual void read_constructor_post_allocation(const NetObjPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) = 0; private: template friend void register_ordered_list_class(NetObjManagerTypeList& t); static void write_constructor_func(const NetObjPtr>& l, cereal::PortableBinaryOutputArchive& a) { l->write_constructor(l, a); } static NetObjPtr read_constructor_func(NetObjManager& objMan, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c); static void read_update_func(const NetObjPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) { l->read_update(l, a, c); } }; template class NetObjOrderedListServer : public NetObjOrderedList { public: virtual uint32_t size() const override { return data.size(); } virtual const std::vector>& get_data() const override { return data; } protected: virtual void push_back(const NetObjPtr>& l, const NetObjPtr& newObj) override { auto& newObjInfoPtr = data.emplace_back(std::make_shared>(newObj, static_cast(data.size()), false)); l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { a(ObjPtrOrderedListCommand_StoC::INSERT_SINGLE, newObjInfoPtr->pos); newObjInfoPtr->obj.write_create_message(a); }); } virtual void insert(const NetObjPtr>& l, uint32_t posToInsertAt, const NetObjPtr& newObj) override { posToInsertAt = std::min(posToInsertAt, data.size()); auto& newObjInfoPtr = *data.insert(data.begin() + posToInsertAt, std::make_shared>(newObj, posToInsertAt, false)); set_positions_for_object_info_vector(data, posToInsertAt); l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { a(ObjPtrOrderedListCommand_StoC::INSERT_SINGLE, newObjInfoPtr->pos); newObjInfoPtr->obj.write_create_message(a); }); } virtual void erase_by_index(const NetObjPtr>& l, uint32_t indexToErase) override { l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [indexToErase](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { a(ObjPtrOrderedListCommand_StoC::ERASE_SINGLE, indexToErase); }); data.erase(data.begin() + indexToErase); set_positions_for_object_info_vector(data, indexToErase); } virtual void erase_by_ptr(const NetObjPtr>& l, const NetObjOrderedListObjectInfoPtr& p) override { l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [indexToErase = p->pos](const NetObjPtr>&, 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(data, iToErase); } virtual void write_constructor(const NetObjPtr>& l, cereal::PortableBinaryOutputArchive& a) override { } virtual void read_constructor_post_allocation(const NetObjPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) override { } virtual void read_update(const NetObjPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) override { ObjPtrOrderedListCommand_CtoS command; a(command); switch(command) { case ObjPtrOrderedListCommand_CtoS::INSERT_SINGLE: uint32_t newPos; a(newPos); insert(l, newPos, l.get_obj_man()->template read_create_message(a, c)); break; case ObjPtrOrderedListCommand_CtoS::ERASE_SINGLE: NetObjID idToErase; a(idToErase); std::erase_if(data, [&](NetObjOrderedListObjectInfoPtr& o) { if(o->obj.get_net_id() == idToErase) { erase_by_ptr(l, o); return true; } return false; }); break; } } private: std::vector> data; }; template class NetObjOrderedListClient : public NetObjOrderedList { public: virtual uint32_t size() const override { return clientData.size(); } virtual const std::vector>& get_data() const override { return clientData; } protected: virtual void push_back(const NetObjPtr>& l, const NetObjPtr& newObj) override { auto& newObjInfoPtr = clientData.emplace_back(std::make_shared>(newObj, static_cast(clientData.size()), true)); l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { a(ObjPtrOrderedListCommand_CtoS::INSERT_SINGLE, newObjInfoPtr->pos); newObjInfoPtr->obj.write_create_message(a); }); } virtual void insert(const NetObjPtr>& l, uint32_t posToInsertAt, const NetObjPtr& newObj) { } virtual void erase_by_index(const NetObjPtr>& l, uint32_t indexToErase) override { indexToErase = std::min(indexToErase, clientData.size()); auto& newObjInfoPtr = clientData[indexToErase]; l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&newObjInfoPtr](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { a(ObjPtrOrderedListCommand_CtoS::ERASE_SINGLE, newObjInfoPtr->obj.get_net_id()); }); clientData.erase(clientData.begin() + indexToErase); set_positions_for_object_info_vector(clientData, indexToErase); } virtual void erase_by_ptr(const NetObjPtr>& l, const NetObjOrderedListObjectInfoPtr& p) override { l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&p](const NetObjPtr>&, cereal::PortableBinaryOutputArchive& a) { a(ObjPtrOrderedListCommand_CtoS::ERASE_SINGLE, p->obj.get_net_id()); }); uint32_t iToErase = p->pos; clientData.erase(clientData.begin() + iToErase); set_positions_for_object_info_vector(clientData, iToErase); } virtual void write_constructor(const NetObjPtr>& l, cereal::PortableBinaryOutputArchive& a) override { } virtual void read_constructor_post_allocation(const NetObjPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) override { } virtual void read_update(const NetObjPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) override { ObjPtrOrderedListCommand_StoC c; a(c); switch(c) { case ObjPtrOrderedListCommand_StoC::INSERT_SINGLE: uint32_t newPos; a(newPos); serverData.insert(serverData.begin() + newPos, std::make_shared>(l.get_obj_man()->template read_create_message(a, nullptr), newPos, false)); break; case ObjPtrOrderedListCommand_StoC::ERASE_SINGLE: uint32_t posToErase; a(posToErase); serverData.erase(serverData.begin() + posToErase); break; } } private: std::vector> serverData; std::vector> clientData; }; template NetObjPtr NetObjOrderedList::read_constructor_func(NetObjManager& objMan, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) { NetObjPtr> allocatedPtr = objMan.is_server() ? objMan.make_obj>().template cast>() : objMan.make_obj>().template cast>(); allocatedPtr->read_constructor_post_allocation(allocatedPtr, a, c); return allocatedPtr.template cast(); } template void register_ordered_list_class(NetObjManagerTypeList& t) { t.register_class, NetObjOrderedList>({ .writeConstructorFuncClient = NetObjOrderedList::write_constructor_func, .readConstructorFuncClient = NetObjOrderedList::read_constructor_func, .readUpdateFuncClient = NetObjOrderedList::read_update_func, .writeConstructorFuncServer = NetObjOrderedList::write_constructor_func, .readConstructorFuncServer = NetObjOrderedList::read_constructor_func, .readUpdateFuncServer = NetObjOrderedList::read_update_func, }); } }