/*
* InfiniPaint
* Copyright (C) 2025-2026 Yousef Khadadeh
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#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);
void set_server(std::shared_ptr initServer, MessageCommandType initUpdateCommandID);
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
};
// 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(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(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(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(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(const std::string& channel, const NetObjTemporaryPtr& ptr, std::function&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) {
if(ptr.get_obj_man()->is_connected()) {
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;
};
bool isServer;
std::shared_ptr client;
std::shared_ptr server;
MessageCommandType updateCommandID;
std::unordered_map objectData;
NetObjManagerTypeList typeList;
NetTypeIDType nextTypeID;
std::function netIDReassignCallback;
std::function destroyCallback;
};
}