From 9e3341a32ae61994aa5ce4646199dbddfc22ad16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Wed, 25 Jan 2017 15:55:07 +0100 Subject: [PATCH] Commit current work This is a temporary branch because I'm missing a USB drive, huehue --- include/Nazara/Network/ENetHost.hpp | 121 ++++ include/Nazara/Network/ENetHost.inl | 65 ++ include/Nazara/Network/ENetPacket.hpp | 78 +++ include/Nazara/Network/ENetPeer.hpp | 205 ++++++ include/Nazara/Network/ENetPeer.inl | 29 + include/Nazara/Network/ENetProtocol.hpp | 272 ++++++++ src/Nazara/Network/ENetHost.cpp | 743 ++++++++++++++++++++ src/Nazara/Network/ENetPacket.cpp | 23 + src/Nazara/Network/ENetPeer.cpp | 885 ++++++++++++++++++++++++ src/Nazara/Network/ENetSocket.cpp | 642 +++++++++++++++++ 10 files changed, 3063 insertions(+) create mode 100644 include/Nazara/Network/ENetHost.hpp create mode 100644 include/Nazara/Network/ENetHost.inl create mode 100644 include/Nazara/Network/ENetPacket.hpp create mode 100644 include/Nazara/Network/ENetPeer.hpp create mode 100644 include/Nazara/Network/ENetPeer.inl create mode 100644 include/Nazara/Network/ENetProtocol.hpp create mode 100644 src/Nazara/Network/ENetHost.cpp create mode 100644 src/Nazara/Network/ENetPacket.cpp create mode 100644 src/Nazara/Network/ENetPeer.cpp create mode 100644 src/Nazara/Network/ENetSocket.cpp diff --git a/include/Nazara/Network/ENetHost.hpp b/include/Nazara/Network/ENetHost.hpp new file mode 100644 index 000000000..87d4dd37b --- /dev/null +++ b/include/Nazara/Network/ENetHost.hpp @@ -0,0 +1,121 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ENETHOST_HPP +#define NAZARA_ENETHOST_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class ENetPeer; + + class NAZARA_NETWORK_API ENetHost + { + friend ENetPeer; + friend class Network; + + public: + inline ENetHost(); + ENetHost(const ENetHost&) = delete; + ENetHost(ENetHost&&) = default; + inline ~ENetHost(); + + void Broadcast(UInt8 channelId, ENetPacketFlags flags, NetPacket&& packet); + + bool Connect(const IpAddress& remoteAddress, std::size_t channelCount = 0, UInt32 data = 0); + bool Connect(const String& hostName, NetProtocol protocol = NetProtocol_Any, const String& service = "http", ResolveError* error = nullptr, std::size_t channelCount = 0, UInt32 data = 0); + + inline bool Create(NetProtocol protocol, UInt16 port, std::size_t peerCount, std::size_t channelCount = 0); + bool Create(const IpAddress& address, std::size_t peerCount, std::size_t channelCount = 0); + bool Create(const IpAddress& address, std::size_t peerCount, std::size_t channelCount, UInt32 incomingBandwidth, UInt32 outgoingBandwidth); + void Destroy(); + + void Flush(); + + int Service(ENetEvent* event, UInt32 timeout); + + ENetHost& operator=(const ENetHost&) = delete; + ENetHost& operator=(ENetHost&&) = default; + + private: + bool InitSocket(const IpAddress& address); + + inline void AddToDispatchQueue(ENetPeer* peer); + inline void RemoveFromDispatchQueue(ENetPeer* peer); + + bool DispatchIncomingCommands(ENetEvent* event); + + ENetPeer* HandleConnect(ENetProtocolHeader* header, ENetProtocol* command); + bool HandleIncomingCommands(ENetEvent* event); + bool HandleSendReliable(ENetPeer& peer, const ENetProtocol& command, UInt8** currentData); + + int ReceiveIncomingCommands(ENetEvent* event); + + void NotifyConnect(ENetPeer* peer, ENetEvent* event); + void NotifyDisconnect(ENetPeer*, ENetEvent* event); + + void ThrottleBandwidth(); + + static bool Initialize(); + static void Uninitialize(); + + std::array m_commands; + std::array m_packetData[2]; + std::bernoulli_distribution m_packetLossProbability; + std::size_t m_bandwidthLimitedPeers; + std::size_t m_bufferCount; + std::size_t m_channelLimit; + std::size_t m_commandCount; + std::size_t m_duplicatePeers; + std::size_t m_maximumPacketSize; + std::size_t m_maximumWaitingData; + std::size_t m_receivedDataLength; + std::vector m_peers; + Bitset m_dispatchQueue; + MemoryPool m_packetPool; + IpAddress m_address; + IpAddress m_receivedAddress; + SocketPoller m_poller; + UdpSocket m_socket; + UInt32 m_bandwidthThrottleEpoch; + UInt32 m_connectedPeers; + UInt32 m_mtu; + UInt32 m_randomSeed; + UInt32 m_incomingBandwidth; + UInt32 m_outgoingBandwidth; + UInt32 m_serviceTime; + UInt32 m_totalSentData; + UInt32 m_totalSentPackets; + UInt32 m_totalReceivedData; + UInt32 m_totalReceivedPackets; + UInt8* m_receivedData; + bool m_isSimulationEnabled; + bool m_shouldAcceptConnections; + bool m_recalculateBandwidthLimits; + + static std::mt19937 s_randomGenerator; + static std::mt19937_64 s_randomGenerator64; + }; +} + +#include + +#endif // NAZARA_RUDPSERVER_HPP diff --git a/include/Nazara/Network/ENetHost.inl b/include/Nazara/Network/ENetHost.inl new file mode 100644 index 000000000..c4a077e8b --- /dev/null +++ b/include/Nazara/Network/ENetHost.inl @@ -0,0 +1,65 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + inline ENetHost::ENetHost() : + m_packetPool(sizeof(ENetPacket)) + { + } + + inline ENetHost::~ENetHost() + { + Destroy(); + } + + inline bool ENetHost::Create(NetProtocol protocol, UInt16 port, std::size_t peerCount, std::size_t channelCount) + { + NazaraAssert(protocol != NetProtocol_Any, "Any protocol not supported for Listen"); //< TODO + NazaraAssert(protocol != NetProtocol_Unknown, "Invalid protocol"); + + IpAddress any; + switch (protocol) + { + case NetProtocol_Any: + case NetProtocol_Unknown: + NazaraInternalError("Invalid protocol Any at this point"); + return false; + + case NetProtocol_IPv4: + any = IpAddress::AnyIpV4; + break; + + case NetProtocol_IPv6: + any = IpAddress::AnyIpV6; + break; + } + + any.SetPort(port); + return Create(any, peerCount, channelCount); + } + + inline void ENetHost::Destroy() + { + m_poller.Clear(); + m_peers.clear(); + m_socket.Close(); + } + + inline void ENetHost::AddToDispatchQueue(ENetPeer* peer) + { + m_dispatchQueue.UnboundedSet(peer->m_incomingPeerID); + } + + inline void ENetHost::RemoveFromDispatchQueue(ENetPeer* peer) + { + m_dispatchQueue.UnboundedReset(peer->m_incomingPeerID); + } +} + +#include diff --git a/include/Nazara/Network/ENetPacket.hpp b/include/Nazara/Network/ENetPacket.hpp new file mode 100644 index 000000000..ad1610eb4 --- /dev/null +++ b/include/Nazara/Network/ENetPacket.hpp @@ -0,0 +1,78 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ENETPACKET_HPP +#define NAZARA_ENETPACKET_HPP + +#include +#include + +namespace Nz +{ + enum ENetPacketFlag + { + ENetPacketFlag_NoAllocate, + ENetPacketFlag_Reliable, + ENetPacketFlag_Unsequenced, + ENetPacketFlag_UnreliableFragment, + ENetPacketFlag_Sent + }; + + template<> + struct EnumAsFlags + { + static constexpr bool value = true; + static constexpr int max = ENetPacketFlag_Sent; + }; + + using ENetPacketFlags = Flags; + + class MemoryPool; + + struct ENetPacket + { + MemoryPool* owner; + ENetPacketFlags flags; + NetPacket data; + std::size_t referenceCount = 0; + }; + + struct ENetPacketRef + { + ENetPacketRef() = default; + + ENetPacketRef(ENetPacket* packet) + { + Reset(packet); + } + + ~ENetPacketRef() + { + Reset(); + } + + void Reset(ENetPacket* packet = nullptr); + + operator ENetPacket*() const + { + return m_packet; + } + + ENetPacket* operator->() const + { + return m_packet; + } + + ENetPacketRef& operator=(ENetPacket* packet) + { + Reset(packet); + } + + ENetPacket* m_packet = nullptr; + }; +} + +#endif // NAZARA_ENETPACKET_HPP diff --git a/include/Nazara/Network/ENetPeer.hpp b/include/Nazara/Network/ENetPeer.hpp new file mode 100644 index 000000000..4ae8ca16b --- /dev/null +++ b/include/Nazara/Network/ENetPeer.hpp @@ -0,0 +1,205 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ENETPEER_HPP +#define NAZARA_ENETPEER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class ENetHost; + + class NAZARA_NETWORK_API ENetPeer + { + friend ENetHost; + friend struct PacketRef; + + public: + ENetPeer(const ENetPeer&) = delete; + ENetPeer(ENetPeer&&) = default; + ~ENetPeer() = default; + + void Disconnect(UInt32 data); + void DisconnectLater(UInt32 data); + void DisconnectNow(UInt32 data); + + void Ping(); + + bool Receive(ENetPacketRef* packet, UInt8* channelId); + void Reset(); + + bool Send(UInt8 channelId, ENetPacketRef packetRef); + bool Send(UInt8 channelId, ENetPacketFlags flags, NetPacket&& packet); + + void ThrottleConfigure(UInt32 interval, UInt32 acceleration, UInt32 deceleration); + + ENetPeer& operator=(const ENetPeer&) = delete; + ENetPeer& operator=(ENetPeer&&) = default; + + private: + ENetPeer(ENetHost* host, UInt16 peerId); + + void InitIncoming(std::size_t channelCount, const IpAddress& address, ENetProtocolConnect& incomingCommand); + void InitOutgoing(std::size_t channelCount, const IpAddress& address, UInt32 connectId, UInt32 windowSize); + + struct Acknowledgement; + struct Channel; + struct IncomingCommmand; + struct OutgoingCommand; + + // Protocol functions + inline void ChangeState(ENetPeerState state); + inline void DispatchState(ENetPeerState state); + + void DispatchIncomingReliableCommands(Channel& channel); + void DispatchIncomingUnreliableCommands(Channel& channel); + + void OnConnect(); + void OnDisconnect(); + + ENetProtocolCommand RemoveSentReliableCommands(UInt16 reliableSequenceNumber, UInt8 channelId); + void RemoveSentUnreliableCommands(); + + void ResetQueues(); + + bool QueueAcknowledgement(ENetProtocol& command, UInt16 sentTime); + IncomingCommmand* QueueIncomingCommand(ENetProtocol& command, const void* data, std::size_t dataLength, UInt32 flags, UInt32 fragmentCount); + void QueueOutgoingCommand(ENetProtocol& command, ENetPacketRef packet, UInt32 offset, UInt16 length); + + void SetupOutgoingCommand(OutgoingCommand& outgoingCommand); + + int Throttle(UInt32 rtt); + + struct Acknowledgement + { + ENetProtocol command; + UInt32 sentTime; + }; + + struct Channel + { + Channel() + { + incomingReliableSequenceNumber = 0; + incomingUnreliableSequenceNumber = 0; + outgoingReliableSequenceNumber = 0; + outgoingUnreliableSequenceNumber = 0; + usedReliableWindows = 0; + reliableWindows.fill(0); + } + + std::array reliableWindows; + std::list incomingReliableCommands; + std::list incomingUnreliableCommands; + UInt16 incomingReliableSequenceNumber; + UInt16 incomingUnreliableSequenceNumber; + UInt16 outgoingReliableSequenceNumber; + UInt16 outgoingUnreliableSequenceNumber; + UInt16 usedReliableWindows; + }; + + struct IncomingCommmand + { + ENetProtocol command; + UInt16 reliableSequenceNumber; + UInt16 unreliableSequenceNumber; + UInt32 fragmentsRemaining; + std::vector fragments; + ENetPacketRef packet; + }; + + struct OutgoingCommand + { + ENetProtocol command; + ENetPacketRef packet; + UInt16 fragmentLength; + UInt16 reliableSequenceNumber; + UInt16 sendAttempts; + UInt16 unreliableSequenceNumber; + UInt32 fragmentOffset; + UInt32 roundTripTimeout; + UInt32 roundTripTimeoutLimit; + UInt32 sentTime; + }; + + ENetHost* m_host; + IpAddress m_address; /**< Internet address of the peer */ + std::vector m_channels; + std::list m_acknowledgements; + std::list m_dispatchedCommands; + std::list m_outgoingReliableCommands; + std::list m_outgoingUnreliableCommands; + std::list m_sentReliableCommands; + std::list m_sentUnreliableCommands; + MemoryPool m_packetPool; + //ENetListNode m_dispatchList; + ENetPeerState m_state; + UInt8 m_incomingSessionID; + UInt8 m_outgoingSessionID; + UInt16 m_incomingPeerID; + UInt16 m_incomingUnsequencedGroup; + UInt16 m_outgoingPeerID; + UInt16 m_outgoingReliableSequenceNumber; + UInt16 m_outgoingUnsequencedGroup; + UInt32 m_connectID; + UInt32 m_earliestTimeout; + UInt32 m_eventData; + UInt32 m_highestRoundTripTimeVariance; + UInt32 m_incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */ + UInt32 m_incomingBandwidthThrottleEpoch; + UInt32 m_incomingDataTotal; + UInt32 m_lastReceiveTime; + UInt32 m_lastRoundTripTime; + UInt32 m_lastRoundTripTimeVariance; + UInt32 m_lastSendTime; + UInt32 m_lowestRoundTripTime; + UInt32 m_mtu; + UInt32 m_nextTimeout; + UInt32 m_outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */ + UInt32 m_outgoingBandwidthThrottleEpoch; + UInt32 m_outgoingDataTotal; + UInt32 m_packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */ + UInt32 m_packetLossEpoch; + UInt32 m_packetLossVariance; + UInt32 m_packetThrottle; + UInt32 m_packetThrottleAcceleration; + UInt32 m_packetThrottleCounter; + UInt32 m_packetThrottleDeceleration; + UInt32 m_packetThrottleEpoch; + UInt32 m_packetThrottleInterval; + UInt32 m_packetThrottleLimit; + UInt32 m_packetsLost; + UInt32 m_packetsSent; + UInt32 m_pingInterval; + UInt32 m_reliableDataInTransit; + UInt32 m_roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgment */ + UInt32 m_roundTripTimeVariance; + UInt32 m_timeoutLimit; + UInt32 m_timeoutMaximum; + UInt32 m_timeoutMinimum; + UInt32 m_unsequencedWindow[ENetPeer_ReliableWindowSize / 32]; + UInt32 m_windowSize; + std::size_t m_totalWaitingData; + }; +} + +#include + +#endif // NAZARA_ENETPEER_HPP diff --git a/include/Nazara/Network/ENetPeer.inl b/include/Nazara/Network/ENetPeer.inl new file mode 100644 index 000000000..a0cebbc4e --- /dev/null +++ b/include/Nazara/Network/ENetPeer.inl @@ -0,0 +1,29 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + inline void ENetPeer::ChangeState(ENetPeerState state) + { + if (state == ENetPeerState::Connected || state == ENetPeerState::DisconnectLater) + OnConnect(); + else + OnDisconnect(); + + m_state = state; + } + + inline void ENetPeer::DispatchState(ENetPeerState state) + { + ChangeState(state); + + m_host->AddToDispatchQueue(this); + } +} + +#include diff --git a/include/Nazara/Network/ENetProtocol.hpp b/include/Nazara/Network/ENetProtocol.hpp new file mode 100644 index 000000000..d4d74fd01 --- /dev/null +++ b/include/Nazara/Network/ENetProtocol.hpp @@ -0,0 +1,272 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ENETPROTOCOL_HPP +#define NAZARA_ENETPROTOCOL_HPP + +#include +#include +#include + +namespace Nz +{ + // Constants for the ENet implementation and protocol + enum ENetConstants + { + ENetHost_BandwidthThrottleInterval = 1000, + ENetHost_DefaultMaximumPacketSize = 32 * 1024 * 1024, + ENetHost_DefaultMaximumWaitingData = 32 * 1024 * 1024, + ENetHost_DefaultMTU = 1400, + ENetHost_ReceiveBufferSize = 256 * 1024, + ENetHost_SendBufferSize = 256 * 1024, + + ENetPeer_DefaultPacketThrottle = 32, + ENetPeer_DefaultRoundTripTime = 500, + ENetPeer_FreeReliableWindows = 8, + ENetPeer_FreeUnsequencedWindows = 32, + ENetPeer_PacketLossInterval = 10000, + ENetPeer_PacketLossScale = (1 << 16), + ENetPeer_PacketThrottleAcceleration = 2, + ENetPeer_PacketThrottleCounter = 7, + ENetPeer_PacketThrottleDeceleration = 2, + ENetPeer_PacketThrottleInterval = 5000, + ENetPeer_PacketThrottleScale = 32, + ENetPeer_PingInterval = 500, + ENetPeer_ReliableWindows = 16, + ENetPeer_ReliableWindowSize = 0x1000, + ENetPeer_TimeoutLimit = 32, + ENetPeer_TimeoutMaximum = 30000, + ENetPeer_TimeoutMinimum = 5000, + ENetPeer_UnsequencedWindows = 64, + ENetPeer_UnsequencedWindowSize = 1024, + ENetPeer_WindowSizeScale = 64 * 1024, + + ENetProtocol_MaximumChannelCount = 255, + ENetProtocol_MaximumFragmentCount = 1024 * 1024, + ENetProtocol_MaximumMTU = 4096, + ENetProtocol_MaximumPacketCommands = 32, + ENetProtocol_MaximumPeerId = 0xFFF, + ENetProtocol_MaximumWindowSize = 65536, + ENetProtocol_MinimumChannelCount = 1, + ENetProtocol_MinimumMTU = 576, + ENetProtocol_MinimumWindowSize = 4096 + }; + + enum class ENetPeerState + { + AcknowledgingConnect = 2, + AcknowledgingDisconnect = 8, + Connecting = 1, + ConnectionPending = 3, + ConnectionSucceeded = 4, + Connected = 5, + Disconnected = 0, + Disconnecting = 7, + DisconnectLater = 6, + Zombie = 9 + }; + + enum ENetProtocolCommand + { + // Keeping the values is important for compatibility with the native ENet protocol + ENetProtocolCommand_Acknowledge = 1, + ENetProtocolCommand_BandwidthLimit = 10, + ENetProtocolCommand_Connect = 2, + ENetProtocolCommand_Disconnect = 4, + ENetProtocolCommand_None = 0, + ENetProtocolCommand_Ping = 5, + ENetProtocolCommand_SendFragment = 8, + ENetProtocolCommand_SendReliable = 6, + ENetProtocolCommand_SendUnreliable = 7, + ENetProtocolCommand_SendUnreliableFragment = 12, + ENetProtocolCommand_SendUnsequenced = 9, + ENetProtocolCommand_ThrottleConfigure = 11, + ENetProtocolCommand_VerifyConnect = 3, + ENetProtocolCommand_Count = 13, + + ENetProtocolCommand_Mask = 0x0F + }; + + enum ENetProtocolFlag + { + ENetProtocolFlag_Acknowledge = (1 << 7), + ENetProtocolFlag_Unsequenced = (1 << 6), + + ENetProtocolHeaderFlag_Compressed = (1 << 14), + ENetProtocolHeaderFlag_SentTime = (1 << 15), + ENetProtocolHeaderFlag_Mask = ENetProtocolHeaderFlag_Compressed | ENetProtocolHeaderFlag_SentTime, + + ENetProtocolHeaderSessionMask = (3 << 12), + ENetProtocolHeaderSessionShift = 12 + }; + + enum class ENetEventType + { + /** no event occurred within the specified time limit */ + None, + + /** a connection request initiated by enet_host_connect has completed. + * The peer field contains the peer which successfully connected. + */ + Connect, + + /** a peer has disconnected. This event is generated on a successful + * completion of a disconnect initiated by enet_peer_disconnect, if + * a peer has timed out, or if a connection request initialized by + * enet_host_connect has timed out. The peer field contains the peer + * which disconnected. The data field contains user supplied data + * describing the disconnection, or 0, if none is available. + */ + Disconnect, + + /** a packet has been received from a peer. The peer field specifies the + * peer which sent the packet. The channelID field specifies the channel + * number upon which the packet was received. The packet field contains + * the packet that was received; this packet must be destroyed with + * enet_packet_destroy after use. + */ + Receive + }; + + struct ENetEvent + { + ENetEventType type; + ENetPeer* peer; + UInt8 channelId; + UInt32 data; + ENetPacketRef packet; + }; + + struct ENetProtocolHeader + { + UInt16 peerID; + UInt16 sentTime; + }; + + struct ENetProtocolCommandHeader + { + UInt8 command; + UInt8 channelID; + UInt16 reliableSequenceNumber; + }; + + struct ENetProtocolAcknowledge + { + ENetProtocolCommandHeader header; + UInt16 receivedReliableSequenceNumber; + UInt16 receivedSentTime; + }; + + struct ENetProtocolConnect + { + ENetProtocolCommandHeader header; + UInt16 outgoingPeerID; + UInt8 incomingSessionID; + UInt8 outgoingSessionID; + UInt32 mtu; + UInt32 windowSize; + UInt32 channelCount; + UInt32 incomingBandwidth; + UInt32 outgoingBandwidth; + UInt32 packetThrottleInterval; + UInt32 packetThrottleAcceleration; + UInt32 packetThrottleDeceleration; + UInt32 connectID; + UInt32 data; + }; + + struct ENetProtocolBandwidthLimit + { + ENetProtocolCommandHeader header; + UInt32 incomingBandwidth; + UInt32 outgoingBandwidth; + }; + + struct ENetProtocolDisconnect + { + ENetProtocolCommandHeader header; + UInt32 data; + }; + + struct ENetProtocolPing + { + ENetProtocolCommandHeader header; + }; + + struct ENetProtocolSendFragment + { + ENetProtocolCommandHeader header; + UInt16 startSequenceNumber; + UInt16 dataLength; + UInt32 fragmentCount; + UInt32 fragmentNumber; + UInt32 totalLength; + UInt32 fragmentOffset; + }; + + struct ENetProtocolSendReliable + { + ENetProtocolCommandHeader header; + UInt16 dataLength; + }; + + struct ENetProtocolSendUnreliable + { + ENetProtocolCommandHeader header; + UInt16 unreliableSequenceNumber; + UInt16 dataLength; + }; + + struct ENetProtocolSendUnsequenced + { + ENetProtocolCommandHeader header; + UInt16 unsequencedGroup; + UInt16 dataLength; + }; + + struct ENetProtocolThrottleConfigure + { + ENetProtocolCommandHeader header; + UInt32 packetThrottleInterval; + UInt32 packetThrottleAcceleration; + UInt32 packetThrottleDeceleration; + }; + + struct ENetProtocolVerifyConnect + { + ENetProtocolCommandHeader header; + UInt16 outgoingPeerID; + UInt8 incomingSessionID; + UInt8 outgoingSessionID; + UInt32 mtu; + UInt32 windowSize; + UInt32 channelCount; + UInt32 incomingBandwidth; + UInt32 outgoingBandwidth; + UInt32 packetThrottleInterval; + UInt32 packetThrottleAcceleration; + UInt32 packetThrottleDeceleration; + UInt32 connectID; + }; + + union ENetProtocol + { + ENetProtocolCommandHeader header; + ENetProtocolAcknowledge acknowledge; + ENetProtocolBandwidthLimit bandwidthLimit; + ENetProtocolConnect connect; + ENetProtocolDisconnect disconnect; + ENetProtocolPing ping; + ENetProtocolSendReliable sendReliable; + ENetProtocolSendUnreliable sendUnreliable; + ENetProtocolSendUnsequenced sendUnsequenced; + ENetProtocolSendFragment sendFragment; + ENetProtocolThrottleConfigure throttleConfigure; + ENetProtocolVerifyConnect verifyConnect; + }; +} + +#endif // NAZARA_ENETPROTOCOL_HPP diff --git a/src/Nazara/Network/ENetHost.cpp b/src/Nazara/Network/ENetHost.cpp new file mode 100644 index 000000000..05179e615 --- /dev/null +++ b/src/Nazara/Network/ENetHost.cpp @@ -0,0 +1,743 @@ +#include +#include +#include +#include +#include +#include +#include + +#define ENET_TIME_OVERFLOW 86400000 + +#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW) +#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW) +#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b)) +#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b)) + +#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b)) + +namespace Nz +{ + /// Temporary + template + T HostToNet(T value) + { + #ifdef NAZARA_LITTLE_ENDIAN + return SwapBytes(value); + #else + return value; + #endif + } + + /// Temporary + template + T NetToHost(T value) + { + #ifdef NAZARA_LITTLE_ENDIAN + return SwapBytes(value); + #else + return value; + #endif + } + + + void ENetHost::Broadcast(UInt8 channelId, ENetPacketFlags flags, NetPacket&& packet) + { + ENetPacket* enetPacket = m_packetPool.New(); + enetPacket->flags = flags; + enetPacket->data = std::move(packet); + enetPacket->owner = &m_packetPool; + + for (ENetPeer& peer : m_peers) + { + if (peer.m_state != ENetPeerState::Connected) + continue; + + peer.Send(channelId, enetPacket); + } + } + + bool ENetHost::Connect(const IpAddress& remoteAddress, std::size_t channelCount, UInt32 data) + { + NazaraAssert(remoteAddress.IsValid(), "Invalid remote address"); + NazaraAssert(remoteAddress.GetPort() != 0, "Remote address has no port"); + + std::size_t peerId; + for (peerId = 0; peerId < m_peers.size(); ++peerId) + { + if (m_peers[peerId].m_state == ENetPeerState::Disconnected) + break; + } + + if (peerId >= m_peers.size()) + { + NazaraError("Insufficient peers"); + return false; + } + + m_channelLimit = Clamp(channelCount, ENetConstants::ENetProtocol_MinimumChannelCount, ENetConstants::ENetProtocol_MaximumChannelCount); + + UInt32 windowSize; + if (m_outgoingBandwidth == 0) + windowSize = ENetProtocol_MaximumWindowSize; + else + windowSize = (m_outgoingBandwidth / ENetConstants::ENetPeer_WindowSizeScale) * ENetProtocol_MinimumWindowSize; + + ENetPeer& peer = m_peers[peerId]; + peer.InitOutgoing(channelCount, remoteAddress, ++m_randomSeed, windowSize); + + ENetProtocol command; + command.header.command = ENetProtocolCommand_Connect | ENetProtocolFlag_Acknowledge; + command.header.channelID = 0xFF; + + command.connect.channelCount = HostToNet(static_cast(channelCount)); + command.connect.connectID = peer.m_connectID; + command.connect.data = HostToNet(data); + command.connect.incomingBandwidth = HostToNet(m_incomingBandwidth); + command.connect.incomingSessionID = peer.m_incomingSessionID; + command.connect.mtu = HostToNet(peer.m_mtu); + command.connect.outgoingBandwidth = HostToNet(m_outgoingBandwidth); + command.connect.outgoingPeerID = HostToNet(peer.m_incomingPeerID); + command.connect.outgoingSessionID = peer.m_outgoingSessionID; + command.connect.packetThrottleAcceleration = HostToNet(peer.m_packetThrottleAcceleration); + command.connect.packetThrottleDeceleration = HostToNet(peer.m_packetThrottleDeceleration); + command.connect.packetThrottleInterval = HostToNet(peer.m_packetThrottleInterval); + command.connect.windowSize = HostToNet(peer.m_windowSize); + + peer.QueueOutgoingCommand(command, nullptr, 0, 0); + + return true; + } + + bool ENetHost::Connect(const String& hostName, NetProtocol protocol, const String& service, ResolveError* error, std::size_t channelCount, UInt32 data) + { + std::vector results = IpAddress::ResolveHostname(protocol, hostName, service, error); + if (results.empty()) + return false; + + IpAddress hostnameAddress; + for (const HostnameInfo& result : results) + { + if (!result.address) + continue; + + if (result.socketType != SocketType_UDP) + continue; + + hostnameAddress = result.address; + break; //< Take first valid address + } + + return Connect(hostnameAddress, channelCount, data); + } + + bool ENetHost::Create(const IpAddress& address, std::size_t peerCount, std::size_t channelCount) + { + return Create(address, peerCount, channelCount, 0, 0); + } + + bool ENetHost::Create(const IpAddress& address, std::size_t peerCount, std::size_t channelCount, UInt32 incomingBandwidth, UInt32 outgoingBandwidth) + { + NazaraAssert(address.IsValid(), "Invalid listening address"); + + if (peerCount > ENetConstants::ENetProtocol_MaximumPeerId) + { + NazaraError("Peer count exceeds maximum peer count supported by protocol (" + String::Number(ENetConstants::ENetProtocol_MaximumPeerId) + ")"); + return false; + } + + if (!InitSocket(address)) + return false; + + m_peers.resize(peerCount); + + m_address = address; + m_randomSeed = *reinterpret_cast(this); + m_randomSeed += s_randomGenerator(); + m_randomSeed = (m_randomSeed << 16) | (m_randomSeed >> 16); + m_channelLimit = Clamp(channelCount, ENetConstants::ENetProtocol_MinimumChannelCount, ENetConstants::ENetProtocol_MaximumChannelCount); + m_incomingBandwidth = incomingBandwidth; + m_outgoingBandwidth = outgoingBandwidth; + m_bandwidthThrottleEpoch = 0; + m_recalculateBandwidthLimits = false; + m_mtu = ENetConstants::ENetHost_DefaultMTU; + m_commandCount = 0; + m_bufferCount = 0; + m_receivedAddress = IpAddress::AnyIpV4; + m_receivedData = nullptr; + m_receivedDataLength = 0; + + m_totalSentData = 0; + m_totalSentPackets = 0; + m_totalReceivedData = 0; + m_totalReceivedPackets = 0; + + m_bandwidthLimitedPeers = 0; + m_connectedPeers = 0; + m_duplicatePeers = ENetConstants::ENetProtocol_MaximumPeerId; + m_maximumPacketSize = ENetConstants::ENetHost_DefaultMaximumPacketSize; + m_maximumWaitingData = ENetConstants::ENetHost_DefaultMaximumWaitingData; + + return true; + } + + void ENetHost::Flush() + { + m_serviceTime = GetElapsedMilliseconds(); + + SendOutgoingCommands(nullptr, 0); + } + + int ENetHost::Service(ENetEvent* event, UInt32 timeout) + { + UInt32 waitCondition; + + if (event) + { + event->type = ENetEventType::None; + event->peer = nullptr; + event->packet = nullptr; + + if (DispatchIncomingCommands(event)) + return 1; + } + + m_serviceTime = GetElapsedMilliseconds(); + timeout += m_serviceTime; + + do + { + if (ENET_TIME_DIFFERENCE(m_serviceTime, m_bandwidthThrottleEpoch) >= ENetConstants::ENetHost_BandwidthThrottleInterval) + ThrottleBandwidth(); + + switch (SendOutgoingCommands(event, true)) + { + case 1: + return 1; + + case -1: + #ifdef ENET_DEBUG + perror("Error sending outgoing packets"); + #endif + return -1; + + default: + break; + } + + switch (ReceiveIncomingCommands(event)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror("Error receiving incoming packets"); +#endif + + return -1; + + default: + break; + } + + switch (SendOutgoingCommands(event, 1)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror("Error sending outgoing packets"); +#endif + + return -1; + + default: + break; + } + + if (event) + { + switch (DispatchIncomingCommands(event)) + { + case 1: + return 1; + + case -1: + #ifdef ENET_DEBUG + perror("Error dispatching incoming packets"); + #endif + + return -1; + + default: + break; + } + } + + if (ENET_TIME_GREATER_EQUAL(m_serviceTime, timeout)) + return 0; + + for (;;) + { + m_serviceTime = GetElapsedMilliseconds(); + + if (ENET_TIME_GREATER_EQUAL(m_serviceTime, timeout)) + return 0; + + SocketError error; + if (m_poller.Wait(ENET_TIME_DIFFERENCE(timeout, m_serviceTime), &error)) + break; + + if (error != SocketError_NoError) + return -1; + } + + m_serviceTime = GetElapsedMilliseconds(); + } + while (m_poller.IsReady(m_socket)); + + return 0; + } + + bool ENetHost::InitSocket(const IpAddress& address) + { + if (!m_socket.Create(address.GetProtocol())) + return false; + + m_socket.EnableBlocking(false); + m_socket.EnableBroadcasting(true); + m_socket.SetReceiveBufferSize(ENetConstants::ENetHost_ReceiveBufferSize); + m_socket.SetSendBufferSize(ENetConstants::ENetHost_SendBufferSize); + + if (!address.IsLoopback()) + { + if (m_socket.Bind(address) != SocketState_Bound) + { + NazaraError("Failed to bind address " + address.ToString()); + return false; + } + } + + m_poller.RegisterSocket(m_socket); + + return true; + } + + bool ENetHost::DispatchIncomingCommands(ENetEvent* event) + { + for (std::size_t bit = m_dispatchQueue.FindFirst(); bit != m_dispatchQueue.npos; bit = m_dispatchQueue.FindNext(bit)) + { + m_dispatchQueue.Reset(bit); + + ENetPeer& peer = m_peers[bit]; + switch (peer.m_state) + { + case ENetPeerState::ConnectionPending: + case ENetPeerState::ConnectionSucceeded: + peer.ChangeState(ENetPeerState::Connected); + + event->type = ENetEventType::Connect; + event->peer = &peer; + event->data = peer.m_eventData; + return true; + + case ENetPeerState::Zombie: + m_recalculateBandwidthLimits = true; + + event->type = ENetEventType::Disconnect; + event->peer = &peer; + event->data = peer.m_eventData; + + peer.Reset(); + return true; + + case ENetPeerState::Connected: + if (peer.m_dispatchedCommands.empty()) + continue; + + if (!peer.Receive(&event->packet, &event->channelId)) + continue; + + event->type = ENetEventType::Receive; + event->peer = &peer; + + if (!peer.m_dispatchedCommands.empty()) + AddToDispatchQueue(&peer); + + return true; + } + } + + return false; + } + + ENetPeer* ENetHost::HandleConnect(ENetProtocolHeader* header, ENetProtocol* command) + { + UInt32 channelCount = NetToHost(command->connect.channelCount); + + if (channelCount < ENetProtocol_MinimumChannelCount || channelCount > ENetProtocol_MaximumChannelCount) + return nullptr; + + std::size_t duplicatePeers = 0; + ENetPeer* peer = nullptr; + for (ENetPeer& currentPeer : m_peers) + { + if (currentPeer.m_state == ENetPeerState::Disconnected) + { + if (!peer) + peer = ¤tPeer; + } + else if (currentPeer.m_state != ENetPeerState::Connecting) + { + // Compare users without comparing their port + IpAddress first(currentPeer.m_address); + first.SetPort(0); + + IpAddress second(m_receivedAddress); + second.SetPort(0); + + if (first == second) + { + if (currentPeer.m_address.GetPort() == m_receivedAddress.GetPort() && currentPeer.m_connectID == command->connect.connectID) + return nullptr; + + ++duplicatePeers; + } + } + } + + if (!peer || duplicatePeers >= m_duplicatePeers) + return nullptr; + + channelCount = std::min(channelCount, m_channelLimit); + + peer->InitIncoming(channelCount, m_receivedAddress, command->connect); + + UInt32 windowSize; + if (m_incomingBandwidth == 0) + windowSize = ENetConstants::ENetProtocol_MaximumWindowSize; + else + windowSize = (m_incomingBandwidth / ENetConstants::ENetPeer_WindowSizeScale) * ENetConstants::ENetProtocol_MinimumWindowSize; + + windowSize = std::max(windowSize, NetToHost(command->connect.windowSize)); + windowSize = Clamp(windowSize, ENetConstants::ENetProtocol_MinimumWindowSize, ENetConstants::ENetProtocol_MaximumWindowSize); + + ENetProtocol verifyCommand; + verifyCommand.header.command = ENetProtocolCommand_VerifyConnect | ENetProtocolFlag_Acknowledge; + verifyCommand.header.channelID = 0xFF; + verifyCommand.verifyConnect.outgoingPeerID = HostToNet(peer->m_incomingPeerID); + verifyCommand.verifyConnect.incomingSessionID = peer->m_outgoingSessionID; + verifyCommand.verifyConnect.outgoingSessionID = peer->m_incomingSessionID; + verifyCommand.verifyConnect.mtu = HostToNet(peer->m_mtu); + verifyCommand.verifyConnect.windowSize = HostToNet(windowSize); + verifyCommand.verifyConnect.channelCount = HostToNet(channelCount); + verifyCommand.verifyConnect.incomingBandwidth = HostToNet(m_incomingBandwidth); + verifyCommand.verifyConnect.outgoingBandwidth = HostToNet(m_outgoingBandwidth); + verifyCommand.verifyConnect.packetThrottleInterval = HostToNet(peer->m_packetThrottleInterval); + verifyCommand.verifyConnect.packetThrottleAcceleration = HostToNet(peer->m_packetThrottleAcceleration); + verifyCommand.verifyConnect.packetThrottleDeceleration = HostToNet(peer->m_packetThrottleDeceleration); + verifyCommand.verifyConnect.connectID = peer->m_connectID; + + peer->QueueOutgoingCommand(verifyCommand, nullptr, 0, 0); + + return peer; + } + + bool ENetHost::HandleIncomingCommands(ENetEvent* event) + { + if (m_receivedDataLength < NazaraOffsetOf(ENetProtocolHeader, sentTime)) + return false; + + ENetProtocolHeader* header = reinterpret_cast(m_receivedData); + + peerID = NetToHost(header->peerID); + sessionID = (peerID & ENET_PROTOCOL_HEADER_SESSION_MASK) >> ENET_PROTOCOL_HEADER_SESSION_SHIFT; + flags = peerID & ENET_PROTOCOL_HEADER_FLAG_MASK; + peerID &= ~(ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK); + + headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof(ENetProtocolHeader) : (size_t) & ((ENetProtocolHeader *)0)->sentTime); + if (host->checksum != NULL) + headerSize += sizeof(enet_uint32); + + if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID) + peer = NULL; + else + if (peerID >= host->peerCount) + return 0; + else + { + peer = &host->peers[peerID]; + + if (peer->state == ENET_PEER_STATE_DISCONNECTED || + peer->state == ENET_PEER_STATE_ZOMBIE || + ((host->receivedAddress.host != peer->address.host || + host->receivedAddress.port != peer->address.port) && + peer->address.host != ENET_HOST_BROADCAST) || + (peer->outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && + sessionID != peer->incomingSessionID)) + return 0; + } + + return false; + } + + bool ENetHost::HandleSendReliable(ENetPeer& peer, const ENetProtocol& command, UInt8** currentData) + { + if (command.header.channelID >= peer.m_channels.size() || (peer.m_state != ENetPeerState::Connected && peer.m_state != ENetPeerState::DisconnectLater)) + return false; + + UInt16 dataLength = NetToHost(command.sendReliable.dataLength); + *currentData += dataLength; + if (dataLength >= m_maximumPacketSize || *currentData < m_receivedData || *currentData > &m_receivedData[m_receivedDataLength]) + return false; + + if (!peer.QueueIncomingCommand(command, co)) + + return true; + } + + int ENetHost::ReceiveIncomingCommands(ENetEvent* event) + { + for (unsigned int i = 0; i < 256; ++i) + { + NetPacket packet; + + 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; + + m_receivedData = m_packetData[0].data(); + m_receivedDataLength = receivedLength; + + m_totalReceivedData += receivedLength; + m_totalReceivedPackets++; + + // Intercept + + switch (HandleIncomingCommands(event)) + { + case 1: + return 1; + + case -1: + return -1; + + default: + break; + } + } + + return -1; + } + + void ENetHost::NotifyConnect(ENetPeer* peer, ENetEvent* event) + { + m_recalculateBandwidthLimits = true; + + if (event) + { + peer->ChangeState(ENetPeerState::Connected); + + event->type = ENetEventType::Connect; + event->peer = peer; + event->data = peer->m_eventData; + } + else + peer->DispatchState(peer->m_state == ENetPeerState::Connecting ? ENetPeerState::ConnectionSucceeded : ENetPeerState::ConnectionPending); + } + + void ENetHost::NotifyDisconnect(ENetPeer* peer, ENetEvent* event) + { + if (peer->m_state >= ENetPeerState::ConnectionPending) + m_recalculateBandwidthLimits = true; + + if (peer->m_state != ENetPeerState::Connecting && (peer->m_state < ENetPeerState::ConnectionSucceeded)) + peer->Reset(); + else if (event) + { + event->type = ENetEventType::Disconnect; + event->peer = peer; + event->data = peer->m_eventData; + + peer->Reset(); + } + else + { + peer->m_eventData = 0; + + peer->DispatchState(ENetPeerState::Zombie); + } + } + + void ENetHost::ThrottleBandwidth() + { + UInt32 currentTime = GetElapsedMilliseconds(); + UInt32 elapsedTime = currentTime - m_bandwidthThrottleEpoch; + + if (elapsedTime < ENetConstants::ENetHost_BandwidthThrottleInterval) + return; + + m_bandwidthThrottleEpoch = currentTime; + + if (m_connectedPeers == 0) + return; + + UInt32 dataTotal = ~0; + UInt32 bandwidth = ~0; + + if (m_outgoingBandwidth != 0) + { + bandwidth = (m_outgoingBandwidth * elapsedTime) / 1000; + + dataTotal = 0; + for (ENetPeer& peer : m_peers) + { + if (peer.m_state != ENetPeerState::Connected && peer.m_state != ENetPeerState::DisconnectLater) + continue; + + dataTotal += peer.m_outgoingDataTotal; + } + } + + UInt32 peersRemaining = m_connectedPeers; + UInt32 bandwidthLimit = ~0; + UInt32 throttle = ~0; + bool needsAdjustment = m_bandwidthLimitedPeers > 0; + + while (peersRemaining > 0 && needsAdjustment) + { + needsAdjustment = false; + + if (dataTotal <= bandwidth) + throttle = ENetConstants::ENetPeer_PacketThrottleScale; + else + throttle = (bandwidth * ENetConstants::ENetPeer_PacketThrottleScale) / dataTotal; + + for (ENetPeer& peer : m_peers) + { + if ((peer.m_state != ENetPeerState::Connected && peer.m_state != ENetPeerState::DisconnectLater) || + peer.m_incomingBandwidth == 0 || peer.m_outgoingBandwidthThrottleEpoch == currentTime) + continue; + + UInt32 peerBandwidth = (peer.m_incomingBandwidth * elapsedTime) / 1000; + if ((throttle * peer.m_outgoingDataTotal) / ENetConstants::ENetPeer_PacketThrottleScale <= peerBandwidth) + continue; + + peer.m_packetThrottleLimit = (peerBandwidth * ENetConstants::ENetPeer_PacketThrottleScale) / peer.m_outgoingDataTotal; + + if (peer.m_packetThrottleLimit == 0) + peer.m_packetThrottleLimit = 1; + + if (peer.m_packetThrottle > peer.m_packetThrottleLimit) + peer.m_packetThrottle = peer.m_packetThrottleLimit; + + peer.m_outgoingBandwidthThrottleEpoch = currentTime; + + peer.m_incomingDataTotal = 0; + peer.m_outgoingDataTotal = 0; + + needsAdjustment = true; + --peersRemaining; + bandwidth -= peerBandwidth; + dataTotal -= peerBandwidth; + } + } + + if (peersRemaining > 0) + { + if (dataTotal <= bandwidth) + throttle = ENetConstants::ENetPeer_PacketThrottleScale; + else + throttle = (bandwidth * ENetConstants::ENetPeer_PacketThrottleScale) / dataTotal; + + for (ENetPeer& peer : m_peers) + { + if ((peer.m_state != ENetPeerState::Connected && peer.m_state != ENetPeerState::DisconnectLater) || + peer.m_outgoingBandwidthThrottleEpoch == currentTime) + continue; + + peer.m_packetThrottleLimit = throttle; + + if (peer.m_packetThrottle > peer.m_packetThrottleLimit) + peer.m_packetThrottle = peer.m_packetThrottleLimit; + + peer.m_incomingDataTotal = 0; + peer.m_outgoingDataTotal = 0; + } + } + + if (m_recalculateBandwidthLimits) + { + m_recalculateBandwidthLimits = false; + + peersRemaining = m_connectedPeers; + bandwidth = m_incomingBandwidth; + needsAdjustment = true; + + if (bandwidth == 0) + bandwidthLimit = 0; + else + { + while (peersRemaining > 0 && needsAdjustment) + { + needsAdjustment = false; + bandwidthLimit = bandwidth / peersRemaining; + + for (ENetPeer& peer : m_peers) + { + if ((peer.m_state != ENetPeerState::Connected && peer.m_state != ENetPeerState::DisconnectLater) || + peer.m_incomingBandwidthThrottleEpoch == currentTime) + continue; + + if (peer.m_outgoingBandwidth > 0 && peer.m_outgoingBandwidth >= bandwidthLimit) + continue; + + peer.m_incomingBandwidthThrottleEpoch = currentTime; + + needsAdjustment = true; + --peersRemaining; + bandwidth -= peer.m_outgoingBandwidth; + } + } + } + + for (ENetPeer& peer : m_peers) + { + if (peer.m_state != ENetPeerState::Connected && peer.m_state != ENetPeerState::DisconnectLater) + continue; + + ENetProtocol command; + command.header.command = ENetProtocolCommand_BandwidthLimit | ENetProtocolFlag_Acknowledge; + command.header.channelID = 0xFF; + command.bandwidthLimit.outgoingBandwidth = HostToNet(m_outgoingBandwidth); + + if (peer.m_incomingBandwidthThrottleEpoch == currentTime) + command.bandwidthLimit.incomingBandwidth = HostToNet(peer.m_outgoingBandwidth); + else + command.bandwidthLimit.incomingBandwidth = HostToNet(bandwidthLimit); + + peer.QueueOutgoingCommand(command, nullptr, 0, 0); + } + } + } + + bool ENetHost::Initialize() + { + std::random_device device; + s_randomGenerator.seed(device()); + s_randomGenerator64.seed(device()); + + return true; + } + + void ENetHost::Uninitialize() + { + } + + std::mt19937 ENetHost::s_randomGenerator; + std::mt19937_64 ENetHost::s_randomGenerator64; +} \ No newline at end of file diff --git a/src/Nazara/Network/ENetPacket.cpp b/src/Nazara/Network/ENetPacket.cpp new file mode 100644 index 000000000..ec2bc4ab7 --- /dev/null +++ b/src/Nazara/Network/ENetPacket.cpp @@ -0,0 +1,23 @@ +#include +#include +#include + +namespace Nz +{ + /// Temporary + void ENetPacketRef::Reset(ENetPacket* packet = nullptr) + { + if (m_packet) + { + if (--m_packet->referenceCount == 0) + { + m_packet->owner->Delete(m_packet); + return; + } + } + + m_packet = packet; + if (m_packet) + m_packet->referenceCount++; + } +} \ No newline at end of file diff --git a/src/Nazara/Network/ENetPeer.cpp b/src/Nazara/Network/ENetPeer.cpp new file mode 100644 index 000000000..9e5471c21 --- /dev/null +++ b/src/Nazara/Network/ENetPeer.cpp @@ -0,0 +1,885 @@ +#include +#include +#include +#include +#include + +namespace Nz +{ + /// Temporary + template + T HostToNet(T value) + { + #ifdef NAZARA_LITTLE_ENDIAN + return SwapBytes(value); + #else + return value; + #endif + } + + /// Temporary + template + T NetToHost(T value) + { + #ifdef NAZARA_LITTLE_ENDIAN + return SwapBytes(value); + #else + return value; + #endif + } + + namespace + { + static std::size_t commandSizes[ENetProtocolCommand_Count] = + { + 0, + sizeof(ENetProtocolAcknowledge), + sizeof(ENetProtocolConnect), + sizeof(ENetProtocolVerifyConnect), + sizeof(ENetProtocolDisconnect), + sizeof(ENetProtocolPing), + sizeof(ENetProtocolSendReliable), + sizeof(ENetProtocolSendUnreliable), + sizeof(ENetProtocolSendFragment), + sizeof(ENetProtocolSendUnsequenced), + sizeof(ENetProtocolBandwidthLimit), + sizeof(ENetProtocolThrottleConfigure), + sizeof(ENetProtocolSendFragment) + }; + + std::size_t enet_protocol_command_size(UInt8 commandNumber) + { + return commandSizes[commandNumber & ENetProtocolCommand_Mask]; + } + } + + ENetPeer::ENetPeer(ENetHost* host, UInt16 peerId) : + m_packetPool(sizeof(ENetPacket)), + m_host(host), + m_incomingPeerID(peerId), + m_incomingSessionID(0xFF), + m_outgoingSessionID(0xFF) + { + Reset(); + } + + void ENetPeer::Disconnect(UInt32 data) + { + if (m_state == ENetPeerState::Disconnecting || + m_state == ENetPeerState::Disconnected || + m_state == ENetPeerState::AcknowledgingDisconnect || + m_state == ENetPeerState::Zombie) + return; + + ResetQueues(); + + ENetProtocol command; + command.header.command = ENetProtocolCommand_Disconnect; + command.header.channelID = 0xFF; + command.disconnect.data = HostToNet(data); + + if (m_state == ENetPeerState::Connected || m_state == ENetPeerState::DisconnectLater) + command.header.command |= ENetProtocolFlag_Acknowledge; + else + command.header.command |= ENetProtocolFlag_Unsequenced; + + QueueOutgoingCommand(command, nullptr, 0, 0); + + if (m_state == ENetPeerState::Connected || m_state == ENetPeerState::DisconnectLater) + { + OnDisconnect(); + + m_state = ENetPeerState::Disconnecting; + } + else + { + m_host->Flush(); + + Reset(); + } + } + + void ENetPeer::DisconnectLater(UInt32 data) + { + if ((m_state == ENetPeerState::Connected || m_state == ENetPeerState::DisconnectLater) && + !m_outgoingReliableCommands.empty() && + !m_outgoingUnreliableCommands.empty() && + !m_sentReliableCommands.empty()) + { + m_state = ENetPeerState::DisconnectLater; + m_eventData = data; + } + else + Disconnect(data); + } + + void ENetPeer::DisconnectNow(UInt32 data) + { + if (m_state == ENetPeerState::Disconnected) + return; + + if (m_state != ENetPeerState::Zombie && m_state != ENetPeerState::Disconnecting) + { + ResetQueues(); + + ENetProtocol command; + command.header.command = ENetProtocolCommand_Disconnect | ENetProtocolFlag_Unsequenced; + command.header.channelID = 0xFF; + command.disconnect.data = HostToNet(data); + + QueueOutgoingCommand(command, nullptr, 0, 0); + + m_host->Flush(); + } + + Reset(); + } + + void ENetPeer::Ping() + { + if (m_state != ENetPeerState::Connected) + return; + + ENetProtocol command; + command.header.command = ENetProtocolCommand_Ping | ENetProtocolFlag_Acknowledge; + command.header.channelID = 0xFF; + + QueueOutgoingCommand(command, nullptr, 0, 0); + } + + bool ENetPeer::Receive(ENetPacketRef* packet, UInt8* channelId) + { + if (m_dispatchedCommands.empty()) + return false; + + IncomingCommmand& incomingCommand = m_dispatchedCommands.front(); + + m_totalWaitingData -= incomingCommand.packet->data.GetSize(); + + if (packet) + *packet = std::move(incomingCommand.packet); + + if (channelId) + *channelId = incomingCommand.command.header.channelID; + + m_dispatchedCommands.pop_front(); + + return true; + } + + void ENetPeer::Reset() + { + OnDisconnect(); + + m_outgoingPeerID = ENetConstants::ENetProtocol_MaximumPeerId; + m_connectID = 0; + + m_state = ENetPeerState::Disconnected; + + m_incomingBandwidth = 0; + m_outgoingBandwidth = 0; + m_incomingBandwidthThrottleEpoch = 0; + m_outgoingBandwidthThrottleEpoch = 0; + m_incomingDataTotal = 0; + m_outgoingDataTotal = 0; + m_lastSendTime = 0; + m_lastReceiveTime = 0; + m_nextTimeout = 0; + m_earliestTimeout = 0; + m_packetLossEpoch = 0; + m_packetsSent = 0; + m_packetsLost = 0; + m_packetLoss = 0; + m_packetLossVariance = 0; + m_packetThrottle = ENetConstants::ENetProtocol_MaximumWindowSize; + m_packetThrottleLimit = ENetConstants::ENetPeer_PacketThrottleScale; + m_packetThrottleCounter = 0; + m_packetThrottleEpoch = 0; + m_packetThrottleAcceleration = ENetConstants::ENetPeer_PacketThrottleAcceleration; + m_packetThrottleDeceleration = ENetConstants::ENetPeer_PacketThrottleDeceleration; + m_packetThrottleInterval = ENetConstants::ENetPeer_PacketThrottleInterval; + m_pingInterval = ENetConstants::ENetPeer_PingInterval; + m_timeoutLimit = ENetConstants::ENetPeer_TimeoutLimit; + m_timeoutMinimum = ENetConstants::ENetPeer_TimeoutMinimum; + m_timeoutMaximum = ENetConstants::ENetPeer_TimeoutMaximum; + m_lastRoundTripTime = ENetConstants::ENetPeer_DefaultRoundTripTime; + m_lowestRoundTripTime = ENetConstants::ENetPeer_DefaultRoundTripTime; + m_lastRoundTripTimeVariance = 0; + m_highestRoundTripTimeVariance = 0; + m_roundTripTime = ENetConstants::ENetPeer_DefaultRoundTripTime; + m_roundTripTimeVariance = 0; + m_mtu = m_host->m_mtu; + m_reliableDataInTransit = 0; + m_outgoingReliableSequenceNumber = 0; + m_windowSize = ENetConstants::ENetProtocol_MaximumWindowSize; + m_incomingUnsequencedGroup = 0; + m_outgoingUnsequencedGroup = 0; + m_eventData = 0; + m_totalWaitingData = 0; + + std::memset(m_unsequencedWindow, 0, sizeof(m_unsequencedWindow)); + + ResetQueues(); + } + + bool ENetPeer::Send(UInt8 channelId, ENetPacketFlags flags, NetPacket&& packet) + { + ENetPacket* enetPacket = m_packetPool.New(); + enetPacket->flags = flags; + enetPacket->data = std::move(packet); + enetPacket->owner = &m_packetPool; + + return Send(channelId, enetPacket); + } + + bool ENetPeer::Send(UInt8 channelId, ENetPacketRef packetRef) + { + if (m_state != ENetPeerState::Connected || channelId >= m_channels.size() || packetRef->data.GetSize() > m_host->m_maximumPacketSize) + return false; + + Channel& channel = m_channels[channelId]; + + std::size_t fragmentLength = m_mtu - sizeof(ENetProtocolHeader) - sizeof(ENetProtocolSendFragment); + //if (m_host->m_checksum != nullptr) + // fragmentLength -= sizeof(UInt32); + + UInt32 packetSize = static_cast(packetRef->data.GetSize()); + if (packetSize > fragmentLength) + { + UInt32 fragmentCount = (packetSize + fragmentLength - 1) / fragmentLength; + UInt32 fragmentNumber; + UInt32 fragmentOffset; + + UInt8 commandNumber; + UInt16 startSequenceNumber; + + if (fragmentCount > ENetConstants::ENetProtocol_MaximumFragmentCount) + return false; + + if ((packetRef->flags & (ENetPacketFlag_Reliable | ENetPacketFlag_UnreliableFragment)) == ENetPacketFlag_UnreliableFragment && + channel.outgoingUnreliableSequenceNumber < 0xFFFF) + { + commandNumber = ENetProtocolCommand_SendUnreliable; + startSequenceNumber = HostToNet(channel.outgoingUnreliableSequenceNumber + 1); + } + else + { + commandNumber = ENetProtocolCommand_SendFragment | ENetProtocolFlag_Acknowledge; + startSequenceNumber = HostToNet(channel.outgoingReliableSequenceNumber + 1); + } + + for (fragmentNumber = 0, + fragmentOffset = 0; + fragmentOffset < packetSize; + ++fragmentNumber, + fragmentOffset += fragmentLength) + { + if (packetSize - fragmentOffset < fragmentLength) + fragmentLength = packetSize - fragmentOffset; + + OutgoingCommand outgoingCommand; + outgoingCommand.fragmentOffset = fragmentOffset; + outgoingCommand.fragmentLength = fragmentLength; + outgoingCommand.packet = packetRef; + outgoingCommand.command.header.command = commandNumber; + outgoingCommand.command.header.channelID = channelId; + outgoingCommand.command.sendFragment.startSequenceNumber = startSequenceNumber; + outgoingCommand.command.sendFragment.dataLength = HostToNet(fragmentLength); + outgoingCommand.command.sendFragment.fragmentCount = HostToNet(fragmentCount); + outgoingCommand.command.sendFragment.fragmentNumber = HostToNet(fragmentNumber); + outgoingCommand.command.sendFragment.totalLength = HostToNet(packetSize); + outgoingCommand.command.sendFragment.fragmentOffset = HostToNet(fragmentOffset); + + SetupOutgoingCommand(outgoingCommand); + } + + return true; + } + + ENetProtocol command; + command.header.channelID = channelId; + + if ((packetRef->flags & (ENetPacketFlag_Reliable | ENetPacketFlag_Unsequenced)) == ENetPacketFlag_Unsequenced) + command.header.command = ENetProtocolCommand_SendUnsequenced | ENetProtocolFlag_Unsequenced; + else if (packetRef->flags & ENetPacketFlag_Reliable || channel.outgoingUnreliableSequenceNumber >= 0xFFFF) + command.header.command = ENetProtocolCommand_SendReliable | ENetProtocolFlag_Acknowledge; + else + command.header.command = ENetProtocolCommand_SendUnreliable; + + QueueOutgoingCommand(command, packetRef, 0, packetSize); + + return true; + } + + void ENetPeer::ThrottleConfigure(UInt32 interval, UInt32 acceleration, UInt32 deceleration) + { + m_packetThrottleInterval = interval; + m_packetThrottleAcceleration = acceleration; + m_packetThrottleDeceleration = deceleration; + + ENetProtocol command; + command.header.command = ENetProtocolCommand_ThrottleConfigure | ENetProtocolFlag_Acknowledge; + command.header.channelID = 0xFF; + + command.throttleConfigure.packetThrottleInterval = HostToNet(interval); + command.throttleConfigure.packetThrottleAcceleration = HostToNet(acceleration); + command.throttleConfigure.packetThrottleDeceleration = HostToNet(deceleration); + + QueueOutgoingCommand(command, nullptr, 0, 0); + } + + void ENetPeer::InitIncoming(std::size_t channelCount, const IpAddress& address, ENetProtocolConnect& incomingCommand) + { + m_channels.resize(channelCount); + m_address = address; + + m_connectID = incomingCommand.connectID; + m_eventData = NetToHost(incomingCommand.data); + m_incomingBandwidth = NetToHost(incomingCommand.incomingBandwidth); + m_outgoingBandwidth = NetToHost(incomingCommand.outgoingBandwidth); + m_packetThrottleInterval = NetToHost(incomingCommand.packetThrottleInterval); + m_packetThrottleAcceleration = NetToHost(incomingCommand.packetThrottleAcceleration); + m_packetThrottleDeceleration = NetToHost(incomingCommand.packetThrottleDeceleration); + m_outgoingPeerID = NetToHost(incomingCommand.outgoingPeerID); + m_state = ENetPeerState::AcknowledgingConnect; + + UInt8 incomingSessionId, outgoingSessionId; + + incomingSessionId = incomingCommand.incomingSessionID == 0xFF ? m_outgoingSessionID : incomingCommand.incomingSessionID; + incomingSessionId = (incomingSessionId + 1) & (ENetProtocolHeaderSessionMask >> ENetProtocolHeaderSessionShift); + if (incomingSessionId == m_outgoingSessionID) + incomingSessionId = (incomingSessionId + 1) & (ENetProtocolHeaderSessionMask >> ENetProtocolHeaderSessionShift); + m_outgoingSessionID = incomingSessionId; + + outgoingSessionId = incomingCommand.outgoingSessionID == 0xFF ? m_incomingSessionID : incomingCommand.outgoingSessionID; + outgoingSessionId = (outgoingSessionId + 1) & (ENetProtocolHeaderSessionMask >> ENetProtocolHeaderSessionShift); + if (outgoingSessionId == m_incomingSessionID) + outgoingSessionId = (outgoingSessionId + 1) & (ENetProtocolHeaderSessionMask >> ENetProtocolHeaderSessionShift); + m_incomingSessionID = outgoingSessionId; + + m_mtu = Clamp(NetToHost(incomingCommand.mtu), ENetConstants::ENetProtocol_MinimumMTU, ENetConstants::ENetProtocol_MaximumMTU); + + if (m_host->m_outgoingBandwidth == 0 && m_incomingBandwidth == 0) + m_windowSize = ENetConstants::ENetProtocol_MaximumWindowSize; + else if (m_host->m_outgoingBandwidth == 0 || m_incomingBandwidth == 0) + m_windowSize = (std::max(m_host->m_outgoingBandwidth, m_incomingBandwidth) / ENetConstants::ENetPeer_WindowSizeScale) * ENetConstants::ENetProtocol_MinimumWindowSize; + else + m_windowSize = (std::min(m_host->m_outgoingBandwidth, m_incomingBandwidth) / ENetConstants::ENetPeer_WindowSizeScale) * ENetConstants::ENetProtocol_MinimumWindowSize; + + m_windowSize = Clamp(m_windowSize, ENetConstants::ENetProtocol_MinimumWindowSize, ENetConstants::ENetProtocol_MaximumWindowSize); + } + + void ENetPeer::InitOutgoing(std::size_t channelCount, const IpAddress& address, UInt32 connectId, UInt32 windowSize) + { + m_channels.resize(channelCount); + + m_address = address; + m_connectID = connectId; + m_state = ENetPeerState::Connecting; + m_windowSize = Clamp(windowSize, ENetConstants::ENetProtocol_MinimumWindowSize, ENetConstants::ENetProtocol_MaximumWindowSize); + } + + void ENetPeer::DispatchIncomingReliableCommands(Channel& channel) + { + auto currentCommand = channel.incomingReliableCommands.begin(); + for (; currentCommand != channel.incomingReliableCommands.end(); ++currentCommand) + { + IncomingCommmand& incomingCommand = *currentCommand; + + if (incomingCommand.fragmentsRemaining > 0 || incomingCommand.reliableSequenceNumber != (channel.incomingReliableSequenceNumber + 1)) + break; + + channel.incomingReliableSequenceNumber = incomingCommand.reliableSequenceNumber; + + if (!incomingCommand.fragments.empty()) + channel.incomingReliableSequenceNumber += incomingCommand.fragments.size() - 1; + } + + if (currentCommand == channel.incomingReliableCommands.begin()) + return; + + channel.incomingUnreliableSequenceNumber = 0; + + m_dispatchedCommands.splice(m_dispatchedCommands.end(), m_dispatchedCommands, channel.incomingReliableCommands.begin(), currentCommand); + + m_host->AddToDispatchQueue(this); + + if (!channel.incomingUnreliableCommands.empty()) + DispatchIncomingUnreliableCommands(channel); + } + + void ENetPeer::DispatchIncomingUnreliableCommands(Channel& channel) + { + std::list::iterator currentCommand; + std::list::iterator droppedCommand; + std::list::iterator startCommand; + + for (droppedCommand = startCommand = currentCommand = channel.incomingUnreliableCommands.begin(); + currentCommand != channel.incomingUnreliableCommands.end(); + ++currentCommand) + { + IncomingCommmand& incomingCommand = *currentCommand; + + if ((incomingCommand.command.header.command & ENetProtocolCommand_Mask) == ENetProtocolCommand_SendUnsequenced) + continue; + + if (incomingCommand.reliableSequenceNumber == channel.incomingReliableSequenceNumber) + { + if (incomingCommand.fragmentsRemaining <= 0) + { + channel.incomingUnreliableSequenceNumber = incomingCommand.unreliableSequenceNumber; + continue; + } + + if (startCommand != currentCommand) + { + m_dispatchedCommands.splice(m_dispatchedCommands.end(), m_dispatchedCommands, startCommand, currentCommand); + + m_host->AddToDispatchQueue(this); + + droppedCommand = currentCommand; + } + else if (droppedCommand != currentCommand) + { + droppedCommand = currentCommand; + --droppedCommand; + } + } + else + { + UInt16 reliableWindow = incomingCommand.reliableSequenceNumber / ENetConstants::ENetPeer_ReliableWindowSize; + UInt16 currentWindow = channel.incomingReliableSequenceNumber / ENetConstants::ENetPeer_ReliableWindowSize; + + if (incomingCommand.reliableSequenceNumber < channel.incomingReliableSequenceNumber) + reliableWindow += ENetConstants::ENetPeer_ReliableWindows; + + if (reliableWindow >= currentWindow && reliableWindow < currentWindow + ENetConstants::ENetPeer_ReliableWindowSize - 1) + break; + + droppedCommand = currentCommand; + ++droppedCommand; + + if (startCommand != currentCommand) + { + m_dispatchedCommands.splice(m_dispatchedCommands.end(), m_dispatchedCommands, startCommand, currentCommand); + + m_host->AddToDispatchQueue(this); + } + } + + startCommand = currentCommand; + ++startCommand; + } + + if (startCommand != currentCommand) + { + m_dispatchedCommands.splice(m_dispatchedCommands.end(), m_dispatchedCommands, startCommand, currentCommand); + + m_host->AddToDispatchQueue(this); + + droppedCommand = currentCommand; + } + + channel.incomingUnreliableCommands.erase(channel.incomingUnreliableCommands.begin(), droppedCommand); + } + + void ENetPeer::OnConnect() + { + if (m_state != ENetPeerState::Connected && m_state != ENetPeerState::DisconnectLater) + { + if (m_incomingBandwidth != 0) + ++m_host->m_bandwidthLimitedPeers; + + ++m_host->m_connectedPeers; + } + } + + void ENetPeer::OnDisconnect() + { + if (m_state == ENetPeerState::Connected || m_state == ENetPeerState::DisconnectLater) + { + if (m_incomingBandwidth != 0) + --m_host->m_bandwidthLimitedPeers; + + --m_host->m_connectedPeers; + } + } + + ENetProtocolCommand ENetPeer::RemoveSentReliableCommands(UInt16 reliableSequenceNumber, UInt8 channelId) + { + std::list* commandList = nullptr; + + bool found = true; + auto currentCommand = m_sentReliableCommands.begin(); + commandList = &m_sentReliableCommands; + for (; currentCommand != m_sentReliableCommands.end(); ++currentCommand) + { + found = true; + + if (currentCommand->reliableSequenceNumber == reliableSequenceNumber && + currentCommand->command.header.channelID == channelId) + break; + } + + bool wasSent = true; + if (currentCommand == m_sentReliableCommands.end()) + { + currentCommand = m_outgoingReliableCommands.begin(); + commandList = &m_sentReliableCommands; + for (; currentCommand != m_outgoingReliableCommands.end(); ++currentCommand) + { + found = true; + + if (currentCommand->sendAttempts < 1) + return ENetProtocolCommand_None; + + if (currentCommand->reliableSequenceNumber == reliableSequenceNumber && + currentCommand->command.header.channelID == channelId) + break; + } + + if (currentCommand == m_outgoingReliableCommands.end()) + return ENetProtocolCommand_None; + + wasSent = false; + } + + if (!found) //< Really useful? + return ENetProtocolCommand_None; + + if (channelId < m_channels.size()) + { + Channel& channel = m_channels[channelId]; + + UInt16 reliableWindow = reliableSequenceNumber / ENetConstants::ENetPeer_ReliableWindowSize; + if (channel.reliableWindows[reliableWindow] > 0) + { + --channel.reliableWindows[reliableWindow]; + if (!channel.reliableWindows[reliableWindow]) + channel.usedReliableWindows &= ~(1 << reliableWindow); + } + } + + ENetProtocolCommand commandNumber = static_cast(currentCommand->command.header.command & ENetProtocolCommand_Mask); + + if (currentCommand->packet && wasSent) + m_reliableDataInTransit -= currentCommand->fragmentLength; + + commandList->erase(currentCommand); + + if (m_sentReliableCommands.empty()) + return commandNumber; + + currentCommand = m_sentReliableCommands.begin(); + m_nextTimeout = currentCommand->sentTime + currentCommand->roundTripTimeout; + + return commandNumber; + } + + void ENetPeer::RemoveSentUnreliableCommands() + { + m_sentUnreliableCommands.clear(); + } + + void ENetPeer::ResetQueues() + { + m_host->RemoveFromDispatchQueue(this); + + m_acknowledgements.clear(); + m_dispatchedCommands.clear(); + m_outgoingReliableCommands.clear(); + m_outgoingUnreliableCommands.clear(); + m_sentReliableCommands.clear(); + m_sentUnreliableCommands.clear(); + + m_channels.clear(); + } + + bool ENetPeer::QueueAcknowledgement(ENetProtocol& command, UInt16 sentTime) + { + if (command.header.channelID < m_channels.size()) + { + Channel& channel = m_channels[command.header.channelID]; + + UInt16 reliableWindow = command.header.reliableSequenceNumber / ENetConstants::ENetPeer_ReliableWindowSize; + UInt16 currentWindow = channel.incomingReliableSequenceNumber / ENetConstants::ENetPeer_ReliableWindowSize; + + if (command.header.reliableSequenceNumber < channel.incomingReliableSequenceNumber) + reliableWindow += ENetConstants::ENetPeer_ReliableWindows; + + if (reliableWindow >= currentWindow + ENetConstants::ENetPeer_FreeReliableWindows - 1 && reliableWindow <= currentWindow + ENetConstants::ENetPeer_FreeReliableWindows) + return false; + } + + Acknowledgement acknowledgment; + acknowledgment.command = command; + acknowledgment.sentTime = sentTime; + + m_outgoingDataTotal += sizeof(Acknowledgement); + + m_acknowledgements.emplace_back(acknowledgment); + + return true; + } + + ENetPeer::IncomingCommmand* ENetPeer::QueueIncomingCommand(ENetProtocol& command, const void* data, std::size_t dataLength, UInt32 flags, UInt32 fragmentCount) + { + static IncomingCommmand dummyCommand; + + UInt32 reliableSequenceNumber = 0; + UInt32 unreliableSequenceNumber = 0; + UInt16 reliableWindow; + UInt16 currentWindow; + + auto discardCommand = [&]() -> IncomingCommmand* + { + if (fragmentCount > 0) + return nullptr; //< Error + + return &dummyCommand; + }; + + if (m_state == ENetPeerState::DisconnectLater) + return discardCommand(); + + Channel& channel = m_channels[command.header.channelID]; + + if ((command.header.command & ENetProtocolCommand_Mask) != ENetProtocolCommand_SendUnsequenced) + { + reliableSequenceNumber = command.header.reliableSequenceNumber; + reliableWindow = reliableSequenceNumber / ENetConstants::ENetPeer_ReliableWindowSize; + currentWindow = channel.incomingReliableSequenceNumber / ENetConstants::ENetPeer_ReliableWindowSize; + + if (reliableSequenceNumber < channel.incomingReliableSequenceNumber) + reliableWindow += ENetConstants::ENetPeer_ReliableWindows; + + if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENetConstants::ENetPeer_FreeReliableWindows - 1) + return discardCommand(); + } + + std::list* commandList = nullptr; + std::list::reverse_iterator currentCommand; + + switch (command.header.command & ENetProtocolCommand_Mask) + { + case ENetProtocolCommand_SendFragment: + case ENetProtocolCommand_SendReliable: + { + if (reliableSequenceNumber == channel.incomingReliableSequenceNumber) + return discardCommand(); + + commandList = &channel.incomingReliableCommands; + + for (currentCommand = channel.incomingReliableCommands.rbegin(); currentCommand != channel.incomingReliableCommands.rend(); ++currentCommand) + { + IncomingCommmand& incomingCommand = *currentCommand; + + if (reliableSequenceNumber >= channel.incomingReliableSequenceNumber) + { + if (incomingCommand.reliableSequenceNumber < channel.incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand.reliableSequenceNumber >= channel.incomingReliableSequenceNumber) + break; + + if (incomingCommand.reliableSequenceNumber <= reliableSequenceNumber) + { + if (incomingCommand.reliableSequenceNumber < reliableSequenceNumber) + break; + + return discardCommand(); + } + } + break; + } + + case ENetProtocolCommand_SendUnreliable: + case ENetProtocolCommand_SendUnreliableFragment: + { + unreliableSequenceNumber = NetToHost(command.sendUnreliable.unreliableSequenceNumber); + + if (reliableSequenceNumber == channel.incomingReliableSequenceNumber && unreliableSequenceNumber <= channel.incomingUnreliableSequenceNumber) + return discardCommand(); + + commandList = &channel.incomingUnreliableCommands; + + for (currentCommand = channel.incomingUnreliableCommands.rbegin(); currentCommand != channel.incomingUnreliableCommands.rend(); ++currentCommand) + { + IncomingCommmand& incomingCommand = *currentCommand; + + if ((command.header.command & ENetProtocolCommand_Mask) == ENetProtocolCommand_SendUnsequenced) //< wtf + continue; + + if (reliableSequenceNumber >= channel.incomingReliableSequenceNumber) + { + if (incomingCommand.reliableSequenceNumber < channel.incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand.reliableSequenceNumber >= channel.incomingReliableSequenceNumber) + break; + + if (incomingCommand.reliableSequenceNumber < reliableSequenceNumber) + break; + + if (incomingCommand.reliableSequenceNumber > reliableSequenceNumber) + continue; + + if (incomingCommand.unreliableSequenceNumber <= unreliableSequenceNumber) + { + if (incomingCommand.unreliableSequenceNumber < unreliableSequenceNumber) + break; + + return discardCommand(); + } + } + break; + } + + case ENetProtocolCommand_SendUnsequenced: + { + commandList = &channel.incomingUnreliableCommands; + + currentCommand = channel.incomingUnreliableCommands.rend(); + break; + } + + default: + return discardCommand(); + } + + if (m_totalWaitingData >= m_host->m_maximumWaitingData) + return nullptr; + + ENetPacket* packet = m_packetPool.New(); + packet->flags = flags; + packet->data.Reset(0, data, dataLength); + packet->owner = &m_packetPool; + + IncomingCommmand incomingCommand; + incomingCommand.reliableSequenceNumber = command.header.reliableSequenceNumber; + incomingCommand.unreliableSequenceNumber = unreliableSequenceNumber & 0xFFFF; + incomingCommand.command = command; + incomingCommand.packet = packet; + incomingCommand.fragments.resize(fragmentCount, 0); + incomingCommand.fragmentsRemaining = fragmentCount; + + auto it = commandList->insert(currentCommand.base(), incomingCommand); + + switch (command.header.command & ENetProtocolCommand_Mask) + { + case ENetProtocolCommand_SendFragment: + case ENetProtocolCommand_SendReliable: + DispatchIncomingReliableCommands(channel); + break; + + default: + DispatchIncomingUnreliableCommands(channel); + break; + } + + return &(*it); + } + + void ENetPeer::QueueOutgoingCommand(ENetProtocol& command, ENetPacketRef packet, UInt32 offset, UInt16 length) + { + OutgoingCommand outgoingCommand; + outgoingCommand.command = command; + outgoingCommand.fragmentLength = length; + outgoingCommand.fragmentOffset = length; + outgoingCommand.packet = packet; + + SetupOutgoingCommand(outgoingCommand); + } + + void ENetPeer::SetupOutgoingCommand(OutgoingCommand& outgoingCommand) + { + m_outgoingDataTotal += enet_protocol_command_size(outgoingCommand.command.header.command) + outgoingCommand.fragmentLength; + + if (outgoingCommand.command.header.channelID == 0xFF) + { + ++m_outgoingReliableSequenceNumber; + + outgoingCommand.reliableSequenceNumber = m_outgoingReliableSequenceNumber; + outgoingCommand.unreliableSequenceNumber = 0; + } + else + { + Channel* channel = &m_channels[outgoingCommand.command.header.channelID]; + if (outgoingCommand.command.header.command & ENetProtocolFlag_Acknowledge) + { + ++channel->outgoingReliableSequenceNumber; + channel->outgoingUnreliableSequenceNumber = 0; + + outgoingCommand.reliableSequenceNumber = channel->outgoingReliableSequenceNumber; + outgoingCommand.unreliableSequenceNumber = 0; + } + else if (outgoingCommand.command.header.command & ENetProtocolFlag_Unsequenced) + { + ++m_outgoingUnsequencedGroup; + + outgoingCommand.reliableSequenceNumber = 0; + outgoingCommand.unreliableSequenceNumber = 0; + } + else + { + if (outgoingCommand.fragmentOffset == 0) + ++channel->outgoingUnreliableSequenceNumber; + + outgoingCommand.reliableSequenceNumber = channel->outgoingReliableSequenceNumber; + outgoingCommand.unreliableSequenceNumber = channel->outgoingUnreliableSequenceNumber; + } + } + + outgoingCommand.sendAttempts = 0; + outgoingCommand.sentTime = 0; + outgoingCommand.roundTripTimeout = 0; + outgoingCommand.roundTripTimeoutLimit = 0; + outgoingCommand.command.header.reliableSequenceNumber = HostToNet(outgoingCommand.reliableSequenceNumber); + + switch (outgoingCommand.command.header.command & ENetProtocolCommand_Mask) + { + case ENetProtocolCommand_SendUnreliable: + outgoingCommand.command.sendUnreliable.unreliableSequenceNumber = HostToNet(outgoingCommand.unreliableSequenceNumber); + break; + + case ENetProtocolCommand_SendUnsequenced: + outgoingCommand.command.sendUnsequenced.unsequencedGroup = HostToNet(m_outgoingUnsequencedGroup); + break; + + default: + break; + } + + if (outgoingCommand.command.header.command & ENetProtocolCommand_Acknowledge) + m_outgoingReliableCommands.emplace_back(outgoingCommand); + else + m_outgoingUnreliableCommands.emplace_back(outgoingCommand); + } + + int ENetPeer::Throttle(UInt32 rtt) + { + if (m_lastRoundTripTime <= m_lastRoundTripTimeVariance) + m_packetThrottle = m_packetThrottleLimit; + else + { + if (rtt < m_lastRoundTripTime) + { + m_packetThrottle = std::max(m_packetThrottle + m_packetThrottleAcceleration, m_packetThrottleLimit); + return 1; + } + else if (rtt > m_lastRoundTripTime + 2 * m_lastRoundTripTimeVariance) + { + if (m_packetThrottle > m_packetThrottleDeceleration) + m_packetThrottle -= m_packetThrottleDeceleration; + else + m_packetThrottle = 0; + + return -1; + } + } + + return 0; + } +} \ No newline at end of file diff --git a/src/Nazara/Network/ENetSocket.cpp b/src/Nazara/Network/ENetSocket.cpp new file mode 100644 index 000000000..e86313fb4 --- /dev/null +++ b/src/Nazara/Network/ENetSocket.cpp @@ -0,0 +1,642 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +namespace Nz +{ + /*! + * \ingroup network + * \class Nz::ENetConnection + * \brief Network class that represents a reliable UDP connection, based on ENet library + */ + + /*! + * \brief Constructs a RUdpConnection object by default + */ + + ENetSocket::ENetSocket() : + m_bandwidthThrottleEpoch(0), + m_mtu(ENetConstants::DefaultMTU), + m_isSimulationEnabled(false), + m_shouldAcceptConnections(true) + { + m_randomSeed = *reinterpret_cast(this); + m_randomSeed += s_randomGenerator(); + m_randomSeed = (m_randomSeed << 16) | (m_randomSeed >> 16); + + } + + /*! + * \brief Connects to the IpAddress + * \return true + * + * \param remoteAddress Address to connect to + * + * \remark Produces a NazaraAssert if socket is not bound + * \remark Produces a NazaraAssert if remote is invalid + * \remark Produces a NazaraAssert if port is not specified + */ + + bool RUdpConnection::Connect(const IpAddress& remoteAddress) + { + NazaraAssert(m_socket.GetState() == SocketState_Bound, "Socket must be bound first"); + NazaraAssert(remoteAddress.IsValid(), "Invalid remote address"); + NazaraAssert(remoteAddress.GetPort() != 0, "Remote address has no port"); + + PeerData& client = RegisterPeer(remoteAddress, PeerState_Connecting); + client.stateData1 = s_randomGenerator(); + + NetPacket connectionRequestPacket(NetCode_RequestConnection); + connectionRequestPacket << client.stateData1; + + EnqueuePacket(client, PacketPriority_Immediate, PacketReliability_Reliable, connectionRequestPacket); + return true; + } + + /*! + * \brief Connects to the hostname + * \return true If successful + * + * \param hostName Hostname of the remote + * \param protocol Net protocol to use + * \param service Specify the protocol used + * \param error Optional argument to get the error + */ + + bool RUdpConnection::Connect(const String& hostName, NetProtocol protocol, const String& service, ResolveError* error) + { + std::vector results = IpAddress::ResolveHostname(protocol, hostName, service, error); + if (results.empty()) + { + m_lastError = SocketError_ResolveError; + return false; + } + + IpAddress hostnameAddress; + for (const HostnameInfo& result : results) + { + if (!result.address) + continue; + + if (result.socketType != SocketType_UDP) + continue; + + hostnameAddress = result.address; + break; //< Take first valid address + } + + return Connect(hostnameAddress); + } + + /*! + * \brief Listens to a socket + * \return true If successfully bound + * + * \param remoteAddress Address to listen to + */ + + bool RUdpConnection::Listen(const IpAddress& address) + { + if (!InitSocket(address.GetProtocol())) + return false; + + return m_socket.Bind(address) == SocketState_Bound; + } + + /*! + * \brief Polls the message + * \return true If there is a message + * + * \param message Message to poll + * + * \remark Produces a NazaraAssert if message is invalid + */ + + bool RUdpConnection::PollMessage(RUdpMessage* message) + { + NazaraAssert(message, "Invalid message"); + + if (m_receivedMessages.empty()) + return false; + + *message = std::move(m_receivedMessages.front()); + m_receivedMessages.pop(); + return true; + } + + /*! + * \brief Sends the packet to a peer + * \return true If peer exists (false may result from disconnected client) + * + * \param peerIp IpAddress of the peer + * \param priority Priority of the packet + * \param reliability Policy of reliability of the packet + * \param packet Packet to send + */ + + bool RUdpConnection::Send(const IpAddress& peerIp, PacketPriority priority, PacketReliability reliability, const NetPacket& packet) + { + auto it = m_peerByIP.find(peerIp); + if (it == m_peerByIP.end()) + return false; /// Silently fail (probably a disconnected client) + + EnqueuePacket(m_peers[it->second], priority, reliability, packet); + return true; + } + + /*! + * \brief Updates the reliable connection + */ + + void RUdpConnection::Update() + { + m_currentTime = m_clock.GetMicroseconds(); + + NetPacket receivedPacket; + IpAddress senderIp; + while (m_socket.ReceivePacket(&receivedPacket, &senderIp)) + OnPacketReceived(senderIp, std::move(receivedPacket)); + + //for (unsigned int i = m_activeClients.FindFirst(); i != m_activeClients.npos; i = m_activeClients.FindNext(i)) + //{ + // PeerData& clientData = m_peers[i]; + + CallOnExit resetIterator([this] () { m_peerIterator = m_peers.size(); }); + + for (m_peerIterator = 0; m_peerIterator < m_peers.size(); ++m_peerIterator) + { + PeerData& peer = m_peers[m_peerIterator]; + + UInt32 timeSinceLastPacket = static_cast(m_currentTime - peer.lastPacketTime); + if (timeSinceLastPacket > m_timeBeforeTimeOut) + { + DisconnectPeer(peer.index); + continue; + } + else if (timeSinceLastPacket > m_timeBeforePing) + { + if (m_currentTime - peer.lastPingTime > m_pingInterval) + { + NetPacket pingPacket(NetCode_Ping); + EnqueuePacket(peer, PacketPriority_Low, PacketReliability_Unreliable, pingPacket); + } + } + + if (peer.state == PeerState_WillAck && m_currentTime - peer.stateData1 > m_forceAckSendTime) + { + NetPacket acknowledgePacket(NetCode_Acknowledge); + EnqueuePacket(peer, PacketPriority_Low, PacketReliability_Reliable, acknowledgePacket); + } + + for (unsigned int priority = PacketPriority_Highest; priority <= PacketPriority_Lowest; ++priority) + { + std::vector& pendingPackets = peer.pendingPackets[priority]; + for (PendingPacket& packetData : pendingPackets) + SendPacket(peer, std::move(packetData)); + + pendingPackets.clear(); + } + + auto it = peer.pendingAckQueue.begin(); + while (it != peer.pendingAckQueue.end()) + { + if (m_currentTime - it->timeSent > 2 * peer.roundTripTime) + { + OnPacketLost(peer, std::move(*it)); + it = peer.pendingAckQueue.erase(it); + } + else + ++it; + } + } + //m_activeClients.Reset(); + } + + /*! + * \brief Disconnects a peer + * + * \param peerIndex Index of the peer + * + * \remark Produces a NazaraNotice + */ + + void RUdpConnection::DisconnectPeer(std::size_t peerIndex) + { + PeerData& peer = m_peers[peerIndex]; + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": " + peer.address.ToString() + " has been disconnected due to time-out"); + + OnPeerDisconnected(this, peer.address); + + // Remove from IP lookup table + m_peerByIP.erase(peer.address); + + // Can we safely "remove" this slot? + if (m_peerIterator >= m_peers.size() - 1 || peerIndex > m_peerIterator) + { + // Yes we can + PeerData& newSlot = m_peers[peerIndex]; + newSlot = std::move(m_peers.back()); + newSlot.index = peerIndex; //< Update the moved slot index before resizing (in case it's the last one) + } + else + { + // Nope, let's be tricky + PeerData& current = m_peers[m_peerIterator]; + PeerData& newSlot = m_peers[peerIndex]; + + newSlot = std::move(current); + newSlot.index = peerIndex; //< Update the moved slot index + + current = std::move(m_peers.back()); + current.index = m_peerIterator; //< Update the moved slot index + + --m_peerIterator; + } + + // Pop the last entry (from where we moved our slot) + m_peers.pop_back(); + } + + /*! + * \brief Enqueues a packet in the sending list + * + * \param peer Data relative to the peer + * \param priority Priority of the packet + * \param reliability Policy of reliability of the packet + * \param packet Packet to send + */ + + void RUdpConnection::EnqueuePacket(PeerData& peer, PacketPriority priority, PacketReliability reliability, const NetPacket& packet) + { + UInt16 protocolBegin = static_cast(m_protocol & 0xFFFF); + UInt16 protocolEnd = static_cast((m_protocol & 0xFFFF0000) >> 16); + + NetPacket data(packet.GetNetCode(), MessageHeader + packet.GetDataSize() + MessageFooter); + data << protocolBegin; + + data.GetStream()->SetCursorPos(NetPacket::HeaderSize + MessageHeader); + data.Write(packet.GetConstData() + NetPacket::HeaderSize, packet.GetDataSize()); + + data << protocolEnd; + EnqueuePacketInternal(peer, priority, reliability, std::move(data)); + } + + /*! + * \brief Enqueues internally a packet in the sending list + * + * \param peer Data relative to the peer + * \param priority Priority of the packet + * \param reliability Policy of reliability of the packet + * \param packet Packet to send + */ + + void RUdpConnection::EnqueuePacketInternal(PeerData& peer, PacketPriority priority, PacketReliability reliability, NetPacket&& data) + { + PendingPacket pendingPacket; + pendingPacket.data = std::move(data); + pendingPacket.priority = priority; + pendingPacket.reliability = reliability; + + peer.pendingPackets[priority].emplace_back(std::move(pendingPacket)); + m_activeClients.UnboundedSet(peer.index); + } + + /*! + * \brief Inits the internal socket + * \return true If successful + * + * \param protocol Net protocol to use + */ + + bool ENetSocket::InitSocket(NetProtocol protocol) + { + CallOnExit updateLastError([this] + { + m_lastError = m_socket.GetLastError(); + }); + + if (!m_socket.Create(protocol)) + return false; + + m_socket.EnableBlocking(false); + m_socket.SetReceiveBufferSize(ENetConstants::ReceiveBufferSize); + m_socket.SetSendBufferSize(ENetConstants::SendBufferSize); + + return true; + } + + /*! + * \brief Processes the acks + * + * \param peer Data relative to the peer + * \param lastAck Last index of the ack + * \param ackBits Bits for acking + */ + + void RUdpConnection::ProcessAcks(PeerData& peer, SequenceIndex lastAck, UInt32 ackBits) + { + auto it = peer.pendingAckQueue.begin(); + while (it != peer.pendingAckQueue.end()) + { + bool acked = false; + if (lastAck == it->sequenceId) + acked = true; + else if (!IsAckMoreRecent(it->sequenceId, lastAck)) + { + unsigned int difference = ComputeSequenceDifference(lastAck, it->sequenceId); + if (difference <= 32) + acked = (ackBits >> (difference - 1)) & 1; + } + + if (acked) + { + it = peer.pendingAckQueue.erase(it); + } + else + ++it; + } + } + + /*! + * \brief Registers a peer + * \return Data relative to the peer + * + * \param address Address of the peer + * \param state Status of the peer + */ + + RUdpConnection::PeerData& RUdpConnection::RegisterPeer(const IpAddress& address, PeerState state) + { + PeerData data; + data.address = address; + data.localSequence = 0; + data.remoteSequence = 0; + data.index = m_peers.size(); + data.lastPacketTime = m_currentTime; + data.lastPingTime = m_currentTime; + data.roundTripTime = 1'000'000; ///< Okay that's quite a lot + data.state = state; + + m_activeClients.UnboundedSet(data.index); + m_peerByIP[address] = data.index; + + m_peers.emplace_back(std::move(data)); + return m_peers.back(); + } + + /*! + * \brief Operation to do when client requests a connection + * + * \param address Address of the peer + * \param sequenceId Sequence index for the ack + * \param token Token for connection + */ + + void RUdpConnection::OnClientRequestingConnection(const IpAddress& address, SequenceIndex sequenceId, UInt64 token) + { + // Call hook to check if client should be accepted or not + OnPeerConnection(this, address); + + PeerData& client = RegisterPeer(address, PeerState_Aknowledged); + client.remoteSequence = sequenceId; + + /// Acknowledge connection + NetPacket connectionAcceptedPacket(NetCode_AcknowledgeConnection); + //connectionAcceptedPacket << address; + connectionAcceptedPacket << ~token; + + EnqueuePacket(client, PacketPriority_Immediate, PacketReliability_Reliable, connectionAcceptedPacket); + } + + /*! + * \brief Operation to do when a packet is lost + * + * \param peer Data relative to the peer + * \param packet Pending packet + */ + + void RUdpConnection::OnPacketLost(PeerData& peer, PendingAckPacket&& packet) + { + //NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Lost packet " + String::Number(packet.sequenceId)); + + if (IsReliable(packet.reliability)) + EnqueuePacketInternal(peer, packet.priority, packet.reliability, std::move(packet.data)); + } + + /*! + * \brief Operation to do when receiving a packet + * + * \param peerIndex Index of the peer + * + * \remark Produces a NazaraNotice + */ + + void RUdpConnection::OnPacketReceived(const IpAddress& peerIp, NetPacket&& packet) + { + UInt16 protocolBegin; + UInt16 protocolEnd; + SequenceIndex sequenceId; + SequenceIndex lastAck; + UInt32 ackBits; + + packet.GetStream()->SetCursorPos(packet.GetSize() - MessageFooter); + packet >> protocolEnd; + + packet.GetStream()->SetCursorPos(NetPacket::HeaderSize); + packet >> protocolBegin; + + UInt32 protocolId = static_cast(protocolEnd) << 16 | protocolBegin; + if (protocolId != m_protocol) + return; ///< Ignore + + packet >> sequenceId >> lastAck >> ackBits; + + auto it = m_peerByIP.find(peerIp); + if (it == m_peerByIP.end()) + { + switch (packet.GetNetCode()) + { + case NetCode_RequestConnection: + { + UInt64 token; + packet >> token; + + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received NetCode_RequestConnection from " + peerIp.ToString() + ": " + String::Number(token)); + if (!m_shouldAcceptConnections) + return; //< Ignore + + OnClientRequestingConnection(peerIp, sequenceId, token); + break; + } + + default: + return; //< Ignore + } + } + else + { + PeerData& peer = m_peers[it->second]; + peer.lastPacketTime = m_currentTime; + + if (peer.receivedQueue.find(sequenceId) != peer.receivedQueue.end()) + return; //< Ignore + + if (m_isSimulationEnabled && m_packetLossProbability(s_randomGenerator)) + { + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Lost packet " + String::Number(sequenceId) + " from " + peerIp.ToString() + " for simulation purpose"); + return; + } + + ///< Receiving a packet from an acknowledged client means the connection works in both ways + if (peer.state == PeerState_Aknowledged && packet.GetNetCode() != NetCode_RequestConnection) + { + peer.state = PeerState_Connected; + OnPeerAcknowledged(this, peerIp); + } + + if (IsAckMoreRecent(sequenceId, peer.remoteSequence)) + peer.remoteSequence = sequenceId; + + ProcessAcks(peer, lastAck, ackBits); + + peer.receivedQueue.insert(sequenceId); + + switch (packet.GetNetCode()) + { + case NetCode_Acknowledge: + return; //< Do not switch to will ack mode (to prevent infinite replies, just let's ping/pong do that) + + case NetCode_AcknowledgeConnection: + { + if (peer.state == PeerState_Connected) + break; + + IpAddress externalAddress; + UInt64 token; + packet /*>> externalAddress*/ >> token; + + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received NetCode_AcknowledgeConnection from " + peerIp.ToString() + ": " + String::Number(token)); + if (token == ~peer.stateData1) + { + peer.state = PeerState_Connected; + OnConnectedToPeer(this); + } + else + { + NazaraNotice("Received wrong token (" + String::Number(token) + " instead of " + String::Number(~peer.stateData1) + ") from client " + peer.address); + return; //< Ignore + } + + break; + } + + case NetCode_RequestConnection: + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received NetCode_RequestConnection from " + peerIp.ToString()); + return; //< Ignore + + case NetCode_Ping: + { + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received NetCode_Ping from " + peerIp.ToString()); + + NetPacket pongPacket(NetCode_Pong); + EnqueuePacket(peer, PacketPriority_Low, PacketReliability_Unreliable, pongPacket); + break; + } + + case NetCode_Pong: + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received NetCode_Pong from " + peerIp.ToString()); + break; + + default: + { + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received 0x" + String::Number(packet.GetNetCode(), 16) + " from " + peerIp.ToString()); + RUdpMessage receivedMessage; + receivedMessage.from = peerIp; + receivedMessage.data = std::move(packet); + + m_receivedMessages.emplace(std::move(receivedMessage)); + break; + } + } + + if (!HasPendingPackets(peer)) + { + peer.state = PeerState_WillAck; + peer.stateData1 = m_currentTime; + } + } + } + + /*! + * \brief Sends a packet to a peer + * + * \param peer Data relative to the peer + * \param packet Pending packet + */ + + void RUdpConnection::SendPacket(PeerData& peer, PendingPacket&& packet) + { + if (peer.state == PeerState_WillAck) + peer.state = PeerState_Connected; + + SequenceIndex remoteSequence = peer.remoteSequence; + + UInt32 previousAcks = 0; + for (SequenceIndex ack : peer.receivedQueue) + { + if (ack == remoteSequence) + continue; + + unsigned int difference = ComputeSequenceDifference(remoteSequence, ack); + if (difference <= 32U) + previousAcks |= (1U << (difference - 1)); + } + + SequenceIndex sequenceId = ++peer.localSequence; + + packet.data.GetStream()->SetCursorPos(NetPacket::HeaderSize + sizeof(UInt16)); ///< Protocol begin has already been filled + packet.data << sequenceId; + packet.data << remoteSequence; + packet.data << previousAcks; + + m_socket.SendPacket(peer.address, packet.data); + + PendingAckPacket pendingAckPacket; + pendingAckPacket.data = std::move(packet.data); + pendingAckPacket.priority = packet.priority; + pendingAckPacket.reliability = packet.reliability; + pendingAckPacket.sequenceId = sequenceId; + pendingAckPacket.timeSent = m_currentTime; + + peer.pendingAckQueue.emplace_back(std::move(pendingAckPacket)); + } + + /*! + * \brief Initializes the RUdpConnection class + * \return true + */ + + bool RUdpConnection::Initialize() + { + std::random_device device; + s_randomGenerator.seed(device()); + + return true; + } + + /*! + * \brief Uninitializes the RUdpConnection class + */ + + void RUdpConnection::Uninitialize() + { + } + + std::mt19937_64 RUdpConnection::s_randomGenerator; +}