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