diff --git a/CMakeLists.txt b/CMakeLists.txt index 164011d..baede0d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,8 @@ set(sources "src/main.cpp" "include/Helpers/Networking/NetServer.cpp" "include/Helpers/Networking/NetClient.cpp" "include/Helpers/Networking/ByteStream.cpp" + "include/Helpers/NetworkingObjects/NetObjIDGenerator.cpp" + "include/Helpers/NetworkingObjects/NetObjManager.cpp" "include/Helpers/Hashes.cpp" "include/Helpers/BezierEasing.cpp" "include/Helpers/Random.cpp" diff --git a/include/Helpers/Networking/NetClient.cpp b/include/Helpers/Networking/NetClient.cpp index a6f9abe..c60fd6b 100644 --- a/include/Helpers/Networking/NetClient.cpp +++ b/include/Helpers/Networking/NetClient.cpp @@ -93,6 +93,27 @@ void NetClient::parse_received_messages() { } } +void NetClient::send_string_stream_to_server(const std::string& channel, const std::shared_ptr& ss) { + if(isDisconnected) + return; + + auto& messageQueue = messageQueues[channel]; + + if(channel == UNRELIABLE_COMMAND_CHANNEL) { + if(ss->view().length() <= NetLibrary::MAX_UNRELIABLE_MESSAGE_SIZE) // Drop unreliable messages that are too big + messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, nextMessageOrderToSend), ss); + } + else { + std::vector> fragmentedMessage = fragment_message(ss->view(), NetLibrary::FRAGMENT_MESSAGE_STRIDE); + if(fragmentedMessage.empty()) + messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, nextMessageOrderToSend), ss); + else { + for(auto& ss2 : fragmentedMessage) + messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, nextMessageOrderToSend), ss2); + } + } +} + NetLibrary::DownloadProgress NetClient::get_progress_into_fragmented_message(const std::string& channel) const { auto it = pfm.find(channel); if(it == pfm.end()) diff --git a/include/Helpers/Networking/NetClient.hpp b/include/Helpers/Networking/NetClient.hpp index 112dc51..f2effb8 100644 --- a/include/Helpers/Networking/NetClient.hpp +++ b/include/Helpers/Networking/NetClient.hpp @@ -25,23 +25,9 @@ class NetClient : public std::enable_shared_from_this { cereal::PortableBinaryOutputArchive m(*ss); (m(items), ...); } - - auto& messageQueue = messageQueues[channel]; - - if(channel == UNRELIABLE_COMMAND_CHANNEL) { - if(ss->view().length() <= NetLibrary::MAX_UNRELIABLE_MESSAGE_SIZE) // Drop unreliable messages that are too big - messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, nextMessageOrderToSend), ss); - } - else { - std::vector> fragmentedMessage = fragment_message(ss->view(), NetLibrary::FRAGMENT_MESSAGE_STRIDE); - if(fragmentedMessage.empty()) - messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, nextMessageOrderToSend), ss); - else { - for(auto& ss2 : fragmentedMessage) - messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, nextMessageOrderToSend), ss2); - } - } + send_string_stream_to_server(channel, ss); } + void send_string_stream_to_server(const std::string& channel, const std::shared_ptr& ss); void update(); void add_recv_callback(MessageCommandType commandID, const NetClientRecvCallback& callback); bool is_disconnected() const; diff --git a/include/Helpers/Networking/NetLibrary.hpp b/include/Helpers/Networking/NetLibrary.hpp index bb8003e..2f8496a 100644 --- a/include/Helpers/Networking/NetLibrary.hpp +++ b/include/Helpers/Networking/NetLibrary.hpp @@ -17,6 +17,7 @@ template std::weak_ptr make_weak_ptr(std::shared_ptr ptr) { retu #define MAIN_NETLIBRARY_CHANNEL "main00005" // NOTE: Change this value whenever netcode is incompatible with the previous version typedef uint8_t MessageCommandType; +typedef uint8_t NetTypeIDType; class NetLibrary { public: diff --git a/include/Helpers/Networking/NetServer.cpp b/include/Helpers/Networking/NetServer.cpp index 864f48b..4cbc7db 100644 --- a/include/Helpers/Networking/NetServer.cpp +++ b/include/Helpers/Networking/NetServer.cpp @@ -93,6 +93,66 @@ void NetServer::client_connected(std::shared_ptr connection }); } +void NetServer::send_string_stream_to_client(const std::shared_ptr& client, const std::string& channel, const std::shared_ptr& ss) { + if(!client) + return; + + auto& messageQueue = client->messageQueues[channel]; + if(channel == UNRELIABLE_COMMAND_CHANNEL) { + if(ss->view().length() <= NetLibrary::MAX_UNRELIABLE_MESSAGE_SIZE) // Drop unreliable messages that are too big + messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss); + } + else { + std::vector> fragmentedMessage = fragment_message(ss->view(), NetLibrary::FRAGMENT_MESSAGE_STRIDE); + if(fragmentedMessage.empty()) + messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss); + else { + for(auto& ss2 : fragmentedMessage) + messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss2); + } + } +} + +void NetServer::send_string_stream_to_all_clients(const std::string& channel, const std::shared_ptr& ss) { + send_string_stream_to_client_if([&](const std::shared_ptr& c) { + return true; + }, channel, ss); +} + +void NetServer::send_string_stream_to_all_clients_except(const std::shared_ptr& client, const std::string& channel, const std::shared_ptr& ss) { + send_string_stream_to_client_if([&](const std::shared_ptr& c) { + return c != client; + }, channel, ss); +} + +void NetServer::send_string_stream_to_client_if(std::function&)> clientChecker, const std::string& channel, const std::shared_ptr& ss) { + if(channel == UNRELIABLE_COMMAND_CHANNEL) { + if(ss->view().length() <= NetLibrary::MAX_UNRELIABLE_MESSAGE_SIZE) { // Drop unreliable messages that are too big + for(auto& client : clients) { + if(client && clientChecker(client)) { + auto& messageQueue = client->messageQueues[channel]; + messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss); + } + } + } + } + else { + std::vector> fragmentedMessage = fragment_message(ss->view(), NetLibrary::FRAGMENT_MESSAGE_STRIDE); + + for(auto& client : clients) { + if(client && clientChecker(client)) { + auto& messageQueue = client->messageQueues[channel]; + if(fragmentedMessage.empty()) + messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss); + else { + for(auto& ss2 : fragmentedMessage) + messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss2); + } + } + } + } +} + void NetServer::ClientData::send_queued_messages(NetServer& server) { for(auto& [channelName, messageQueue] : messageQueues) { bool addMessageOrder = NetLibrary::is_ordered_channel(channelName); diff --git a/include/Helpers/Networking/NetServer.hpp b/include/Helpers/Networking/NetServer.hpp index 5b3c5c4..010f968 100644 --- a/include/Helpers/Networking/NetServer.hpp +++ b/include/Helpers/Networking/NetServer.hpp @@ -54,75 +54,32 @@ class NetServer : public std::enable_shared_from_this { NetServer(const std::string& serverLocalID); ~NetServer(); void update(); - template void send_items_to_client(std::shared_ptr client, const std::string& channel, Args&&... items) { - if(!client) - return; - - auto ss(std::make_shared()); - { - cereal::PortableBinaryOutputArchive m(*ss); - (m(items), ...); - } - - auto& messageQueue = client->messageQueues[channel]; - if(channel == UNRELIABLE_COMMAND_CHANNEL) { - if(ss->view().length() <= NetLibrary::MAX_UNRELIABLE_MESSAGE_SIZE) // Drop unreliable messages that are too big - messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss); - } - else { - std::vector> fragmentedMessage = fragment_message(ss->view(), NetLibrary::FRAGMENT_MESSAGE_STRIDE); - if(fragmentedMessage.empty()) - messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss); - else { - for(auto& ss2 : fragmentedMessage) - messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss2); - } - } + template void send_items_to_client(const std::shared_ptr& client, const std::string& channel, Args&&... items) { + send_string_stream_to_client(client, channel, get_string_stream_from_items(items...)); } template void send_items_to_all_clients(const std::string& channel, Args&&... items) { - send_items_to_client_if([&](std::shared_ptr c) { - return true; - }, channel, items...); + send_string_stream_to_all_clients(channel, get_string_stream_from_items(items...)); } - template void send_items_to_all_clients_except(std::shared_ptr client, const std::string& channel, Args&&... items) { - send_items_to_client_if([&](std::shared_ptr c) { - return c != client; - }, channel, items...); + template void send_items_to_all_clients_except(const std::shared_ptr& client, const std::string& channel, Args&&... items) { + send_string_stream_to_all_clients_except(client, channel, get_string_stream_from_items(items...)); } - template void send_items_to_client_if(std::function)> clientChecker, const std::string& channel, Args&&... items) { + template void send_items_to_client_if(std::function&)> clientChecker, const std::string& channel, Args&&... items) { + send_string_stream_to_client_if(clientChecker, channel, get_string_stream_from_items(items...)); + } + template std::shared_ptr get_string_stream_from_items(Args&&... items) { auto ss(std::make_shared()); { cereal::PortableBinaryOutputArchive m(*ss); (m(items), ...); } - - if(channel == UNRELIABLE_COMMAND_CHANNEL) { - if(ss->view().length() <= NetLibrary::MAX_UNRELIABLE_MESSAGE_SIZE) { // Drop unreliable messages that are too big - for(auto& client : clients) { - if(client && clientChecker(client)) { - auto& messageQueue = client->messageQueues[channel]; - messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss); - } - } - } - } - else { - std::vector> fragmentedMessage = fragment_message(ss->view(), NetLibrary::FRAGMENT_MESSAGE_STRIDE); - - for(auto& client : clients) { - if(client && clientChecker(client)) { - auto& messageQueue = client->messageQueues[channel]; - if(fragmentedMessage.empty()) - messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss); - else { - for(auto& ss2 : fragmentedMessage) - messageQueue.emplace(NetLibrary::calc_order_for_queued_message(channel, client->nextMessageOrderToSend), ss2); - } - } - } - } + return ss; } + void send_string_stream_to_client(const std::shared_ptr& client, const std::string& channel, const std::shared_ptr& ss); + void send_string_stream_to_all_clients(const std::string& channel, const std::shared_ptr& ss); + void send_string_stream_to_all_clients_except(const std::shared_ptr& client, const std::string& channel, const std::shared_ptr& ss); + void send_string_stream_to_client_if(std::function&)> clientChecker, const std::string& channel, const std::shared_ptr& ss); + bool is_disconnected() const; void add_recv_callback(MessageCommandType commandID, const NetServerRecvCallback& callback); diff --git a/include/Helpers/NetworkingObjects/NetObjIDGenerator.cpp b/include/Helpers/NetworkingObjects/NetObjIDGenerator.cpp new file mode 100644 index 0000000..ac905b6 --- /dev/null +++ b/include/Helpers/NetworkingObjects/NetObjIDGenerator.cpp @@ -0,0 +1,14 @@ +#include "NetObjIDGenerator.hpp" + +namespace NetworkingObjects { + +void NetObjIDGenerator::set_starting_id(NetObjID c) { + currentID = c; +} + +NetObjID NetObjIDGenerator::gen() { + currentID.clientID++; + return currentID; +} + +} diff --git a/include/Helpers/NetworkingObjects/NetObjIDGenerator.hpp b/include/Helpers/NetworkingObjects/NetObjIDGenerator.hpp new file mode 100644 index 0000000..210d47b --- /dev/null +++ b/include/Helpers/NetworkingObjects/NetObjIDGenerator.hpp @@ -0,0 +1,35 @@ +#pragma once +#include +#include "../Hashes.hpp" + +namespace NetworkingObjects { + +struct NetObjID { + typedef uint16_t ClientPortion; + typedef uint64_t ObjectPortion; + ClientPortion clientID = 0; + ObjectPortion objID = 0; + bool operator==(const NetObjID&) const = default; + template void serialize(Archive& a) { + a(clientID, objID); + } +}; + +class NetObjIDGenerator { +public: + void set_starting_id(NetObjID c); + NetObjID gen(); +private: + NetObjID currentID; +}; + +} + +template <> struct std::hash { + size_t operator()(const NetworkingObjects::NetObjID& v) const + { + uint64_t h = 0; + hash_combine(h, v.clientID, v.objID); + return h; + } +}; diff --git a/include/Helpers/NetworkingObjects/NetObjManager.cpp b/include/Helpers/NetworkingObjects/NetObjManager.cpp new file mode 100644 index 0000000..3509b99 --- /dev/null +++ b/include/Helpers/NetworkingObjects/NetObjManager.cpp @@ -0,0 +1,26 @@ +#include "NetObjManager.hpp" + +namespace NetworkingObjects { + NetObjManager::NetObjManager(std::shared_ptr initIDGen, std::shared_ptr initTypeList): + idGen(initIDGen), + typeList(initTypeList), + nextTypeID(0) + {} + void NetObjManager::read_update_message(cereal::PortableBinaryInputArchive& a) { + NetObjID id; + a(id); + auto it = objectData.find(id); + if(it == objectData.end()) + return; + auto& netTypeIDData = typeList->netTypeIDData[it->second.netTypeID]; + netTypeIDData.readUpdateFunc(it->second.p.get(), a); + } + void NetObjManager::set_client(std::shared_ptr initClient, MessageCommandType initUpdateCommandID) { + client = initClient; + updateCommandID = initUpdateCommandID; + } + void NetObjManager::set_server(std::shared_ptr initServer, MessageCommandType initUpdateCommandID) { + server = initServer; + updateCommandID = initUpdateCommandID; + } +} diff --git a/include/Helpers/NetworkingObjects/NetObjManager.hpp b/include/Helpers/NetworkingObjects/NetObjManager.hpp new file mode 100644 index 0000000..d37a74f --- /dev/null +++ b/include/Helpers/NetworkingObjects/NetObjManager.hpp @@ -0,0 +1,81 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "../Networking/NetClient.hpp" +#include "../Networking/NetServer.hpp" +#include "../Networking/NetLibrary.hpp" +#include "NetObjIDGenerator.hpp" +#include "NetObjManagerTypeList.hpp" + +namespace NetworkingObjects { + template class NetObjPtr; + + class NetObjManager { + public: + NetObjManager(std::shared_ptr initIDGen, std::shared_ptr initTypeList); + void read_update_message(cereal::PortableBinaryInputArchive& a); + void set_client(std::shared_ptr initClient, MessageCommandType initUpdateCommandID); + void set_server(std::shared_ptr initServer, MessageCommandType initUpdateCommandID); + + template NetObjPtr read_create_message(cereal::PortableBinaryInputArchive& a) { + NetObjID id; + NetTypeIDType typeID; + a(id, typeID); + auto& netTypeIDData = typeList->netTypeIDData[typeID]; + std::shared_ptr sharedPtr = netTypeIDData.allocatorFunc(); + auto [it, placed] = objectData.emplace(id, typeID, sharedPtr); + if(!placed) + throw std::runtime_error("[NetObjManager::read_create_message] ID Collision on object creation"); + netTypeIDData.readConstructorFunc(sharedPtr.get(), a); + return NetObjPtr(id, std::static_pointer_cast(sharedPtr)); + } + template NetObjPtr read_get_obj_from_message(cereal::PortableBinaryInputArchive& a) { + NetObjID id; + a(id); + auto it = objectData.find(id); + if(it == objectData.end()) + return NetObjPtr(); + return NetObjPtr(id, std::static_pointer_cast(it->second.p)); + } + template NetObjPtr make_obj(Args&&... items) { + auto sharedPtr = std::make_shared(items...); + NetObjID newID = idGen->gen(); + if(!objectData.emplace(newID, typeList->typeIndexData[std::type_index(typeid(T))].netTypeID, sharedPtr).second) + throw std::runtime_error("[NetObjManager::make_obj] ID Collision"); + return NetObjPtr(newID, sharedPtr); + } + template NetObjPtr obj_from_ptr(T* p) { + std::shared_ptr sharedPtr(p); + NetObjID newID = idGen->gen(); + if(!objectData.emplace(newID, typeList->typeIndexData[std::type_index(typeid(T))].netTypeID, sharedPtr).second) + throw std::runtime_error("[NetObjManager::obj_from_ptr] ID Collision"); + return NetObjPtr(newID, sharedPtr); + } + template NetObjPtr get_obj_from_id(NetObjID id) { + auto it = objectData.find(id); + if(it == objectData.end()) + throw std::runtime_error("[NetObjManager::get_obj_from_id] ID doesn't exist"); + return NetObjPtr(id, std::static_pointer_cast(it->second.p)); + } + private: + template friend class NetObjPtr; + + struct SingleObjectData { + NetTypeIDType netTypeID; + std::shared_ptr p; + std::optional> timeSinceNoReference; + }; + std::shared_ptr client; + std::shared_ptr server; + MessageCommandType updateCommandID; + std::unordered_map objectData; + std::shared_ptr idGen; + std::shared_ptr typeList; + NetTypeIDType nextTypeID; + }; +} diff --git a/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp b/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp new file mode 100644 index 0000000..1e12011 --- /dev/null +++ b/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp @@ -0,0 +1,44 @@ +#pragma once +#include "../Networking/NetLibrary.hpp" +#include + +namespace NetworkingObjects { + +class NetObjManagerTypeList { + public: + template void register_class(NetTypeIDType netTypeID, std::function writeConstructorFunc, std::function readConstructorFunc, std::function readUpdateFunc) { + netTypeIDData.emplace(netTypeID, { + .readConstructorFunc = [readConstructorFunc](void* obj, cereal::PortableBinaryInputArchive& message) { + readConstructorFunc(static_cast(obj), message); + }, + .readUpdateFunc = [readUpdateFunc](void* obj, cereal::PortableBinaryInputArchive& message) { + readUpdateFunc(static_cast(obj), message); + }, + .allocatorFunc = []() { return std::make_shared(); } + }); + typeIndexData.emplace(std::type_index(typeid(T)), { + .netTypeID = netTypeID, + .writeConstructorFunc = [writeConstructorFunc](void* obj, cereal::PortableBinaryInputArchive& message) { + writeConstructorFunc(static_cast(obj), message); + }, + }); + } + private: + friend class NetObjManager; + template friend class NetObjPtr; + + struct TypeIndexContainer { + NetTypeIDType netTypeID; + std::function writeConstructorFunc; + }; + std::unordered_map typeIndexData; + + struct NetTypeIDContainer { + std::function readConstructorFunc; + std::function readUpdateFunc; + std::function()> allocatorFunc; + }; + std::unordered_map netTypeIDData; +}; + +} diff --git a/include/Helpers/NetworkingObjects/NetObjPtr.hpp b/include/Helpers/NetworkingObjects/NetObjPtr.hpp new file mode 100644 index 0000000..7ce3f23 --- /dev/null +++ b/include/Helpers/NetworkingObjects/NetObjPtr.hpp @@ -0,0 +1,93 @@ +#pragma once +#include "NetObjIDGenerator.hpp" +#include +#include +#include +#include +#include "NetObjManager.hpp" +#include "NetObjManagerTypeList.hpp" + +namespace NetworkingObjects { + template class NetObjPtr { + public: + NetObjPtr(): + objMan(nullptr), + sharedPtr(nullptr) + {} + NetObjPtr(const NetObjPtr& other): + objMan(other.objMan), + id(other.id), + sharedPtr(other.sharedPtr) + {} + NetObjPtr& operator=(const NetObjPtr& other) { + if(this != &other) { + if(sharedPtr && sharedPtr.unique()) + objMan->objectData.erase(id); + objMan = other.objMan; + id = other.id; + sharedPtr = other.sharedPtr; + } + return *this; + } + ~NetObjPtr() { + if(sharedPtr && sharedPtr.unique()) + objMan->objectData.erase(id); + } + + NetObjID get_net_id() const { + return id; + } + T* get() const { + return sharedPtr.get(); + } + T& operator*() const { + return *sharedPtr; + } + operator bool() const { + return sharedPtr != nullptr; + } + std::strong_ordering operator<=>(const NetObjPtr& rhs) const { + return sharedPtr <=> rhs.sharedPtr; + } + void send_client_update(const std::string& channel, std::function sendUpdateFunc) const { + if(objMan->client && !objMan->client->is_disconnected()) { + auto ss(std::make_shared()); + { + cereal::PortableBinaryOutputArchive m(*ss); + m(objMan->updateCommandID, id); + sendUpdateFunc(*this, m); + } + objMan->client->send_string_stream_to_server(channel, ss); + } + } + void send_server_update_to_all_clients(const std::string& channel, std::function sendUpdateFunc) const { + if(objMan->server && !objMan->server->is_disconnected()) { + auto ss(std::make_shared()); + { + cereal::PortableBinaryOutputArchive m(*ss); + m(objMan->updateCommandID, id); + sendUpdateFunc(*this, m); + } + objMan->server->send_string_stream_to_all_clients(channel, ss); + } + } + void write_create_message(cereal::PortableBinaryOutputArchive& a) const { + auto& typeIndexData = objMan->typeList->typeIndexData[std::type_index(typeid(T))]; + a(id, typeIndexData.netTypeID); + typeIndexData.writeConstructorFunc(static_cast(this), a); + } + private: + friend class NetObjManager; + + NetObjPtr(NetObjManager* initObjMan, NetObjID initID, const std::shared_ptr& initPtr): + objMan(initObjMan), + id(initID), + sharedPtr(initPtr) + {} + + NetObjManager* objMan; + NetObjID id; + std::shared_ptr sharedPtr; + }; + +}