1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2017-2020, The LegacyClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16
17#pragma once
18
19#include "C4Control.h"
20#include "C4PacketBase.h"
21#include "C4Network2.h"
22
23#include <atomic>
24
25// constants
26const int32_t C4ControlBacklog = 100, // (ctrl ticks)
27 C4ClientIDAll = C4ClientIDUnknown,
28 C4ControlOverflowLimit = 3, // (ctrl ticks)
29 C4MaxPreSend = 15; // (frames) - must be smaller than C4ControlBacklog!
30
31const uint32_t C4ControlRequestInterval = 2000; // (ms)
32
33enum C4ControlDeliveryType
34{
35 CDT_Queue = 0, // Send in control queue (sync)
36 CDT_Sync = 1, // Send, delay execution until net is sync (sync)
37 CDT_Direct = 2, // Send directly to all clients (not sync)
38 CDT_Private = 3, // Send only to some clients (not sync, obviously)
39
40 CDT_Decide, // Use whatever sync mode seems fastest atm (sync)
41};
42
43// Additional notes / requirements:
44// * Direct control always reaches all clients faster than queue control.
45// * Sync is the only synchronous control mode were it's garantueed that
46// the control is actually executed within a fixed time.
47// * CDT_Decide is guesswork, control isn't garantueed to be faster.
48
49enum C4GameControlNetworkMode
50{
51 CNM_Decentral = 0, // 0 is the standard mode set in config
52 CNM_Central = 1,
53 CNM_Async = 2,
54};
55
56// declarations
57class C4GameControlPacket; class C4GameControlClient;
58class C4PacketControlReq; class C4ClientList;
59
60// main class
61class C4GameControlNetwork // run by network thread
62{
63public:
64 static constexpr int32_t DefaultTargetFPS = 38;
65
66 C4GameControlNetwork(class C4GameControl *pParent);
67 ~C4GameControlNetwork();
68
69protected:
70 volatile bool fEnabled, fRunning;
71
72 // local status
73 int32_t iClientID;
74 bool fHost, fActivated;
75 C4GameControlNetworkMode eMode;
76 int32_t iTargetTick;
77
78 // options
79 volatile int32_t iControlPreSend;
80
81 // statistics
82 int32_t iWaitStart;
83 int32_t iAvgControlSendTime;
84 int32_t iTargetFPS; // used for PreSend-colculation
85
86 // control send / recv status
87 std::atomic<std::int32_t> iControlSent, iControlReady;
88
89 // control list
90 C4GameControlPacket *pCtrlStack;
91 CStdCSec CtrlCSec;
92
93 // list of clients (activated only!)
94 C4GameControlClient *pClients;
95 CStdCSec ClientsCSec;
96
97 // holds control that needs to be executed synchronized (main thread only)
98 C4Control SyncControl;
99 C4GameControlPacket *pSyncCtrlQueue;
100
101 // control request timing
102 uint32_t iNextControlReqeust;
103
104 // links
105 C4GameControl *const pParent;
106 C4Network2 *pNetwork;
107
108public:
109 bool IsEnabled() const { return fEnabled; }
110 bool IsRunning() const { return fRunning; }
111 bool IsActivated() const { return fActivated; }
112
113 int32_t getControlPreSend() const { return iControlPreSend; }
114 void setControlPreSend(int32_t iToVal) { iControlPreSend = (std::min)(a: iToVal, b: C4MaxPreSend); }
115 int32_t getAvgControlSendTime() const { return iAvgControlSendTime; }
116 void setTargetFPS(int32_t iToVal) { iTargetFPS = iToVal; }
117
118 // main thread communication
119 bool Init(int32_t iClientID, bool fHost, int32_t iStartTick, bool fActivated, C4Network2 *pNetwork); // by main thread
120 void Clear(); // by main thread
121
122 void Execute(); // by main thread
123 bool CtrlReady(int32_t iTick); // by main thread
124 bool CtrlOverflow(int32_t iTick) const { return fRunning && iControlReady >= iTick + C4ControlOverflowLimit; } // by main thread
125 int32_t GetBehind(int32_t iTick) const { return iControlReady - iTick + 1; } // by main thread
126 bool GetControl(C4Control *pCtrl, int32_t iTick); // by main thread
127 bool ClientReady(int32_t iClientID, int32_t iTick); // by main thread
128 int32_t ClientPerfStat(int32_t iClientID); // by main thread
129 int32_t ClientNextControl(int32_t iClientID); // by main thread
130
131 bool CtrlNeeded(int32_t iTick) const; // by main thread
132 void DoInput(const C4Control &Input); // by main thread
133 void DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, enum C4ControlDeliveryType eType); // by main thread
134
135 // sync control
136 C4ControlDeliveryType DecideControlDelivery() const; // by main thread
137 void ExecSyncControl(); // by main thread
138 void ExecSyncControl(int32_t iControlTick); // by main thread
139
140 // clients
141 void CopyClientList(const C4ClientList &rClients);
142
143 // pausing
144 void SetRunning(bool fnRunning, int32_t inTargetTick = -1); // by main thread
145 void SetActivated(bool fnActivated); // by main thread
146 void SetCtrlMode(C4GameControlNetworkMode enMode); // by main thread
147 C4GameControlNetworkMode GetCtrlMode() const { return eMode; } // by main thread
148
149 // performance
150 void CalcPerformance(int32_t iCtrlTick); // by main thread
151
152 // interfaces
153 void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn);
154 void OnResComplete(C4Network2Res *pRes);
155
156protected:
157 // clients
158 void AddClient(int32_t iClientID, const char *szName); // by main thread
159 void ClearClients(); // by main thread
160
161 // packet handling
162 void HandleControl(int32_t iByClientID, const C4GameControlPacket &rPkt);
163 void HandleControlReq(const C4PacketControlReq &rPkt, C4Network2IOConnection *pConn);
164 void HandleControlPkt(C4PacketType eCtrlType, C4ControlPacket *pPkt, enum C4ControlDeliveryType eType);
165
166 // client list
167 C4GameControlClient *getClient(int32_t iID);
168 void AddClient(C4GameControlClient *pClient);
169 void RemoveClient(C4GameControlClient *pClient);
170
171 // control stack
172 C4GameControlPacket *getCtrl(int32_t iClientID, int32_t iCtrlTick); // by both
173 void AddCtrl(C4GameControlPacket *pCtrl);
174 void ClearCtrl(int32_t iBeforeTick = -1);
175 void CheckCompleteCtrl(bool fSetEvent); // by both
176 C4GameControlPacket *PackCompleteCtrl(int32_t iTick); // by main thread
177
178 // sync control
179 void AddSyncCtrlToQueue(const C4Control &Ctrl, int32_t iTick); // by main thread
180 void ExecQueuedSyncCtrl(); // by main thread
181};
182
183class C4GameControlPacket : public C4PacketBase
184{
185 friend class C4GameControlNetwork;
186
187public:
188 C4GameControlPacket();
189
190 // needed as C4Control doesn't seem to implement correct copying behavior
191 C4GameControlPacket(const C4GameControlPacket &Pkt2);
192 C4GameControlPacket &operator=(const C4GameControlPacket &) = delete;
193
194protected:
195 // header
196 int32_t iClientID, iCtrlTick;
197 int32_t iTime;
198
199 // data
200 C4Control Ctrl;
201
202 // list (C4GameControlNetwork)
203 C4GameControlPacket *pNext;
204
205public:
206 int32_t getClientID() const { return iClientID; }
207 int32_t getCtrlTick() const { return iCtrlTick; }
208 int32_t getTime() const { return iTime; }
209 const C4Control &getControl() const { return Ctrl; }
210
211 void Set(int32_t iClientID, int32_t iCtrlTick);
212 void Set(int32_t iClientID, int32_t iCtrlTick, const C4Control &Ctrl);
213 void Add(const C4GameControlPacket &Ctrl);
214
215 virtual void CompileFunc(StdCompiler *pComp) override;
216};
217
218class C4GameControlClient
219{
220 friend class C4GameControlNetwork;
221
222public:
223 C4GameControlClient();
224
225protected:
226 // core data
227 int32_t iClientID;
228 char szName[C4MaxName + 1];
229
230 // next expected control for this client
231 int32_t iNextControl;
232
233 // performance data
234 int32_t iPerformance;
235
236 // list (C4GameControl)
237 C4GameControlClient *pNext;
238
239public:
240 int32_t getClientID() const { return iClientID; }
241 const char *getName() const { return szName; }
242 int32_t getNextControl() const { return iNextControl; }
243 int32_t getPerfStat() const;
244
245 void Set(int32_t iClientID, const char *szName);
246 void SetNextControl(int32_t inNextControl) { iNextControl = inNextControl; }
247 void AddPerf(int32_t iTime);
248};
249
250// * Packet classes *
251
252class C4PacketControlReq : public C4PacketBase
253{
254public:
255 C4PacketControlReq(int32_t iCtrlTick = -1);
256
257protected:
258 int32_t iCtrlTick;
259
260public:
261 int32_t getCtrlTick() const { return iCtrlTick; }
262
263 virtual void CompileFunc(StdCompiler *pComp) override;
264};
265
266class C4PacketControlPkt : public C4PacketBase
267{
268public:
269 C4PacketControlPkt() {}
270 C4PacketControlPkt(enum C4ControlDeliveryType eDelivery, const C4IDPacket &Ctrl)
271 : eDelivery(eDelivery), Ctrl(Ctrl) {}
272
273protected:
274 enum C4ControlDeliveryType eDelivery;
275 C4IDPacket Ctrl;
276
277public:
278 C4ControlDeliveryType getDelivery() const { return eDelivery; }
279 const C4IDPacket &getCtrl() const { return Ctrl; }
280
281 virtual void CompileFunc(StdCompiler *pComp) override;
282};
283
284class C4PacketExecSyncCtrl : public C4PacketBase
285{
286public:
287 C4PacketExecSyncCtrl(int32_t iControlTick = ~0) : iControlTick(iControlTick) {}
288
289protected:
290 int32_t iControlTick;
291
292public:
293 int32_t getControlTick() const { return iControlTick; }
294
295 virtual void CompileFunc(StdCompiler *pComp) override { pComp->Value(rStruct: mkNamingAdapt(rValue&: iControlTick, szName: "ControlTick", rDefault: -1)); }
296};
297