OpenClonk
C4GameControl.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk 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 /* control management */
17 
18 #include "C4Include.h"
19 #include "control/C4GameControl.h"
20 
21 #include "control/C4Record.h"
22 #include "game/C4Application.h"
23 #include "gui/C4GameOverDlg.h"
24 #include "gui/C4MouseControl.h"
26 #include "platform/C4GamePadCon.h"
27 #include "player/C4PlayerList.h"
28 #include "player/C4Player.h"
29 
30 #ifdef _MSC_VER
31 #pragma warning (disable: 4355)
32 #endif
33 
34 // *** C4GameControl
35 
37  : Network(this)
38 {
39  Default();
40 }
41 
43 {
44  Clear();
45 }
46 
48 {
49  eMode = CM_Local; fInitComplete = true;
50  fHost = true; iClientID = pLocal->getID();
51  ControlRate = 1;
52  // ok
53  return true;
54 }
55 
57 {
58  // network should already be initialized (by C4Network2)
59  if (!Network.IsEnabled())
60  return false;
61  // set mode
62  eMode = CM_Network; fInitComplete = true;
63  fHost = pLocal->isHost(); iClientID = pLocal->getID();
64  // control rate by parameters
66  // ok
67  return true;
68 }
69 
71 {
72  // open replay
73  pPlayback = new C4Playback();
74  if (!pPlayback->Open(rGroup))
75  {
76  LogFatal(LoadResStr("IDS_ERR_REPLAYREAD"));
77  delete pPlayback; pPlayback = nullptr;
78  return false;
79  }
80  // set mode
81  eMode = CM_Replay; fInitComplete = true;
83  // control rate by parameters
85  // just in case
86  StopRecord();
87  // ok
88  return true;
89 }
90 
92 {
93  // changes from any given mode to local
94  // (emergency - think of network disconnect)
95 
96  // remove all non-local clients
98  // activate local client
99  if (Game.Clients.getLocal())
101 
102  // network: clear network
103  if (eMode == CM_Network)
104  {
105  Network.Clear();
106  if (::Network.isEnabled())
107  ::Network.Clear();
108  }
109  // replay: close playback
110  else if (eMode == CM_Replay)
111  { delete pPlayback; pPlayback = nullptr; }
112 
113  // we're now managing our own player info list; make sure counter works
115 
116  // start the game, if we're not in the game over dialog
117  // (otherwise, clients start game when host disconnected!)
119 
120  // set status
121  eMode = CM_Local; fHost = true;
122  ControlRate = 1;
123 }
124 
126 {
127  // start record if desired
128  if (fRecordNeeded)
129  {
130  fRecordNeeded = false;
131  StartRecord(false, false);
132  }
133 }
134 
135 bool C4GameControl::StartRecord(bool fInitial, bool fStreaming)
136 {
137  assert(fInitComplete);
138  // already recording?
139  if (pRecord) StopRecord();
140  // start
141  pRecord = new C4Record();
142  if (!pRecord->Start(fInitial))
143  {
144  delete pRecord; pRecord = nullptr;
145  return false;
146  }
147  // streaming
148  if (fStreaming)
149  {
150  if (!pRecord->StartStreaming(fInitial) ||
151  !::Network.StartStreaming(pRecord))
152  {
153  delete pRecord; pRecord = nullptr;
154  return false;
155  }
156  }
157  // runtime records executed through queue: Must record initial control
158  if (pExecutingControl)
160  // ok
161  return true;
162 }
163 
164 void C4GameControl::StopRecord(StdStrBuf *pRecordName, BYTE *pRecordSHA1)
165 {
166  if (pRecord)
167  {
168  ::Network.FinishStreaming();
169  pRecord->Stop(pRecordName, pRecordSHA1);
170  // just delete
171  delete pRecord; pRecord = nullptr;
172  }
173 }
174 
176 {
177  if (!IsRuntimeRecordPossible()) return; // cannot record
178  fRecordNeeded = true;
179  // request through a synchronize-call
180  // currnetly do not request, but start record with next gamesync, so network runtime join can be debugged
181  if (Config.General.DebugRec)
183 }
184 
186 {
187  // already requested?
188  if (fRecordNeeded) return false;
189  // not from replay
190  if (isReplay()) return false;
191  // not if recording already
192  if (isRecord()) return false;
193  // Record OK
194  return true;
195 }
196 
197 bool C4GameControl::RecAddFile(const char *szLocalFilename, const char *szAddAs)
198 {
199  if (!isRecord() || !pRecord) return false;
200  return pRecord->AddFile(szLocalFilename, szAddAs);
201 }
202 
204 {
205  StopRecord();
206  ChangeToLocal();
207  Default();
208 }
209 
211 {
212  Input.Clear();
213  Network.Clear();
214  eMode = CM_None;
215  fHost = fInitComplete = false;
217  pRecord = nullptr;
218  pPlayback = nullptr;
219  SyncChecks.Clear();
221  ControlTick = 0;
223  DoSync = false;
224  fRecordNeeded = false;
225  pExecutingControl = nullptr;
226 }
227 
229 {
230  assert(fInitComplete);
231 
232  // Prepare control, return true if everything is ready for GameGo.
233  bool is_input_prepared = false;
234 
235  switch (eMode)
236  {
237  // full steam ahead
238  case CM_Local: case CM_Replay:
239  return true;
240 
241  case CM_Network:
242 
243  Network.Execute();
244 
245  // deactivated and got control: request activate
247  ::Network.RequestActivate();
248 
249  // needs input?
251  {
252  if (!is_input_prepared && Game.Clients.getLocal()->isActivated())
253  {
254  // add per-controlframe input
255  PrepareInput();
256  is_input_prepared = true;
257  }
259  Input.Clear();
260  }
261 
262  // control tick?
264  return true;
265 
266  // check GameGo
267  return Network.CtrlReady(ControlTick);
268  default:
269  return false;
270  }
271 }
272 
274 {
275  // Execute all available control
276 
277  assert(fInitComplete);
278 
279  // control tick? replay must always be executed.
281  return;
282 
283  // Get control
285  if (eMode == CM_Local)
286  {
287  // control = input
288  PrepareInput(); // add per-controlframe input
289  Control.Take(Input);
290  }
291  if (eMode == CM_Network)
292  {
293  // control = network input
295  {
296  LogFatal("Network: could not retrieve control from C4GameControlNetwork!");
297  return;
298  }
299  }
300  if (eMode == CM_Replay)
301  {
302  if (!pPlayback) { ChangeToLocal(); return; }
303  // control = replay data
305  }
306 
307  // Record: Save ctrl
308  if (pRecord)
310 
311  // debug: recheck PreExecute
312  assert(Control.PreExecute());
313 
314  // execute
316  Control.Execute();
317  Control.Clear();
318  pExecutingControl = nullptr;
319 
320  // statistics record
321  if (Game.pNetworkStatistics) Game.pNetworkStatistics->ExecuteControlFrame();
322 
323 }
324 
326 {
327  assert(fInitComplete);
328 
329  if (!(Game.FrameCounter % ControlRate))
330  ControlTick++;
331  if (!(Game.FrameCounter % SyncRate))
332  DoSync = true;
333 
334  // calc next tick without waiting for timer? (catchup cases)
335  if (eMode == CM_Network)
337  {
338  Game.GameGo = true;
339  if (Network.GetBehind(ControlTick) >= 25)
340  if (Game.FrameCounter % ((Network.GetBehind(ControlTick) + 15) / 20))
341  Game.DoSkipFrame = true;
342  }
343 }
344 
346 {
347  // 1. control tick reached?
348  if (ControlTick < iTick) return false;
349  // 2. control tick?
350  if (Game.FrameCounter % ControlRate) return false;
351  // ok then
352  return true;
353 }
354 
355 int32_t C4GameControl::getCtrlTick(int32_t iFrame) const
356 {
357  // returns control tick by frame. Note this is a guess, as the control rate
358  // can always change, so don't rely on the return value too much.
359 
360  return iFrame / ControlRate + ControlTick - Game.FrameCounter / ControlRate;
361 }
362 
364 {
365  return ControlTick + (Game.FrameCounter % ControlRate ? 1 : 0);
366 }
367 
369 {
370  // control host only
371  if (isCtrlHost())
373 }
374 
375 void C4GameControl::SetActivated(bool fnActivated)
376 {
377  fActivated = fnActivated;
378  if (eMode == CM_Network)
379  Network.SetActivated(fnActivated);
380 }
381 
383 {
384  assert(fInitComplete || pPkt->Lobby());
385 
386  // check if the control can be executed
387  if (eDelivery == CDT_Direct || eDelivery == CDT_Private)
388  assert(!pPkt->Sync());
389 
390  // decide control type
391  if (eDelivery == CDT_Decide)
392  eDelivery = DecideControlDelivery();
393 
394  // queue?
395  if (eDelivery == CDT_Queue)
396  {
397  // add, will be executed/sent later
398  Input.Add(eCtrlType, pPkt);
399  return;
400  }
401 
402  // Network?
403  if (isNetwork())
404  {
405  Network.DoInput(eCtrlType, pPkt, eDelivery);
406  }
407  else
408  {
409  // Local mode: execute at once
410  ExecControlPacket(eCtrlType, pPkt);
411  delete pPkt;
412  }
413 
414 }
415 
416 void C4GameControl::DbgRec(C4RecordChunkType eType, const uint8_t *pData, size_t iSize)
417 {
418  if (Config.General.DebugRec)
419  {
420  if (DoNoDebugRec>0) return;
421  // record data
422  if (pRecord)
423  {
424  C4PktDebugRec dr(eType, StdBuf(pData, iSize));
425  pRecord->Rec(Game.FrameCounter, DecompileToBuf<StdCompilerBinWrite>(dr), eType);
426  }
427  // check against playback
428  if (pPlayback)
429  pPlayback->Check(eType, pData, iSize);
430  }
431 }
432 
434 {
435  // network
436  if (eMode == CM_Network)
438  // use direct
439  return CDT_Direct;
440 }
441 
443 {
444  // only once
445  if (!DoSync) return;
446  DoSync = false;
447  // create sync check
448  C4ControlSyncCheck *pSyncCheck = new C4ControlSyncCheck();
449  pSyncCheck->Set();
450  // host?
451  if (fHost)
452  // add sync check to control queue or send it directly if the queue isn't active
454  else
455  {
456  // already have sync check?
458  if (!pSyncCheck2)
459  // add to sync check array
460  SyncChecks.Add(CID_SyncCheck, pSyncCheck);
461  else
462  {
463  // check
464  pSyncCheck->Execute();
465  delete pSyncCheck;
466  }
467  }
468  // remove old
470 }
471 
473 {
474  // nothing to do?
475  if (!rCtrl.firstPkt()) return;
476  // execute it
477  if (!rCtrl.PreExecute()) Log("Control: PreExecute failed for sync control!");
478  rCtrl.Execute();
479  // record
480  if (pRecord)
481  pRecord->Rec(rCtrl, Game.FrameCounter);
482 }
483 
485 {
486  // execute it
487  if (!pPkt->PreExecute()) Log("Control: PreExecute failed for direct control!");
488  pPkt->Execute();
489  // record it
490  if (pRecord)
491  pRecord->Rec(eCtrlType, pPkt, Game.FrameCounter);
492 }
493 
495 {
496  for (C4IDPacket *pPkt = SyncChecks.firstPkt(); pPkt; pPkt = SyncChecks.nextPkt(pPkt))
497  {
498  // should be a sync check
499  if (pPkt->getPktType() != CID_SyncCheck) continue;
500  // get sync check
501  C4ControlSyncCheck *pCheck = static_cast<C4ControlSyncCheck *>(pPkt->getPkt());
502  // packet that's searched for?
503  if (pCheck->getFrame() == iTick)
504  return pCheck;
505  }
506  return nullptr;
507 }
508 
510 {
511  C4IDPacket *pNext;
512  for (C4IDPacket *pPkt = SyncChecks.firstPkt(); pPkt; pPkt = pNext)
513  {
514  pNext = SyncChecks.nextPkt(pPkt);
515  // should be a sync check
516  if (pPkt->getPktType() != CID_SyncCheck) continue;
517  // remove?
518  C4ControlSyncCheck *pCheck = static_cast<C4ControlSyncCheck *>(pPkt->getPkt());
519  if (pCheck->getFrame() < Game.FrameCounter - C4SyncCheckMaxKeep)
520  SyncChecks.Delete(pPkt);
521  }
522 }
523 
525 {
526  // add per-controlframe input
529  // per-player input
530  C4Player *plr; int32_t i=0;
531  while ((plr = ::Players.GetLocalByIndex(i++)))
532  plr->Control.PrepareInput();
533 }
534 
const int32_t C4ClientIDUnknown
Definition: C4Client.h:24
C4Config Config
Definition: C4Config.cpp:930
const int C4MaxControlRate
Definition: C4Constants.h:33
@ C4CVT_ControlRate
Definition: C4Control.h:103
C4GameControl Control
const int32_t C4SyncCheckMaxKeep
Definition: C4GameControl.h:55
const int32_t C4SyncCheckRate
Definition: C4GameControl.h:53
@ CM_Replay
Definition: C4GameControl.h:29
@ CM_Network
Definition: C4GameControl.h:28
@ CM_Local
Definition: C4GameControl.h:27
@ CM_None
Definition: C4GameControl.h:26
C4ControlDeliveryType
Definition: C4GameControl.h:33
@ CDT_Decide
Definition: C4GameControl.h:39
@ CDT_Direct
Definition: C4GameControl.h:36
@ CDT_Private
Definition: C4GameControl.h:37
@ CDT_Queue
Definition: C4GameControl.h:34
C4Game Game
Definition: C4Globals.cpp:52
C4Application Application
Definition: C4Globals.cpp:44
C4Network2 Network
Definition: C4Globals.cpp:53
C4MouseControl MouseControl
Definition: C4Globals.cpp:47
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool Log(const char *szMessage)
Definition: C4Log.cpp:204
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:239
C4PacketType
Definition: C4PacketBase.h:77
@ CID_SyncCheck
Definition: C4PacketBase.h:151
@ CID_Synchronize
Definition: C4PacketBase.h:152
@ CID_Set
Definition: C4PacketBase.h:153
C4PlayerList Players
int DoNoDebugRec
Definition: C4Record.cpp:30
C4RecordChunkType
Definition: C4Record.h:45
uint8_t BYTE
int iSize
Definition: TstC4NetIO.cpp:32
C4GamePadControl * pGamePadControl
Definition: C4Application.h:43
int32_t getID() const
Definition: C4Client.h:105
bool isActivated() const
Definition: C4Client.h:110
bool isHost() const
Definition: C4Client.h:106
void SetActivated(bool fnActivated)
Definition: C4Client.cpp:120
C4Client * getLocal() const
Definition: C4Client.h:161
void RemoveRemote()
Definition: C4Client.cpp:346
int32_t DebugRec
Definition: C4Config.h:63
C4ConfigGeneral General
Definition: C4Config.h:255
C4ConfigNetwork Network
Definition: C4Config.h:259
int32_t ControlRate
Definition: C4Config.h:139
C4IDPacket * firstPkt() const
Definition: C4Control.h:78
void Clear()
Definition: C4Control.cpp:85
bool PreExecute() const
Definition: C4Control.cpp:90
void Execute() const
Definition: C4Control.cpp:110
void Add(C4PacketType eType, C4ControlPacket *pCtrl)
Definition: C4Control.h:82
void Delete(C4IDPacket *pPkt)
Definition: C4Control.h:88
C4IDPacket * nextPkt(C4IDPacket *pPkt) const
Definition: C4Control.h:79
virtual bool PreExecute() const
Definition: C4Control.h:48
virtual void Execute() const =0
virtual bool Sync() const
Definition: C4Control.h:55
virtual bool Lobby() const
Definition: C4Control.h:53
int32_t getFrame() const
Definition: C4Control.h:333
void RequestRuntimeRecord()
bool IsRuntimeRecordPossible() const
int32_t iClientID
Definition: C4GameControl.h:75
void ExecControlPacket(C4PacketType eCtrlType, class C4ControlPacket *pPkt)
C4Control SyncChecks
Definition: C4GameControl.h:80
int32_t ControlRate
Definition: C4GameControl.h:88
C4GameControlNetwork Network
Definition: C4GameControl.h:67
void ChangeToLocal()
bool InitLocal(C4Client *pLocal)
bool isReplay() const
Definition: C4GameControl.h:98
C4Control Input
Definition: C4GameControl.h:66
bool StartRecord(bool fInitial, bool fStreaming)
C4Control * pExecutingControl
Definition: C4GameControl.h:84
int32_t ControlTick
Definition: C4GameControl.h:89
void OnGameSynchronizing()
void AdjustControlRate(int32_t iBy)
int32_t SyncRate
Definition: C4GameControl.h:90
void ExecControl(const C4Control &rCtrl)
void RemoveOldSyncChecks()
bool InitNetwork(C4Client *pLocal)
bool CtrlTickReached(int32_t iTick)
int32_t getCtrlTick(int32_t iFrame) const
bool RecAddFile(const char *szLocalFilename, const char *szAddAs)
C4Record * pRecord
Definition: C4GameControl.h:77
void DbgRec(C4RecordChunkType eType, const uint8_t *pData=nullptr, size_t iSize=0)
bool isRecord() const
bool isCtrlHost() const
Definition: C4GameControl.h:99
C4ControlSyncCheck * GetSyncCheck(int32_t iTick)
void DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4ControlDeliveryType eDelivery)
C4ControlMode eMode
Definition: C4GameControl.h:70
int32_t getNextControlTick() const
void StopRecord(StdStrBuf *pRecordName=nullptr, BYTE *pRecordSHA1=nullptr)
C4Playback * pPlayback
Definition: C4GameControl.h:78
friend class C4ControlSyncCheck
Definition: C4GameControl.h:59
C4ControlDeliveryType DecideControlDelivery()
bool isNetwork() const
Definition: C4GameControl.h:97
bool InitReplay(C4Group &rGroup)
void SetActivated(bool fActivated)
bool CtrlNeeded(int32_t iTick) const
void DoInput(const C4Control &Input)
bool CtrlOverflow(int32_t iTick) const
void SetActivated(bool fnActivated)
int32_t GetBehind(int32_t iTick) const
bool CtrlReady(int32_t iTick)
C4ControlDeliveryType DecideControlDelivery() const
bool GetControl(C4Control *pCtrl, int32_t iTick)
std::unique_ptr< C4Network2Stats > pNetworkStatistics
Definition: C4Game.h:95
bool DoSkipFrame
Definition: C4Game.h:138
C4ClientList & Clients
Definition: C4Game.h:69
C4PlayerInfoList & PlayerInfos
Definition: C4Game.h:71
int32_t FrameCounter
Definition: C4Game.h:129
C4GameParameters & Parameters
Definition: C4Game.h:67
bool GameGo
Definition: C4Game.h:136
int32_t HaltCount
Definition: C4Game.h:112
static bool IsShown()
Definition: C4GameOverDlg.h:91
void Check(C4RecordChunkType eType, const uint8_t *pData, int iSize)
Definition: C4Record.cpp:992
bool ExecuteControl(C4Control *pCtrl, int iFrame)
Definition: C4Record.cpp:840
bool Open(C4Group &rGrp)
Definition: C4Record.cpp:382
C4PlayerControl Control
Definition: C4Player.h:129
C4Player * GetLocalByIndex(int iIndex) const
bool StartStreaming(bool fInitial)
Definition: C4Record.cpp:311
bool AddFile(const char *szLocalFilename, const char *szAddAs, bool fDelete=false)
Definition: C4Record.cpp:266
bool Stop(StdStrBuf *pRecordName=nullptr, BYTE *pRecordSHA1=nullptr)
Definition: C4Record.cpp:169
bool Rec(const C4Control &Ctrl, int iFrame)
Definition: C4Record.cpp:211
bool Start(bool fInitial)
Definition: C4Record.cpp:104
Definition: StdBuf.h:30