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