diff --git a/Source/Engine/Networking/Drivers/ENetDriver.cpp b/Source/Engine/Networking/Drivers/ENetDriver.cpp index cdf9f81b1..2ee4dd2ae 100644 --- a/Source/Engine/Networking/Drivers/ENetDriver.cpp +++ b/Source/Engine/Networking/Drivers/ENetDriver.cpp @@ -5,6 +5,8 @@ #include "ENetDriver.h" #include "Engine/Networking/NetworkConfig.h" +#include "Engine/Networking/NetworkEvent.h" +#include "Engine/Networking/NetworkManager.h" #include "Engine/Core/Log.h" #include "Engine/Core/Collections/Array.h" @@ -12,110 +14,88 @@ #define ENET_IMPLEMENTATION #define _WINSOCK_DEPRECATED_NO_WARNINGS #include - -#include "Engine/Networking/NetworkEvent.h" -#include "Engine/Networking/NetworkManager.h" #undef _WINSOCK_DEPRECATED_NO_WARNINGS #undef SendMessage -namespace -{ - bool Initialized = false; - - NetworkConfig Config; - - ENetHost* Server = nullptr; - ENetHost* Client = nullptr; - ENetPeer* ClientPeer = nullptr; -} - void ENetDriver::Initialize(const NetworkConfig& config) { - Config = config; + _config = config; if (enet_initialize () != 0) { LOG(Error, "Failed to initialize ENet driver!"); } LOG(Info, "Initialized ENet driver!"); - Initialized = true; } void ENetDriver::Dispose() { - ASSERT(Initialized); + if(_peer) + enet_peer_disconnect_now((ENetPeer*)_peer, 0); + enet_host_destroy((ENetHost*)_host); - if (Server != nullptr) - { - enet_host_destroy(Server); - Server = nullptr; - } - - if (Client != nullptr) - { - enet_peer_disconnect_now(ClientPeer, 0); - enet_host_destroy(Client); - Client = nullptr; - } - enet_deinitialize(); - Initialized = false; + _peer = nullptr; + _host = nullptr; } bool ENetDriver::Listen() { - ASSERT(Client == nullptr); - ASSERT(Server == nullptr); - ENetAddress address = {0}; address.host = ENET_HOST_ANY; // TODO - address.port = Config.Port; - - Server = enet_host_create(&address, Config.ConnectionsLimit, 0, 0, 0); - - if(Server == nullptr) + address.port = _config.Port; + + // Create ENet host + _host = enet_host_create(&address, _config.ConnectionsLimit, 0, 0, 0); + if(_host == nullptr) { - LOG(Error, "Failed to initialize ENet driver!"); - return true; + LOG(Error, "Failed to initialize ENet host!"); + return false; } LOG(Info, "Created ENet server!"); - return false; + return true; } -void ENetDriver::Connect() +bool ENetDriver::Connect() { - ASSERT(Server == nullptr); - ASSERT(Client == nullptr); - ASSERT(ClientPeer == nullptr); - LOG(Info, "Connecting using ENet..."); ENetAddress address = {0}; - address.port = Config.Port; + address.port = _config.Port; enet_address_set_host(&address, "127.0.0.1"); // TODO - Client = enet_host_create(nullptr, 1, 0, 0, 0); - ClientPeer = enet_host_connect(Client, &address, 0, 0); - - if(ClientPeer == nullptr) + // Create ENet host + _host = enet_host_create(nullptr, 1, 0, 0, 0); + if(_host == nullptr) + { + LOG(Error, "Failed to initialize ENet host!"); + return false; + } + + // Create ENet peer/connect to the server + _peer = enet_host_connect((ENetHost*)_host, &address, 0, 0); + if(_peer == nullptr) { LOG(Error, "Failed to create ENet host!"); - enet_host_destroy(Client); - Client = nullptr; + enet_host_destroy((ENetHost*)_host); + return false; } + + return true; } void ENetDriver::Disconnect() { - ASSERT(Client != nullptr); - ASSERT(ClientPeer != nullptr); + ASSERT(_peer != nullptr); - enet_peer_disconnect_now(ClientPeer, 0); - enet_host_destroy(Client); - - Client = nullptr; - ClientPeer = nullptr; + if(_peer) + { + enet_peer_disconnect_now((ENetPeer*)_peer, 0); + _peer = nullptr; + + LOG(Info, "Disconnected"); + } } void ENetDriver::Disconnect(const NetworkConnection& connection) @@ -125,12 +105,8 @@ void ENetDriver::Disconnect(const NetworkConnection& connection) bool ENetDriver::PopEvent(NetworkEvent* eventPtr) { - ASSERT(Server != nullptr || Client != nullptr); - - ENetHost* host = Server != nullptr ? Server : Client; // TODO: Get host by id - ENetEvent event; - const int result = enet_host_service(host, &event, 0); + const int result = enet_host_service((ENetHost*)_host, &event, 0); if(result < 0) LOG(Error, "Failed to check ENet events!"); @@ -147,16 +123,19 @@ bool ENetDriver::PopEvent(NetworkEvent* eventPtr) eventPtr->EventType = NetworkEventType::Disconnected; LOG(Info, "Disconnected"); // TODO break; + case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: + eventPtr->EventType = NetworkEventType::Disconnected; + LOG(Info, "Disconnected (timeout)"); // TODO + break; case ENET_EVENT_TYPE_RECEIVE: eventPtr->EventType = NetworkEventType::Message; // Acquire message and copy message data - eventPtr->Message = NetworkManager::CreateMessage(); + eventPtr->Message = NetworkManager::CreateMessage(eventPtr->HostId); eventPtr->Message.Length = event.packet->dataLength; Memory::CopyItems(eventPtr->Message.Buffer, event.packet->data, event.packet->dataLength); // TODO: Copy sender info - break; default: break; diff --git a/Source/Engine/Networking/Drivers/ENetDriver.h b/Source/Engine/Networking/Drivers/ENetDriver.h index f2b0c7785..2b1ce9cd5 100644 --- a/Source/Engine/Networking/Drivers/ENetDriver.h +++ b/Source/Engine/Networking/Drivers/ENetDriver.h @@ -4,8 +4,8 @@ #include "Engine/Networking/Types.h" #include "Engine/Networking/INetworkDriver.h" +#include "Engine/Networking/NetworkConfig.h" -#include "Engine/Networking/NetworkConnection.h" #include "Engine/Scripting/ScriptingType.h" API_CLASS(Namespace="FlaxEngine.Networking", Sealed) class FLAXENGINE_API ENetDriver : public INetworkDriver @@ -16,12 +16,17 @@ public: void Dispose() override; bool Listen() override; - void Connect() override; + bool Connect() override; void Disconnect() override; void Disconnect(const NetworkConnection& connection) override; bool PopEvent(NetworkEvent* eventPtr) override; void SendMessage(NetworkChannelType channelType, const NetworkMessage& message, Array targets) override; + +private: + NetworkConfig _config; + void* _host = nullptr; + void* _peer = nullptr; }; diff --git a/Source/Engine/Networking/INetworkDriver.h b/Source/Engine/Networking/INetworkDriver.h index 2bf7b32fd..412f5f0dc 100644 --- a/Source/Engine/Networking/INetworkDriver.h +++ b/Source/Engine/Networking/INetworkDriver.h @@ -12,7 +12,7 @@ public: virtual void Dispose() = 0; virtual bool Listen() = 0; - virtual void Connect() = 0; + virtual bool Connect() = 0; virtual void Disconnect() = 0; virtual void Disconnect(const NetworkConnection& connection) = 0; diff --git a/Source/Engine/Networking/NetworkEvent.h b/Source/Engine/Networking/NetworkEvent.h index b512f4e06..c09356ad7 100644 --- a/Source/Engine/Networking/NetworkEvent.h +++ b/Source/Engine/Networking/NetworkEvent.h @@ -2,6 +2,7 @@ #pragma once +#include "NetworkConnection.h" #include "NetworkMessage.h" #include "Engine/Scripting/ScriptingType.h" @@ -26,4 +27,7 @@ public: API_FIELD(); NetworkConnection Sender; + + API_FIELD(); + int32 HostId; }; diff --git a/Source/Engine/Networking/NetworkHost.cpp b/Source/Engine/Networking/NetworkHost.cpp new file mode 100644 index 000000000..955efa71c --- /dev/null +++ b/Source/Engine/Networking/NetworkHost.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "NetworkHost.h" + +#include "Drivers/ENetDriver.h" + +#include "Engine/Core/Log.h" +#include "Engine/Core/Math/Math.h" +#include "Engine/Platform/CPUInfo.h" + +void NetworkHost::Initialize(const NetworkConfig& config) +{ + Config = config; + + ASSERT(NetworkDriver == nullptr); + ASSERT(Config.NetworkDriverType != NetworkTransportType::Undefined); + ASSERT(Config.ConnectionsLimit > 0); + ASSERT(Config.MessageSize > 32); // TODO: Adjust this, not sure what the lowest limit should be. + ASSERT(Config.MessagePoolSize > 128); + + // Setup messages + CreateMessageBuffers(); + MessagePool.Clear(); + + // Warmup message pool + for (uint32 messageId = Config.MessagePoolSize; messageId > 0; messageId --) + MessagePool.Push(messageId); + + // Setup network driver + NetworkDriver = New(); + NetworkDriver->Initialize(Config); + + LOG(Info, "NetworkManager initialized using driver = {0}", static_cast(Config.NetworkDriverType)); +} + +void NetworkHost::Shutdown() +{ + Delete(NetworkDriver); + DisposeMessageBuffers(); + + NetworkDriver = nullptr; +} + +void NetworkHost::CreateMessageBuffers() +{ + ASSERT(MessageBuffer == nullptr); + + const uint32 pageSize = Platform::GetCPUInfo().PageSize; + + // Calculate total size in bytes + const uint64 totalSize = static_cast(Config.MessagePoolSize) * Config.MessageSize; + + // Calculate the amount of pages that we need + const uint32 numPages = totalSize > pageSize ? Math::CeilToInt(totalSize / static_cast(pageSize)) : 1; + + MessageBuffer = static_cast(Platform::AllocatePages(numPages, pageSize)); + Platform::MemorySet(MessageBuffer, 0, numPages * pageSize); +} + +void NetworkHost::DisposeMessageBuffers() +{ + ASSERT(MessageBuffer != nullptr); + + Platform::FreePages(MessageBuffer); + MessageBuffer = nullptr; +} diff --git a/Source/Engine/Networking/NetworkHost.h b/Source/Engine/Networking/NetworkHost.h new file mode 100644 index 000000000..7def414d4 --- /dev/null +++ b/Source/Engine/Networking/NetworkHost.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Types.h" +#include "NetworkConfig.h" + +#include "Engine/Core/Collections/Array.h" + +struct FLAXENGINE_API NetworkHost +{ +public: + NetworkConfig Config; + INetworkDriver* NetworkDriver = nullptr; + + uint8* MessageBuffer = nullptr; + Array MessagePool; + +public: + void Initialize(const NetworkConfig& config); + void Shutdown(); + void CreateMessageBuffers(); + void DisposeMessageBuffers(); + + bool IsValid() const + { + return NetworkDriver != nullptr; + } + + uint8* GetMessageBuffer(const uint32 messageId) const + { + // Calculate and return the buffer slice using previously calculated slice. + return MessageBuffer + Config.MessageSize * messageId; + } +}; + diff --git a/Source/Engine/Networking/NetworkManager.cpp b/Source/Engine/Networking/NetworkManager.cpp index f54fa5d2d..03221b494 100644 --- a/Source/Engine/Networking/NetworkManager.cpp +++ b/Source/Engine/Networking/NetworkManager.cpp @@ -6,170 +6,133 @@ #include "NetworkConfig.h" #include "NetworkConnection.h" #include "INetworkDriver.h" - -#include "Drivers/ENetDriver.h" +#include "NetworkEvent.h" +#include "NetworkHost.h" #include "Engine/Core/Log.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Math/Math.h" -#include "Engine/Platform/CPUInfo.h" namespace { - NetworkConfig Config; - INetworkDriver* NetworkDriver = nullptr; - - uint8* MessageBuffer = nullptr; - Array MessagePool; + Array Hosts; } -bool NetworkManager::Initialize(const NetworkConfig& config) +int NetworkManager::Initialize(const NetworkConfig& config) { - Config = config; - - ASSERT(NetworkDriver == nullptr); - ASSERT(Config.NetworkDriverType != NetworkTransportType::Undefined); - ASSERT(Config.ConnectionsLimit > 0); - ASSERT(Config.MessageSize > 32); // TODO: Adjust this, not sure what the lowest limit should be. - ASSERT(Config.MessagePoolSize > 128); + // Alloc new host + const int hostId = Hosts.Count(); + Hosts.Add(NetworkHost()); + NetworkHost& host = Hosts.Last(); - // Setup messages - CreateMessageBuffers(); - MessagePool.Clear(); - - // Warmup message pool - for (uint32 messageId = Config.MessagePoolSize; messageId > 0; messageId --) - MessagePool.Push(messageId); - - // Setup network driver - NetworkDriver = New(); - NetworkDriver->Initialize(Config); + // Initialize the host + host.Initialize(config); - LOG(Info, "NetworkManager initialized using driver = {0}", static_cast(Config.NetworkDriverType)); - - return false; + return hostId; } -void NetworkManager::Shutdown() +void NetworkManager::Shutdown(const int hostId) { - Delete(NetworkDriver); - DisposeMessageBuffers(); - - LOG(Info, "NetworkManager shutdown"); + ASSERT(Hosts[hostId].IsValid()); + NetworkHost& host = Hosts[hostId]; + host.Shutdown(); } -bool NetworkManager::Listen() +bool NetworkManager::Listen(const int hostId) { - LOG(Info, "NetworkManager starting to listen on address = any:{0}", Config.Port); + ASSERT(Hosts[hostId].IsValid()); + NetworkHost& host = Hosts[hostId]; - ASSERT(NetworkDriver != nullptr); - return NetworkDriver->Listen(); + LOG(Info, "NetworkManager starting to listen on address = any:{0}", host.Config.Port); + + return host.NetworkDriver->Listen(); } -void NetworkManager::Connect() +bool NetworkManager::Connect(const int hostId) { - // TODO: Support multiple hosts + ASSERT(Hosts[hostId].IsValid()); + NetworkHost& host = Hosts[hostId]; + LOG(Info, "Connecting to 127.0.0.1:{0}...", host.Config.Port); // TODO: Proper IP address - LOG(Info, "Connecting to 127.0.0.1:{0}...", Config.Port); // TODO: Proper IP address - - ASSERT(NetworkDriver != nullptr); // TODO: Assert address/endpoint - NetworkDriver->Connect(); + return host.NetworkDriver->Connect(); } -void NetworkManager::Disconnect() +void NetworkManager::Disconnect(const int hostId) { + ASSERT(Hosts[hostId].IsValid()); + NetworkHost& host = Hosts[hostId]; LOG(Info, "Disconnecting..."); - ASSERT(NetworkDriver != nullptr); - NetworkDriver->Disconnect(); + host.NetworkDriver->Disconnect(); } -void NetworkManager::Disconnect(const NetworkConnection& connection) +void NetworkManager::Disconnect(const int hostId, const NetworkConnection& connection) { + ASSERT(Hosts[hostId].IsValid()); + NetworkHost& host = Hosts[hostId]; LOG(Info, "Disconnecting connection with id = {0}...", connection.ConnectionId); - ASSERT(NetworkDriver != nullptr); - NetworkDriver->Disconnect(connection); + host.NetworkDriver->Disconnect(connection); } -bool NetworkManager::PopEvent(NetworkEvent& eventPtr) +bool NetworkManager::PopEvent(const int hostId, NetworkEvent& eventPtr) { - ASSERT(NetworkDriver != nullptr); - return NetworkDriver->PopEvent(&eventPtr); + ASSERT(Hosts[hostId].IsValid()); + NetworkHost& host = Hosts[hostId]; + + // Set host id of the event + eventPtr.HostId = hostId; + + return host.NetworkDriver->PopEvent(&eventPtr); } -NetworkMessage NetworkManager::CreateMessage() +NetworkMessage NetworkManager::CreateMessage(const int hostId) { - ASSERT(MessagePool.Count() > 0); + ASSERT(Hosts[hostId].IsValid()); + NetworkHost& host = Hosts[hostId]; + ASSERT(host.MessagePool.Count() > 0); - const uint32 messageId = MessagePool.Pop(); - uint8* messageBuffer = GetMessageBuffer(messageId); + const uint32 messageId = host.MessagePool.Pop(); + uint8* messageBuffer = host.GetMessageBuffer(messageId); - return NetworkMessage(messageBuffer, messageId, Config.MessageSize, 0, 0); + return NetworkMessage(messageBuffer, messageId, host.Config.MessageSize, 0, 0); } -void NetworkManager::RecycleMessage(const NetworkMessage& message) +void NetworkManager::RecycleMessage(const int hostId, const NetworkMessage& message) { + ASSERT(Hosts[hostId].IsValid()); + NetworkHost& host = Hosts[hostId]; ASSERT(message.IsValid()); #ifdef BUILD_DEBUG - ASSERT(MessagePool.Contains(message.MessageId) == false); + ASSERT(host.MessagePool.Contains(message.MessageId) == false); #endif // Return the message id - MessagePool.Push(message.MessageId); + host.MessagePool.Push(message.MessageId); } -NetworkMessage NetworkManager::BeginSendMessage() +NetworkMessage NetworkManager::BeginSendMessage(const int hostId) { - ASSERT(NetworkDriver != nullptr); - return CreateMessage(); + ASSERT(Hosts[hostId].IsValid()); + return CreateMessage(hostId); } -void NetworkManager::AbortSendMessage(const NetworkMessage& message) +void NetworkManager::AbortSendMessage(const int hostId, const NetworkMessage& message) { - ASSERT(NetworkDriver != nullptr); + ASSERT(Hosts[hostId].IsValid()); ASSERT(message.IsValid()); - RecycleMessage(message); + RecycleMessage(hostId, message); } -bool NetworkManager::EndSendMessage(const NetworkChannelType channelType, const NetworkMessage& message, const Array targets) +bool NetworkManager::EndSendMessage(const int hostId, const NetworkChannelType channelType, const NetworkMessage& message, const Array targets) { - ASSERT(NetworkDriver != nullptr); + ASSERT(Hosts[hostId].IsValid()); + NetworkHost& host = Hosts[hostId]; ASSERT(message.IsValid()); - NetworkDriver->SendMessage(channelType, message, targets); + host.NetworkDriver->SendMessage(channelType, message, targets); - RecycleMessage(message); + RecycleMessage(hostId, message); return false; } - -void NetworkManager::CreateMessageBuffers() -{ - ASSERT(MessageBuffer == nullptr); - - const uint32 pageSize = Platform::GetCPUInfo().PageSize; - - // Calculate total size in bytes - const uint64 totalSize = static_cast(Config.MessagePoolSize) * Config.MessageSize; - - // Calculate the amount of pages that we need - const uint32 numPages = totalSize > pageSize ? Math::CeilToInt(totalSize / static_cast(pageSize)) : 1; - - MessageBuffer = static_cast(Platform::AllocatePages(numPages, pageSize)); - Platform::MemorySet(MessageBuffer, 0, numPages * pageSize); -} - -void NetworkManager::DisposeMessageBuffers() -{ - ASSERT(MessageBuffer != nullptr); - - Platform::FreePages(MessageBuffer); - MessageBuffer = nullptr; -} - -uint8* NetworkManager::GetMessageBuffer(const uint32 messageId) -{ - // Calculate and return the buffer slice using previously calculated slice. - return MessageBuffer + Config.MessageSize * messageId; -} diff --git a/Source/Engine/Networking/NetworkManager.h b/Source/Engine/Networking/NetworkManager.h index 09138121e..ddfd222e3 100644 --- a/Source/Engine/Networking/NetworkManager.h +++ b/Source/Engine/Networking/NetworkManager.h @@ -10,29 +10,25 @@ API_CLASS(Namespace="FlaxEngine.Networking", Static) class FLAXENGINE_API Networ DECLARE_SCRIPTING_TYPE_NO_SPAWN(NetworkManager); public: - API_FUNCTION() static bool Initialize(const NetworkConfig& config); - API_FUNCTION() static void Shutdown(); + API_FUNCTION() static int Initialize(const NetworkConfig& config); + API_FUNCTION() static void Shutdown(int hostId); - API_FUNCTION() static bool Listen(); - API_FUNCTION() static void Connect(); - API_FUNCTION() static void Disconnect(); - API_FUNCTION() static void Disconnect(const NetworkConnection& connection); + API_FUNCTION() static bool Listen(int hostId); + API_FUNCTION() static bool Connect(int hostId); + API_FUNCTION() static void Disconnect(int hostId); + API_FUNCTION() static void Disconnect(int hostId, const NetworkConnection& connection); - API_FUNCTION() static bool PopEvent(API_PARAM(out) NetworkEvent& eventPtr); + API_FUNCTION() static bool PopEvent(int hostId, API_PARAM(out) NetworkEvent& eventPtr); - API_FUNCTION() static NetworkMessage CreateMessage(); - API_FUNCTION() static void RecycleMessage(const NetworkMessage& message); + API_FUNCTION() static NetworkMessage CreateMessage(int hostId); + API_FUNCTION() static void RecycleMessage(int hostId, const NetworkMessage& message); - API_FUNCTION() static NetworkMessage BeginSendMessage(); - API_FUNCTION() static void AbortSendMessage(const NetworkMessage& message); - API_FUNCTION() static bool EndSendMessage(NetworkChannelType channelType, const NetworkMessage& message, Array targets); + API_FUNCTION() static NetworkMessage BeginSendMessage(int hostId); + API_FUNCTION() static void AbortSendMessage(int hostId, const NetworkMessage& message); + API_FUNCTION() static bool EndSendMessage(int hostId, NetworkChannelType channelType, const NetworkMessage& message, Array targets); // TODO: Stats API // TODO: Simulation API + // TODO: Optimize 'targets' in EndSendMessage -private: - - static void CreateMessageBuffers(); - static void DisposeMessageBuffers(); - static uint8* GetMessageBuffer(uint32 messageId); };