/*
* 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 "Helpers/Networking/NetLibrary.hpp"
#include "Helpers/NetworkingObjects/NetObjOwnerPtr.decl.hpp"
#include "NetObjOwnerPtr.hpp"
#include "NetObjTemporaryPtr.hpp"
#include "NetObjWeakPtr.hpp"
#include "NetObjID.hpp"
#include
#include
#include "NetObjManagerTypeList.hpp"
#include
namespace NetworkingObjects {
enum class ObjPtrUnorderedSetCommand_StoC : uint8_t {
INSERT_SINGLE_CONSTRUCT,
ERASE_SINGLE
};
enum class ObjPtrUnorderedSetCommand_CtoS : uint8_t {
INSERT_SINGLE,
ERASE_SINGLE
};
template class NetObjUnorderedSet {
public:
// Don't use this function unless you're sure that class T isn't a base class
template NetObjTemporaryPtr emplace_direct(const NetObjTemporaryPtr>& l, Args&&... items) {
NetObjOwnerPtr newObj = l.get_obj_man()->template make_obj_direct(items...);
return l->insert(l, nullptr, std::move(newObj));
}
static NetObjTemporaryPtr insert_and_send_create(const NetObjTemporaryPtr>& l, T* newObj) {
NetObjOwnerPtr newObjOwner = l.get_obj_man()->template make_obj_from_ptr(newObj);
return l->insert(l, nullptr, std::move(newObjOwner));
}
static bool erase(const NetObjTemporaryPtr>& l, const NetObjID& id) {
return l->erase_by_id(l, id);
}
typedef std::function& objPtr)> InsertCallback;
typedef std::function& objPtr)> EraseCallback;
void set_insert_callback(const InsertCallback& func) {
insertCallback = func;
}
void set_erase_callback(const EraseCallback& func) {
eraseCallback = func;
}
bool contains(const NetObjID& p) const {
return std::find_if(data.begin(), data.end(), [&p](const NetObjOwnerPtr& o){ return o.get_net_id() == p; }) != data.end();
}
const std::vector>& get_data() const {
return data;
}
uint32_t size() const {
return data.size();
}
bool empty() const {
return data.empty();
}
virtual ~NetObjUnorderedSet() {}
protected:
NetObjTemporaryPtr insert(const NetObjTemporaryPtr>& l, const std::shared_ptr& clientInserting, NetObjOwnerPtr newObj) {
auto& objAdded = this->data.emplace_back(std::move(newObj));
insert_network(l, clientInserting, objAdded);
call_insert_callback(objAdded);
return objAdded;
}
bool erase_by_id(const NetObjTemporaryPtr>& l, const NetObjID& id) {
auto it = std::find_if(this->data.begin(), this->data.end(), [&id](const NetObjOwnerPtr& o){ return o.get_net_id() == id; });
if(it == this->data.end())
return false;
call_erase_callback(*it);
erase_network(l, id);
data.erase(it);
return true;
}
virtual void insert_network(const NetObjTemporaryPtr>& l, const std::shared_ptr& clientInserting, const NetObjOwnerPtr& newObj) const = 0;
virtual void erase_network(const NetObjTemporaryPtr>& l, const NetObjID& id) const = 0;
virtual void read_update(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) = 0;
void write_constructor(const NetObjTemporaryPtr>& l, cereal::PortableBinaryOutputArchive& a) {
a(static_cast(data.size()));
for(size_t i = 0; i < data.size(); i++)
data[i].write_create_message(a);
}
void read_constructor(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) {
uint32_t newDataSize;
a(newDataSize);
for(size_t i = 0; i < newDataSize; i++)
data.emplace_back(l.get_obj_man()->template read_create_message(a, c));
}
// Call after inserting
void call_insert_callback(const NetObjOwnerPtr& objPtr) {
if(insertCallback)
insertCallback(objPtr);
}
// Call before erasing
void call_erase_callback(const NetObjOwnerPtr& objPtr) {
if(eraseCallback)
eraseCallback(objPtr);
}
std::vector> data;
private:
InsertCallback insertCallback;
EraseCallback eraseCallback;
template friend void register_unordered_set_class(NetObjManager& t);
static void write_constructor_func(const NetObjTemporaryPtr>& l, cereal::PortableBinaryOutputArchive& a) {
l->write_constructor(l, a);
}
static void read_constructor_func(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) {
l->read_constructor(l, a, c);
}
static void read_update_func(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) {
l->read_update(l, a, c);
}
};
template class NetObjUnorderedSetServer : public NetObjUnorderedSet {
public:
NetObjUnorderedSetServer() {}
protected:
virtual void insert_network(const NetObjTemporaryPtr>& l, const std::shared_ptr& clientInserting, const NetObjOwnerPtr& newObj) const override {
l.send_server_update_to_all_clients_except(clientInserting, RELIABLE_COMMAND_CHANNEL, [&newObj](const NetObjTemporaryPtr>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrUnorderedSetCommand_StoC::INSERT_SINGLE_CONSTRUCT);
newObj.write_create_message(a);
});
}
virtual void erase_network(const NetObjTemporaryPtr>& l, const NetObjID& id) const override {
l.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [id](const NetObjTemporaryPtr>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrUnorderedSetCommand_StoC::ERASE_SINGLE, id);
});
}
virtual void read_update(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) override {
ObjPtrUnorderedSetCommand_CtoS command;
a(command);
switch(command) {
case ObjPtrUnorderedSetCommand_CtoS::INSERT_SINGLE: {
NetObjUnorderedSet::insert(l, c, l.get_obj_man()->template read_create_message(a, c));
break;
}
case ObjPtrUnorderedSetCommand_CtoS::ERASE_SINGLE: {
NetObjID idToErase;
a(idToErase);
NetObjUnorderedSet::erase_by_id(l, idToErase);
break;
}
}
}
};
template class NetObjUnorderedSetClient : public NetObjUnorderedSet {
public:
NetObjUnorderedSetClient() {}
protected:
virtual void insert_network(const NetObjTemporaryPtr>& l, const std::shared_ptr&, const NetObjOwnerPtr& newObj) const override {
l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&newObj](const NetObjTemporaryPtr>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrUnorderedSetCommand_CtoS::INSERT_SINGLE);
newObj.write_create_message(a);
});
}
virtual void erase_network(const NetObjTemporaryPtr>& l, const NetObjID& id) const override {
l.send_client_update(RELIABLE_COMMAND_CHANNEL, [id](const NetObjTemporaryPtr>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrUnorderedSetCommand_CtoS::ERASE_SINGLE, id);
});
}
virtual void read_update(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) override {
ObjPtrUnorderedSetCommand_StoC command;
a(command);
switch(command) {
case ObjPtrUnorderedSetCommand_StoC::INSERT_SINGLE_CONSTRUCT: {
this->data.emplace_back(l.get_obj_man()->template read_create_message(a, c));
break;
}
case ObjPtrUnorderedSetCommand_StoC::ERASE_SINGLE: {
NetObjID id;
a(id);
auto it = std::find_if(this->data.begin(), this->data.end(), [&id](const NetObjOwnerPtr& o){ return o.get_net_id() == id; });
if(it != this->data.end())
this->data.erase(it);
break;
}
}
}
};
template void register_unordered_set_class(NetObjManager& objMan) {
objMan.register_class, NetObjUnorderedSet, NetObjUnorderedSetClient, NetObjUnorderedSetServer>({
.writeConstructorFuncClient = NetObjUnorderedSet::write_constructor_func,
.readConstructorFuncClient = NetObjUnorderedSet::read_constructor_func,
.readUpdateFuncClient = NetObjUnorderedSet::read_update_func,
.writeConstructorFuncServer = NetObjUnorderedSet::write_constructor_func,
.readConstructorFuncServer = NetObjUnorderedSet::read_constructor_func,
.readUpdateFuncServer = NetObjUnorderedSet::read_update_func,
});
}
}