1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2013-2018, The OpenClonk Team and contributors
6 * Copyright (c) 2017-2021, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18#pragma once
19
20#include "C4Network2Address.h"
21#include "C4NetIO.h"
22#include "C4Client.h"
23#include "C4InteractiveThread.h"
24#include "C4Log.h"
25#include "C4PuncherPacket.h"
26
27#include <atomic>
28#include <cstdint>
29
30class C4Network2IOConnection;
31
32// constants
33
34const int C4NetTimer = 500, // ms
35 C4NetPingFreq = 1000, // ms
36 C4NetStatisticsFreq = 1000, // ms
37 C4NetAcceptTimeout = 10, // s
38 C4NetPingTimeout = 30000; // ms
39
40// client count
41const int C4NetMaxClients = 256;
42
43class C4Network2IO
44 : protected C4InteractiveThread::Callback,
45 protected C4NetIO::CBClass,
46 protected StdSchedulerProc
47{
48public:
49 C4Network2IO();
50 virtual ~C4Network2IO();
51
52protected:
53 // main traffic net i/o classes
54 C4NetIO *pNetIO_TCP, *pNetIO_UDP;
55
56 // discovery net i/o
57 class C4Network2IODiscover *pNetIODiscover;
58
59 // UPnP
60 std::unique_ptr<class C4Network2UPnP> UPnP;
61
62 // reference server
63 class C4Network2RefServer *pRefServer;
64
65 // local client core
66 C4ClientCore LCCore;
67 CStdCSec LCCoreCSec;
68
69 // connection list
70 C4Network2IOConnection *pConnList;
71 CStdCSec ConnListCSec, BroadcastCSec;
72
73 // next connection ID to use
74 uint32_t iNextConnID;
75
76 // allow incoming connections?
77 bool fAllowConnect;
78
79 // connection acceptance
80 struct AutoAccept
81 {
82 C4ClientCore CCore;
83 AutoAccept *Next;
84 }
85 *pAutoAcceptList;
86 CStdCSec AutoAcceptCSec;
87
88 // make sure only one connection is established?
89 bool fExclusiveConn;
90
91 // timer & ping
92 unsigned long iLastExecute, iLastPing;
93
94 // statistics
95 unsigned long iLastStatistic;
96 int iTCPIRate, iTCPORate, iTCPBCRate,
97 iUDPIRate, iUDPORate, iUDPBCRate;
98
99 // punching
100 C4NetIO::addr_t PuncherAddrIPv4, PuncherAddrIPv6;
101 bool IsPuncherAddr(const C4NetIO::addr_t &addr) const;
102
103 // logger
104 std::shared_ptr<spdlog::logger> logger;
105
106public:
107 bool hasTCP() const { return !!pNetIO_TCP; }
108 bool hasUDP() const { return !!pNetIO_UDP; }
109
110 // initialization
111 bool Init(std::uint16_t iPortTCP, std::uint16_t iPortUDP, std::uint16_t iPortDiscovery = 0, std::uint16_t iPortRefServer = 0); // by main thread
112 void Clear(); // by main thread
113 void SetLocalCCore(const C4ClientCore &CCore); // by main thread
114
115 // i/o types
116 C4NetIO *MsgIO(); // by both
117 C4NetIO *DataIO(); // by both
118
119 // connections
120 bool Connect(const C4NetIO::addr_t &addr, C4Network2IOProtocol prot, const C4ClientCore &ccore, const char *password = nullptr); // by main thread
121 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
122 void SetAcceptMode(bool fAcceptAll); // by main thread
123 void SetExclusiveConnMode(bool fExclusiveConn); // by main thread
124 int getConnectionCount(); // by main thread
125
126 void ClearAutoAccept(); // by main thread
127 void AddAutoAccept(const C4ClientCore &CCore); // by main thread
128 void RemoveAutoAccept(const C4ClientCore &CCore); // by main thread
129
130 C4Network2IOConnection *GetMsgConnection(int iClientID); // by both (returns referenced connection!)
131 C4Network2IOConnection *GetDataConnection(int iClientID); // by both (returns referenced connection!)
132
133 // broadcasting
134 void BeginBroadcast(bool fSelectAll = false); // by both
135 void EndBroadcast(); // by both
136 bool Broadcast(const C4NetIOPacket &rPkt); // by both
137
138 // sending helpers
139 bool BroadcastMsg(const C4NetIOPacket &rPkt); // by both
140
141 // punch
142 bool InitPuncher(C4NetIO::addr_t puncherAddr); // by main thread
143 void SendPuncherPacket(const C4NetpuncherPacket &, C4Network2HostAddress::AddressFamily family);
144 void Punch(const C4NetIO::addr_t &); // Sends a ping packet
145
146 // stuff
147 C4NetIO *getNetIO(C4Network2IOProtocol eProt); // by both
148 const char *getNetIOName(C4NetIO *pNetIO);
149 C4Network2IOProtocol getNetIOProt(C4NetIO *pNetIO);
150
151 // statistics
152 int getProtIRate (C4Network2IOProtocol eProt) const { return eProt == P_TCP ? iTCPIRate : iUDPIRate; }
153 int getProtORate (C4Network2IOProtocol eProt) const { return eProt == P_TCP ? iTCPORate : iUDPORate; }
154 int getProtBCRate(C4Network2IOProtocol eProt) const { return eProt == P_TCP ? iTCPBCRate : iUDPBCRate; }
155
156 // reference
157 void SetReference(class C4Network2Reference *pReference);
158 bool IsReferenceNeeded();
159
160protected:
161 // *** callbacks
162 // C4NetIO-Callbacks
163 virtual bool OnConn(const C4NetIO::addr_t &addr, const C4NetIO::addr_t &AddrConnect, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO) override;
164 virtual void OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const char *szReason) override;
165 virtual void OnPacket(const C4NetIOPacket &rPacket, C4NetIO *pNetIO) override;
166 // StdSchedulerProc
167 virtual bool Execute(int iTimeout) override;
168 virtual int GetTimeout() override;
169 // Event callback by C4InteractiveThread
170 void OnThreadEvent(C4InteractiveEventType eEvent, const std::any &eventData) override; // by main thread
171
172 // connections list
173 void AddConnection(C4Network2IOConnection *pConn); // by both
174 void RemoveConnection(C4Network2IOConnection *pConn); // by both
175 C4Network2IOConnection *GetConnection(const C4NetIO::addr_t &addr, C4NetIO *pNetIO); // by both
176 C4Network2IOConnection *GetConnectionByConnAddr(const C4NetIO::addr_t &addr, C4NetIO *pNetIO); // by both
177 C4Network2IOConnection *GetConnectionByID(uint32_t iConnID); // by thread
178
179 // network events (signals to main thread)
180 struct NetEvPacketData;
181
182 // connection acceptance
183 bool doAutoAccept(const C4ClientCore &CCore, const C4Network2IOConnection &Conn);
184
185 // general packet handling (= forward in most cases)
186 bool HandlePacket(const C4NetIOPacket &rPacket, C4Network2IOConnection *pConn, bool fThread); // by both
187 void CallHandlers(int iHandlers, const class C4IDPacket *pPacket, C4Network2IOConnection *pConn, bool fThread); // by both
188
189 // packet handling (some are really handled here)
190 void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn);
191 void HandleFwdReq(const class C4PacketFwd &rFwd, C4Network2IOConnection *pBy);
192 void HandlePuncherPacket(const C4NetIOPacket &packet);
193
194 // misc
195 bool Ping();
196 void CheckTimeout();
197 void GenerateStatistics(int iInterval);
198 void SendConnPackets();
199};
200
201C4LOGGERCONFIG_NAME_TYPE(C4Network2IO);
202
203template<>
204struct C4LoggerConfig::Defaults<C4Network2IO>
205{
206 static constexpr spdlog::level::level_enum GuiLogLevel{spdlog::level::err};
207};
208
209enum C4Network2IOConnStatus
210{
211 CS_Connect, // waiting for connection
212 CS_Connected, // waiting for Conn
213 CS_HalfAccepted, // got Conn (peer identified, client class created if neccessary)
214 CS_Accepted, // got ConnRe (peer did accept)
215 CS_Closed,
216 CS_ConnectFail, // got closed before HalfAccepted was reached
217};
218
219class C4Network2IOConnection // shared
220{
221 friend class C4Network2IO;
222
223public:
224 C4Network2IOConnection();
225 ~C4Network2IOConnection();
226
227protected:
228 // connection data
229 class C4NetIO *pNetClass;
230 C4Network2IOProtocol eProt;
231 C4NetIO::addr_t PeerAddr, ConnectAddr;
232 std::unique_ptr<C4NetIOTCP::Socket> tcpSimOpenSocket;
233
234 // status data
235 C4Network2IOConnStatus Status;
236 uint32_t iID, iRemoteID; // connection ID for this and the remote client
237 bool fAutoAccept; // auto accepted by thread?
238 bool fBroadcastTarget; // broadcast target?
239 time_t iTimestamp; // timestamp of last status change
240 int iPingTime; // ping
241 unsigned long iLastPing; // if > iLastPong, it's the first ping that hasn't been answered yet
242 unsigned long iLastPong; // last pong received
243 C4ClientCore CCore; // client core (>= CS_HalfAccepted)
244 CStdCSec CCoreCSec;
245 int iIRate, iORate; // input/output rates (by C4NetIO, in b/s)
246 int iPacketLoss; // lost packets (in the last seconds)
247 StdStrBuf Password; // password to use for connect
248 bool fConnSent; // initial connection packet send
249 bool fPostMortemSent; // post mortem send
250
251 // packet backlog
252 uint32_t iOutPacketCounter, iInPacketCounter;
253 struct PacketLogEntry
254 {
255 uint32_t Number;
256 C4NetIOPacket Pkt;
257 PacketLogEntry *Next;
258 };
259 PacketLogEntry *pPacketLog;
260 CStdCSec PacketLogCSec;
261
262 // list (C4Network2IO)
263 C4Network2IOConnection *pNext;
264
265 // reference counter
266 std::atomic<long> iRefCnt;
267
268public:
269 C4NetIO *getNetClass() const { return pNetClass; }
270 C4Network2IOProtocol getProtocol() const { return eProt; }
271 const C4NetIO::addr_t &getPeerAddr() const { return PeerAddr.GetPort() ? PeerAddr : ConnectAddr; }
272 const C4NetIO::addr_t &getConnectAddr() const { return ConnectAddr; }
273 uint32_t getID() const { return iID; }
274 time_t getTimestamp() const { return iTimestamp; }
275 const C4ClientCore &getCCore() const { return CCore; }
276 int getClientID() const { return CCore.getID(); }
277 bool isHost() const { return CCore.isHost(); }
278 int getPingTime() const { return iPingTime; }
279 int getLag() const;
280 int getPacketLoss() const { return iPacketLoss; }
281 const char *getPassword() const { return Password.getData(); }
282 bool isConnSent() const { return fConnSent; }
283
284 uint32_t getInPacketCounter() const { return iInPacketCounter; }
285 uint32_t getOutPacketCounter() const { return iOutPacketCounter; }
286
287 bool isConnecting() const { return Status == CS_Connect; }
288 bool isOpen() const { return Status != CS_Connect && Status != CS_Closed && Status != CS_ConnectFail; }
289 bool isHalfAccepted() const { return Status == CS_HalfAccepted || Status == CS_Accepted; }
290 bool isAccepted() const { return Status == CS_Accepted; }
291 bool isClosed() const { return Status == CS_Closed || Status == CS_ConnectFail; }
292 bool isAutoAccepted() const { return fAutoAccept; }
293 bool isBroadcastTarget() const { return fBroadcastTarget; }
294 bool isFailed() const { return Status == CS_ConnectFail; }
295
296protected:
297 // called by C4Network2IO only
298 void Set(C4NetIO *pnNetClass, C4Network2IOProtocol eProt, const C4NetIO::addr_t &nPeerAddr, const C4NetIO::addr_t &nConnectAddr, C4Network2IOConnStatus nStatus, const char *szPassword, uint32_t iID);
299 void SetSocket(std::unique_ptr<C4NetIOTCP::Socket> socket);
300 void SetRemoteID(uint32_t iRemoteID);
301 void SetPeerAddr(const C4NetIO::addr_t &nPeerAddr);
302 void OnPing();
303 void SetPingTime(int iPingTime);
304 void SetStatus(C4Network2IOConnStatus nStatus);
305 void SetAutoAccepted();
306 void OnPacketReceived(uint8_t iPacketType);
307 void ClearPacketLog(uint32_t iStartNumber = ~0);
308
309public:
310 // status changing
311 void SetHalfAccepted() { SetStatus(CS_HalfAccepted); }
312 void SetAccepted() { SetStatus(CS_Accepted); }
313 void SetCCore(const C4ClientCore &nCCore);
314 void ResetAutoAccepted() { fAutoAccept = false; }
315 void SetConnSent() { fConnSent = true; }
316
317 // connection operations
318 bool Connect();
319 void Close();
320 bool Send(const C4NetIOPacket &rPkt);
321 void SetBroadcastTarget(bool fSet); // (only call after C4Network2IO::BeginBroadcast!)
322
323 // statistics
324 void DoStatistics(int iInterval, int *pIRateSum, int *pORateSum);
325
326 // reference counting
327 void AddRef(); void DelRef();
328
329 // post mortem
330 bool CreatePostMortem(class C4PacketPostMortem *pPkt);
331};
332
333// Packets
334
335class C4PacketPing : public C4PacketBase
336{
337public:
338 C4PacketPing(uint32_t iPacketCounter = 0);
339
340protected:
341 uint32_t iTime;
342 uint32_t iPacketCounter;
343
344public:
345 uint32_t getTravelTime() const;
346 uint32_t getPacketCounter() const { return iPacketCounter; }
347
348 virtual void CompileFunc(StdCompiler *pComp) override;
349};
350
351class C4PacketConn : public C4PacketBase
352{
353public:
354 C4PacketConn();
355 C4PacketConn(const class C4ClientCore &nCCore, uint32_t iConnID, const char *szPassword = nullptr);
356
357protected:
358 int32_t iVer;
359 uint32_t iConnID;
360 C4ClientCore CCore;
361 StdStrBuf Password;
362
363public:
364 int32_t getVer() const { return iVer; }
365 uint32_t getConnID() const { return iConnID; }
366 const C4ClientCore &getCCore() const { return CCore; }
367 const char *getPassword() const { return Password.getData(); }
368
369 virtual void CompileFunc(StdCompiler *pComp) override;
370};
371
372class C4PacketConnRe : public C4PacketBase
373{
374public:
375 C4PacketConnRe();
376 C4PacketConnRe(bool fOK, bool fWrongPassword, const char *szMsg = nullptr);
377
378protected:
379 bool fOK, fWrongPassword;
380 StdStrBuf szMsg;
381
382public:
383 bool isOK() const { return fOK; }
384 bool isPasswordWrong() const { return fWrongPassword; }
385 const char *getMsg() const { return szMsg.getData(); }
386
387 virtual void CompileFunc(StdCompiler *pComp) override;
388};
389
390class C4PacketFwd : public C4PacketBase
391{
392public:
393 C4PacketFwd();
394
395protected:
396 bool fNegativeList;
397 int32_t iClients[C4NetMaxClients];
398 int32_t iClientCnt;
399 C4NetIOPacket Data;
400
401public:
402 const C4NetIOPacket &getData() const { return Data; }
403 int32_t getClient(int32_t i) const { return iClients[i]; }
404 int32_t getClientCnt() const { return iClientCnt; }
405
406 bool DoFwdTo(int32_t iClient) const;
407
408 void SetData(const C4NetIOPacket &Pkt);
409 void SetListType(bool fnNegativeList);
410 void AddClient(int32_t iClient);
411
412 virtual void CompileFunc(StdCompiler *pComp) override;
413};
414
415class C4PacketPostMortem : public C4PacketBase
416{
417public:
418 C4PacketPostMortem();
419 ~C4PacketPostMortem();
420
421private:
422 uint32_t iConnID;
423 uint32_t iPacketCounter; // last packet counter of dead connection
424 uint32_t iPacketCount;
425 struct PacketLink
426 {
427 C4NetIOPacket Pkt;
428 PacketLink *Next;
429 };
430 PacketLink *pPackets;
431
432public:
433 const uint32_t getConnID() const { return iConnID; }
434 const uint32_t getPacketCount() const { return iPacketCount; }
435 void SetConnID(uint32_t inConnID) { iConnID = inConnID; }
436
437 const C4NetIOPacket *getPacket(uint32_t iNumber) const;
438 void SetPacketCounter(uint32_t iPacketCounter);
439 void Add(const C4NetIOPacket &rPkt);
440
441 virtual void CompileFunc(StdCompiler *pComp) override;
442};
443