diff --git a/include/Nazara/Network/ENetHost.hpp b/include/Nazara/Network/ENetHost.hpp index 344ffce82..0dcf58a86 100644 --- a/include/Nazara/Network/ENetHost.hpp +++ b/include/Nazara/Network/ENetHost.hpp @@ -55,6 +55,8 @@ namespace Nz int Service(ENetEvent* event, UInt32 timeout); + void SimulateNetwork(double packetLossProbability, UInt16 minDelay, UInt16 maxDelay); + ENetHost& operator=(const ENetHost&) = delete; ENetHost& operator=(ENetHost&&) = default; @@ -96,6 +98,13 @@ namespace Nz static bool Initialize(); static void Uninitialize(); + struct PendingPacket + { + IpAddress from; + NetPacket data; + UInt32 deliveryTime; + }; + std::array m_commands; std::array m_buffers; std::array m_packetData[2]; @@ -110,7 +119,10 @@ namespace Nz std::size_t m_packetSize; std::size_t m_peerCount; std::size_t m_receivedDataLength; + std::uniform_int_distribution m_packetDelayDistribution; std::vector m_peers; + std::vector m_pendingPackets; + UInt8* m_receivedData; Bitset m_dispatchQueue; MemoryPool m_packetPool; IpAddress m_address; @@ -129,10 +141,8 @@ namespace Nz UInt32 m_totalSentPackets; UInt32 m_totalReceivedData; UInt32 m_totalReceivedPackets; - UInt8* m_receivedData; bool m_continueSending; bool m_isSimulationEnabled; - bool m_shouldAcceptConnections; bool m_recalculateBandwidthLimits; static std::mt19937 s_randomGenerator; diff --git a/include/Nazara/Network/ENetHost.inl b/include/Nazara/Network/ENetHost.inl index e547d4e5d..c7e65ab95 100644 --- a/include/Nazara/Network/ENetHost.inl +++ b/include/Nazara/Network/ENetHost.inl @@ -9,7 +9,8 @@ namespace Nz { inline ENetHost::ENetHost() : - m_packetPool(sizeof(ENetPacket)) + m_packetPool(sizeof(ENetPacket)), + m_isSimulationEnabled(false) { } diff --git a/src/Nazara/Network/ENetHost.cpp b/src/Nazara/Network/ENetHost.cpp index d9a09d2c8..3587e19d1 100644 --- a/src/Nazara/Network/ENetHost.cpp +++ b/src/Nazara/Network/ENetHost.cpp @@ -312,6 +312,20 @@ namespace Nz return 0; } + void ENetHost::SimulateNetwork(double packetLossProbability, UInt16 minDelay, UInt16 maxDelay) + { + NazaraAssert(maxDelay >= minDelay, "Maximum delay cannot be greater than minimum delay"); + + if (packetLossProbability <= 0.0 && minDelay == 0 && maxDelay == 0) + m_isSimulationEnabled = false; + else + { + m_isSimulationEnabled = true; + m_packetDelayDistribution = std::uniform_int_distribution(minDelay, maxDelay); + m_packetLossProbability = std::bernoulli_distribution(packetLossProbability); + } + } + bool ENetHost::InitSocket(const IpAddress& address) { if (!m_socket.Create(address.GetProtocol())) @@ -1111,14 +1125,58 @@ namespace Nz { for (unsigned int i = 0; i < 256; ++i) { - NetPacket packet; - + bool shouldReceive = true; std::size_t receivedLength; - if (!m_socket.Receive(m_packetData[0].data(), m_packetData[0].size(), &m_receivedAddress, &receivedLength)) - return -1; //< Error - if (receivedLength == 0) - return 0; + if (m_isSimulationEnabled) + { + for (auto it = m_pendingPackets.begin(); it != m_pendingPackets.end(); ++it) + { + if (m_serviceTime >= it->deliveryTime) + { + shouldReceive = false; + + m_receivedAddress = it->from; + receivedLength = it->data.GetDataSize(); + std::memcpy(m_packetData[0].data(), it->data.GetConstData() + NetPacket::HeaderSize, receivedLength); + + m_pendingPackets.erase(it); + break; + } + } + } + + if (shouldReceive) + { + if (!m_socket.Receive(m_packetData[0].data(), m_packetData[0].size(), &m_receivedAddress, &receivedLength)) + return -1; //< Error + + if (receivedLength == 0) + return 0; + + if (m_isSimulationEnabled) + { + if (m_packetLossProbability(s_randomGenerator)) + continue; + + UInt16 delay = m_packetDelayDistribution(s_randomGenerator); + if (delay > 0) + { + PendingPacket pendingPacket; + pendingPacket.deliveryTime = m_serviceTime + delay; + pendingPacket.from = m_receivedAddress; + pendingPacket.data.Reset(0, m_packetData[0].data(), receivedLength); + + auto it = std::upper_bound(m_pendingPackets.begin(), m_pendingPackets.end(), pendingPacket, [] (const PendingPacket& first, const PendingPacket& second) + { + return first.deliveryTime < second.deliveryTime; + }); + + m_pendingPackets.emplace(it, std::move(pendingPacket)); + continue; + } + } + } m_receivedData = m_packetData[0].data(); m_receivedDataLength = receivedLength; diff --git a/src/Nazara/Network/ENetPeer.cpp b/src/Nazara/Network/ENetPeer.cpp index c969dde4b..d0181a01d 100644 --- a/src/Nazara/Network/ENetPeer.cpp +++ b/src/Nazara/Network/ENetPeer.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #define ENET_TIME_OVERFLOW 86400000