1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#ifndef ENGINE_SHARED_NETWORK_H
4#define ENGINE_SHARED_NETWORK_H
5
6#include "ringbuffer.h"
7#include "stun.h"
8
9#include <base/types.h>
10
11#include <array>
12#include <optional>
13
14class CHuffman;
15class CNetBan;
16class CPacker;
17
18/*
19
20CURRENT:
21 packet header: 3 bytes
22 unsigned char flags_ack; // 6bit flags, 2bit ack
23 0.6: ORNCaaAA
24 0.6.5: ORNCT-AA
25 0.7: --NORCAA
26
27 O = flag compression
28 R = flag resend
29 N = flag connless
30 C = flag control
31 T = flag token (0.6.5 only not supported by ddnet)
32 - = unused, should be zero
33 a = should be zero otherwise it messes up the ack number
34 A = bit of ack number
35
36
37 unsigned char ack; // 8 bit ack
38 unsigned char num_chunks; // 8 bit chunks
39
40 (unsigned char padding[3]) // 24 bit extra in case it's a connection less packet
41 // this is to make sure that it's compatible with the
42 // old protocol
43
44 chunk header: 2-3 bytes
45 unsigned char flags_size; // 2bit flags, 6 bit size
46 unsigned char size_seq; // 4bit size, 4bit seq
47 (unsigned char seq;) // 8bit seq, if vital flag is set
48*/
49
50enum
51{
52 NETSENDFLAG_VITAL = 1 << 0,
53 NETSENDFLAG_CONNLESS = 1 << 1,
54 NETSENDFLAG_FLUSH = 1 << 2,
55 NETSENDFLAG_EXTENDED = 1 << 3,
56};
57
58enum
59{
60 NETSTATE_OFFLINE = 0,
61 NETSTATE_CONNECTING,
62 NETSTATE_ONLINE,
63};
64
65enum
66{
67 NET_MAX_PACKETSIZE = 1400,
68 NET_MAX_PAYLOAD = NET_MAX_PACKETSIZE - 6,
69 NET_MAX_CHUNKHEADERSIZE = 3,
70 NET_PACKETHEADERSIZE = 3,
71 NET_CONNLESS_EXTRA_SIZE = 4,
72 NET_MAX_CLIENTS = 64,
73 NET_MAX_CONSOLE_CLIENTS = 4,
74 NET_MAX_SEQUENCE = 1 << 10,
75 NET_MAX_PACKET_CHUNKS = 0xFF,
76
77 NET_PACKETFLAG_UNUSED = 1 << 0,
78 NET_PACKETFLAG_TOKEN = 1 << 1,
79 NET_PACKETFLAG_CONTROL = 1 << 2,
80 NET_PACKETFLAG_CONNLESS = 1 << 3,
81 NET_PACKETFLAG_RESEND = 1 << 4,
82 NET_PACKETFLAG_COMPRESSION = 1 << 5,
83 // NOT SENT VIA THE NETWORK DIRECTLY:
84 NET_PACKETFLAG_EXTENDED = 1 << 6,
85
86 NET_CHUNKFLAG_VITAL = 1,
87 NET_CHUNKFLAG_RESEND = 2,
88
89 NET_CTRLMSG_KEEPALIVE = 0,
90 NET_CTRLMSG_CONNECT = 1,
91 NET_CTRLMSG_CONNECTACCEPT = 2,
92 NET_CTRLMSG_ACCEPT = 3,
93 NET_CTRLMSG_CLOSE = 4,
94
95 NET_CONN_BUFFERSIZE = 1024 * 32,
96
97 NET_CONNLIMIT_IPS = 16,
98
99 NET_TOKENCACHE_ADDRESSEXPIRY = 64,
100 NET_TOKENCACHE_PACKETEXPIRY = 5,
101};
102enum
103{
104 NET_TOKEN_MAX = 0xffffffff,
105 NET_TOKEN_NONE = NET_TOKEN_MAX,
106 NET_TOKEN_MASK = NET_TOKEN_MAX,
107
108 NET_TOKENREQUEST_DATASIZE = 512,
109};
110
111typedef int SECURITY_TOKEN;
112typedef unsigned int TOKEN;
113
114SECURITY_TOKEN ToSecurityToken(const unsigned char *pData);
115void WriteSecurityToken(unsigned char *pData, SECURITY_TOKEN Token);
116
117extern const unsigned char SECURITY_TOKEN_MAGIC[4];
118
119enum
120{
121 NET_SECURITY_TOKEN_UNKNOWN = -1,
122 NET_SECURITY_TOKEN_UNSUPPORTED = 0,
123};
124
125typedef int (*NETFUNC_DELCLIENT)(int ClientId, const char *pReason, void *pUser);
126typedef int (*NETFUNC_NEWCLIENT_CON)(int ClientId, void *pUser);
127typedef int (*NETFUNC_NEWCLIENT)(int ClientId, void *pUser, bool Sixup);
128typedef int (*NETFUNC_NEWCLIENT_NOAUTH)(int ClientId, void *pUser);
129typedef int (*NETFUNC_CLIENTREJOIN)(int ClientId, void *pUser);
130
131struct CNetChunk
132{
133 // -1 means that it's a stateless packet
134 // 0 on the client means the server
135 int m_ClientId;
136 NETADDR m_Address; // only used when client_id == -1
137 int m_Flags;
138 int m_DataSize;
139 const void *m_pData;
140 // only used if the flags contain NETSENDFLAG_EXTENDED and NETSENDFLAG_CONNLESS
141 unsigned char m_aExtraData[NET_CONNLESS_EXTRA_SIZE];
142};
143
144class CNetChunkHeader
145{
146public:
147 int m_Flags;
148 int m_Size;
149 int m_Sequence;
150
151 unsigned char *Pack(unsigned char *pData, int Split = 4) const;
152 unsigned char *Unpack(unsigned char *pData, int Split = 4);
153};
154
155class CNetChunkResend
156{
157public:
158 int m_Flags;
159 int m_DataSize;
160 unsigned char *m_pData;
161
162 int m_Sequence;
163 int64_t m_LastSendTime;
164 int64_t m_FirstSendTime;
165};
166
167class CNetPacketConstruct
168{
169public:
170 int m_Flags;
171 int m_Ack;
172 int m_NumChunks;
173 int m_DataSize;
174 unsigned char m_aChunkData[NET_MAX_PAYLOAD];
175 unsigned char m_aExtraData[NET_CONNLESS_EXTRA_SIZE];
176};
177
178enum class CONNECTIVITY
179{
180 UNKNOWN,
181 CHECKING,
182 UNREACHABLE,
183 REACHABLE,
184 ADDRESS_KNOWN,
185};
186
187class CStun
188{
189 class CProtocol
190 {
191 int m_Index;
192 NETSOCKET m_Socket;
193 CStunData m_Stun;
194 bool m_HaveStunServer = false;
195 NETADDR m_StunServer;
196 bool m_HaveAddr = false;
197 NETADDR m_Addr;
198 int64_t m_LastResponse = -1;
199 int64_t m_NextTry = -1;
200 int m_NumUnsuccessfulTries = -1;
201
202 public:
203 CProtocol(int Index, NETSOCKET Socket);
204 void FeedStunServer(NETADDR StunServer);
205 void Refresh();
206 void Update();
207 bool OnPacket(NETADDR Addr, unsigned char *pData, int DataSize);
208 CONNECTIVITY GetConnectivity(NETADDR *pGlobalAddr);
209 };
210 CProtocol m_aProtocols[2];
211
212public:
213 CStun(NETSOCKET Socket);
214 void FeedStunServer(NETADDR StunServer);
215 void Refresh();
216 void Update();
217 bool OnPacket(NETADDR Addr, unsigned char *pData, int DataSize);
218 CONNECTIVITY GetConnectivity(int NetType, NETADDR *pGlobalAddr);
219};
220
221class CNetConnection
222{
223 // TODO: is this needed because this needs to be aware of
224 // the ack sequencing number and is also responsible for updating
225 // that. this should be fixed.
226 friend class CNetRecvUnpacker;
227
228public:
229 enum class EState
230 {
231 OFFLINE,
232 WANT_TOKEN,
233 CONNECT,
234 PENDING,
235 ONLINE,
236 ERROR,
237 };
238
239 SECURITY_TOKEN m_SecurityToken;
240
241private:
242 unsigned short m_Sequence;
243 unsigned short m_Ack;
244 unsigned short m_PeerAck;
245 EState m_State = EState::OFFLINE;
246 int m_RemoteClosed;
247 bool m_BlockCloseMsg;
248 bool m_UnknownSeq;
249
250 CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> m_Buffer;
251
252 int64_t m_LastUpdateTime;
253 int64_t m_LastRecvTime;
254 int64_t m_LastSendTime;
255
256 char m_aErrorString[256];
257
258 CNetPacketConstruct m_Construct;
259
260 NETADDR m_aConnectAddrs[16];
261 int m_NumConnectAddrs;
262 NETADDR m_PeerAddr;
263 NETSOCKET m_Socket;
264 NETSTATS m_Stats;
265
266 std::array<char, NETADDR_MAXSTRSIZE> m_aPeerAddrStr;
267 std::array<char, NETADDR_MAXSTRSIZE> m_aPeerAddrStrNoPort;
268 // client 0.7
269 static TOKEN GenerateToken7(const NETADDR *pPeerAddr);
270 class CNetBase *m_pNetBase;
271 bool IsSixup() const { return m_Sixup; }
272
273 //
274 void SetPeerAddr(const NETADDR *pAddr);
275 void ClearPeerAddr();
276 void ResetStats();
277 void SetError(const char *pString);
278 void AckChunks(int Ack);
279
280 int QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence);
281 void SendConnect();
282 void SendControl(int ControlMsg, const void *pExtra, int ExtraSize);
283 void SendControlWithToken7(int ControlMsg, SECURITY_TOKEN ResponseToken);
284 void ResendChunk(CNetChunkResend *pResend);
285 void Resend();
286
287public:
288 bool m_TimeoutProtected;
289 bool m_TimeoutSituation;
290
291 void SetToken7(TOKEN Token);
292
293 void Reset(bool Rejoin = false);
294 void Init(NETSOCKET Socket, bool BlockCloseMsg);
295 int Connect(const NETADDR *pAddr, int NumAddrs);
296 int Connect7(const NETADDR *pAddr, int NumAddrs);
297 void Disconnect(const char *pReason);
298
299 int Update();
300 int Flush();
301
302 int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED, SECURITY_TOKEN ResponseToken = NET_SECURITY_TOKEN_UNSUPPORTED);
303 int QueueChunk(int Flags, int DataSize, const void *pData);
304
305 const char *ErrorString();
306 void SignalResend();
307 EState State() const { return m_State; }
308 const NETADDR *PeerAddress() const { return &m_PeerAddr; }
309 const std::array<char, NETADDR_MAXSTRSIZE> &PeerAddressString(bool IncludePort) const
310 {
311 return IncludePort ? m_aPeerAddrStr : m_aPeerAddrStrNoPort;
312 }
313 void ConnectAddresses(const NETADDR **ppAddrs, int *pNumAddrs) const
314 {
315 *ppAddrs = m_aConnectAddrs;
316 *pNumAddrs = m_NumConnectAddrs;
317 }
318
319 void ResetErrorString() { m_aErrorString[0] = 0; }
320 const char *ErrorString() const { return m_aErrorString; }
321
322 // Needed for GotProblems in NetClient
323 int64_t LastRecvTime() const { return m_LastRecvTime; }
324 int64_t ConnectTime() const { return m_LastUpdateTime; }
325
326 int AckSequence() const { return m_Ack; }
327 int SeqSequence() const { return m_Sequence; }
328 int SecurityToken() const { return m_SecurityToken; }
329 CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *ResendBuffer() { return &m_Buffer; }
330
331 void ResumeConnection(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken, CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *pResendBuffer, bool Sixup);
332
333 // anti spoof
334 void DirectInit(const NETADDR &Addr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN Token, bool Sixup);
335 void SetUnknownSeq() { m_UnknownSeq = true; }
336 void SetSequence(int Sequence) { m_Sequence = Sequence; }
337
338 bool m_Sixup;
339 SECURITY_TOKEN m_Token;
340};
341
342class CConsoleNetConnection
343{
344public:
345 enum class EState
346 {
347 OFFLINE,
348 ONLINE,
349 ERROR,
350 };
351
352private:
353 EState m_State;
354
355 NETADDR m_PeerAddr;
356 NETSOCKET m_Socket;
357
358 char m_aBuffer[NET_MAX_PACKETSIZE];
359 int m_BufferOffset;
360
361 char m_aErrorString[256];
362
363 bool m_LineEndingDetected;
364 char m_aLineEnding[3];
365
366public:
367 int Init(NETSOCKET Socket, const NETADDR *pAddr);
368 void Disconnect(const char *pReason);
369
370 EState State() const { return m_State; }
371 const NETADDR *PeerAddress() const { return &m_PeerAddr; }
372 const char *ErrorString() const { return m_aErrorString; }
373
374 void Reset();
375 int Update();
376 int Send(const char *pLine);
377 int Recv(char *pLine, int MaxLength);
378};
379
380class CNetRecvUnpacker
381{
382public:
383 bool m_Valid;
384
385 NETADDR m_Addr;
386 CNetConnection *m_pConnection;
387 int m_CurrentChunk;
388 int m_ClientId;
389 CNetPacketConstruct m_Data;
390 unsigned char m_aBuffer[NET_MAX_PACKETSIZE];
391
392 CNetRecvUnpacker() { Clear(); }
393 void Clear();
394 void Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientId);
395 int FetchChunk(CNetChunk *pChunk);
396};
397
398// server side
399class CNetServer
400{
401 struct CSlot
402 {
403 public:
404 CNetConnection m_Connection;
405 };
406
407 struct CSpamConn
408 {
409 NETADDR m_Addr;
410 int64_t m_Time;
411 int m_Conns;
412 };
413
414 NETADDR m_Address;
415 NETSOCKET m_Socket;
416 CNetBan *m_pNetBan;
417 CSlot m_aSlots[NET_MAX_CLIENTS];
418 int m_MaxClients = NET_MAX_CLIENTS;
419 int m_MaxClientsPerIp;
420
421 NETFUNC_NEWCLIENT m_pfnNewClient;
422 NETFUNC_NEWCLIENT_NOAUTH m_pfnNewClientNoAuth;
423 NETFUNC_DELCLIENT m_pfnDelClient;
424 NETFUNC_CLIENTREJOIN m_pfnClientRejoin;
425 void *m_pUser;
426
427 unsigned char m_aSecurityTokenSeed[16];
428
429 // vanilla connect flood detection
430 int64_t m_VConnFirst;
431 int m_VConnNum;
432
433 CSpamConn m_aSpamConns[NET_CONNLIMIT_IPS];
434
435 CNetRecvUnpacker m_RecvUnpacker;
436
437 void OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet);
438 int OnSixupCtrlMsg(NETADDR &Addr, CNetChunk *pChunk, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN &ResponseToken, SECURITY_TOKEN Token);
439 void OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet);
440 void OnConnCtrlMsg(NETADDR &Addr, int ClientId, int ControlMsg, const CNetPacketConstruct &Packet);
441 bool ClientExists(const NETADDR &Addr) { return GetClientSlot(Addr) != -1; }
442 int GetClientSlot(const NETADDR &Addr);
443 void SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);
444
445 int TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth = false, bool Sixup = false, SECURITY_TOKEN Token = 0);
446 int NumClientsWithAddr(NETADDR Addr);
447 bool Connlimit(NETADDR Addr);
448 void SendMsgs(NETADDR &Addr, const CPacker **ppMsgs, int Num);
449
450public:
451 int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
452 int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_NEWCLIENT_NOAUTH pfnNewClientNoAuth, NETFUNC_CLIENTREJOIN pfnClientRejoin, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
453
454 //
455 bool Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIp);
456 void Close();
457
458 //
459 int Recv(CNetChunk *pChunk, SECURITY_TOKEN *pResponseToken);
460 int Send(CNetChunk *pChunk);
461 void Update();
462
463 //
464 void Drop(int ClientId, const char *pReason);
465
466 // status requests
467 const NETADDR *ClientAddr(int ClientId) const { return m_aSlots[ClientId].m_Connection.PeerAddress(); }
468 const std::array<char, NETADDR_MAXSTRSIZE> &ClientAddrString(int ClientId, bool IncludePort) const { return m_aSlots[ClientId].m_Connection.PeerAddressString(IncludePort); }
469 bool HasSecurityToken(int ClientId) const { return m_aSlots[ClientId].m_Connection.SecurityToken() != NET_SECURITY_TOKEN_UNSUPPORTED; }
470 NETADDR Address() const { return m_Address; }
471 NETSOCKET Socket() const { return m_Socket; }
472 CNetBan *NetBan() const { return m_pNetBan; }
473 int NetType() const { return net_socket_type(sock: m_Socket); }
474 int MaxClients() const { return m_MaxClients; }
475
476 void SendTokenSixup(NETADDR &Addr, SECURITY_TOKEN Token);
477
478 //
479 void SetMaxClientsPerIp(int Max);
480 bool HasErrored(int ClientId);
481 void ResumeOldConnection(int ClientId, int OrigId);
482 void IgnoreTimeouts(int ClientId);
483
484 void ResetErrorString(int ClientId);
485 const char *ErrorString(int ClientId);
486
487 // anti spoof
488 SECURITY_TOKEN GetGlobalToken();
489 SECURITY_TOKEN GetToken(const NETADDR &Addr);
490 SECURITY_TOKEN GetVanillaToken(const NETADDR &Addr);
491};
492
493class CNetConsole
494{
495 struct CSlot
496 {
497 CConsoleNetConnection m_Connection;
498 };
499
500 NETSOCKET m_Socket;
501 CNetBan *m_pNetBan;
502 CSlot m_aSlots[NET_MAX_CONSOLE_CLIENTS];
503
504 NETFUNC_NEWCLIENT_CON m_pfnNewClient;
505 NETFUNC_DELCLIENT m_pfnDelClient;
506 void *m_pUser;
507
508 CNetRecvUnpacker m_RecvUnpacker;
509
510public:
511 void SetCallbacks(NETFUNC_NEWCLIENT_CON pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
512
513 //
514 bool Open(NETADDR BindAddr, CNetBan *pNetBan);
515 void Close();
516
517 //
518 int Recv(char *pLine, int MaxLength, int *pClientId = nullptr);
519 int Send(int ClientId, const char *pLine);
520 void Update();
521
522 //
523 int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr);
524 void Drop(int ClientId, const char *pReason);
525
526 // status requests
527 const NETADDR *ClientAddr(int ClientId) const { return m_aSlots[ClientId].m_Connection.PeerAddress(); }
528 CNetBan *NetBan() const { return m_pNetBan; }
529};
530
531class CNetTokenCache
532{
533public:
534 void Init(NETSOCKET Socket);
535 void SendPacketConnless(CNetChunk *pChunk);
536 void FetchToken(NETADDR *pAddr);
537 void AddToken(const NETADDR *pAddr, TOKEN Token);
538 TOKEN GetToken(const NETADDR *pAddr);
539 TOKEN GenerateToken();
540 void Update();
541
542private:
543 class CConnlessPacketInfo
544 {
545 public:
546 NETADDR m_Addr;
547 int m_DataSize;
548 unsigned char m_aData[NET_MAX_PAYLOAD];
549 int64_t m_Expiry;
550 };
551
552 class CAddressInfo
553 {
554 public:
555 NETADDR m_Addr;
556 TOKEN m_Token;
557 int64_t m_Expiry;
558 };
559
560 NETSOCKET m_Socket;
561 std::vector<CAddressInfo> m_TokenCache;
562 std::vector<CConnlessPacketInfo> m_ConnlessPackets;
563};
564
565// client side
566class CNetClient
567{
568 CNetConnection m_Connection;
569 CNetRecvUnpacker m_RecvUnpacker;
570 CNetTokenCache m_TokenCache;
571
572 CStun *m_pStun = nullptr;
573
574public:
575 NETSOCKET m_Socket = nullptr;
576 // openness
577 bool Open(NETADDR BindAddr);
578 void Close();
579
580 // connection state
581 void Disconnect(const char *pReason);
582 void Connect(const NETADDR *pAddr, int NumAddrs);
583 void Connect7(const NETADDR *pAddr, int NumAddrs);
584
585 // communication
586 int Recv(CNetChunk *pChunk, SECURITY_TOKEN *pResponseToken, bool Sixup);
587 int Send(CNetChunk *pChunk);
588
589 // pumping
590 void Update();
591 int Flush();
592
593 void ResetErrorString();
594
595 // error and state
596 int NetType() const { return net_socket_type(sock: m_Socket); }
597 int State();
598 const NETADDR *ServerAddress() const { return m_Connection.PeerAddress(); }
599 void ConnectAddresses(const NETADDR **ppAddrs, int *pNumAddrs) const { m_Connection.ConnectAddresses(ppAddrs, pNumAddrs); }
600 bool GotProblems(int64_t MaxLatency) const;
601 const char *ErrorString() const;
602
603 // stun
604 void FeedStunServer(NETADDR StunServer);
605 void RefreshStun();
606 CONNECTIVITY GetConnectivity(int NetType, NETADDR *pGlobalAddr);
607};
608
609// TODO: both, fix these. This feels like a junk class for stuff that doesn't fit anywhere
610class CNetBase
611{
612 static IOHANDLE ms_DataLogSent;
613 static IOHANDLE ms_DataLogRecv;
614 static CHuffman ms_Huffman;
615
616public:
617 static void OpenLog(IOHANDLE DataLogSent, IOHANDLE DataLogRecv);
618 static void CloseLog();
619 static void Init();
620 static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize);
621 static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize);
622
623 static bool IsValidConnectionOrientedPacket(const CNetPacketConstruct *pPacket);
624
625 static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken, bool Sixup = false);
626 static void SendControlMsgWithToken7(NETSOCKET Socket, NETADDR *pAddr, TOKEN Token, int Ack, int ControlMsg, TOKEN MyToken, bool Extended);
627 static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, bool Extended, unsigned char aExtra[NET_CONNLESS_EXTRA_SIZE]);
628 static void SendPacketConnlessWithToken7(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, SECURITY_TOKEN Token, SECURITY_TOKEN ResponseToken);
629 static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken, bool Sixup = false);
630
631 static std::optional<int> UnpackPacketFlags(unsigned char *pBuffer, int Size);
632 static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket, bool &Sixup, SECURITY_TOKEN *pSecurityToken = nullptr, SECURITY_TOKEN *pResponseToken = nullptr);
633
634 // The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not
635 static bool IsSeqInBackroom(int Seq, int Ack);
636};
637
638#endif
639