Skip to content

Commit

Permalink
f
Browse files Browse the repository at this point in the history
  • Loading branch information
CCIGAMES committed Mar 2, 2024
1 parent 3a9be70 commit cb65f8f
Show file tree
Hide file tree
Showing 4 changed files with 360 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<uint8> 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<uint8> 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<TCPSocket>& getIncomingTCPConnections() { return mIncomingTCPConnections; }

bool sendUDPPacketData(const std::vector<uint8>& data, const SocketAddress& remoteAddress);
bool sendTCPPacketData(const std::vector<uint8>& 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<uint8>& buffer, const SocketAddress& senderAddress, NetConnection* connection);

private:
struct SyncedPacketQueue
{
std::deque<ReceivedPacket*> mWorkerQueue; // Used by the worker thread that adds packets
std::deque<ReceivedPacket*> mSyncedQueue; // Used by the main thread that reads packets
ReceivedPacket::Dump mToBeReturned;
};

typedef HandleProvider<uint16, NetConnection*, 16> 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<uint8> mHighLevelProtocolVersionRange = { 1, 1 };

std::unordered_map<uint16, NetConnection*> mActiveConnections; // Using local connection ID as key
std::unordered_map<uint64, NetConnection*> mConnectionsBySender; // Using a sender key (= hash for the sender address + remote connection ID) as key
ConnectionsProvider mConnectionsProvider;

std::vector<NetConnection*> mTCPNetConnections;

SyncedPacketQueue mReceivedPackets;
std::list<TCPSocket> mIncomingTCPConnections;

RentableObjectPool<SentPacket> mSentPacketPool;
RentableObjectPool<ReceivedPacket> mReceivedPacketPool;
};
Original file line number Diff line number Diff line change
@@ -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 <rmxbase.h>


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<uint32, std::string> 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:
Original file line number Diff line number Diff line change
@@ -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 <rmxbase.h>
#include <chrono>


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::milliseconds>(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
Original file line number Diff line number Diff line change
@@ -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<uint8> 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<uint8> mLowLevelProtocolVersionRange;
VersionRange<uint8> 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<uint8>(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);
}
};

}

0 comments on commit cb65f8f

Please sign in to comment.