diff --git a/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/ConnectionManager.h b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/ConnectionManager.h new file mode 100644 index 00000000..79cfbb02 --- /dev/null +++ b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/ConnectionManager.h @@ -0,0 +1,98 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include "oxygen_netcore/network/internal/SentPacketCache.h" +#include "oxygen_netcore/network/internal/ReceivedPacket.h" +#include "oxygen_netcore/network/VersionRange.h" +#include "oxygen_netcore/base/HandleProvider.h" + +namespace lowlevel +{ + struct PacketBase; +} +struct ConnectionListenerInterface; + + +class ConnectionManager +{ +friend class NetConnection; + +public: + struct DebugSettings + { + float mSendingPacketLoss = 0.0f; // Fraction of "lost" packets in sending + float mReceivingPacketLoss = 0.0f; // Fraction of "lost" packets in receiving + }; + DebugSettings mDebugSettings; + +public: + ConnectionManager(UDPSocket* udpSocket, TCPSocket* tcpListenSocket, ConnectionListenerInterface& listener, VersionRange highLevelProtocolVersionRange); + + inline bool hasUDPSocket() const { return (nullptr != mUDPSocket); } + inline UDPSocket* getUDPSocket() const { return mUDPSocket; } + inline TCPSocket* getTCPListenSocket() const { return mTCPListenSocket; } + + inline ConnectionListenerInterface& getListener() const { return mListener; } + inline size_t getNumActiveConnections() const { return mActiveConnections.size(); } + + inline VersionRange getHighLevelProtocolVersionRange() const { return mHighLevelProtocolVersionRange; } + + void updateConnections(uint64 currentTimestamp); + bool updateReceivePackets(); // TODO: This is meant to be executed by a thread later on + + void syncPacketQueues(); + + inline bool hasAnyPacket() const { return !mReceivedPackets.mSyncedQueue.empty(); } + ReceivedPacket* getNextReceivedPacket(); + std::list& getIncomingTCPConnections() { return mIncomingTCPConnections; } + + bool sendUDPPacketData(const std::vector& data, const SocketAddress& remoteAddress); + bool sendTCPPacketData(const std::vector& data, TCPSocket& socket, bool isWebSocketServer); + bool sendConnectionlessLowLevelPacket(lowlevel::PacketBase& lowLevelPacket, const SocketAddress& remoteAddress, uint16 localConnectionID, uint16 remoteConnectionID); + + NetConnection* findConnectionTo(uint64 senderKey) const; + +protected: + // Only meant to be called from the NetConnection + void addConnection(NetConnection& connection); + void removeConnection(NetConnection& connection); + SentPacket& rentSentPacket(); + + // Internal + void receivedPacketInternal(const std::vector& buffer, const SocketAddress& senderAddress, NetConnection* connection); + +private: + struct SyncedPacketQueue + { + std::deque mWorkerQueue; // Used by the worker thread that adds packets + std::deque mSyncedQueue; // Used by the main thread that reads packets + ReceivedPacket::Dump mToBeReturned; + }; + + typedef HandleProvider ConnectionsProvider; + +private: + UDPSocket* mUDPSocket = nullptr; // Only set if UDP is used (or both UDP and TCP) + TCPSocket* mTCPListenSocket = nullptr; // Only set if TCP is used (or both UDP and TCP) + ConnectionListenerInterface& mListener; + VersionRange mHighLevelProtocolVersionRange = { 1, 1 }; + + std::unordered_map mActiveConnections; // Using local connection ID as key + std::unordered_map mConnectionsBySender; // Using a sender key (= hash for the sender address + remote connection ID) as key + ConnectionsProvider mConnectionsProvider; + + std::vector mTCPNetConnections; + + SyncedPacketQueue mReceivedPackets; + std::list mIncomingTCPConnections; + + RentableObjectPool mSentPacketPool; + RentableObjectPool mReceivedPacketPool; +}; diff --git a/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/HighLevelPacketBase.h b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/HighLevelPacketBase.h new file mode 100644 index 00000000..cf3d8202 --- /dev/null +++ b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/HighLevelPacketBase.h @@ -0,0 +1,60 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include + + +namespace highlevel +{ + + // All high-level packets are built upon "lowlevel::HighLevelPacket" as their shared header + // -> Yes, that name sounds a bit dumb, bit it's a low-level packet after all... + + struct PacketBase + { + public: + bool serializePacket(VectorBinarySerializer& serializer, uint8 protocolVersion) + { + serializeContent(serializer, protocolVersion); + return !serializer.hasError(); + } + + public: + virtual uint32 getPacketType() const = 0; + virtual bool isReliablePacket() const { return true; } + + protected: + virtual void serializeContent(VectorBinarySerializer& serializer, uint8 protocolVersion) = 0; + + public: + static inline std::unordered_map mPacketTypeRegistry; + }; + + + struct PacketTypeRegistration + { + inline PacketTypeRegistration(uint32 packetType, const std::string& packetName) + { + RMX_ASSERT(PacketBase::mPacketTypeRegistry.count(packetType) == 0, "Multiple definition of packet type '" << packetName << "'"); + PacketBase::mPacketTypeRegistry[packetType] = packetName; + } + }; + +} + + +#define HIGHLEVEL_PACKET_DEFINE_PACKET_TYPE(_name_) \ + public: \ + static inline const std::string PACKET_NAME = _name_; \ + static const constexpr uint32 PACKET_TYPE = rmx::compileTimeFNV_32(_name_); \ + virtual uint32 getPacketType() const override { return PACKET_TYPE; } \ + private: \ + static inline highlevel::PacketTypeRegistration mPacketTypeRegistration { PACKET_TYPE, PACKET_NAME }; \ + public: diff --git a/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/LagStopwatch.h b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/LagStopwatch.h new file mode 100644 index 00000000..b81e2f03 --- /dev/null +++ b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/LagStopwatch.h @@ -0,0 +1,37 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include +#include + + +class LagStopwatch +{ +public: + inline LagStopwatch(const char* text, int maxMs = 2000) : mText(text), mMaximumMilliseconds(maxMs), mStartTime(std::chrono::steady_clock::now()) {} + + inline ~LagStopwatch() + { + const uint64 milliseconds = std::chrono::duration_cast(std::chrono::steady_clock::now() - mStartTime).count(); + if (milliseconds > mMaximumMilliseconds) + RMX_LOG_INFO("LagStopwatch: " << mText << " took " << milliseconds << " ms"); + } + +private: + const char* mText; + int mMaximumMilliseconds = 2000; + std::chrono::steady_clock::time_point mStartTime; +}; + +#ifdef OXYGEN_SERVER + #define LAG_STOPWATCH(_text_, _maxMs_) LagStopwatch lagStopwatch_##LINE_NUMBER(_text_, _maxMs_) +#else + #define LAG_STOPWATCH(_text_, _maxMs_) +#endif diff --git a/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/LowLevelPackets.h b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/LowLevelPackets.h new file mode 100644 index 00000000..17d5158b --- /dev/null +++ b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/LowLevelPackets.h @@ -0,0 +1,165 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include "oxygen_netcore/network/VersionRange.h" + + +namespace lowlevel +{ + // Note that for all low-level packets, an additional six bytes is sent preceding the actual packet-specific content: + // - the packet signature + // - the connection IDs (local and remote, as seen from the sender's point of view) + + struct PacketBase + { + public: + static const constexpr VersionRange LOWLEVEL_PROTOCOL_VERSIONS { 1, 1 }; + + public: + bool serializePacket(VectorBinarySerializer& serializer, uint8 protocolVersion) + { + serializeContent(serializer, protocolVersion); + return !serializer.hasError(); + } + + public: + virtual uint16 getSignature() const = 0; + + protected: + virtual void serializeContent(VectorBinarySerializer& serializer, uint8 protocolVersion) = 0; + }; + + + struct StartConnectionPacket : public PacketBase + { + VersionRange mLowLevelProtocolVersionRange; + VersionRange mHighLevelProtocolVersionRange; + + static const constexpr uint16 SIGNATURE = 0x87a1; + virtual uint16 getSignature() const override { return SIGNATURE; } + + virtual void serializeContent(VectorBinarySerializer& serializer, uint8 protocolVersion) override + { + mLowLevelProtocolVersionRange.serialize(serializer); + mHighLevelProtocolVersionRange.serialize(serializer); + } + }; + + + struct AcceptConnectionPacket : public PacketBase + { + uint8 mLowLevelProtocolVersion = 0; + uint8 mHighLevelProtocolVersion = 0; + + static const constexpr uint16 SIGNATURE = 0x1b22; + virtual uint16 getSignature() const override { return SIGNATURE; } + + virtual void serializeContent(VectorBinarySerializer& serializer, uint8 protocolVersion) override + { + serializer.serialize(mLowLevelProtocolVersion); + serializer.serialize(mHighLevelProtocolVersion); + } + }; + + + struct ErrorPacket : public PacketBase + { + enum class ErrorCode : uint8 + { + // The errors marking with (*) are sent without an actual establishes connection - that means they do not include a proper local connection ID and just re-use the received remote connection ID + UNKNOWN = 0, + CONNECTION_INVALID = 1, // (*) Received a packet with an unknown connection ID + UNSUPPORTED_VERSION = 2, // (*) Received a start connection packet that uses protocol versions that can't be supported + TOO_MANY_CONNECTIONS = 3, // (*) Remote server / client has too many active connections already + }; + ErrorCode mErrorCode = ErrorCode::UNKNOWN; + uint32 mParameter = 0; + + static const constexpr uint16 SIGNATURE = 0xf584; + virtual uint16 getSignature() const override { return SIGNATURE; } + + inline ErrorPacket() {} + inline ErrorPacket(ErrorCode errorCode, uint32 parameter = 0) : mErrorCode(errorCode), mParameter(parameter) {} + + virtual void serializeContent(VectorBinarySerializer& serializer, uint8 protocolVersion) override + { + serializer.serializeAs(mErrorCode); + } + }; + + + struct HighLevelPacket : public PacketBase + { + struct Flags + { + // None defined yet + }; + + // This is only the header, afterwards comes the actual packet-specific content + uint32 mPacketType = 0; + uint8 mPacketFlags = 0; + uint32 mUniquePacketID = 0; + + static const constexpr uint16 SIGNATURE = 0xe994; + virtual uint16 getSignature() const override { return SIGNATURE; } + + virtual void serializeContent(VectorBinarySerializer& serializer, uint8 protocolVersion) override + { + serializer.serialize(mPacketType); + serializer.serialize(mPacketFlags); + serializer.serialize(mUniquePacketID); + } + }; + + + struct RequestQueryPacket : public HighLevelPacket + { + // No need for any custom members here + + static const constexpr uint16 SIGNATURE = 0x0c7a; + virtual uint16 getSignature() const override { return SIGNATURE; } + + virtual void serializeContent(VectorBinarySerializer& serializer, uint8 protocolVersion) override + { + HighLevelPacket::serializeContent(serializer, protocolVersion); + } + }; + + + struct RequestResponsePacket : public HighLevelPacket + { + // Extension to the high level packet + uint32 mUniqueRequestID = 0; + + static const constexpr uint16 SIGNATURE = 0xd028; + virtual uint16 getSignature() const override { return SIGNATURE; } + + virtual void serializeContent(VectorBinarySerializer& serializer, uint8 protocolVersion) override + { + HighLevelPacket::serializeContent(serializer, protocolVersion); + serializer.serialize(mUniqueRequestID); + } + }; + + + struct ReceiveConfirmationPacket : public PacketBase + { + uint32 mUniquePacketID = 0; + + static const constexpr uint16 SIGNATURE = 0x276f; + virtual uint16 getSignature() const override { return SIGNATURE; } + + virtual void serializeContent(VectorBinarySerializer& serializer, uint8 protocolVersion) override + { + serializer.serialize(mUniquePacketID); + } + }; + +}