OpenClonk
C4GameControlNetwork.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 managament: network part */
17 
18 #include "C4Include.h"
20 
21 #include "control/C4GameControl.h"
22 #include "game/C4Application.h"
23 #include "game/C4GraphicsSystem.h"
24 
25 // *** C4GameControlNetwork
26 
28  : fEnabled(false), fRunning(false), iClientID(C4ClientIDUnknown),
29  fActivated(false), iTargetTick(-1),
30  iControlPreSend(1), tWaitStart(C4TimeMilliseconds::PositiveInfinity), iAvgControlSendTime(0), iTargetFPS(38),
31  iControlSent(0), iControlReady(0),
32  pCtrlStack(nullptr),
33  tNextControlRequest(0),
34  pParent(pnParent)
35 {
36  assert(pParent);
37 }
38 
40 {
41  Clear();
42 }
43 
44 bool C4GameControlNetwork::Init(int32_t inClientID, bool fnHost, int32_t iStartTick, bool fnActivated, C4Network2 *pnNetwork) // by main thread
45 {
46  if (IsEnabled()) Clear();
47  // init
48  iClientID = inClientID; fHost = fnHost;
49  ::Control.ControlTick = iStartTick;
51  fActivated = fnActivated;
52  pNetwork = pnNetwork;
53  // check
54  CheckCompleteCtrl(false);
55  // make sure no control has been lost
57  // ok
58  fEnabled = true; fRunning = false;
59  iTargetFPS = 38;
62  return true;
63 }
64 
65 void C4GameControlNetwork::Clear() // by main thread
66 {
67  fEnabled = false; fRunning = false;
70  // clear sync control
72  while (pSyncCtrlQueue)
73  {
75  pSyncCtrlQueue = pPkt->pNext;
76  delete pPkt;
77  }
78 }
79 
80 void C4GameControlNetwork::Execute() // by main thread
81 {
82  // Control ticks only
84  return;
85 
86  // Save time the control tick was reached
88 
89  // Execute any queued sync control
91 }
92 
93 bool C4GameControlNetwork::CtrlReady(int32_t iTick) // by main thread
94 {
95  // check for complete control and pack it
96  CheckCompleteCtrl(false);
97  // control ready?
98  return iControlReady >= iTick;
99 }
100 
101 bool C4GameControlNetwork::GetControl(C4Control *pCtrl, int32_t iTick) // by main thread
102 {
103  // lock
104  CStdLock CtrlLock(&CtrlCSec);
105  // look for control
106  C4GameControlPacket *pPkt = getCtrl(C4ClientIDAll, iTick);
107  if (!pPkt)
108  return false;
109  // set
110  pCtrl->Clear();
111  pCtrl->Append(pPkt->getControl());
112  // calc performance
113  CalcPerformance(iTick);
115  // ok
116  return true;
117 }
118 
119 bool C4GameControlNetwork::ClientReady(int32_t iClientID, int32_t iTick) // by main thread
120 {
121  if (eMode == CNM_Central && !fHost) return true;
122  return !!getCtrl(iClientID, iTick);
123 }
124 
125 int32_t C4GameControlNetwork::ClientPerfStat(int32_t iClientID) // by main thread
126 {
127  if (eMode == CNM_Central && !fHost) return true;
128  // get client
129  CStdLock ClientsLock(&ClientsCSec);
131  // return performance
132  return pClient ? pClient->getPerfStat() : 0;
133 }
134 
135 int32_t C4GameControlNetwork::ClientNextControl(int32_t iClientID) // by main thread
136 {
137  // get client
138  CStdLock ClientsLock(&ClientsCSec);
140  // return performance
141  return pClient ? pClient->getNextControl() : 0;
142 }
143 
144 bool C4GameControlNetwork::CtrlNeeded(int32_t iFrame) const // by main thread
145 {
146  if (!IsEnabled() || !fActivated) return false;
147  // check: should we send something at the moment?
148  int32_t iSendFor = pParent->getCtrlTick(iFrame + iControlPreSend);
149  // target tick set? do special check
150  if (iTargetTick >= 0 && iControlSent >= iTargetTick) return false;
151  // control sent for this ctrl tick?
152  return iSendFor > iControlSent;
153 }
154 
155 void C4GameControlNetwork::DoInput(const C4Control &Input) // by main thread
156 {
157  if (!fEnabled) return;
158  // pack
160  pCtrl->Set(iClientID, iControlSent+1, Input);
161  // client in central or async mode: send to host (will resend it to the other clients)
162  C4NetIOPacket CtrlPkt = MkC4NetIOPacket(PID_Control, *pCtrl);
163  if (eMode != CNM_Decentral)
164  {
165  if (!fHost)
166  if (!pNetwork->Clients.SendMsgToHost(CtrlPkt))
167  Application.InteractiveThread.ThreadLog("Failed to send control to host!");
168  }
169  // decentral mode: always broadcast to everybody
170  else
171  {
172  assert (eMode == CNM_Decentral);
173  if (!pNetwork->Clients.BroadcastMsgToClients(CtrlPkt))
174  Application.InteractiveThread.ThreadLog("Failed to broadcast control!");
175  }
176  // add to list
177  AddCtrl(pCtrl);
178  // ok, control is sent for this control tick
179  iControlSent++;
180  // ctrl complete?
181  CheckCompleteCtrl(false);
182 }
183 
184 void C4GameControlNetwork::DoInput(C4PacketType eCtrlType, C4ControlPacket *pCtrl, C4ControlDeliveryType eDelivery) // by main thread
185 {
186  if (!fEnabled) return;
187 
188  // Create packet
189  C4PacketControlPkt CtrlPkt(eDelivery, C4IDPacket(eCtrlType, pCtrl, false));
190 
191  switch (eDelivery)
192  {
193 
194  // Sync control
195  case CDT_Sync:
196  {
197  if (!fHost)
198  {
199  // Client: send to host
201  { LogFatal("Network: could not send direct control packet!"); break; }
202  delete pCtrl;
203  }
204  else
205  {
206  // Host: send to all clients
208  // Execute at once, if possible
209  if (::Network.isFrozen())
210  {
211  pParent->ExecControlPacket(eCtrlType, pCtrl);
212  delete pCtrl;
215  }
216  else
217  {
218  // Safe back otherwise
219  SyncControl.Add(eCtrlType, pCtrl);
220  // And sync
221  ::Network.Sync();
222  }
223  }
224  }
225  break;
226 
227  // Direct/private control:
228  case CDT_Direct:
229  case CDT_Private:
230  {
231  // Send to all clients
233  { LogFatal("Network: could not send direct control packet!"); break; }
234  // Exec at once
235  pParent->ExecControlPacket(eCtrlType, pCtrl);
236  delete pCtrl;
237  }
238  break;
239 
240  // Only some delivery types support single packets (queue control must be tick-stamped)
241  default:
242  assert(false);
243 
244  }
245 
246 }
247 
249 {
250  // This doesn't make sense for clients
251  if (!fHost)
252  return CDT_Queue;
253  // Decide the fastest control delivery type atm. Note this is a guess.
254  // Control sent with the returned delivery type may in theory be delayed infinitely.
255  if (::Network.isFrozen())
256  return CDT_Sync;
257  if (!Game.Clients.getLocal()->isActivated())
258  return CDT_Sync;
259  return CDT_Queue;
260 }
261 
263 {
264  assert(fHost);
265 
266  // This is a callback from C4Network informing that a point where accumulated sync control
267  // can be executed has been reached (it's "momentarily" safe to execute)
268 
269  // Nothing to do? Save some sweat.
270  if (!SyncControl.firstPkt())
271  return;
272 
273  // So let's spread the word, so clients will call ExecSyncControl, too.
276 
277  // Execute it
279 
280 }
281 
282 void C4GameControlNetwork::ExecSyncControl(int32_t iControlTick) // by main thread
283 {
284  // Nothing to do?
285  if (!SyncControl.firstPkt())
286  return;
287 
288  // Copy control and clear
290  SyncControl.Clear();
291 
292  // Given control tick reached?
293  if (pParent->ControlTick == iControlTick)
295 
296  else if (pParent->ControlTick > iControlTick)
297  // The host should make sure this doesn't happen.
298  LogF("Network: Fatal: got sync control to execute for ctrl tick %d, but already in ctrl tick %d!", iControlTick, pParent->ControlTick);
299 
300  else
301  // This sync control must be executed later, so safe it back
302  AddSyncCtrlToQueue(Control, iControlTick);
303 
304 }
305 
306 void C4GameControlNetwork::AddClient(int32_t iClientID, const char *szName) // by main thread
307 {
308  // security
309  if (!fEnabled || getClient(iClientID)) return;
310  // create new
311  C4GameControlClient *pClient = new C4GameControlClient();
312  pClient->Set(iClientID, szName);
313  pClient->SetNextControl(::Control.ControlTick);
314  // add client
315  AddClient(pClient);
316 }
317 
319 {
320  CStdLock ClientsLock(&ClientsCSec);
321  while (pClients) { C4GameControlClient *pClient = pClients; RemoveClient(pClient); delete pClient; }
322 }
323 
325 {
326  CStdLock ClientLock(&ClientsCSec);
327  // create local copy of activated client list
328  ClearClients();
329  C4Client *pClient = nullptr;
330  while ((pClient = rClients.getClient(pClient)))
331  if (pClient->isActivated())
332  AddClient(pClient->getID(), pClient->getName());
333 }
334 
335 void C4GameControlNetwork::SetRunning(bool fnRunning, int32_t inTargetTick) // by main thread
336 {
337  assert(fEnabled);
338  // check for redundant update, stop if running (safety)
339  if (fRunning == fnRunning && iTargetTick == inTargetTick) return;
340  fRunning = false;
341  // set
342  iTargetTick = inTargetTick;
343  fRunning = fnRunning;
344  // run?
345  if (fRunning)
346  {
347  // refresh client list
349  // check for complete ctrl
350  CheckCompleteCtrl(false);
351  }
352 }
353 
354 void C4GameControlNetwork::SetActivated(bool fnActivated) // by main thread
355 {
356  assert(fEnabled);
357  // no change? ignore
358  if (fActivated == fnActivated) return;
359  // set
360  fActivated = fnActivated;
361  // Activated? Start to send control at next tick
362  if (fActivated)
364 }
365 
367 {
368  assert(fEnabled);
369  // no change?
370  if (eMode == enMode) return;
371  // set mode
372  eMode = enMode;
373  // changed to decentral? rebroadcast all own control
374  if (enMode == CNM_Decentral)
375  {
376  CStdLock CtrlLock(&CtrlCSec); C4GameControlPacket *pPkt;
377  for (int32_t iCtrlTick = ::Control.ControlTick; (pPkt = getCtrl(iClientID, iCtrlTick)); iCtrlTick++)
379  }
380  else if (enMode == CNM_Central && fHost)
381  {
382  CStdLock CtrlLock(&CtrlCSec); C4GameControlPacket *pPkt;
383  for (int32_t iCtrlTick = ::Control.ControlTick; (pPkt = getCtrl(C4ClientIDAll, iCtrlTick)); iCtrlTick++)
385  }
386 }
387 
389 {
390  CStdLock ControlLock(&CtrlCSec);
391  CStdLock ClientLock(&ClientsCSec);
392  // should only be called if ready
393  assert(CtrlReady(iCtrlTick));
394  // calc perfomance for all clients
395  int32_t iClientsPing=0; int32_t iPingClientCount=0; int32_t iNumTunnels=0; int32_t iHostPing=0;
396  for (C4GameControlClient *pClient = pClients; pClient; pClient = pClient->pNext)
397  {
398  // Some rudimentary PreSend-calculation
399  // get associated connection - nullptr for self
400  C4Network2Client *pNetClt = ::Network.Clients.GetClientByID(pClient->getClientID());
401  if (pNetClt && !pNetClt->isLocal())
402  {
403  C4Network2IOConnection *pConn = pNetClt->getMsgConn();
404  if (!pConn)
405  // remember tunnel
406  ++iNumTunnels;
407  else
408  // store ping
409  if (pClient->getClientID() == C4ClientIDHost)
410  iHostPing = pConn->getPingTime();
411  else
412  {
413  iClientsPing += pConn->getPingTime();
414  ++iPingClientCount;
415  }
416  }
417  // Performance statistics
418  // find control (may not be found, if we only got the complete ctrl)
419  C4GameControlPacket *pCtrl = getCtrl(pClient->getClientID(), iCtrlTick);
420  if (!pCtrl || tWaitStart.IsInfinite()) continue;
421  // calc stats
422  pClient->AddPerf(pCtrl->getTime() - tWaitStart);
423  }
424  // Now do PreSend-calcs based on ping times
425  int32_t iControlSendTime;
426  if (eMode == CNM_Decentral)
427  {
428  // average ping time
429  iControlSendTime = (iClientsPing + iHostPing * (iNumTunnels + 1)) / (iPingClientCount + iNumTunnels + 1);
430  // decentral mode: Only half the ping is used if there are no tunnels
431  if (!iNumTunnels) iControlSendTime /= 2;
432  }
433  else
434  {
435  // central mode: Control must go to host and back
436  iControlSendTime = iHostPing;
437  }
438  // calc some average
439  if (iControlSendTime)
440  {
441  iAvgControlSendTime = (iAvgControlSendTime * 149 + iControlSendTime * 1000) / 150;
442  // now calculate the all-time optimum PreSend there is
443  int32_t iBestPreSend = Clamp((iTargetFPS * iAvgControlSendTime) / 1000000 + 1, 1, 15);
444  // fixed PreSend?
445  if (iTargetFPS <= 0) iBestPreSend = -iTargetFPS;
446  // Ha! Set it!
447  if (getControlPreSend() != iBestPreSend)
448  {
449  setControlPreSend(iBestPreSend);
450  ::GraphicsSystem.FlashMessage(FormatString("PreSend: %d - TargetFPS: %d", iBestPreSend, iTargetFPS).getData());
451  }
452  }
453 }
454 
456 {
457  // security
458  if (!pConn)
459  return;
460 
461 #define GETPKT(type, name) \
462  assert(pPacket); const type &name = \
463  static_cast<const type &>(*pPacket);
464 
465  switch (cStatus)
466  {
467 
468  case PID_Control: // control
469  {
471  HandleControl(pConn->getClientID(), rPkt);
472  }
473  break;
474 
475  case PID_ControlReq: // control request
476  {
477  if (!IsEnabled()) break;
478  if (pConn->isClosed() || !pConn->isAccepted()) break;
480  HandleControlReq(rPkt, pConn);
481  }
482  break;
483 
484  case PID_ControlPkt: // single control packet (main thread!)
485  {
487  // security
488  if (!fEnabled) break;
489  if (rPkt.getCtrl().getPktType() < CID_First) break;
490  // create copy (HandleControlPkt will store or delete)
491  C4IDPacket Copy(rPkt.getCtrl());
492  // some sanity checks
493  C4ControlPacket *pCtrlPkt = static_cast<C4ControlPacket *>(Copy.getPkt());
494  if (!pConn->isHost() && pConn->getClientID() != pCtrlPkt->getByClient())
495  break;
496  // handle
497  HandleControlPkt(Copy.getPktType(), pCtrlPkt, rPkt.getDelivery());
498  Copy.Default();
499  }
500  break;
501 
502  case PID_ExecSyncCtrl:
503  {
505  // security
506  if (!fEnabled) break;
507  // handle
508  ExecSyncControl(rPkt.getControlTick());
509  }
510  break;
511 
512  }
513 
514 #undef GETPKT
515 }
516 
518 {
519  // player?
520  if (pRes->getType() == NRT_Player)
521  // check for ctrl ready
522  CheckCompleteCtrl(true);
523 }
524 
525 void C4GameControlNetwork::HandleControl(int32_t iByClientID, const C4GameControlPacket &rPkt)
526 {
527  // already got that control? just ignore
528  if (getCtrl(rPkt.getClientID(), rPkt.getCtrlTick())) return;
529  // create copy, add to list
530  C4GameControlPacket *pCopy = new C4GameControlPacket(rPkt);
531  AddCtrl(pCopy);
532  // check: control complete?
533  if (IsEnabled())
534  CheckCompleteCtrl(true);
535  // note that C4GameControlNetwork will save incoming control even before
536  // Init() was called.
537 }
538 
540 {
541  CStdLock CtrlLock(&CtrlCSec);
542  for (int iTick = rPkt.getCtrlTick(); ; iTick++)
543  {
544  // search complete control
545  C4GameControlPacket *pCtrl = getCtrl(C4ClientIDAll, iTick);
546  if (pCtrl)
547  {
548  // send
549  pConn->Send(MkC4NetIOPacket(PID_Control, *pCtrl));
550  continue;
551  }
552  // send everything we have for this tick (this is an emergency case, so efficiency
553  // isn't that important for now).
554  bool fFound = false;
555  for (pCtrl = pCtrlStack; pCtrl; pCtrl = pCtrl->pNext)
556  if (pCtrl->getCtrlTick() == iTick)
557  {
558  pConn->Send(MkC4NetIOPacket(PID_Control, *pCtrl));
559  fFound = true;
560  }
561  // nothing found for this tick?
562  if (!fFound) break;
563  }
564 }
565 
567 {
568 
569  // direct control? execute at once
570  if (eType == CDT_Direct || eType == CDT_Private)
571  {
572  pParent->ExecControlPacket(eCtrlType, pCtrl);
573  delete pCtrl;
574  return;
575  }
576 
577  // sync ctrl from client? resend
578  if (fHost && eType == CDT_Sync)
579  {
580  DoInput(eCtrlType, pCtrl, eType);
581  return;
582  }
583 
584  // Execute queued control first
586 
587  // Execute at once, if possible
588  if (::Network.isFrozen())
589  {
590  pParent->ExecControlPacket(eCtrlType, pCtrl);
591  delete pCtrl;
592  }
593  else
594  {
595  // Safe back otherwise
596  SyncControl.Add(eCtrlType, pCtrl);
597  }
598 
599 }
600 
602 {
603  CStdLock ClientsLock(&ClientsCSec);
604  for (C4GameControlClient *pClient = pClients; pClient; pClient = pClient->pNext)
605  if (pClient->getClientID() == iID)
606  return pClient;
607  return nullptr;
608 }
609 
610 void C4GameControlNetwork::AddClient(C4GameControlClient *pClient) // by main thread
611 {
612  if (!pClient) return;
613  // lock
614  CStdLock ClientsLock(&ClientsCSec);
615  // add (ordered)
616  C4GameControlClient *pPrev = nullptr, *pPos = pClients;
617  for (; pPos; pPrev = pPos, pPos = pPos->pNext)
618  if (pPos->getClientID() > pClient->getClientID())
619  break;
620  // insert
621  (pPrev ? pPrev->pNext : pClients) = pClient;
622  pClient->pNext = pPos;
623 }
624 
626 {
627  // obtain lock
628  CStdLock ClientsLock(&ClientsCSec);
629  // first client?
630  if (pClient == pClients)
631  pClients = pClient->pNext;
632  else
633  {
634  C4GameControlClient *pPrev;
635  for (pPrev = pClients; pPrev && pPrev->pNext; pPrev = pPrev->pNext)
636  if (pPrev->pNext == pClient)
637  break;
638  if (pPrev && pPrev->pNext == pClient)
639  pPrev->pNext = pClient->pNext;
640  }
641 }
642 
643 C4GameControlPacket *C4GameControlNetwork::getCtrl(int32_t iClientID, int32_t iCtrlTick) // by both
644 {
645  // lock
646  CStdLock CtrlLock(&CtrlCSec);
647  // search
648  for (C4GameControlPacket *pCtrl = pCtrlStack; pCtrl; pCtrl = pCtrl->pNext)
649  if (pCtrl->getClientID() == iClientID && pCtrl->getCtrlTick() == iCtrlTick)
650  return pCtrl;
651  return nullptr;
652 }
653 
655 {
656  // lock
657  CStdLock CtrlLock(&CtrlCSec);
658  // add to list
659  pCtrl->pNext = pCtrlStack;
660  pCtrlStack = pCtrl;
661 }
662 
663 void C4GameControlNetwork::ClearCtrl(int32_t iBeforeTick) // by main thread
664 {
665  // lock
666  CStdLock CtrlLock(&CtrlCSec);
667  // clear all old control
668  C4GameControlPacket *pCtrl = pCtrlStack, *pLast = nullptr;
669  while (pCtrl)
670  {
671  // old?
672  if (iBeforeTick == -1 || pCtrl->getCtrlTick() < iBeforeTick)
673  {
674  // unlink
675  C4GameControlPacket *pDelete = pCtrl;
676  pCtrl = pCtrl->pNext;
677  (pLast ? pLast->pNext : pCtrlStack) = pCtrl;
678  // delete
679  delete pDelete;
680  }
681  else
682  {
683  pLast = pCtrl;
684  pCtrl = pCtrl->pNext;
685  }
686  }
687 }
688 
689 void C4GameControlNetwork::CheckCompleteCtrl(bool fSetEvent) // by both
690 {
691  // only when running (client list may be invalid)
692  if (!fRunning || !fEnabled) return;
693 
694  CStdLock CtrlLock(&CtrlCSec);
695  CStdLock ClientLock(&ClientsCSec);
696 
697  for (;;)
698  {
699  // control available?
701  // get stop tick
702  int32_t iStopTick = iTargetTick;
703  if (pSyncCtrlQueue)
704  if (iStopTick < 0 || iStopTick > pSyncCtrlQueue->getCtrlTick())
705  iStopTick = pSyncCtrlQueue->getCtrlTick();
706  // pack control?
707  if (!pComplete)
708  {
709  // own control not ready?
710  if (fActivated && iControlSent <= iControlReady) break;
711  // no clients? no need to pack more than one tick into the future
712  if (!pClients && ::Control.ControlTick <= iControlReady) break;
713  // stop packing?
714  if (iStopTick >= 0 && iControlReady + 1 >= iStopTick) break;
715  // central mode and not host?
716  if (eMode != CNM_Decentral && !fHost) break;
717  // (try to) pack
718  if (!(pComplete = PackCompleteCtrl(iControlReady + 1)))
719  break;
720  }
721  // preexecute to check if it's ready for execute
722  if (!pComplete->getControl().PreExecute())
723  break;
724  // ok, control for this tick is ready
725  iControlReady++;
726  // tell the main thread to move on
727  if (fSetEvent && Game.GameGo && iControlReady >= ::Control.ControlTick)
729  }
730  // clear old ctrl
733  // target ctrl tick to reach?
734  if (iControlReady < iTargetTick &&
737  {
738  Application.InteractiveThread.ThreadLogS("Network: Recovering: Requesting control for tick %d...", iControlReady + 1);
739  // make request
741  // send control requests
742  if (eMode == CNM_Central)
744  else
746  // set time for next request
748  }
749 }
750 
752 {
753  CStdLock CtrlLock(&CtrlCSec);
754  CStdLock ClientLock(&ClientsCSec);
755 
756  // check if ctrl by all clients is ready
757  C4GameControlClient *pClient;
758  for (pClient = pClients; pClient; pClient = pClient->pNext)
759  if (!getCtrl(pClient->getClientID(), iTick))
760  break;
761  if (pClient)
762  {
763  // async mode: wait n extra frames for slow clients
764  const int iMaxWait = (Config.Network.AsyncMaxWait * 1000) / iTargetFPS;
765  if (eMode != CNM_Async || C4TimeMilliseconds::Now() <= tWaitStart + iMaxWait)
766  return nullptr;
767  }
768 
769  // create packet
770  C4GameControlPacket *pComplete = new C4GameControlPacket();
771  pComplete->Set(C4ClientIDAll, iTick);
772 
773  // pack everything in ID order (client list is ordered this way)
774  C4GameControlPacket *pCtrl;
775  for (pClient = pClients; pClient; pClient = pClient->pNext)
776  while (pClient->getNextControl() <= iTick)
777  {
778  // get control
779  int32_t iNextControl = pClient->getNextControl();
780  pCtrl = getCtrl(pClient->getClientID(), iNextControl);
781  if (!pCtrl) break;
782  pClient->SetNextControl(iNextControl + 1);
783  assert(pCtrl);
784  // add
785  pComplete->Add(*pCtrl);
786  }
787 
788  // add to list
789  AddCtrl(pComplete);
790 
791  // host: send to clients (central and async mode)
792  if (eMode != CNM_Decentral)
794 
795  // advance control request time
797 
798  // return
799  return pComplete;
800 }
801 
802 void C4GameControlNetwork::AddSyncCtrlToQueue(const C4Control &Ctrl, int32_t iTick) // by main thread
803 {
804  // search place in queue. It's vitally important that new packets are placed
805  // behind packets for the same tick, so they will be executed in the right order.
806  C4GameControlPacket *pAfter = nullptr, *pBefore = pSyncCtrlQueue;
807  while (pBefore && pBefore->getCtrlTick() <= iTick)
808  { pAfter = pBefore; pBefore = pBefore->pNext; }
809  // create
811  pnPkt->Set(C4ClientIDUnknown, iTick, Ctrl);
812  // insert
813  (pAfter ? pAfter->pNext : pSyncCtrlQueue) = pnPkt;
814  pnPkt->pNext = pBefore;
815 }
816 
818 {
819  // security
821  {
822  LogF("Network: Fatal: got sync control to execute for ctrl tick %d, but already in ctrl tick %d!", pSyncCtrlQueue->getCtrlTick(), pParent->ControlTick);
823  // remove it
825  pSyncCtrlQueue = pPkt->pNext;
826  delete pPkt;
827  }
828  // nothing to do?
830  return;
831  // this should hold by now
833  do
834  {
835  // execute it
837  // remove the packet
839  pSyncCtrlQueue = pPkt->pNext;
840  delete pPkt;
841  }
843  // refresh copy of client list
845 }
846 
847 // *** C4GameControlPacket
848 
850  : iClientID(C4ClientIDUnknown),
851  tTime(C4TimeMilliseconds::Now())
852 {
853 
854 }
855 
857  : C4PacketBase(Pkt2), iClientID(Pkt2.getClientID()),
858  iCtrlTick(Pkt2.getCtrlTick()),
859  tTime(C4TimeMilliseconds::Now())
860 {
861  Ctrl.Copy(Pkt2.getControl());
862 }
863 
865 {
866  Set(Pkt2.getClientID(), Pkt2.getCtrlTick(), Pkt2.getControl());
867  return *this;
868 }
869 
870 void C4GameControlPacket::Set(int32_t inClientID, int32_t inCtrlTick)
871 {
872  iClientID = inClientID;
873  iCtrlTick = inCtrlTick;
874 }
875 
876 void C4GameControlPacket::Set(int32_t inClientID, int32_t inCtrlTick, const C4Control &nCtrl)
877 {
878  iClientID = inClientID;
879  iCtrlTick = inCtrlTick;
880  Ctrl.Copy(nCtrl);
881 }
882 
884 {
885  Ctrl.Append(Ctrl2.getControl());
886 }
887 
889 {
891  pComp->Value(mkNamingAdapt(mkIntPackAdapt(iCtrlTick), "CtrlTick", -1));
892  pComp->Value(mkNamingAdapt(Ctrl, "Ctrl"));
893 }
894 
895 // *** C4GameControlClient
896 
898  : iClientID(C4ClientIDUnknown)
899 {
900  szName[0] = '\0';
901 }
902 
904 {
905  return iPerformance / 100;
906 }
907 
908 void C4GameControlClient::Set(int32_t inClientID, const char *sznName)
909 {
910  iClientID = inClientID;
911  SCopy(sznName, szName, sizeof(szName)-1);
912 }
913 
914 void C4GameControlClient::AddPerf(int32_t iTime)
915 {
916  iPerformance += (iTime * 100 - iPerformance) / 100;
917 }
const int32_t C4ClientIDHost
Definition: C4Client.h:25
const int32_t C4ClientIDUnknown
Definition: C4Client.h:24
C4Config Config
Definition: C4Config.cpp:930
C4GameControl Control
C4ControlDeliveryType
Definition: C4GameControl.h:33
@ CDT_Direct
Definition: C4GameControl.h:36
@ CDT_Sync
Definition: C4GameControl.h:35
@ CDT_Private
Definition: C4GameControl.h:37
@ CDT_Queue
Definition: C4GameControl.h:34
#define GETPKT(type, name)
const int32_t C4ClientIDAll
C4GameControlNetworkMode
@ CNM_Async
@ CNM_Decentral
@ CNM_Central
const uint32_t C4ControlRequestInterval
const int32_t C4ControlBacklog
C4Game Game
Definition: C4Globals.cpp:52
C4Application Application
Definition: C4Globals.cpp:44
C4Network2 Network
Definition: C4Globals.cpp:53
C4GraphicsSystem GraphicsSystem
Definition: C4Globals.cpp:51
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:262
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:239
@ NRT_Player
Definition: C4Network2Res.h:45
C4NetIOPacket MkC4NetIOPacket(char cStatus, const class C4PacketBase &Pkt, const C4NetIO::addr_t &addr=C4NetIO::addr_t())
Definition: C4PacketBase.h:40
C4PacketType
Definition: C4PacketBase.h:77
@ PID_ControlReq
Definition: C4PacketBase.h:137
@ PID_ExecSyncCtrl
Definition: C4PacketBase.h:139
@ PID_Control
Definition: C4PacketBase.h:136
@ CID_First
Definition: C4PacketBase.h:142
@ PID_ControlPkt
Definition: C4PacketBase.h:138
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
T Clamp(T bval, T lbound, T rbound)
Definition: Standard.h:44
StdIntPackAdapt< T > mkIntPackAdapt(T &rVal)
Definition: StdAdaptors.h:791
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
C4InteractiveThread InteractiveThread
Definition: C4Application.h:45
int32_t getID() const
Definition: C4Client.h:105
bool isActivated() const
Definition: C4Client.h:110
const char * getName() const
Definition: C4Client.h:107
C4Client * getClient(const C4Client *pAfter=nullptr) const
Definition: C4Client.h:160
C4Client * getLocal() const
Definition: C4Client.h:161
C4ConfigNetwork Network
Definition: C4Config.h:259
int32_t AsyncMaxWait
Definition: C4Config.h:170
C4IDPacket * firstPkt() const
Definition: C4Control.h:78
void Append(const C4Control &Ctrl)
Definition: C4Control.h:85
void Clear()
Definition: C4Control.cpp:85
bool PreExecute() const
Definition: C4Control.cpp:90
void Add(C4PacketType eType, C4ControlPacket *pCtrl)
Definition: C4Control.h:82
void Copy(const C4Control &Ctrl)
Definition: C4Control.h:86
int32_t getByClient() const
Definition: C4Control.h:42
C4GameControlClient * pNext
void AddPerf(int32_t iTime)
int32_t getNextControl() const
char szName[C4MaxName+1]
void SetNextControl(int32_t inNextControl)
int32_t getClientID() const
void Set(int32_t iClientID, const char *szName)
void ExecControlPacket(C4PacketType eCtrlType, class C4ControlPacket *pPkt)
int32_t ControlRate
Definition: C4GameControl.h:88
int32_t ControlTick
Definition: C4GameControl.h:89
void ExecControl(const C4Control &rCtrl)
int32_t getNextControlTick() const
C4GameControlPacket * PackCompleteCtrl(int32_t iTick)
C4GameControlPacket * pSyncCtrlQueue
void AddClient(int32_t iClientID, const char *szName)
C4TimeMilliseconds tNextControlRequest
C4GameControlNetworkMode eMode
int32_t ClientNextControl(int32_t iClientID)
C4GameControl *const pParent
int32_t ClientPerfStat(int32_t iClientID)
C4TimeMilliseconds tWaitStart
void HandleControlPkt(C4PacketType eCtrlType, C4ControlPacket *pPkt, enum C4ControlDeliveryType eType)
void ClearCtrl(int32_t iBeforeTick=-1)
bool CtrlNeeded(int32_t iTick) const
void setControlPreSend(int32_t iToVal)
void CalcPerformance(int32_t iCtrlTick)
void DoInput(const C4Control &Input)
C4GameControlClient * getClient(int32_t iID)
void SetCtrlMode(C4GameControlNetworkMode enMode)
C4GameControlClient * pClients
int32_t getControlPreSend() const
C4GameControlNetwork(class C4GameControl *pParent)
void SetActivated(bool fnActivated)
void CopyClientList(const C4ClientList &rClients)
void SetRunning(bool fnRunning, int32_t inTargetTick=-1)
bool Init(int32_t iClientID, bool fHost, int32_t iStartTick, bool fActivated, C4Network2 *pNetwork)
void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
bool CtrlReady(int32_t iTick)
C4GameControlPacket * getCtrl(int32_t iClientID, int32_t iCtrlTick)
void AddSyncCtrlToQueue(const C4Control &Ctrl, int32_t iTick)
C4ControlDeliveryType DecideControlDelivery() const
void HandleControlReq(const C4PacketControlReq &rPkt, C4Network2IOConnection *pConn)
bool GetControl(C4Control *pCtrl, int32_t iTick)
bool ClientReady(int32_t iClientID, int32_t iTick)
void AddCtrl(C4GameControlPacket *pCtrl)
void CheckCompleteCtrl(bool fSetEvent)
void RemoveClient(int32_t iClientID)
void HandleControl(int32_t iByClientID, const C4GameControlPacket &rPkt)
C4GameControlPacket * pCtrlStack
volatile int32_t iControlReady
volatile int32_t iControlSent
void OnResComplete(C4Network2Res *pRes)
int32_t getClientID() const
const C4Control & getControl() const
void Add(const C4GameControlPacket &Ctrl)
void Set(int32_t iClientID, int32_t iCtrlTick)
C4TimeMilliseconds getTime() const
C4GameControlPacket * pNext
void CompileFunc(StdCompiler *pComp) override
int32_t getCtrlTick() const
C4GameControlPacket & operator=(const C4GameControlPacket &Pkt2)
C4ClientList & Clients
Definition: C4Game.h:69
int32_t FrameCounter
Definition: C4Game.h:129
bool GameGo
Definition: C4Game.h:136
void FlashMessage(const char *message)
void Default()
Definition: C4Packet2.cpp:218
C4PacketType getPktType() const
Definition: C4PacketBase.h:259
C4PacketBase * getPkt() const
Definition: C4PacketBase.h:260
bool ThreadLog(const char *szMessage,...) GNUC_FORMAT_ATTRIBUTE_O
bool ThreadLogS(const char *szMessage,...) GNUC_FORMAT_ATTRIBUTE_O
bool isLocal() const
C4Network2IOConnection * getMsgConn() const
C4Network2Client * GetClientByID(int32_t iID) const
bool BroadcastMsgToClients(const C4NetIOPacket &rPkt)
bool BroadcastMsgToConnClients(const C4NetIOPacket &rPkt)
bool SendMsgToHost(C4NetIOPacket rPkt)
bool Sync()
Definition: C4Network2.cpp:521
C4Network2ClientList Clients
Definition: C4Network2.h:116
bool isFrozen() const
bool Send(const C4NetIOPacket &rPkt)
bool isAccepted() const
Definition: C4Network2IO.h:289
C4Network2ResType getType() const
int32_t getCtrlTick() const
int32_t getControlTick() const
static C4TimeMilliseconds Now()
void Value(const T &rStruct)
Definition: StdCompiler.h:161