forked from Mirrors/openclonk
447 lines
15 KiB
C++
447 lines
15 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
#ifndef INC_C4Network2IO
|
|
#define INC_C4Network2IO
|
|
|
|
#include "netpuncher/C4PuncherPacket.h"
|
|
#include "network/C4Client.h"
|
|
#include "network/C4InteractiveThread.h"
|
|
#include "network/C4NetIO.h"
|
|
|
|
#include <atomic>
|
|
|
|
class C4Network2IOConnection;
|
|
|
|
// enums & constants
|
|
enum C4Network2IOProtocol
|
|
{
|
|
P_UDP, P_TCP, P_NONE = -1
|
|
};
|
|
|
|
const int C4NetTimer = 500, // ms
|
|
C4NetPingFreq = 1000, // ms
|
|
C4NetStatisticsFreq = 1000, // ms
|
|
C4NetAcceptTimeout = 10, // s
|
|
C4NetPingTimeout = 30000;// ms
|
|
|
|
// client count
|
|
const int C4NetMaxClients = 256;
|
|
|
|
class C4Network2IO
|
|
: protected C4InteractiveThread::Callback,
|
|
protected C4NetIO::CBClass,
|
|
protected StdSchedulerProc
|
|
{
|
|
public:
|
|
C4Network2IO();
|
|
~C4Network2IO() override;
|
|
|
|
protected:
|
|
|
|
// main traffic net i/o classes
|
|
C4NetIO *pNetIO_TCP{nullptr}, *pNetIO_UDP{nullptr};
|
|
|
|
// discovery net i/o
|
|
class C4Network2IODiscover *pNetIODiscover{nullptr};
|
|
|
|
// reference server
|
|
class C4Network2RefServer *pRefServer{nullptr};
|
|
|
|
// UPnP port mapping manager
|
|
class C4Network2UPnP *UPnPMgr{nullptr};
|
|
|
|
// local client core
|
|
C4ClientCore LCCore;
|
|
CStdCSec LCCoreCSec;
|
|
|
|
// connection list
|
|
C4Network2IOConnection *pConnList{nullptr};
|
|
CStdCSec ConnListCSec, BroadcastCSec;
|
|
|
|
// next connection ID to use
|
|
uint32_t iNextConnID{0};
|
|
|
|
// allow incoming connections?
|
|
bool fAllowConnect{false};
|
|
|
|
// connection acceptance
|
|
struct AutoAccept
|
|
{
|
|
C4ClientCore CCore;
|
|
AutoAccept *Next;
|
|
}
|
|
*pAutoAcceptList{nullptr};
|
|
CStdCSec AutoAcceptCSec;
|
|
|
|
// make sure only one connection is established?
|
|
bool fExclusiveConn{false};
|
|
|
|
// timer & ping
|
|
C4TimeMilliseconds tLastExecute;
|
|
C4TimeMilliseconds tLastPing;
|
|
|
|
// statistics
|
|
C4TimeMilliseconds tLastStatistic;
|
|
int iTCPIRate{0}, iTCPORate{0}, iTCPBCRate{0},
|
|
iUDPIRate{0}, iUDPORate{0}, iUDPBCRate{0};
|
|
|
|
// punching
|
|
C4NetIO::addr_t PuncherAddrIPv4, PuncherAddrIPv6;
|
|
bool IsPuncherAddr(const C4NetIO::addr_t& addr) const;
|
|
|
|
public:
|
|
|
|
bool hasTCP() const { return !! pNetIO_TCP; }
|
|
bool hasUDP() const { return !! pNetIO_UDP; }
|
|
|
|
// initialization
|
|
bool Init(int16_t iPortTCP, int16_t iPortUDP, int16_t iPortDiscovery = -1, int16_t iPortRefServer = -1, bool fBroadcast = false, bool enable_upnp = true); // by main thread
|
|
void Clear(); // by main thread
|
|
void SetLocalCCore(const C4ClientCore &CCore); // by main thread
|
|
|
|
// i/o types
|
|
C4NetIO *MsgIO(); // by both
|
|
C4NetIO *DataIO(); // by both
|
|
|
|
// connections
|
|
bool Connect(const C4NetIO::addr_t &addr, C4Network2IOProtocol eProt, const C4ClientCore &nCCore, const char *szPassword = nullptr); // by main thread
|
|
bool ConnectWithSocket(const C4NetIO::addr_t &addr, C4Network2IOProtocol eProt, const C4ClientCore &nCCore, std::unique_ptr<C4NetIOTCP::Socket> socket, const char *szPassword = nullptr); // by main thread
|
|
void SetAcceptMode(bool fAcceptAll); // by main thread
|
|
void SetExclusiveConnMode(bool fExclusiveConn); // by main thread
|
|
int getConnectionCount(); // by main thread
|
|
|
|
void ClearAutoAccept(); // by main thread
|
|
void AddAutoAccept(const C4ClientCore &CCore); // by main thread
|
|
void RemoveAutoAccept(const C4ClientCore &CCore); // by main thread
|
|
|
|
C4Network2IOConnection *GetMsgConnection(int iClientID); // by both (returns referenced connection!)
|
|
C4Network2IOConnection *GetDataConnection(int iClientID); // by both (returns referenced connection!)
|
|
|
|
// broadcasting
|
|
void BeginBroadcast(bool fSelectAll = false); // by both
|
|
void EndBroadcast(); // by both
|
|
bool Broadcast(const C4NetIOPacket &rPkt); // by both
|
|
|
|
// sending helpers
|
|
bool SendMsgToClient(C4NetIOPacket &rPkt, int iClient); // by both
|
|
bool BroadcastMsg(const C4NetIOPacket &rPkt); // by both
|
|
|
|
// punch
|
|
bool InitPuncher(C4NetIO::addr_t PuncherAddr); // by main thread
|
|
void SendPuncherPacket(const C4NetpuncherPacket&, C4NetIO::HostAddress::AddressFamily family);
|
|
void Punch(const C4NetIO::addr_t&); // sends a ping packet
|
|
|
|
// stuff
|
|
C4NetIO *getNetIO(C4Network2IOProtocol eProt); // by both
|
|
const char *getNetIOName(C4NetIO *pNetIO);
|
|
C4Network2IOProtocol getNetIOProt(C4NetIO *pNetIO);
|
|
|
|
// statistics
|
|
int getProtIRate(C4Network2IOProtocol eProt) const { return eProt == P_TCP ? iTCPIRate : iUDPIRate; }
|
|
int getProtORate(C4Network2IOProtocol eProt) const { return eProt == P_TCP ? iTCPORate : iUDPORate; }
|
|
int getProtBCRate(C4Network2IOProtocol eProt) const { return eProt == P_TCP ? iTCPBCRate : iUDPBCRate; }
|
|
|
|
// reference
|
|
void SetReference(class C4Network2Reference *pReference);
|
|
bool IsReferenceNeeded();
|
|
|
|
protected:
|
|
// *** callbacks
|
|
// C4NetIO-Callbacks
|
|
bool OnConn(const C4NetIO::addr_t &addr, const C4NetIO::addr_t &AddrConnect, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO) override;
|
|
void OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const char *szReason) override;
|
|
void OnPacket(const C4NetIOPacket &rPacket, C4NetIO *pNetIO) override;
|
|
// C4NetIOMan
|
|
virtual void OnError(const char *strError, C4NetIO *pNetIO);
|
|
// StdSchedulerProc
|
|
bool Execute(int iTimeout, pollfd *) override;
|
|
C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow) override;
|
|
// Event callback by C4InteractiveThread
|
|
void OnThreadEvent(C4InteractiveEventType eEvent, void *pEventData) override; // by main thread
|
|
|
|
// connections list
|
|
void AddConnection(C4Network2IOConnection *pConn); // by both
|
|
void RemoveConnection(C4Network2IOConnection *pConn); // by both
|
|
C4Network2IOConnection *GetConnection(const C4NetIO::addr_t &addr, C4NetIO *pNetIO); // by both
|
|
C4Network2IOConnection *GetConnectionByConnAddr(const C4NetIO::addr_t &addr, C4NetIO *pNetIO); // by both
|
|
C4Network2IOConnection *GetConnectionByID(uint32_t iConnID); // by thread
|
|
|
|
// network events (signals to main thread)
|
|
struct NetEvPacketData;
|
|
|
|
// connection acceptance
|
|
bool doAutoAccept(const C4ClientCore &CCore, const C4Network2IOConnection &Conn);
|
|
|
|
// general packet handling (= forward in most cases)
|
|
bool HandlePacket(const C4NetIOPacket &rPacket, C4Network2IOConnection *pConn, bool fThread); // by both
|
|
void CallHandlers(int iHandlers, const class C4IDPacket *pPacket, C4Network2IOConnection *pConn, bool fThread); // by both
|
|
|
|
// packet handling (some are really handled here)
|
|
void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn);
|
|
void HandleFwdReq(const class C4PacketFwd &rFwd, C4Network2IOConnection *pBy);
|
|
void HandlePuncherPacket(const C4NetIOPacket &rPacket);
|
|
|
|
// misc
|
|
bool Ping();
|
|
void CheckTimeout();
|
|
void GenerateStatistics(int iInterval);
|
|
void SendConnPackets();
|
|
};
|
|
|
|
enum C4Network2IOConnStatus
|
|
{
|
|
CS_Connect, // waiting for connection
|
|
CS_Connected, // waiting for Conn
|
|
CS_HalfAccepted, // got Conn (peer identified, client class created if neccessary)
|
|
CS_Accepted, // got ConnRe (peer did accept)
|
|
CS_Closed,
|
|
CS_ConnectFail // got closed before HalfAccepted was reached
|
|
};
|
|
|
|
class C4Network2IOConnection // shared
|
|
{
|
|
friend class C4Network2IO;
|
|
public:
|
|
C4Network2IOConnection();
|
|
~C4Network2IOConnection();
|
|
|
|
protected:
|
|
|
|
// connection data
|
|
class C4NetIO *pNetClass{nullptr};
|
|
C4Network2IOProtocol eProt;
|
|
C4NetIO::addr_t PeerAddr, ConnectAddr;
|
|
std::unique_ptr<C4NetIOTCP::Socket> TcpSimOpenSocket;
|
|
|
|
// status data
|
|
C4Network2IOConnStatus Status;
|
|
uint32_t iID, iRemoteID; // connection ID for this and the remote client
|
|
bool fAutoAccept{false}; // auto accepted by thread?
|
|
bool fBroadcastTarget{false}; // broadcast target?
|
|
time_t iTimestamp{0}; // timestamp of last status change
|
|
int iPingTime{-1}; // ping
|
|
C4TimeMilliseconds tLastPing; // if > iLastPong, it's the first ping that hasn't been answered yet, nullptr if no ping received yet
|
|
C4TimeMilliseconds tLastPong; // last pong received, nullptr if no pong received yet
|
|
C4ClientCore CCore; // client core (>= CS_HalfAccepted)
|
|
CStdCSec CCoreCSec;
|
|
int iIRate, iORate; // input/output rates (by C4NetIO, in b/s)
|
|
int iPacketLoss; // lost packets (in the last seconds)
|
|
StdCopyStrBuf Password; // password to use for connect
|
|
bool fConnSent{false}; // initial connection packet send
|
|
bool fPostMortemSent{false}; // post mortem send
|
|
|
|
// packet backlog
|
|
uint32_t iOutPacketCounter{0}, iInPacketCounter{0};
|
|
struct PacketLogEntry
|
|
{
|
|
uint32_t Number;
|
|
C4NetIOPacket Pkt;
|
|
PacketLogEntry *Next;
|
|
};
|
|
PacketLogEntry *pPacketLog{nullptr};
|
|
CStdCSec PacketLogCSec;
|
|
|
|
// list (C4Network2IO)
|
|
C4Network2IOConnection *pNext{nullptr};
|
|
|
|
// reference counter
|
|
std::atomic_long iRefCnt;
|
|
|
|
public:
|
|
C4NetIO *getNetClass() const { return pNetClass; }
|
|
C4Network2IOProtocol getProtocol() const { return eProt; }
|
|
const C4NetIO::addr_t &getPeerAddr() const { return PeerAddr.GetPort() ? PeerAddr : ConnectAddr; }
|
|
const C4NetIO::addr_t &getConnectAddr() const { return ConnectAddr; }
|
|
uint32_t getID() const { return iID; }
|
|
uint32_t getRemoteID() const { return iRemoteID; }
|
|
time_t getTimestamp() const { return iTimestamp; }
|
|
const C4ClientCore &getCCore() const { return CCore; }
|
|
CStdCSec &getCCoreCSec() { return CCoreCSec; }
|
|
int getClientID() const { return CCore.getID(); }
|
|
bool isHost() const { return CCore.isHost(); }
|
|
int getPingTime() const { return iPingTime; }
|
|
int getLag() const;
|
|
int getIRate() const { return iIRate; }
|
|
int getORate() const { return iORate; }
|
|
int getPacketLoss() const { return iPacketLoss; }
|
|
const char *getPassword() const { return Password.getData(); }
|
|
bool isConnSent() const { return fConnSent; }
|
|
|
|
uint32_t getInPacketCounter() const { return iInPacketCounter; }
|
|
uint32_t getOutPacketCounter() const { return iOutPacketCounter; }
|
|
|
|
bool isConnecting() const { return Status == CS_Connect; }
|
|
bool isOpen() const { return Status != CS_Connect && Status != CS_Closed && Status != CS_ConnectFail; }
|
|
bool isHalfAccepted()const { return Status == CS_HalfAccepted || Status == CS_Accepted; }
|
|
bool isAccepted() const { return Status == CS_Accepted; }
|
|
bool isClosed() const { return Status == CS_Closed || Status == CS_ConnectFail; }
|
|
bool isAutoAccepted()const { return fAutoAccept; }
|
|
bool isBroadcastTarget() const { return fBroadcastTarget; }
|
|
bool isFailed() const { return Status == CS_ConnectFail; }
|
|
|
|
protected:
|
|
// called by C4Network2IO only
|
|
void Set(C4NetIO *pnNetClass, C4Network2IOProtocol eProt, const C4NetIO::addr_t &nPeerAddr, const C4NetIO::addr_t &nConnectAddr, C4Network2IOConnStatus nStatus, const char *szPassword, uint32_t iID);
|
|
void SetSocket(std::unique_ptr<C4NetIOTCP::Socket> socket);
|
|
void SetRemoteID(uint32_t iRemoteID);
|
|
void SetPeerAddr(const C4NetIO::addr_t &nPeerAddr);
|
|
void OnPing();
|
|
void SetPingTime(int iPingTime);
|
|
void SetStatus(C4Network2IOConnStatus nStatus);
|
|
void SetAutoAccepted();
|
|
void OnPacketReceived(uint8_t iPacketType);
|
|
void ClearPacketLog(uint32_t iStartNumber = ~0);
|
|
|
|
public:
|
|
// status changing
|
|
void SetHalfAccepted() { SetStatus(CS_HalfAccepted); }
|
|
void SetAccepted() { SetStatus(CS_Accepted); }
|
|
void SetCCore(const C4ClientCore &nCCore);
|
|
void ResetAutoAccepted() { fAutoAccept = false; }
|
|
void SetConnSent() { fConnSent = true; }
|
|
|
|
// connection operations
|
|
bool Connect();
|
|
void Close();
|
|
bool Send(const C4NetIOPacket &rPkt);
|
|
void SetBroadcastTarget(bool fSet); // (only call after C4Network2IO::BeginBroadcast!)
|
|
|
|
// statistics
|
|
void DoStatistics(int iInterval, int *pIRateSum, int *pORateSum);
|
|
|
|
// reference counting
|
|
void AddRef(); void DelRef();
|
|
|
|
// post mortem
|
|
bool CreatePostMortem(class C4PacketPostMortem *pPkt);
|
|
|
|
};
|
|
|
|
// * Packets *
|
|
|
|
class C4PacketPing : public C4PacketBase
|
|
{
|
|
public:
|
|
C4PacketPing(uint32_t iPacketCounter = 0, uint32_t iRemotePacketCounter = 0);
|
|
|
|
protected:
|
|
C4TimeMilliseconds tTime;
|
|
uint32_t iPacketCounter;
|
|
|
|
public:
|
|
uint32_t getTravelTime() const;
|
|
uint32_t getPacketCounter() const { return iPacketCounter; }
|
|
|
|
void CompileFunc(StdCompiler *pComp) override;
|
|
};
|
|
|
|
class C4PacketConn : public C4PacketBase
|
|
{
|
|
public:
|
|
C4PacketConn();
|
|
C4PacketConn(const class C4ClientCore &nCCore, uint32_t iConnID, const char *szPassword = nullptr);
|
|
|
|
protected:
|
|
int32_t iVer;
|
|
uint32_t iConnID;
|
|
C4ClientCore CCore;
|
|
StdCopyStrBuf Password;
|
|
|
|
public:
|
|
int32_t getVer() const { return iVer; }
|
|
uint32_t getConnID() const { return iConnID; }
|
|
const C4ClientCore &getCCore() const { return CCore; }
|
|
const char *getPassword() const { return Password.getData(); }
|
|
|
|
void CompileFunc(StdCompiler *pComp) override;
|
|
};
|
|
|
|
class C4PacketConnRe : public C4PacketBase
|
|
{
|
|
public:
|
|
C4PacketConnRe();
|
|
C4PacketConnRe(bool fOK, bool fWrongPassword, const char *szMsg = nullptr);
|
|
|
|
protected:
|
|
bool fOK, fWrongPassword;
|
|
StdStrBuf szMsg;
|
|
|
|
public:
|
|
bool isOK() const { return fOK; }
|
|
bool isPasswordWrong() const { return fWrongPassword; }
|
|
const char *getMsg() const { return szMsg.getData(); }
|
|
|
|
void CompileFunc(StdCompiler *pComp) override;
|
|
};
|
|
|
|
class C4PacketFwd : public C4PacketBase
|
|
{
|
|
public:
|
|
C4PacketFwd();
|
|
C4PacketFwd(const StdBuf &Pkt);
|
|
|
|
protected:
|
|
bool fNegativeList{false};
|
|
int32_t iClients[C4NetMaxClients];
|
|
int32_t iClientCnt{0};
|
|
StdCopyBuf Data;
|
|
|
|
public:
|
|
const StdCopyBuf &getData() const { return Data; }
|
|
bool isNegativeList() const { return fNegativeList; }
|
|
int32_t getClient(int32_t i) const { return iClients[i]; }
|
|
int32_t getClientCnt() const { return iClientCnt; }
|
|
|
|
bool DoFwdTo(int32_t iClient) const;
|
|
|
|
void SetData(const StdBuf &Pkt);
|
|
void SetListType(bool fnNegativeList);
|
|
void AddClient(int32_t iClient);
|
|
|
|
void CompileFunc(StdCompiler *pComp) override;
|
|
};
|
|
|
|
class C4PacketPostMortem : public C4PacketBase
|
|
{
|
|
public:
|
|
C4PacketPostMortem();
|
|
~C4PacketPostMortem() override;
|
|
|
|
private:
|
|
uint32_t iConnID;
|
|
uint32_t iPacketCounter; // last packet counter of dead connection
|
|
uint32_t iPacketCount{0};
|
|
struct PacketLink
|
|
{
|
|
C4NetIOPacket Pkt;
|
|
PacketLink *Next;
|
|
};
|
|
PacketLink *pPackets{nullptr};
|
|
|
|
public:
|
|
uint32_t getConnID() const { return iConnID; }
|
|
uint32_t getPacketCount() const { return iPacketCount; }
|
|
void SetConnID(uint32_t inConnID) { iConnID = inConnID; }
|
|
|
|
const C4NetIOPacket *getPacket(uint32_t iNumber) const;
|
|
void SetPacketCounter(uint32_t iPacketCounter);
|
|
void Add(const C4NetIOPacket &rPkt);
|
|
|
|
void CompileFunc(StdCompiler *pComp) override;
|
|
};
|
|
|
|
#endif
|