#pragma once #include #include #include #include #include #include #include #include #include "../Networking/NetClient.hpp" #include "../Networking/NetServer.hpp" #include "../Networking/NetLibrary.hpp" #include "Helpers/NetworkingObjects/NetObjID.hpp" #include "NetObjManagerTypeList.hpp" #include "NetObjOwnerPtr.decl.hpp" #include "NetObjTemporaryPtr.decl.hpp" #include "../Networking/NetServer.hpp" namespace NetworkingObjects { class NetObjManager { public: NetObjManager(bool initIsServer); void read_update_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr& clientReceivedFrom); void read_many_update_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr& clientReceivedFrom); void set_client(std::shared_ptr initClient, MessageCommandType initUpdateCommandID, MessageCommandType initMultiUpdateCommandID); void set_server(std::shared_ptr initServer, MessageCommandType initUpdateCommandID, MessageCommandType initMultiUpdateCommandID); void disconnect(); bool is_server() const; bool is_connected() const; void set_netid_reassign_callback(const std::function& newNetIDReassignCallback); void set_netobj_destroy_callback(const std::function& newDestroyCallback); template NetObjOwnerPtr read_create_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr& clientReceivedFrom) { NetObjID id; a(id); auto it = objectData.find(id); if(it != objectData.end()) throw std::runtime_error("[NetObjManager::read_create_message] Attempted to create an object with a used ID"); NetObjOwnerPtr newPtr = emplace_raw_ptr(id, static_cast(typeList.get_type_index_data(isServer).allocatorFunc())); typeList.get_type_index_data(isServer).readConstructorFunc(NetObjTemporaryPtr(newPtr).template cast(), a, clientReceivedFrom); return newPtr; } template NetObjTemporaryPtr read_get_obj_temporary_ref_from_message(cereal::PortableBinaryInputArchive& a) { NetObjID id; a(id); auto it = objectData.find(id); if(it == objectData.end()) return NetObjTemporaryPtr(); return NetObjTemporaryPtr(this, id, std::static_pointer_cast(it->second.p)); } enum class SendUpdateType { SEND_TO_ALL, SEND_TO_ALL_EXCEPT, SEND_TO_SPECIFIC_CLIENT }; void send_multi_update_messsage(std::function captureSendBlock, SendUpdateType updateType, const std::shared_ptr& specificClient); // NOTE: DO NOT USE THESE FUNCTIONS UNLESS SITUATION ISN'T NORMAL, EVERY OBJECT SHOULD HAVE A UNIQUE ID AND IDS SHOULDNT BE REUSED UNDER ANY CIRCUMSTANCES template NetObjOwnerPtr make_obj_with_specific_id(const NetObjID& id) { return emplace_raw_ptr(id, static_cast(typeList.get_type_index_data(isServer).allocatorFunc())); } template NetObjOwnerPtr make_obj_direct_with_specific_id(const NetObjID& id, Args&&... items) { return emplace_raw_ptr(id, new T(items...)); } template NetObjOwnerPtr make_obj() { return emplace_raw_ptr(NetObjID::random_gen(), static_cast(typeList.get_type_index_data(isServer).allocatorFunc())); } // Don't use this function unless you're sure that class T isn't a base class template NetObjOwnerPtr make_obj_direct(Args&&... items) { return emplace_raw_ptr(NetObjID::random_gen(), new T(items...)); } template NetObjOwnerPtr make_obj_from_ptr(T* p) { return emplace_raw_ptr(NetObjID::random_gen(), p); } template NetObjTemporaryPtr get_obj_temporary_ref_from_id(NetObjID id) { auto it = objectData.find(id); if(it == objectData.end()) return NetObjTemporaryPtr(); return NetObjTemporaryPtr(this, id, static_cast(it->second.p)); } template void register_class(const NetObjManagerTypeList::ServerClientClassFunctions& funcs) { typeList.register_class(funcs); } private: void send_update_data_by_type(const std::string& channel, const std::shared_ptr& ss, SendUpdateType updateType, const std::shared_ptr& specificClient); template NetObjOwnerPtr emplace_raw_ptr(NetObjID id, T* rawPtr) { if(!objectData.emplace(id, SingleObjectData{.netTypeID = typeList.get_type_index_data(isServer).netTypeID, .p = rawPtr}).second) throw std::runtime_error("[NetObjManager::emplace_raw_ptr] ID Collision"); return NetObjOwnerPtr(this, id, rawPtr); } template static void send_update_to_all(const NetObjTemporaryPtr& ptr, const std::string& channel, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { if(ptr.get_obj_man()->isServer) NetObjManager::send_server_update_to_all_clients(ptr, channel, sendUpdateFunc); else NetObjManager::send_client_update(ptr, channel, sendUpdateFunc); } template static void send_client_update(const NetObjTemporaryPtr& ptr, const std::string& channel, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { auto ss = send_update_general_check_for_multi_update(channel, ptr, sendUpdateFunc); if(ss) ptr.get_obj_man()->client->send_string_stream_to_server(channel, ss); } template static void send_server_update_to_client(const NetObjTemporaryPtr& ptr, const std::shared_ptr& clientToSendTo, const std::string& channel, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { if(clientToSendTo) { auto ss = send_update_general_check_for_multi_update(channel, ptr, sendUpdateFunc); if(ss) ptr.get_obj_man()->server->send_string_stream_to_client(clientToSendTo, channel, ss); } } template static void send_server_update_to_all_clients_except(const NetObjTemporaryPtr& ptr, const std::shared_ptr& clientToNotSendTo, const std::string& channel, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { auto ss = send_update_general_check_for_multi_update(channel, ptr, sendUpdateFunc); if(ss) ptr.get_obj_man()->server->send_string_stream_to_all_clients_except(clientToNotSendTo, channel, ss); } template static void send_server_update_to_all_clients(const NetObjTemporaryPtr& ptr, const std::string& channel, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { auto ss = send_update_general_check_for_multi_update(channel, ptr, sendUpdateFunc); if(ss) ptr.get_obj_man()->server->send_string_stream_to_all_clients(channel, ss); } template static std::shared_ptr send_update_general_check_for_multi_update(const std::string& channel, const NetObjTemporaryPtr& ptr, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) { if(ptr.get_obj_man()->is_connected()) { std::shared_ptr mData = ptr.get_obj_man()->multiUpdateData.lock(); if(mData) { mData->channelToSendTo = channel; (*mData->outArchive)(true, ptr.get_net_id()); sendUpdateFunc(ptr, *mData->outArchive); return nullptr; } else { auto ss(std::make_shared(std::ios::binary | std::ios::out)); { cereal::PortableBinaryOutputArchive m(*ss); m(ptr.get_obj_man()->updateCommandID, ptr.get_net_id()); sendUpdateFunc(ptr, m); } return ss; } } return nullptr; } template static void write_create_message(const NetObjTemporaryPtr& ptr, cereal::PortableBinaryOutputArchive& a) { a(ptr.get_net_id()); ptr.get_obj_man()->typeList.template get_type_index_data(ptr.get_obj_man()->isServer).writeConstructorFunc(ptr.template cast(), a); } template friend class NetObjOwnerPtr; template friend class NetObjTemporaryPtr; template friend class NetObjWeakPtr; struct SingleObjectData { NetTypeIDType netTypeID; void* p; }; struct MultiUpdateData { std::string channelToSendTo; std::shared_ptr ss; std::unique_ptr outArchive; }; std::weak_ptr multiUpdateData; bool isServer; std::shared_ptr client; std::shared_ptr server; MessageCommandType updateCommandID; MessageCommandType multiUpdateCommandID; std::unordered_map objectData; NetObjManagerTypeList typeList; NetTypeIDType nextTypeID; std::function netIDReassignCallback; std::function destroyCallback; }; }