OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4Network2.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 
17 #include "C4Include.h"
19 #include "network/C4Network2.h"
20 
21 #include "C4Version.h"
22 #include "control/C4GameControl.h"
23 #include "control/C4GameSave.h"
24 #include "control/C4RoundResults.h"
25 #include "editor/C4Console.h"
26 #include "game/C4Application.h"
27 #include "game/C4GraphicsSystem.h"
28 #include "graphics/C4Draw.h"
30 
31 // lobby
32 #include "gui/C4GameLobby.h"
33 
35 #include "network/C4League.h"
36 
37 #ifdef _WIN32
38 #include <direct.h>
39 #endif
40 #ifndef HAVE_WINSOCK
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 #endif
45 
46 // compile options
47 #ifdef _MSC_VER
48 #pragma warning (disable: 4355)
49 #endif
50 
51 // *** C4Network2Status
52 
54 
55 const char *C4Network2Status::getStateName() const
56 {
57  switch (eState)
58  {
59  case GS_None: return "none";
60  case GS_Init: return "init";
61  case GS_Lobby: return "lobby";
62  case GS_Pause: return "pause";
63  case GS_Go: return "go";
64  }
65  return "???";
66 }
67 
69 {
70  switch (eState)
71  {
72  case GS_None: return LoadResStr("IDS_DESC_NOTINITED");
73  case GS_Init: return LoadResStr("IDS_DESC_WAITFORHOST");
74  case GS_Lobby: return LoadResStr("IDS_DESC_EXPECTING");
75  case GS_Pause: return LoadResStr("IDS_DESC_GAMEPAUSED");
76  case GS_Go: return LoadResStr("IDS_DESC_GAMERUNNING");
77  }
78  return LoadResStr("IDS_DESC_UNKNOWNGAMESTATE");
79 }
80 
81 void C4Network2Status::Set(C4NetGameState enState, int32_t inTargetTick)
82 {
83  eState = enState; iTargetCtrlTick = inTargetTick;
84 }
85 
86 void C4Network2Status::SetCtrlMode(int32_t inCtrlMode)
87 {
88  iCtrlMode = inCtrlMode;
89 }
90 
91 void C4Network2Status::SetTargetTick(int32_t inTargetCtrlTick)
92 {
93  iTargetCtrlTick = inTargetCtrlTick;
94 }
95 
97 {
99 }
100 
102 {
103  CompileFunc(pComp, false);
104 }
105 
106 void C4Network2Status::CompileFunc(StdCompiler *pComp, bool fReference)
107 {
108  StdEnumEntry<C4NetGameState> GameStates[] =
109  {
110  { "None", GS_None },
111  { "Init", GS_Init },
112  { "Lobby", GS_Lobby },
113  { "Paused", GS_Pause },
114  { "Running", GS_Go },
115  };
116  pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(eState, GameStates), "State", GS_None));
117  pComp->Value(mkNamingAdapt(mkIntPackAdapt(iCtrlMode), "CtrlMode", -1));
118 
119  if (!fReference)
120  pComp->Value(mkNamingAdapt(mkIntPackAdapt(iTargetCtrlTick), "TargetTick", -1));
121 }
122 
123 // *** C4Network2
124 
126  : Clients(&NetIO),
127  tLastActivateRequest(C4TimeMilliseconds::NegativeInfinity),
128  NetpuncherGameID(C4NetpuncherID())
129 {
130 
131 }
132 
134 {
135  Clear();
136 }
137 
138 bool C4Network2::InitHost(bool fLobby)
139 {
140  if (isEnabled()) Clear();
141  // initialize everything
142  Status.Set(fLobby ? GS_Lobby : GS_Go, ::Control.ControlTick);
144  fHost = true;
145  fStatusAck = fStatusReached = true;
146  fChasing = false;
147  fAllowJoin = false;
151  // initialize client list
152  Clients.Init(&Game.Clients, true);
153  // initialize resource list
155  { LogFatal("Network: failed to initialize resource list!"); Clear(); return false; }
157  return false;
158  // create initial dynamic
159  if (!CreateDynamic(true))
160  return false;
161  // initialize net i/o
162  if (!InitNetIO(false, true))
163  { Clear(); return false; }
164  // init network control
166  pControl->Init(C4ClientIDHost, true, ::Control.getNextControlTick(), true, this);
167  // init league
168  bool fCancel = true;
169  if (!InitLeague(&fCancel) || !LeagueStart(&fCancel))
170  {
171  // deinit league
172  DeinitLeague();
173  // user cancelled?
174  if (fCancel)
175  return false;
176  // in console mode, bail out
177 #ifdef USE_CONSOLE
178  return false;
179 #endif
180  }
181  // allow connect
182  NetIO.SetAcceptMode(true);
183  // timer
184  Application.Add(this);
185  // ok
186  return true;
187 }
188 
189 // Orders connection addresses to optimize joining.
190 static void SortAddresses(std::vector<C4Network2Address>& addrs)
191 {
192  // TODO: Maybe use addresses from local client to avoid the extra system calls.
193  auto localAddrs = C4NetIO::GetLocalAddresses();
194  bool haveIPv6 = false;
195  for (auto& addr : localAddrs)
196  {
197  if (addr.GetFamily() == C4NetIO::HostAddress::IPv6 && !addr.IsLocal() && !addr.IsPrivate())
198  {
199  haveIPv6 = true;
200  break;
201  }
202  }
203 
204  auto rank = [&](const C4Network2Address& Addr)
205  {
206  // Rank addresses. For IPv6-enabled clients, try public IPv6 addresses first, then IPv4,
207  // then link-local IPv6. For IPv4-only clients, skip IPv6.
208  int rank = 0;
209  auto addr = Addr.getAddr();
210  switch (addr.GetFamily())
211  {
213  if (addr.IsLocal())
214  rank = 100;
215  else if (addr.IsPrivate())
216  rank = 150;
217  else if (haveIPv6)
218  // TODO: Rank public IPv6 addresses by longest matching prefix with local addresses.
219  rank = 300;
220  break;
222  if (addr.IsPrivate())
223  rank = 150;
224  else
225  rank = 200;
226  break;
227  default:
228  assert(!"Unexpected address family");
229  }
230  return rank;
231  };
232 
233  // Sort by decreasing rank. Use stable sort to allow the host to prioritize addresses within a family.
234  std::stable_sort(addrs.begin(), addrs.end(), [&](auto a, auto b) { return rank(a) > rank(b); });
235 }
236 
238 {
239  if (isEnabled()) Clear();
240  // Get host core
241  const C4ClientCore &HostCore = Ref.Parameters.Clients.getHost()->getCore();
242  // host core revision check
243  if (!SEqualNoCase(HostCore.getRevision(), Application.GetRevision()))
244  {
245  StdStrBuf msg;
246  msg.Format(LoadResStr("IDS_NET_ERR_VERSIONMISMATCH"), HostCore.getRevision(), Application.GetRevision());
247  if (!Application.isEditor)
248  {
249  if (!pGUI->ShowMessageModal(msg.getData(), "[!]Network warning", C4GUI::MessageDialog::btnOKAbort, C4GUI::Ico_Notify, nullptr /* do not allow to skip this message! */))
250  return IR_Fatal;
251  }
252  else
253  {
254  Log(msg.getData());
255  }
256  }
257  // repeat if wrong password
261  StdStrBuf Password;
262 
263  // copy addresses
264  std::vector<C4Network2Address> Addrs;
265  for (int i = 0; i < Ref.getAddrCnt(); i++)
266  {
267  C4Network2Address a = Ref.getAddr(i);
269  Addrs.push_back(std::move(a));
270  }
271  SortAddresses(Addrs);
272  for (;;)
273  {
274  // ask for password (again)?
275  if (fWrongPassword)
276  {
277  Password.Take(QueryClientPassword());
278  if (!Password.getLength())
279  return IR_Error;
280  fWrongPassword = false;
281  }
282  // Try to connect to host
283  if (InitClient(Addrs, HostCore, Password.getData()) == IR_Fatal)
284  return IR_Fatal;
285  // success?
286  if (isEnabled())
287  break;
288  // Retry only for wrong password
289  if (!fWrongPassword)
290  {
291  LogSilent("Network: Could not connect!");
292  return IR_Error;
293  }
294  }
295  // initialize resources
297  return IR_Fatal;
298  // init league
299  if (!InitLeague(nullptr))
300  {
301  // deinit league
302  DeinitLeague();
303  return IR_Fatal;
304  }
305  // allow connect
306  NetIO.SetAcceptMode(true);
307  // timer
308  Application.Add(this);
309  // ok, success
310  return IR_Success;
311 }
312 
313 C4Network2::InitialConnect::InitialConnect(const std::vector<C4Network2Address>& Addrs, const C4ClientCore& HostCore, const char *Password)
314  : CStdTimerProc(DELAY), Addrs(Addrs), CurrentAddr(this->Addrs.cbegin()),
315  HostCore(HostCore), Password(Password)
316 {
317  Application.Add(this);
318 }
319 
321 {
322  Done();
323 }
324 
326 {
327  if (CheckAndReset())
328  TryNext();
329  return true;
330 }
331 
332 void C4Network2::InitialConnect::TryNext()
333 {
334  StdStrBuf strAddresses; int Successes = 0;
335  for (; Successes < ADDR_PER_TRY && CurrentAddr != Addrs.cend(); ++CurrentAddr)
336  {
337  if (!CurrentAddr->isIPNull())
338  {
339  auto addr = CurrentAddr->getAddr();
340  std::vector<C4NetIO::addr_t> addrs;
341  if (addr.IsLocal())
342  {
343  // Local IPv6 addresses need a scope id.
344  for (auto& id : Network.Clients.GetLocal()->getInterfaceIDs())
345  {
346  addr.SetScopeId(id);
347  addrs.push_back(addr);
348  }
349  }
350  else
351  addrs.push_back(addr);
352  // connection
353  int cnt = 0;
354  for (auto& a : addrs)
355  if (Network.NetIO.Connect(a, CurrentAddr->getProtocol(), HostCore, Password))
356  cnt++;
357  if (cnt == 0) continue;
358  // format for message
359  if (strAddresses.getLength())
360  strAddresses.Append(", ");
361  strAddresses.Append(CurrentAddr->toString());
362  Successes++;
363  }
364  }
365  if (Successes > 0)
366  {
367  LogF(LoadResStr("IDS_NET_CONNECTHOST"), strAddresses.getData());
368  }
369  else
370  {
371  Done();
372  }
373 }
374 
375 void C4Network2::InitialConnect::Done()
376 {
377  Application.Remove(this);
378 }
379 
380 C4Network2::InitResult C4Network2::InitClient(const std::vector<class C4Network2Address>& Addrs, const C4ClientCore &HostCore, const char *szPassword)
381 {
382  // initialization
383  Status.Set(GS_Init, -1);
384  fHost = false;
385  fStatusAck = fStatusReached = true;
386  fChasing = true;
387  fAllowJoin = false;
388  // initialize client list
390  Clients.Init(&Game.Clients, false);
391  // initialize resource list
393  { LogFatal(LoadResStr("IDS_NET_ERR_INITRESLIST")); Clear(); return IR_Fatal; }
394  // initialize net i/o
395  if (!InitNetIO(true, false))
396  { Clear(); return IR_Fatal; }
397  // set network control
399  // set exclusive connection mode
401  // warm up netpuncher
402  InitPuncher();
403  // try to connect host
404  InitialConnect iconn(Addrs, HostCore, szPassword);
405  // show box
406  std::unique_ptr<C4GUI::MessageDialog> pDlg = nullptr;
407  if (!Application.isEditor)
408  {
409  StdStrBuf strMessage = FormatString(LoadResStr("IDS_NET_JOINGAMEBY"), HostCore.getName());
410  // create & show
411  pDlg = std::make_unique<C4GUI::MessageDialog>(
412  strMessage.getData(), LoadResStr("IDS_NET_JOINGAME"),
414  if (!pDlg->Show(::pGUI, true)) { Clear(); return IR_Fatal; }
415  }
416  // wait for connect / timeout / abort by user (host will change status on succesful connect)
417  while (Status.getState() == GS_Init)
418  {
419  if (!Application.ScheduleProcs(100))
420  { return IR_Fatal;}
421  if (pDlg && pDlg->IsAborted())
422  { return IR_Fatal; }
423  }
424  // error?
425  if (!isEnabled())
426  return IR_Error;
427  // deactivate exclusive connection mode
429  return IR_Success;
430 }
431 
433 {
434  // shouldn't do lobby?
435  if (!isEnabled() || (!isHost() && !isLobbyActive()))
436  return true;
437 
438  // lobby runs
439  fLobbyRunning = true;
440  fAllowJoin = true;
441  Log(LoadResStr("IDS_NET_LOBBYWAITING"));
442 
443  // client: lobby status reached, message to host
444  if (!isHost())
446  // host: set lobby mode
447  else
449 
450  // determine lobby type
451  if (Console.Active)
452  {
453  // console lobby - update console
455  // init lobby countdown if specified
457  // do console lobby
458  while (isLobbyActive())
459  if (!Application.ScheduleProcs())
460  { Clear(); return false; }
461  }
462  else
463  {
464  // fullscreen lobby
465 
466  // init lobby dialog
468  if (!pLobby->FadeIn(::pGUI)) { delete pLobby; pLobby = nullptr; Clear(); return false; }
469 
470  // init lobby countdown if specified
472 
473  // while state lobby: keep looping
474  while (isLobbyActive() && pLobby && pLobby->IsShown())
475  if (!Application.ScheduleProcs())
476  { Clear(); return false; }
477 
478  // check whether lobby was aborted
479  if (pLobby && pLobby->IsAborted()) { delete pLobby; pLobby = nullptr; Clear(); return false; }
480 
481  // deinit lobby
482  if (pLobby && pLobby->IsShown()) pLobby->Close(true);
483  delete pLobby; pLobby = nullptr;
484 
485  // close any other dialogs
486  ::pGUI->CloseAllDialogs(false);
487  }
488 
489  // lobby end
490  delete pLobbyCountdown; pLobbyCountdown = nullptr;
491  fLobbyRunning = false;
493 
494  // notify user that the lobby has ended (for people who tasked out)
496 
497  // notify lobby end
498  bool fGameGo = isEnabled();
499  if (fGameGo) Log(LoadResStr("IDS_PRC_GAMEGO"));;
500 
501  // disabled?
502  return fGameGo;
503 }
504 
506 {
507  if (!isEnabled() || !isHost()) return false;
508  // change mode: go
510  return true;
511 }
512 
514 {
515  if (!isEnabled() || !isHost()) return false;
516  // change mode: pause
518 }
519 
521 {
522  // host only
523  if (!isEnabled() || !isHost()) return false;
524  // already syncing the network?
525  if (!fStatusAck)
526  {
527  // maybe we are already sync?
529  return true;
530  }
531  // already sync?
532  if (isFrozen()) return true;
533  // ok, so let's do a sync: change in the same state we are already in
535 }
536 
538 {
539  // check reach
540  CheckStatusReached(true);
541  // reached, waiting for ack?
542  if (fStatusReached && !fStatusAck)
543  {
544  // wait for go acknowledgement
545  Log(LoadResStr("IDS_NET_JOINREADY"));
546 
547  // any pending keyboard commands should not be routed to cancel the wait dialog - flush the message queue!
548  if (!Application.FlushMessages()) return false;
549 
550  // show box
551  C4GUI::Dialog *pDlg = nullptr;
552  if (!Application.isEditor)
553  {
554  // separate dlgs for host/client
555  if (isHost())
556  pDlg = new C4Network2StartWaitDlg();
557  else
558  pDlg = new C4GUI::MessageDialog(LoadResStr("IDS_NET_WAITFORSTART"), LoadResStr("IDS_NET_CAPTION"),
560  // show it
561  if (!pDlg->Show(::pGUI, true)) return false;
562  }
563 
564  // wait for acknowledgement
565  while (fStatusReached && !fStatusAck)
566  {
567  if (pDlg)
568  {
569  // execute
570  if (!pDlg->Execute()) { delete pDlg; Clear(); return false; }
571  // aborted?
572  if (pDlg->IsAborted()) { delete pDlg; Clear(); return false; }
573  }
574  else if (!Application.ScheduleProcs())
575  { Clear(); return false; }
576  }
577  delete pDlg;
578  // log
579  Log(LoadResStr("IDS_NET_START"));
580  }
581  // synchronize
583  Game.Synchronize(false);
584  // finished
585  return isEnabled();
586 }
587 
588 
589 bool C4Network2::RetrieveScenario(char *szScenario)
590 {
591  // client only
592  if (isHost()) return false;
593 
594  // wait for scenario
596  C4NetResRetrieveTimeout, LoadResStr("IDS_NET_RES_SCENARIO"));
597  if (!pScenario)
598  return false;
599 
600  // wait for dynamic data
601  C4Network2Res::Ref pDynamic = RetrieveRes(ResDynamic, C4NetResRetrieveTimeout, LoadResStr("IDS_NET_RES_DYNAMIC"));
602  if (!pDynamic)
603  return false;
604 
605  // create unpacked copy of scenario
606  if (!ResList.FindTempResFileName(FormatString("Combined%d.ocs", Game.Clients.getLocalID()).getData(), szScenario) ||
607  !C4Group_CopyItem(pScenario->getFile(), szScenario) ||
608  !C4Group_UnpackDirectory(szScenario))
609  return false;
610 
611  // create unpacked copy of dynamic data
612  char szTempDynamic[_MAX_PATH + 1];
613  if (!ResList.FindTempResFileName(pDynamic->getFile(), szTempDynamic) ||
614  !C4Group_CopyItem(pDynamic->getFile(), szTempDynamic) ||
615  !C4Group_UnpackDirectory(szTempDynamic))
616  return false;
617 
618  // unpack Material.ocg if materials need to be merged
619  StdStrBuf MaterialScenario, MaterialDynamic;
620  MaterialScenario.Format("%s" DirSep C4CFN_Material, szScenario);
621  MaterialDynamic.Format("%s" DirSep C4CFN_Material, szTempDynamic);
622  if (FileExists(MaterialScenario.getData()) && FileExists(MaterialDynamic.getData()))
623  if (!C4Group_UnpackDirectory(MaterialScenario.getData()) ||
624  !C4Group_UnpackDirectory(MaterialDynamic.getData()))
625  return false;
626 
627  // move all dynamic files to scenario
628  C4Group ScenGrp;
629  if (!ScenGrp.Open(szScenario) ||
630  !ScenGrp.Merge(szTempDynamic))
631  return false;
632  ScenGrp.Close();
633 
634  // remove dynamic temp file
635  EraseDirectory(szTempDynamic);
636 
637  // remove dynamic - isn't needed any more and will soon be out-of-date
638  pDynamic->Remove();
639 
640  return true;
641 }
642 
644 {
645  Execute();
646 }
647 
649 {
650 
651  // client connections
653 
654  // status reached?
656 
657  if (isHost())
658  {
659  // remove dynamic
661  RemoveDynamic();
662  // Set chase target
664  // check for inactive clients and deactivate them
666  // reference
667  if (!iLastReferenceUpdate || time(nullptr) > (time_t) (iLastReferenceUpdate + C4NetReferenceUpdateInterval))
668  if (NetIO.IsReferenceNeeded())
669  {
670  // create
672  pRef->InitLocal();
673  // set
674  NetIO.SetReference(pRef);
675  iLastReferenceUpdate = time(nullptr);
676  }
677  // league server reference
678  if (!iLastLeagueUpdate || time(nullptr) > (time_t) (iLastLeagueUpdate + iLeagueUpdateDelay))
679  {
680  LeagueUpdate();
681  }
682  // league update reply receive
684  {
686  }
687  // voting timeout
688  if (Votes.firstPkt() && time(nullptr) > (time_t) (iVoteStartTime + C4NetVotingTimeout))
689  {
690  C4ControlVote *pVote = static_cast<C4ControlVote *>(Votes.firstPkt()->getPkt());
692  CID_VoteEnd,
693  new C4ControlVoteEnd(pVote->getType(), false, pVote->getData()),
694  CDT_Sync);
695  iVoteStartTime = time(nullptr);
696  }
697  // record streaming
698  if (fStreaming)
699  {
700  StreamIn(false);
701  StreamOut();
702  }
703  }
704  else
705  {
706  // request activate, if neccessary
708  }
709 }
710 
712 {
713  // stop timer
714  Application.Remove(this);
715  // stop streaming
716  StopStreaming();
717  // clear league
718  if (pLeagueClient)
719  {
720  LeagueEnd();
721  DeinitLeague();
722  }
723  // stop lobby countdown
724  delete pLobbyCountdown; pLobbyCountdown = nullptr;
725  // cancel lobby
726  delete pLobby; pLobby = nullptr;
727  fLobbyRunning = false;
728  // deactivate
729  Status.Clear();
730  fStatusAck = fStatusReached = true;
731  // if control mode is network: change to local
732  if (::Control.isNetwork())
734  // clear all player infos
735  Players.Clear();
736  // remove all clients
737  Clients.Clear();
738  // close net classes
739  NetIO.Clear();
740  // clear resources
741  ResList.Clear();
742  // clear password
743  sPassword.Clear();
744  // stuff
745  fAllowJoin = false;
746  iDynamicTick = -1; fDynamicNeeded = false;
749  fDelayedActivateReq = false;
750  delete pVoteDialog; pVoteDialog = nullptr;
751  fPausedForVote = false;
752  iLastOwnVoting = 0;
754  Votes.Clear();
755  // don't clear fPasswordNeeded here, it's needed by InitClient
756 }
757 
759 {
760  // just toggle
762  return true; // toggled
763 }
764 
766 {
768  return true;
769 }
770 
771 void C4Network2::SetPassword(const char *szToPassword)
772 {
773  bool fHadPassword = isPassworded();
774  // clear password?
775  if (!szToPassword || !*szToPassword)
776  sPassword.Clear();
777  else
778  // no? then set it
779  sPassword.Copy(szToPassword);
780  // if the has-password-state has changed, the reference is invalidated
781  if (fHadPassword != isPassworded()) InvalidateReference();
782 }
783 
785 {
786  // ask client for a password; return nothing if user canceled
787  StdStrBuf sCaption; sCaption.Copy(LoadResStr("IDS_MSG_ENTERPASSWORD"));
788  C4GUI::InputDialog *pInputDlg = new C4GUI::InputDialog(LoadResStr("IDS_MSG_ENTERPASSWORD"), sCaption.getData(), C4GUI::Ico_Ex_Locked, nullptr, false);
789  pInputDlg->SetDelOnClose(false);
790  if (!::pGUI->ShowModalDlg(pInputDlg, false))
791  {
792  delete pInputDlg;
793  return StdStrBuf();
794  }
795  // copy to buffer
796  StdStrBuf Buf; Buf.Copy(pInputDlg->GetInputText());
797  delete pInputDlg;
798  return Buf;
799 }
800 
801 void C4Network2::AllowJoin(bool fAllow)
802 {
803  if (!isHost()) return;
804  fAllowJoin = fAllow;
805  if (Game.IsRunning)
806  {
807  ::GraphicsSystem.FlashMessage(LoadResStr(fAllowJoin ? "IDS_NET_RUNTIMEJOINFREE" : "IDS_NET_RUNTIMEJOINBARRED"));
809  }
810 }
811 
813 {
814  if (!isHost()) return;
815  fAllowObserve = fAllow;
816 }
817 
818 void C4Network2::SetCtrlMode(int32_t iCtrlMode)
819 {
820  if (!isHost()) return;
821  // no change?
822  if (iCtrlMode == Status.getCtrlMode()) return;
823  // change game status
825 }
826 
828 {
829  // Nothing to do atm... New pending connections are managed mainly by C4Network2IO
830  // until they are accepted, see PID_Conn/PID_ConnRe handlers in HandlePacket.
831 
832  // Note this won't get called anymore because of this (see C4Network2IO::OnConn)
833 }
834 
836 {
837  // could not establish host connection?
838  if (Status.getState() == GS_Init && !isHost())
839  {
840  if (!NetIO.getConnectionCount())
841  Clear();
842  return;
843  }
844 
845  // connection failed?
846  if (pConn->isFailed())
847  {
848  // call handler
849  OnConnectFail(pConn);
850  return;
851  }
852 
853  // search client
854  C4Network2Client *pClient = Clients.GetClient(pConn);
855  // not found? Search by ID (not associated yet, half-accepted connection)
856  if (!pClient) pClient = Clients.GetClientByID(pConn->getClientID());
857  // not found? ignore
858  if (!pClient) return;
859  // remove connection
860  pClient->RemoveConn(pConn);
861 
862  // create post-mortem if needed
863  C4PacketPostMortem PostMortem;
864  if (pConn->CreatePostMortem(&PostMortem))
865  {
866  LogSilentF("Network: Sending %d packets for recovery (%d-%d)", PostMortem.getPacketCount(), pConn->getOutPacketCounter() - PostMortem.getPacketCount(), pConn->getOutPacketCounter() - 1);
867  // This might fail because of this disconnect
868  // (If it's the only host connection. We're toast then anyway.)
870  assert(isHost() || !Clients.GetHost()->isConnected());
871  }
872 
873  // call handler
874  OnDisconnect(pClient, pConn);
875 
876 }
877 
878 void C4Network2::HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
879 {
880  // find associated client
881  C4Network2Client *pClient = Clients.GetClient(pConn);
882  if (!pClient) pClient = Clients.GetClientByID(pConn->getClientID());
883 
884  // local? ignore
885  if (pClient && pClient->isLocal()) { pConn->Close(); return; }
886 
887 #define GETPKT(type, name) \
888  assert(pPacket); const type &name = \
889  static_cast<const type &>(*pPacket);
890 
891  switch (cStatus)
892  {
893  case PID_Conn: // connection request
894  {
895  if (!pConn->isOpen()) break;
896  GETPKT(C4PacketConn, rPkt);
897  HandleConn(rPkt, pConn, pClient);
898  }
899  break;
900 
901  case PID_ConnRe: // connection request reply
902  {
903  GETPKT(C4PacketConnRe, rPkt);
904  HandleConnRe(rPkt, pConn, pClient);
905  }
906  break;
907 
908  case PID_JoinData:
909  {
910  // host->client only
911  if (isHost() || !pClient || !pClient->isHost()) break;
912  if (!pConn->isOpen()) break;
913  // handle
914  GETPKT(C4PacketJoinData, rPkt)
915  HandleJoinData(rPkt);
916  }
917  break;
918 
919  case PID_Status: // status change
920  {
921  // by host only
922  if (isHost() || !pClient || !pClient->isHost()) break;
923  if (!pConn->isOpen()) break;
924  // must be initialized
925  if (Status.getState() == GS_Init) break;
926  // handle
927  GETPKT(C4Network2Status, rPkt);
928  HandleStatus(rPkt);
929  }
930  break;
931 
932  case PID_StatusAck: // status change acknowledgement
933  {
934  // host->client / client->host only
935  if (!pClient) break;
936  if (!isHost() && !pClient->isHost()) break;
937  // must be initialized
938  if (Status.getState() == GS_Init) break;
939  // handle
940  GETPKT(C4Network2Status, rPkt);
941  HandleStatusAck(rPkt, pClient);
942  }
943  break;
944 
945  case PID_ClientActReq: // client activation request
946  {
947  // client->host only
948  if (!isHost() || !pClient || pClient->isHost()) break;
949  // must be initialized
950  if (Status.getState() == GS_Init) break;
951  // handle
953  HandleActivateReq(rPkt.getTick(), pClient);
954  }
955  break;
956 
957  }
958 
959 #undef GETPKT
960 }
961 
962 void C4Network2::HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
963 {
964  // find associated client
965  C4Network2Client *pClient = Clients.GetClient(pConn);
966  if (!pClient) pClient = Clients.GetClientByID(pConn->getClientID());
967  // forward directly to lobby
968  if (pLobby) pLobby->HandlePacket(cStatus, pBasePkt, pClient);
969 }
970 
972 {
973  // TODO: is this all thread-safe?
974  assert(pkt);
975 #define GETPKT(c) dynamic_cast<C4NetpuncherPacket##c*>(pkt.get())
976  switch (pkt->GetType())
977  {
978  case PID_Puncher_CReq:
979  if (isHost())
980  {
981  NetIO.Punch(GETPKT(CReq)->GetAddr());
982  return true;
983  }
984  else
985  {
986  // The IP/Port should be already in the masterserver list, so just keep trying.
987  return Status.getState() == GS_Init;
988  }
989  case PID_Puncher_AssID:
990  if (isHost())
991  {
992  getNetpuncherGameID(family) = GETPKT(AssID)->GetID();
994  }
995  else
996  {
997  // The netpuncher hands out IDs for everyone, but clients have no use for them.
998  }
999  return true;
1000  default: return false;
1001  }
1002 }
1003 
1005 {
1006  switch (family)
1007  {
1010  case C4NetIO::HostAddress::UnknownFamily: assert(!"Unexpected address family");
1011  }
1012  // We need to return a valid reference to satisfy the compiler, even though the code here is unreachable.
1013  return NetpuncherGameID.v4;
1014 }
1015 
1017 {
1018  // NAT punching is only relevant for IPv4, so convert here to show a proper address.
1019  auto maybe_v4 = addr.AsIPv4();
1020  Application.InteractiveThread.ThreadLogS("Adding address from puncher: %s", maybe_v4.ToString().getData());
1021  // Add for local client
1022  C4Network2Client *pLocal = Clients.GetLocal();
1023  if (pLocal)
1024  {
1025  pLocal->AddAddr(C4Network2Address(maybe_v4, P_UDP), true);
1026  // If the outside port matches the inside port, there is no port translation and the
1027  // TCP address will probably work as well.
1028  if (addr.GetPort() == Config.Network.PortUDP && Config.Network.PortTCP > 0)
1029  {
1030  maybe_v4.SetPort(Config.Network.PortTCP);
1031  pLocal->AddAddr(C4Network2Address(maybe_v4, P_TCP), true);
1032  }
1033  // Do not ::Network.InvalidateReference(); yet, we're expecting an ID from the netpuncher
1034  }
1035  // Client connection: request packet from host.
1036  if (!isHost())
1037  {
1038  auto family = maybe_v4.GetFamily();
1039  if (Status.getState() == GS_Init && getNetpuncherGameID(family))
1040  NetIO.SendPuncherPacket(C4NetpuncherPacketSReq(getNetpuncherGameID(family)), family);
1041  }
1042 }
1043 
1044 
1046 {
1047  // We have an internet connection, so let's punch the puncher server here in order to open an udp port
1048  C4NetIO::addr_t PuncherAddr;
1050  if (!PuncherAddr.IsNull())
1051  {
1052  PuncherAddr.SetDefaultPort(C4NetStdPortPuncher);
1053  NetIO.InitPuncher(PuncherAddr);
1054  }
1056  if (!PuncherAddr.IsNull())
1057  {
1058  PuncherAddr.SetDefaultPort(C4NetStdPortPuncher);
1059  NetIO.InitPuncher(PuncherAddr);
1060  }
1061 }
1062 
1064 {
1065  // savegame needed?
1066  if (fDynamicNeeded)
1067  {
1068  // create dynamic
1069  bool fSuccess = CreateDynamic(false);
1070  // check for clients that still need join-data
1071  C4Network2Client *pClient = nullptr;
1072  while ((pClient = Clients.GetNextClient(pClient)))
1073  if (!pClient->hasJoinData())
1074  {
1075  if (fSuccess)
1076  // now we can provide join data: send it
1077  SendJoinData(pClient);
1078  else
1079  // join data could not be created: emergency kick
1080  Game.Clients.CtrlRemove(pClient->getClient(), LoadResStr("IDS_ERR_ERRORWHILECREATINGJOINDAT"));
1081  }
1082  }
1083 }
1084 
1086 {
1087  if (!isEnabled()) return;
1088 
1089  C4Network2Client *pLocal = Clients.GetLocal();
1090 
1091  StdStrBuf Stat;
1092 
1093  // local client status
1094  Stat.AppendFormat("Local: %s %s %s (ID %d)",
1095  pLocal->isObserver() ? "Observing" : pLocal->isActivated() ? "Active" : "Inactive", pLocal->isHost() ? "host" : "client",
1096  pLocal->getName(), pLocal->getID());
1097 
1098  // game status
1099  Stat.AppendFormat( "|Game Status: %s (tick %d)%s%s",
1101  fStatusReached ? " reached" : "", fStatusAck ? " ack" : "");
1102 
1103  // available protocols
1104  C4NetIO *pMsgIO = NetIO.MsgIO(), *pDataIO = NetIO.DataIO();
1105  if (pMsgIO && pDataIO)
1106  {
1107  C4Network2IOProtocol eMsgProt = NetIO.getNetIOProt(pMsgIO),
1108  eDataProt = NetIO.getNetIOProt(pDataIO);
1109  int32_t iMsgPort = 0, iDataPort = 0;
1110  switch (eMsgProt)
1111  {
1112  case P_TCP: iMsgPort = Config.Network.PortTCP; break;
1113  case P_UDP: iMsgPort = Config.Network.PortUDP; break;
1114  case P_NONE: assert(eMsgProt != P_NONE); break;
1115  }
1116  switch (eDataProt)
1117  {
1118  case P_TCP: iDataPort = Config.Network.PortTCP; break;
1119  case P_UDP: iDataPort = Config.Network.PortUDP; break;
1120  case P_NONE: assert(eMsgProt != P_NONE); break;
1121  }
1122  Stat.AppendFormat( "|Protocols: %s: %s (%d i%d o%d bc%d)",
1123  pMsgIO != pDataIO ? "Msg" : "Msg/Data",
1124  NetIO.getNetIOName(pMsgIO), iMsgPort,
1125  NetIO.getProtIRate(eMsgProt), NetIO.getProtORate(eMsgProt), NetIO.getProtBCRate(eMsgProt));
1126  if (pMsgIO != pDataIO)
1127  Stat.AppendFormat( ", Data: %s (%d i%d o%d bc%d)",
1128  NetIO.getNetIOName(pDataIO), iDataPort,
1129  NetIO.getProtIRate(eDataProt), NetIO.getProtORate(eDataProt), NetIO.getProtBCRate(eDataProt));
1130  }
1131  else
1132  Stat.Append("|Protocols: none");
1133 
1134  // some control statistics
1135  Stat.AppendFormat( "|Control: %s, Tick %d, Behind %d, Rate %d, PreSend %d, ACT: %d",
1136  Status.getCtrlMode() == CNM_Decentral ? "Decentral" : Status.getCtrlMode() == CNM_Central ? "Central" : "Async",
1139 
1140  // Streaming statistics
1141  if (fStreaming)
1142  Stat.AppendFormat( "|Streaming: %lu waiting, %u in, %lu out, %lu sent",
1143  static_cast<unsigned long>(pStreamedRecord ? pStreamedRecord->GetStreamingBuf().getSize() : 0),
1145  static_cast<unsigned long>(getPendingStreamData()),
1146  static_cast<unsigned long>(iCurrentStreamPosition));
1147 
1148  // clients
1149  Stat.Append("|Clients:");
1150  for (C4Network2Client *pClient = Clients.GetNextClient(nullptr); pClient; pClient = Clients.GetNextClient(pClient))
1151  {
1152  // ignore local
1153  if (pClient->isLocal()) continue;
1154  // client status
1155  const C4ClientCore &Core = pClient->getCore();
1156  const char *szClientStatus;
1157  switch (pClient->getStatus())
1158  {
1159  case NCS_Joining: szClientStatus = " (joining)"; break;
1160  case NCS_Chasing: szClientStatus = " (chasing)"; break;
1161  case NCS_NotReady: szClientStatus = " (!rdy)"; break;
1162  case NCS_Remove: szClientStatus = " (removed)"; break;
1163  default: szClientStatus = ""; break;
1164  }
1165  Stat.AppendFormat( "|- %s %s %s (ID %d) (wait %d ms, behind %d)%s%s",
1166  Core.isObserver() ? "Observing" : Core.isActivated() ? "Active" : "Inactive", Core.isHost() ? "host" : "client",
1167  Core.getName(), Core.getID(),
1168  pControl->ClientPerfStat(pClient->getID()),
1169  ::Control.ControlTick - pControl->ClientNextControl(pClient->getID()),
1170  szClientStatus,
1171  pClient->isActivated() && !pControl->ClientReady(pClient->getID(), ::Control.ControlTick) ? " (!ctrl)" : "");
1172  // connections
1173  if (pClient->isConnected())
1174  {
1175  Stat.AppendFormat( "| Connections: %s: %s (%s p%d l%d)",
1176  pClient->getMsgConn() == pClient->getDataConn() ? "Msg/Data" : "Msg",
1177  NetIO.getNetIOName(pClient->getMsgConn()->getNetClass()),
1178  pClient->getMsgConn()->getPeerAddr().ToString().getData(),
1179  pClient->getMsgConn()->getPingTime(),
1180  pClient->getMsgConn()->getPacketLoss());
1181  if (pClient->getMsgConn() != pClient->getDataConn())
1182  Stat.AppendFormat( ", Data: %s (%s:%d p%d l%d)",
1183  NetIO.getNetIOName(pClient->getDataConn()->getNetClass()),
1184  pClient->getDataConn()->getPeerAddr().ToString().getData(),
1185  pClient->getDataConn()->getPingTime(),
1186  pClient->getDataConn()->getPacketLoss());
1187  }
1188  else
1189  Stat.Append("| Not connected");
1190  }
1191  if (!Clients.GetNextClient(nullptr))
1192  Stat.Append("| - none -");
1193 
1194  // draw
1195  pDraw->TextOut(Stat.getData(), ::GraphicsResource.FontRegular, 1.0, cgo.Surface,cgo.X + 20,cgo.Y + 50);
1196 }
1197 
1198 bool C4Network2::InitNetIO(bool fNoClientID, bool fHost)
1199 {
1200  // clear
1201  NetIO.Clear();
1203  // discovery: disable for client
1204  int16_t iPortDiscovery = fHost ? Config.Network.PortDiscovery : -1;
1205  int16_t iPortRefServer = fHost ? Config.Network.PortRefServer : -1;
1206  // init subclass
1207  if (!NetIO.Init(Config.Network.PortTCP, Config.Network.PortUDP, iPortDiscovery, iPortRefServer, fHost, !!Config.Network.EnableUPnP))
1208  return false;
1209  // set core (unset ID if sepecified, has to be set later)
1211  if (fNoClientID) Core.SetID(C4ClientIDUnknown);
1212  NetIO.SetLocalCCore(Core);
1213  // safe addresses of local client
1215  NetIO.hasTCP() ? Config.Network.PortTCP : -1,
1216  NetIO.hasUDP() ? Config.Network.PortUDP : -1);
1217  // ok
1218  return true;
1219 }
1220 
1222 {
1223  // security
1224  if (!pConn) return;
1225 
1226  // Handles a connect request (packet PID_Conn).
1227  // Check if this peer should be allowed to connect, make space for the new connection.
1228 
1229  // connection is closed?
1230  if (pConn->isClosed())
1231  return;
1232 
1233  // set up core
1234  const C4ClientCore &CCore = Pkt.getCCore();
1235  C4ClientCore NewCCore = CCore;
1236 
1237  // accept connection?
1238  StdStrBuf reply;
1239  bool fOK = false;
1240 
1241  // search client
1242  if (!pClient && Pkt.getCCore().getID() != C4ClientIDUnknown)
1243  pClient = Clients.GetClient(Pkt.getCCore());
1244 
1245  // check engine version
1246  bool fWrongPassword = false;
1247  if (Pkt.getVer() != C4XVER1*100 + C4XVER2)
1248  {
1249  reply.Format("wrong engine (%d.%d, I have %d.%d)", Pkt.getVer()/100, Pkt.getVer()%100, C4XVER1, C4XVER2);
1250  fOK = false;
1251  }
1252  else
1253  {
1254  if (pClient)
1255  if (CheckConn(NewCCore, pConn, pClient, &reply))
1256  {
1257  // accept
1258  if (!reply) reply = "connection accepted";
1259  fOK = true;
1260  }
1261  // client: host connection?
1262  if (!fOK && !isHost() && Status.getState() == GS_Init && !Clients.GetHost())
1263  if (HostConnect(NewCCore, pConn, &reply))
1264  {
1265  // accept
1266  if (!reply) reply = "host connection accepted";
1267  fOK = true;
1268  }
1269  // host: client join? (NewCCore will be changed by Join()!)
1270  if (!fOK && isHost() && !pClient)
1271  {
1272  // check password
1273  if (!sPassword.isNull() && !SEqual(Pkt.getPassword(), sPassword.getData()))
1274  {
1275  reply = "wrong password";
1276  fWrongPassword = true;
1277  }
1278  // accept join
1279  else if (Join(NewCCore, pConn, &reply))
1280  {
1281  // save core
1282  pConn->SetCCore(NewCCore);
1283  // accept
1284  if (!reply) reply = "join accepted";
1285  fOK = true;
1286  }
1287  }
1288  }
1289 
1290  // denied? set default reason
1291  if (!fOK && !reply) reply = "connection denied";
1292 
1293  // OK and already half accepted? Skip (double-checked: ok).
1294  if (fOK && pConn->isHalfAccepted())
1295  return;
1296 
1297  // send answer
1298  C4PacketConnRe pcr(fOK, fWrongPassword, reply.getData());
1299  if (!pConn->Send(MkC4NetIOPacket(PID_ConnRe, pcr)))
1300  return;
1301 
1302  // accepted?
1303  if (fOK)
1304  {
1305  // set status
1306  if (!pConn->isClosed())
1307  pConn->SetHalfAccepted();
1308  }
1309  // denied? close
1310  else
1311  {
1312  // log & close
1313  LogSilentF("Network: connection by %s (%s) blocked: %s", CCore.getName(), pConn->getPeerAddr().ToString().getData(), reply.getData());
1314  pConn->Close();
1315  }
1316 }
1317 
1319 {
1320  if (!pConn || !pClient) return false;
1321  // already connected? (shouldn't happen really)
1322  if (pClient->hasConn(pConn))
1323  { *szReply = "already connected"; return true; }
1324  // check core
1325  if (CCore.getDiffLevel(pClient->getCore()) > C4ClientCoreDL_IDMatch)
1326  { *szReply = "wrong client core"; return false; }
1327  // accept
1328  return true;
1329 }
1330 
1332 {
1333  if (!pConn) return false;
1334  if (!CCore.isHost()) { *szReply = "not host"; return false; }
1335  // create client class for host
1336  // (core is unofficial, see InitClient() - will be overwritten later in HandleJoinData)
1337  C4Client *pClient = Game.Clients.Add(CCore);
1338  if (!pClient) return false;
1339  // accept
1340  return true;
1341 }
1342 
1344 {
1345  if (!pConn) return false;
1346  // security
1347  if (!isHost()) { *szReply = "not host"; return false; }
1348  if (!fAllowJoin && !fAllowObserve) { *szReply = "join denied"; return false; }
1349  if (CCore.getID() != C4ClientIDUnknown) { *szReply = "join with set id not allowed"; return false; }
1350  // find free client id
1351  CCore.SetID(iNextClientID++);
1352  // observer?
1353  if (!fAllowJoin) CCore.SetObserver(true);
1354  // deactivate - client will have to ask for activation.
1355  CCore.SetActivated(false);
1356  // Name already in use? Find unused one
1357  if (Clients.GetClient(CCore.getName()))
1358  {
1359  char szNameTmpl[256+1], szNewName[256+1];
1360  SCopy(CCore.getName(), szNameTmpl, 254); SAppend("%d", szNameTmpl, 256);
1361  int32_t i = 1;
1362  do
1363  sprintf(szNewName, szNameTmpl, ++i);
1364  while (Clients.GetClient(szNewName));
1365  CCore.SetName(szNewName);
1366  }
1367  // join client
1369  // get client, set status
1370  C4Network2Client *pClient = Clients.GetClient(CCore);
1371  if (pClient) pClient->SetStatus(NCS_Joining);
1372  // warn if client revision doesn't match our host revision
1374  {
1375  LogF("[!]WARNING! Client %s engine revision (%s) differs from local revision (%s). Client might run out of sync.", CCore.getName(), CCore.getRevision(), Application.GetRevision());
1376  }
1377  // ok, client joined.
1378  return true;
1379  // Note that the connection isn't fully accepted at this point and won't be
1380  // associated with the client. The new-created client is waiting for connect.
1381  // Somewhat ironically, the connection may still timeout (resulting in an instant
1382  // removal and maybe some funny message sequences).
1383  // The final client initialization will be done at OnClientConnect.
1384 }
1385 
1387 {
1388  // Handle the connection request reply. After this handling, the connection should
1389  // be either fully associated with a client (fully accepted) or closed.
1390  // Note that auto-accepted connection have to processed here once, too, as the
1391  // client must get associated with the connection. After doing so, the connection
1392  // auto-accept flag will be reset to mark the connection fully accepted.
1393 
1394  // security
1395  if (!pConn) return;
1396  if (!pClient) { pConn->Close(); return; }
1397 
1398  // negative reply?
1399  if (!Pkt.isOK())
1400  {
1401  // wrong password?
1403  // show message
1404  LogSilentF("Network: connection to %s (%s) refused: %s", pClient->getName(), pConn->getPeerAddr().ToString().getData(), Pkt.getMsg());
1405  // close connection
1406  pConn->Close();
1407  return;
1408  }
1409 
1410  // connection is closed?
1411  if (!pConn->isOpen())
1412  return;
1413 
1414  // already accepted? ignore
1415  if (pConn->isAccepted() && !pConn->isAutoAccepted()) return;
1416 
1417  // first connection?
1418  bool fFirstConnection = !pClient->isConnected();
1419 
1420  // accept connection
1421  pConn->SetAccepted(); pConn->ResetAutoAccepted();
1422 
1423  // add connection
1424  pConn->SetCCore(pClient->getCore());
1425  if (pConn->getNetClass() == NetIO.MsgIO()) pClient->SetMsgConn(pConn);
1426  if (pConn->getNetClass() == NetIO.DataIO()) pClient->SetDataConn(pConn);
1427 
1428  // add peer connect address to client address list
1429  if (!pConn->getConnectAddr().IsNull())
1430  {
1431  C4Network2Address Addr(pConn->getConnectAddr(), pConn->getProtocol());
1432  pClient->AddAddr(Addr, Status.getState() != GS_Init);
1433  }
1434 
1435  // handle
1436  OnConnect(pClient, pConn, Pkt.getMsg(), fFirstConnection);
1437 }
1438 
1440 {
1441  // set
1442  Status = nStatus;
1443  // log
1444  LogSilentF("Network: going into status %s (tick %d)", Status.getStateName(), nStatus.getTargetCtrlTick());
1445  // reset flags
1446  fStatusReached = fStatusAck = false;
1447  // check: reached?
1449 }
1450 
1452 {
1453  // security
1454  if (!pClient->hasJoinData() || pClient->isRemoved()) return;
1455  // status doesn't match?
1456  if (nStatus.getState() != Status.getState() || nStatus.getTargetCtrlTick() < Status.getTargetCtrlTick())
1457  return;
1458  // host: wait until all clients are ready
1459  if (isHost())
1460  {
1461  // check: target tick change?
1462  if (!fStatusAck && nStatus.getTargetCtrlTick() > Status.getTargetCtrlTick())
1463  // take the new status
1464  ChangeGameStatus(nStatus.getState(), nStatus.getTargetCtrlTick());
1465  // already acknowledged? Send another ack
1466  if (fStatusAck)
1467  pClient->SendMsg(MkC4NetIOPacket(PID_StatusAck, nStatus));
1468  // mark as ready (will clear chase-flag)
1469  pClient->SetStatus(NCS_Ready);
1470  // check: everyone ready?
1471  if (!fStatusAck && fStatusReached)
1472  CheckStatusAck();
1473  }
1474  else
1475  {
1476  // target tick doesn't match? ignore
1477  if (nStatus.getTargetCtrlTick() != Status.getTargetCtrlTick())
1478  return;
1479  // reached?
1480  // can be ignored safely otherwise - when the status is reached, we will send
1481  // status ack on which the host should generate another status ack (see above)
1482  if (fStatusReached)
1483  {
1484  // client: set flags, call handler
1485  fStatusAck = true; fChasing = false;
1486  OnStatusAck();
1487  }
1488 
1489  }
1490 }
1491 
1492 void C4Network2::HandleActivateReq(int32_t iTick, C4Network2Client *pByClient)
1493 {
1494  if (!isHost()) return;
1495  // not allowed or already activated? ignore
1496  if (pByClient->isObserver() || pByClient->isActivated()) return;
1497  // not joined completely yet? ignore
1498  if (!pByClient->isWaitedFor()) return;
1499  // check behind limit
1500  if (isRunning())
1501  {
1502  // make a guess how much the client lags.
1503  int32_t iLagFrames = Clamp(pByClient->getMsgConn()->getPingTime() * Game.FPS / 500, 0, 100);
1504  if (iTick < Game.FrameCounter - iLagFrames - C4NetMaxBehind4Activation)
1505  return;
1506  }
1507  // activate him
1509  new C4ControlClientUpdate(pByClient->getID(), CUT_Activate, true),
1510  CDT_Sync);
1511 }
1512 
1514 {
1515  // init only
1516  if (Status.getState() != GS_Init)
1517  { LogSilentF("Network: unexpected join data received!"); return; }
1518  // get client ID
1519  if (rPkt.getClientID() == C4ClientIDUnknown)
1520  { LogSilentF("Network: host didn't set client ID!"); Clear(); return; }
1521  // set local ID
1522  ResList.SetLocalID(rPkt.getClientID());
1524  // read and validate status
1525  HandleStatus(rPkt.getStatus());
1526  if (Status.getState() != GS_Lobby && Status.getState() != GS_Pause && Status.getState() != GS_Go)
1527  { LogSilentF("Network: join data has bad game status: %s", Status.getStateName()); Clear(); return; }
1528  // copy scenario parameter defs for lobby display
1530  // copy parameters
1531  ::Game.Parameters = rPkt.Parameters;
1532  // set local client
1533  C4Client *pLocalClient = Game.Clients.getClientByID(rPkt.getClientID());
1534  if (!pLocalClient)
1535  { LogSilentF("Network: Could not find local client in join data!"); Clear(); return; }
1536  // save back dynamic data
1537  ResDynamic = rPkt.getDynamicCore();
1538  iDynamicTick = rPkt.getStartCtrlTick();
1539  // initialize control
1541  pControl->Init(rPkt.getClientID(), false, rPkt.getStartCtrlTick(), pLocalClient->isActivated(), this);
1543  // set local core
1544  NetIO.SetLocalCCore(pLocalClient->getCore());
1545  // add the resources to the network resource list
1547  // load dynamic
1549  { LogFatal("Network: can not not retrieve dynamic!"); Clear(); return; }
1550  // load player resources
1552  // send additional addresses
1553  Clients.SendAddresses(nullptr);
1554 }
1555 
1556 void C4Network2::OnConnect(C4Network2Client *pClient, C4Network2IOConnection *pConn, const char *szMsg, bool fFirstConnection)
1557 {
1558  // log
1559  LogSilentF("Network: %s %s connected (%s/%s) (%s)", pClient->isHost() ? "host" : "client",
1560  pClient->getName(), pConn->getPeerAddr().ToString().getData(),
1561  NetIO.getNetIOName(pConn->getNetClass()), szMsg ? szMsg : "");
1562 
1563  // first connection for this peer? call special handler
1564  if (fFirstConnection) OnClientConnect(pClient, pConn);
1565 }
1566 
1568 {
1569  LogSilentF("Network: %s connection to %s failed!", NetIO.getNetIOName(pConn->getNetClass()),
1570  pConn->getPeerAddr().ToString().getData());
1571 
1572  // maybe client connection failure
1573  // (happens if the connection is not fully accepted and the client disconnects.
1574  // See C4Network2::Join)
1575  C4Network2Client *pClient = Clients.GetClientByID(pConn->getClientID());
1576  if (pClient && !pClient->isConnected())
1577  OnClientDisconnect(pClient);
1578 }
1579 
1581 {
1582  LogSilentF("Network: %s connection to %s (%s) lost!", NetIO.getNetIOName(pConn->getNetClass()),
1583  pClient->getName(), pConn->getPeerAddr().ToString().getData());
1584 
1585  // connection lost?
1586  if (!pClient->isConnected())
1587  OnClientDisconnect(pClient);
1588 }
1589 
1591 {
1592  // host: new client?
1593  if (isHost())
1594  {
1595  // dynamic available?
1596  if (!pClient->hasJoinData())
1597  SendJoinData(pClient);
1598 
1599  // notice lobby (doesn't do anything atm?)
1600  C4GameLobby::MainDlg *pDlg = GetLobby();
1601  if (isLobbyActive()) pDlg->OnClientConnect(pClient->getClient(), pConn);
1602 
1603  }
1604 
1605  // discover resources
1606  ResList.OnClientConnect(pConn);
1607 
1608 }
1609 
1611 {
1612  // league: Notify regular client disconnect within the game
1613  if (pLeagueClient && (isHost() || pClient->isHost())) LeagueNotifyDisconnect(pClient->getID(), C4LDR_ConnectionFailed);
1614  // host? Remove this client from the game.
1615  if (isHost())
1616  {
1617  // log
1618  LogSilentF(LoadResStr("IDS_NET_CLIENTDISCONNECTED"), pClient->getName()); // silent, because a duplicate message with disconnect reason will follow
1619  // remove the client
1620  Game.Clients.CtrlRemove(pClient->getClient(), LoadResStr("IDS_MSG_DISCONNECTED"));
1621  // check status ack (disconnected client might be the last that was waited for)
1622  CheckStatusAck();
1623  // unreached pause/go? retry setting the state with current control tick
1624  // (client might be the only one claiming to have the given control)
1625  if (!fStatusReached)
1626  if (Status.getState() == GS_Go || Status.getState() == GS_Pause)
1628 #ifdef USE_CONSOLE
1629  // Dedicated server: stop hosting if there is only one client left we're hosting for.
1630  // TODO: Find a better place to do this.
1631  if (Game.IsRunning && Clients.Count() <= 3) Application.Quit(); // Off-by-1 error
1632 #endif // USE_CONSOLE
1633  }
1634  // host disconnected? Clear up
1635  if (!isHost() && pClient->isHost())
1636  {
1637  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_NET_HOSTDISCONNECTED"), pClient->getName());
1638  Log(sMsg.getData());
1639  // host connection lost: clear up everything
1641  Clear();
1642  }
1643 }
1644 
1646 {
1647  if (pClient->hasJoinData()) return;
1648  // host only, scenario must be available
1649  assert(isHost());
1650  // dynamic available?
1652  {
1653  fDynamicNeeded = true;
1654  // add synchronization control (will callback, see C4Game::Synchronize)
1656  return;
1657  }
1658  // save his client ID
1659  C4PacketJoinData JoinData;
1660  JoinData.SetClientID(pClient->getID());
1661  // save status into packet
1662  JoinData.SetGameStatus(Status);
1663  // scenario parameter defs for lobby display (localized in host language)
1665  // parameters
1666  JoinData.Parameters = Game.Parameters;
1667  // core join data
1668  JoinData.SetStartCtrlTick(iDynamicTick);
1669  JoinData.SetDynamicCore(ResDynamic);
1670  // send
1671  pClient->SendMsg(MkC4NetIOPacket(PID_JoinData, JoinData));
1672  // send addresses
1673  Clients.SendAddresses(pClient->getMsgConn());
1674  // flag client (he will have to accept the network status sent next)
1675  pClient->SetStatus(NCS_Chasing);
1676  if (!iLastChaseTargetUpdate) iLastChaseTargetUpdate = time(nullptr);
1677 }
1678 
1679 C4Network2Res::Ref C4Network2::RetrieveRes(const C4Network2ResCore &Core, int32_t iTimeoutLen, const char *szResName, bool fWaitForCore)
1680 {
1681  C4GUI::ProgressDialog *pDlg = nullptr;
1682  bool fLog = false;
1683  int32_t iProcess = -1;
1684  C4TimeMilliseconds tTimeout = C4TimeMilliseconds::Now() + iTimeoutLen;
1685  // wait for resource
1686  while (isEnabled())
1687  {
1688  // find resource
1689  C4Network2Res::Ref pRes = ResList.getRefRes(Core.getID());
1690  // res not found?
1691  if (!pRes)
1692  {
1693  if (Core.isNull())
1694  {
1695  // should wait for core?
1696  if (!fWaitForCore) return nullptr;
1697  }
1698  else
1699  {
1700  // start loading
1701  pRes = ResList.AddByCore(Core);
1702  }
1703  }
1704  // res found and loaded completely
1705  else if (!pRes->isLoading())
1706  {
1707  // log
1708  if (fLog) LogF(LoadResStr("IDS_NET_RECEIVED"), szResName, pRes->getCore().getFileName());
1709  // return
1710  if (pDlg) delete pDlg;
1711  return pRes;
1712  }
1713 
1714  // check: progress?
1715  if (pRes && pRes->getPresentPercent() != iProcess)
1716  {
1717  iProcess = pRes->getPresentPercent();
1718  tTimeout = C4TimeMilliseconds::Now() + iTimeoutLen;
1719  }
1720  else
1721  {
1722  // if not: check timeout
1723  if (C4TimeMilliseconds::Now() > tTimeout)
1724  {
1725  LogFatal(FormatString(LoadResStr("IDS_NET_ERR_RESTIMEOUT"), szResName).getData());
1726  if (pDlg) delete pDlg;
1727  return nullptr;
1728  }
1729  }
1730 
1731  // log
1732  if (!fLog)
1733  {
1734  LogF(LoadResStr("IDS_NET_WAITFORRES"), szResName);
1735  fLog = true;
1736  }
1737  // show progress dialog
1738  if (!pDlg && !Console.Active && ::pGUI)
1739  {
1740  // create
1741  pDlg = new C4GUI::ProgressDialog(FormatString(LoadResStr("IDS_NET_WAITFORRES"), szResName).getData(),
1742  LoadResStr("IDS_NET_CAPTION"), 100, 0, C4GUI::Ico_NetWait);
1743  // show dialog
1744  if (!pDlg->Show(::pGUI, true)) { delete pDlg; return nullptr; }
1745  }
1746 
1747  // wait
1748  if (pDlg)
1749  {
1750  // set progress bar
1751  pDlg->SetProgress(iProcess);
1752  // execute (will do message handling)
1753  if (!pDlg->Execute())
1754  { if (pDlg) delete pDlg; return nullptr; }
1755  // aborted?
1756  if (pDlg->IsAborted()) break;
1757  }
1758  else
1759  {
1761  { return nullptr; }
1762  }
1763 
1764  }
1765  // aborted
1766  delete pDlg;
1767  return nullptr;
1768 }
1769 
1770 
1772 {
1773  if (!isHost()) return false;
1774  // remove all existing dynamic data
1775  RemoveDynamic();
1776  // log
1777  Log(LoadResStr("IDS_NET_SAVING"));
1778  // compose file name
1779  char szDynamicBase[_MAX_PATH+1], szDynamicFilename[_MAX_PATH+1];
1781  if (!ResList.FindTempResFileName(szDynamicBase, szDynamicFilename))
1782  Log(LoadResStr("IDS_NET_SAVE_ERR_CREATEDYNFILE"));
1783  // save dynamic data
1784  C4GameSaveNetwork SaveGame(fInit);
1785  if (!SaveGame.Save(szDynamicFilename) || !SaveGame.Close())
1786  { Log(LoadResStr("IDS_NET_SAVE_ERR_SAVEDYNFILE")); return false; }
1787  // add resource
1788  C4Network2Res::Ref pRes = ResList.AddByFile(szDynamicFilename, true, NRT_Dynamic);
1789  if (!pRes) { Log(LoadResStr("IDS_NET_SAVE_ERR_ADDDYNDATARES")); return false; }
1790  // save
1791  ResDynamic = pRes->getCore();
1793  fDynamicNeeded = false;
1794  // ok
1795  return true;
1796 }
1797 
1799 {
1801  if (pRes) pRes->Remove();
1802  ResDynamic.Clear();
1803  iDynamicTick = -1;
1804 }
1805 
1807 {
1808  // "frozen" means all clients are garantueed to be in the same tick.
1809  // This is only the case if the game is not started yet (lobby) or the
1810  // tick has been ensured (pause) and acknowledged by all joined clients.
1811  // Note unjoined clients must be ignored here - they can't be faster than
1812  // the host, anyway.
1813  if (Status.getState() == GS_Lobby) return true;
1814  if (Status.getState() == GS_Pause && fStatusAck) return true;
1815  return false;
1816 }
1817 
1818 bool C4Network2::ChangeGameStatus(C4NetGameState enState, int32_t iTargetCtrlTick, int32_t iCtrlMode)
1819 {
1820  // change game status, announce. Can only be done by host.
1821  if (!isHost()) return false;
1822  // set status
1823  Status.Set(enState, iTargetCtrlTick);
1824  // update reference
1826  // control mode change?
1827  if (iCtrlMode >= 0) Status.SetCtrlMode(iCtrlMode);
1828  // log
1829  LogSilentF("Network: going into status %s (tick %d)", Status.getStateName(), iTargetCtrlTick);
1830  // set flags
1831  Clients.ResetReady();
1832  fStatusReached = fStatusAck = false;
1833  // send new status to all clients
1835  // check reach/ack
1837  // ok
1838  return true;
1839 }
1840 
1841 void C4Network2::CheckStatusReached(bool fFromFinalInit)
1842 {
1843  // already reached?
1844  if (fStatusReached) return;
1845  if (Status.getState() == GS_Lobby)
1847  // game go / pause: control must be initialized and target tick reached
1848  else if (Status.getState() == GS_Go || Status.getState() == GS_Pause)
1849  {
1850  if (Game.IsRunning || fFromFinalInit)
1851  {
1852  // Make sure we have reached the tick and the control queue is empty (except for chasing)
1855  fStatusReached = true;
1856  else
1857  {
1858  // run ctrl so the tick can be reached
1860  Game.HaltCount = 0;
1862  }
1863  }
1864  }
1865  if (!fStatusReached) return;
1866  // call handler
1867  OnStatusReached();
1868  // host?
1869  if (isHost())
1870  // all clients ready?
1871  CheckStatusAck();
1872  else
1873  {
1875  // send response to host
1877  // do delayed activation request
1878  if (fDelayedActivateReq)
1879  {
1880  fDelayedActivateReq = false;
1881  RequestActivate();
1882  }
1883  }
1884 }
1885 
1887 {
1888  // host only
1889  if (!isHost()) return;
1890  // status must be reached and not yet acknowledged
1891  if (!fStatusReached || fStatusAck) return;
1892  // all clients ready?
1894  {
1895  // pause/go: check for sync control that can be executed
1896  if (Status.getState() == GS_Go || Status.getState() == GS_Pause)
1898  // broadcast ack
1900  // handle
1901  OnStatusAck();
1902  }
1903 }
1904 
1906 {
1907  // stop ctrl, wait for ack
1908  if (pControl->IsEnabled())
1909  {
1911  pControl->SetRunning(false);
1912  }
1913 }
1914 
1916 {
1917  // log it
1918  LogSilentF("Network: status %s (tick %d) reached", Status.getStateName(), Status.getTargetCtrlTick());
1919  // pause?
1920  if (Status.getState() == GS_Pause)
1921  {
1922  // set halt-flag (show hold message)
1924  }
1925  // go?
1926  if (Status.getState() == GS_Go)
1927  {
1928  // set mode
1929  pControl->SetCtrlMode(static_cast<C4GameControlNetworkMode>(Status.getCtrlMode()));
1930  // notify player list of reached status - will add some input to the queue
1932  // start ctrl
1933  pControl->SetRunning(true);
1934  // reset halt-flag
1935  Game.HaltCount = 0;
1937  }
1938 }
1939 
1941 {
1942  // neither observer nor activated?
1944  {
1946  return;
1947  }
1948  // host? just do it
1949  if (fHost)
1950  {
1951  // activate him
1954  CDT_Sync);
1955  return;
1956  }
1957  // ensure interval
1959  return;
1960  // status not reached yet? May be chasing, let's delay this.
1961  if (!fStatusReached)
1962  {
1963  fDelayedActivateReq = true;
1964  return;
1965  }
1966  // request
1968  // store time
1970 }
1971 
1973 {
1974  // host only and not in editor
1975  if (!isHost() || ::Application.isEditor) return;
1976  // update activity
1978  // find clients to deactivate
1979  for (C4Network2Client *pClient = Clients.GetNextClient(nullptr); pClient; pClient = Clients.GetNextClient(pClient))
1980  if (!pClient->isLocal() && pClient->isActivated())
1981  if (pClient->getLastActivity() + C4NetDeactivationDelay < Game.FrameCounter)
1982  ::Control.DoInput(CID_ClientUpdate, new C4ControlClientUpdate(pClient->getID(), CUT_Activate, false), CDT_Sync);
1983 }
1984 
1986 {
1987  // no chasing clients?
1988  C4Network2Client *pClient;
1989  for (pClient = Clients.GetNextClient(nullptr); pClient; pClient = Clients.GetNextClient(pClient))
1990  if (pClient->isChasing())
1991  break;
1992  if (!pClient)
1993  {
1995  return;
1996  }
1997  // not time for an update?
1999  return;
2000  // copy status, set current tick
2001  C4Network2Status ChaseTarget = Status;
2002  ChaseTarget.SetTargetTick(::Control.ControlTick);
2003  // send to everyone involved
2004  for (pClient = Clients.GetNextClient(nullptr); pClient; pClient = Clients.GetNextClient(pClient))
2005  if (pClient->isChasing())
2006  pClient->SendMsg(MkC4NetIOPacket(PID_Status, ChaseTarget));
2007  iLastChaseTargetUpdate = time(nullptr);
2008 }
2009 
2010 void C4Network2::LeagueGameEvaluate(const char *szRecordName, const BYTE *pRecordSHA)
2011 {
2012  // already off?
2013  if (!pLeagueClient) return;
2014  // already evaluated?
2015  if (fLeagueEndSent) return;
2016  // end
2017  LeagueEnd(szRecordName, pRecordSHA);
2018 }
2019 
2021 {
2022  // already off?
2023  if (!pLeagueClient) return;
2024  // no post-disable if league is active
2025  if (pLeagueClient && Game.Parameters.isLeague()) return;
2026  // signup end
2027  LeagueEnd(); DeinitLeague();
2028 }
2029 
2031 {
2032  // already running?
2033  if (pLeagueClient) return true;
2034  // Start it!
2035  if (InitLeague(nullptr) && LeagueStart(nullptr)) return true;
2036  // Failure :'(
2037  DeinitLeague();
2038  return false;
2039 }
2040 
2042 {
2043  // Update both local and league reference as soon as possible
2046 }
2047 
2048 bool C4Network2::InitLeague(bool *pCancel)
2049 {
2050 
2051  if (fHost)
2052  {
2053 
2054  // Clear parameters
2058  if (pLeagueClient) delete pLeagueClient; pLeagueClient = nullptr;
2059 
2060  // Not needed?
2062  return true;
2063 
2064  // Save address
2067  {
2069  // enforce some league rules
2071  }
2072 
2073  }
2074  else
2075  {
2076 
2077  // Get league server from parameters
2079 
2080  // Not needed?
2082  return true;
2083 
2084  }
2085 
2086  // Init
2087  pLeagueClient = new C4LeagueClient();
2088  if (!pLeagueClient->Init() ||
2090  {
2091  // Log message
2092  StdStrBuf Message = FormatString(LoadResStr("IDS_NET_ERR_LEAGUEINIT"), pLeagueClient->GetError());
2093  LogFatal(Message.getData());
2094  // Clear league
2095  delete pLeagueClient; pLeagueClient = nullptr;
2096  if (fHost)
2098  // Show message, allow abort
2099  bool fResult = true;
2100  if (!Application.isEditor)
2101  fResult = ::pGUI->ShowMessageModal(Message.getData(), LoadResStr("IDS_NET_ERR_LEAGUE"),
2104  if (pCancel) *pCancel = fResult;
2105  return false;
2106  }
2107 
2108  // Add to message loop
2110 
2111  // OK
2112  return true;
2113 }
2114 
2116 {
2117  // league clear
2121  if (pLeagueClient)
2122  {
2124  delete pLeagueClient; pLeagueClient = nullptr;
2125  }
2126 }
2127 
2128 bool C4Network2::LeagueStart(bool *pCancel)
2129 {
2130  // Not needed?
2131  if (!pLeagueClient || !fHost)
2132  return true;
2133 
2134  // Default
2135  if (pCancel) *pCancel = true;
2136 
2137  // Do update
2138  C4Network2Reference Ref;
2139  Ref.InitLocal();
2140  if (!pLeagueClient->Start(Ref))
2141  {
2142  // Log message
2143  StdStrBuf Message = FormatString(LoadResStr("IDS_NET_ERR_LEAGUE_STARTGAME"), pLeagueClient->GetError());
2144  LogFatal(Message.getData());
2145  // Show message
2146  if (!Application.isEditor)
2147  {
2148  // Show option to cancel, if possible
2149  bool fResult = ::pGUI->ShowMessageModal(
2150  Message.getData(),
2151  LoadResStr("IDS_NET_ERR_LEAGUE"),
2154  if (pCancel)
2155  *pCancel = !fResult;
2156  }
2157  // Failed
2158  return false;
2159  }
2160 
2161  InitPuncher();
2162 
2163  // Let's wait for response
2164  StdStrBuf Message = FormatString(LoadResStr("IDS_NET_LEAGUE_REGGAME"), pLeagueClient->getServerName());
2165  Log(Message.getData());
2166  // Set up a dialog
2167  C4GUI::MessageDialog *pDlg = nullptr;
2168  if (!Application.isEditor)
2169  {
2170  // create & show
2171  pDlg = new C4GUI::MessageDialog(Message.getData(), LoadResStr("IDS_NET_LEAGUE_STARTGAME"),
2173  if (!pDlg || !pDlg->Show(::pGUI, true)) return false;
2174  }
2175  // Wait for response
2176  while (pLeagueClient->isBusy())
2177  {
2178  // Execute GUI
2179  if (!Application.ScheduleProcs() ||
2180  (pDlg && pDlg->IsAborted()))
2181  {
2182  // Clear up
2183  if (pDlg) delete pDlg;
2184  return false;
2185  }
2186  // Check if league server has responded
2187  if (!pLeagueClient->Execute(100))
2188  break;
2189  }
2190  // Close dialog
2191  if (pDlg)
2192  {
2193  pDlg->Close(true);
2194  delete pDlg;
2195  }
2196  // Error?
2197  StdStrBuf LeagueServerMessage, League, StreamingAddr;
2198  int32_t Seed = Game.RandomSeed, MaxPlayersLeague = 0;
2199  if (!pLeagueClient->isSuccess() ||
2200  !pLeagueClient->GetStartReply(&LeagueServerMessage, &League, &StreamingAddr, &Seed, &MaxPlayersLeague))
2201  {
2202  const char *pError = pLeagueClient->GetError() ? pLeagueClient->GetError() :
2203  LeagueServerMessage.getLength() ? LeagueServerMessage.getData() :
2204  LoadResStr("IDS_NET_ERR_LEAGUE_EMPTYREPLY");
2205  StdStrBuf Message = FormatString(LoadResStr("IDS_NET_ERR_LEAGUE_REGGAME"), pError);
2206  // Log message
2207  Log(Message.getData());
2208  // Show message
2209  if (!Application.isEditor)
2210  {
2211  // Show option to cancel, if possible
2212  bool fResult = ::pGUI->ShowMessageModal(
2213  Message.getData(),
2214  LoadResStr("IDS_NET_ERR_LEAGUE"),
2217  if (pCancel)
2218  *pCancel = !fResult;
2219  }
2220  // Failed
2221  return false;
2222  }
2223 
2224  // Show message
2225  if (LeagueServerMessage.getLength())
2226  {
2227  StdStrBuf Message = FormatString(LoadResStr("IDS_MSG_LEAGUEGAMESIGNUP"), pLeagueClient->getServerName(), LeagueServerMessage.getData());
2228  // Log message
2229  Log(Message.getData());
2230  // Show message
2231  if (!Application.isEditor)
2232  {
2233  // Show option to cancel, if possible
2234  bool fResult = ::pGUI->ShowMessageModal(
2235  Message.getData(),
2236  LoadResStr("IDS_NET_ERR_LEAGUE"),
2239  if (pCancel)
2240  *pCancel = !fResult;
2241  if (!fResult)
2242  {
2243  LeagueEnd(); DeinitLeague();
2244  return false;
2245  }
2246  }
2247  }
2248 
2249  // Set game parameters for league game
2250  Game.Parameters.League = League;
2251  Game.RandomSeed = Seed;
2252  if (MaxPlayersLeague)
2253  Game.Parameters.MaxPlayers = MaxPlayersLeague;
2254  if (!League.getLength())
2255  {
2258  }
2259  else
2260  {
2261  Game.Parameters.StreamAddress = StreamingAddr;
2262  }
2263 
2264  // All ok
2265  fLeagueEndSent = false;
2266  return true;
2267 }
2268 
2270 {
2271  // Not needed?
2272  if (!pLeagueClient || !fHost)
2273  return true;
2274 
2275  // League client currently busy?
2276  if (pLeagueClient->isBusy())
2277  return true;
2278 
2279  // Create reference
2280  C4Network2Reference Ref;
2281  Ref.InitLocal();
2282 
2283  // Do update
2284  if (!pLeagueClient->Update(Ref))
2285  {
2286  // Log
2287  LogF(LoadResStr("IDS_NET_ERR_LEAGUE_UPDATEGAME"), pLeagueClient->GetError());
2288  return false;
2289  }
2290 
2291  // Timing
2292  iLastLeagueUpdate = time(nullptr);
2294 
2295  return true;
2296 }
2297 
2299 {
2300  // safety: A reply must be present
2301  assert(pLeagueClient);
2302  assert(fHost);
2303  assert(!pLeagueClient->isBusy());
2305  // check reply success
2306  C4ClientPlayerInfos PlayerLeagueInfos;
2307  StdStrBuf LeagueServerMessage;
2308  bool fSucc = pLeagueClient->isSuccess() && pLeagueClient->GetUpdateReply(&LeagueServerMessage, &PlayerLeagueInfos);
2310  if (!fSucc)
2311  {
2312  const char *pError = pLeagueClient->GetError() ? pLeagueClient->GetError() :
2313  LeagueServerMessage.getLength() ? LeagueServerMessage.getData() :
2314  LoadResStr("IDS_NET_ERR_LEAGUE_EMPTYREPLY");
2315  StdStrBuf Message = FormatString(LoadResStr("IDS_NET_ERR_LEAGUE_UPDATEGAME"), pError);
2316  // Show message - no dialog, because it's not really fatal and might happen in the running game
2317  Log(Message.getData());
2318  return false;
2319  }
2320  // evaluate reply: Transfer data to players
2321  // Take round results
2322  C4PlayerInfoList &TargetList = Game.PlayerInfos;
2323  C4ClientPlayerInfos *pInfos; C4PlayerInfo *pInfo, *pResultInfo;
2324  for (int iClient = 0; (pInfos = TargetList.GetIndexedInfo(iClient)); iClient++)
2325  for (int iInfo = 0; (pInfo = pInfos->GetPlayerInfo(iInfo)); iInfo++)
2326  if ((pResultInfo = PlayerLeagueInfos.GetPlayerInfoByID(pInfo->GetID())))
2327  {
2328  int32_t iLeagueProjectedGain = pResultInfo->GetLeagueProjectedGain();
2329  if (iLeagueProjectedGain != pInfo->GetLeagueProjectedGain())
2330  {
2331  pInfo->SetLeagueProjectedGain(iLeagueProjectedGain);
2332  pInfos->SetUpdated();
2333  }
2334  }
2335  // transfer info update to other clients
2337  // if lobby is open, notify lobby of updated players
2338  if (pLobby) pLobby->OnPlayersChange();
2339  // OMFG SUCCESS!
2340  return true;
2341 }
2342 
2343 bool C4Network2::LeagueEnd(const char *szRecordName, const BYTE *pRecordSHA)
2344 {
2345  C4RoundResultsPlayers RoundResults;
2346  StdStrBuf sResultMessage;
2347  bool fIsError = true;
2348 
2349  // Not needed?
2350  if (!pLeagueClient || !fHost || fLeagueEndSent)
2351  return true;
2352 
2353  // Make sure league client is available
2355 
2356  // Try until either aborted or successful
2357  const int MAX_RETRIES = 10;
2358  for (int iRetry = 0; iRetry < MAX_RETRIES; iRetry++)
2359  {
2360 
2361  // Do update
2362  C4Network2Reference Ref;
2363  Ref.InitLocal();
2364  if (!pLeagueClient->End(Ref, szRecordName, pRecordSHA))
2365  {
2366  // Log message
2367  sResultMessage = FormatString(LoadResStr("IDS_NET_ERR_LEAGUE_FINISHGAME"), pLeagueClient->GetError());
2368  Log(sResultMessage.getData());
2369  // Show message, allow retry
2370  if (Application.isEditor) break;
2371  bool fRetry = ::pGUI->ShowMessageModal(sResultMessage.getData(), LoadResStr("IDS_NET_ERR_LEAGUE"),
2373  if (fRetry) continue;
2374  break;
2375  }
2376  // Let's wait for response
2377  StdStrBuf Message = FormatString(LoadResStr("IDS_NET_LEAGUE_SENDRESULT"), pLeagueClient->getServerName());
2378  Log(Message.getData());
2379  // Wait for response
2380  while (pLeagueClient->isBusy())
2381  {
2382  // Check if league server has responded
2383  if (!pLeagueClient->Execute(100))
2384  break;
2385  }
2386  // Error?
2387  StdStrBuf LeagueServerMessage;
2388  if (!pLeagueClient->isSuccess() || !pLeagueClient->GetEndReply(&LeagueServerMessage, &RoundResults))
2389  {
2390  const char *pError = pLeagueClient->GetError() ? pLeagueClient->GetError() :
2391  LeagueServerMessage.getLength() ? LeagueServerMessage.getData() :
2392  LoadResStr("IDS_NET_ERR_LEAGUE_EMPTYREPLY");
2393  sResultMessage.Take(FormatString(LoadResStr("IDS_NET_ERR_LEAGUE_SENDRESULT"), pError));
2394  if (Application.isEditor) continue;
2395  // Only retry if we didn't get an answer from the league server
2396  bool fRetry = !pLeagueClient->isSuccess();
2397  fRetry = ::pGUI->ShowMessageModal(sResultMessage.getData(), LoadResStr("IDS_NET_ERR_LEAGUE"),
2400  if (fRetry) continue;
2401  }
2402  else
2403  {
2404  // All OK!
2405  sResultMessage.Copy(LoadResStr(Game.Parameters.isLeague() ? "IDS_MSG_LEAGUEEVALUATIONSUCCESSFU" : "IDS_MSG_INTERNETGAMEEVALUATED"));
2406  fIsError = false;
2407  }
2408  // Done
2409  break;
2410  }
2411 
2412  // Show message
2413  Log(sResultMessage.getData());
2414 
2415  // Take round results
2416  Game.RoundResults.EvaluateLeague(sResultMessage.getData(), !fIsError, RoundResults);
2417 
2418  // Send round results to other clients
2419  C4PacketLeagueRoundResults LeagueUpdatePacket(sResultMessage.getData(), !fIsError, RoundResults);
2421 
2422  // All done
2423  fLeagueEndSent = true;
2424  return true;
2425 }
2426 
2428 {
2429 
2430  // Not possible?
2431  if (!pLeagueClient)
2432  return false;
2433 
2434  // Make sure league client is avilable
2436 
2437  // Official league?
2438  bool fOfficialLeague = SEqual(pLeagueClient->getServerName(), "league.openclonk.org");
2439 
2440  StdStrBuf Account, Password;
2441  bool fRememberLogin = false;
2442 
2443  // Default password from login token if present
2444  if (Config.Network.GetLeagueLoginData(pLeagueClient->getServerName(), pInfo->GetName(), &Account, &Password))
2445  {
2446  fRememberLogin = (Password.getLength()>0);
2447  }
2448  else
2449  {
2450  Account.Copy(pInfo->GetName());
2451  }
2452 
2453  for (;;)
2454  {
2455  // ask for account name and password
2456  if (!C4LeagueSignupDialog::ShowModal(pInfo->GetName(), Account.getData(), pLeagueClient->getServerName(), &Account, &Password, !fOfficialLeague, false, &fRememberLogin))
2457  return false;
2458 
2459  // safety (modal dlg may have deleted network)
2460  if (!pLeagueClient) return false;
2461 
2462  // Send authentication request
2463  if (!pLeagueClient->Auth(*pInfo, Account.getData(), Password.getData(), nullptr, nullptr, fRememberLogin))
2464  return false;
2465 
2466  // safety (modal dlg may have deleted network)
2467  if (!pLeagueClient) return false;
2468 
2469  // Wait for a response
2470  StdStrBuf Message = FormatString(LoadResStr("IDS_MSG_TRYLEAGUESIGNUP"), pInfo->GetName(), Account.getData(), pLeagueClient->getServerName());
2471  Log(Message.getData());
2472  // Set up a dialog
2473  C4GUI::MessageDialog *pDlg = nullptr;
2474  if (!Application.isEditor)
2475  {
2476  // create & show
2478  if (!pDlg || !pDlg->Show(::pGUI, true)) return false;
2479  }
2480  // Wait for response
2481  while (pLeagueClient->isBusy())
2482  {
2483  // Execute GUI
2484  if (!Application.ScheduleProcs() ||
2485  (pDlg && pDlg->IsAborted()))
2486  {
2487  // Clear up
2488  if (pDlg) delete pDlg;
2489  return false;
2490  }
2491  // Check if league server has responded
2492  if (!pLeagueClient->Execute(0))
2493  break;
2494  }
2495  // Close dialog
2496  if (pDlg)
2497  {
2498  pDlg->Close(true);
2499  delete pDlg;
2500  }
2501 
2502  // Success?
2503  StdStrBuf AUID, AccountMaster, LoginToken; bool fUnregistered = false;
2504  if (pLeagueClient->GetAuthReply(&Message, &AUID, &AccountMaster, &fUnregistered, &LoginToken))
2505  {
2506 
2507  // Set AUID
2508  pInfo->SetAuthID(AUID.getData());
2509 
2510  // Remember login data; set or clear login token
2511  Config.Network.SetLeagueLoginData(pLeagueClient->getServerName(), pInfo->GetName(), Account.getData(), fRememberLogin ? LoginToken.getData() : "");
2512 
2513  // Show welcome message, if any
2514  bool fSuccess;
2515  if (Message.getLength())
2516  fSuccess = ::pGUI->ShowMessageModal(
2517  Message.getData(), LoadResStr("IDS_DLG_LEAGUESIGNUPCONFIRM"),
2519  else if (AccountMaster.getLength())
2520  fSuccess = ::pGUI->ShowMessageModal(
2521  FormatString(LoadResStr("IDS_MSG_LEAGUEPLAYERSIGNUPAS"), pInfo->GetName(), AccountMaster.getData(), pLeagueClient->getServerName()).getData(), LoadResStr("IDS_DLG_LEAGUESIGNUPCONFIRM"),
2523  else
2524  fSuccess = ::pGUI->ShowMessageModal(
2525  FormatString(LoadResStr("IDS_MSG_LEAGUEPLAYERSIGNUP"), pInfo->GetName(), pLeagueClient->getServerName()).getData(), LoadResStr("IDS_DLG_LEAGUESIGNUPCONFIRM"),
2527 
2528  // Approved?
2529  if (fSuccess)
2530  // Done
2531  return true;
2532  else
2533  // Sign-up was cancelled by user
2534  ::pGUI->ShowMessageModal(FormatString(LoadResStr("IDS_MSG_LEAGUESIGNUPCANCELLED"), pInfo->GetName()).getData(), LoadResStr("IDS_DLG_LEAGUESIGNUP"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Notify);
2535 
2536  }
2537  else
2538  {
2539 
2540  // Authentification error
2541  LogF(LoadResStr("IDS_MSG_LEAGUESIGNUPERROR"), Message.getData());
2542  ::pGUI->ShowMessageModal(FormatString(LoadResStr("IDS_MSG_LEAGUESERVERMSG"), Message.getData()).getData(), LoadResStr("IDS_DLG_LEAGUESIGNUPFAILED"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error);
2543  // after a league server error message, always fall-through to try again
2544  }
2545 
2546  // Try given account name as default next time
2547  if (AccountMaster.getLength())
2548  Account.Take(std::move(AccountMaster));
2549 
2550  // safety (modal dlg may have deleted network)
2551  if (!pLeagueClient) return false;
2552  }
2553 
2554 }
2555 
2557 {
2558 
2559  // Not possible?
2560  if (!pLeagueClient)
2561  return false;
2562 
2563  // Make sure league client is available
2565 
2566  // Ask league server to check the code
2567  if (!pLeagueClient->AuthCheck(*pInfo))
2568  return false;
2569 
2570  // Log
2571  StdStrBuf Message = FormatString(LoadResStr("IDS_MSG_LEAGUEJOINING"), pInfo->GetName());
2572  Log(Message.getData());
2573 
2574  // Wait for response
2575  while (pLeagueClient->isBusy())
2576  if (!pLeagueClient->Execute(100))
2577  break;
2578 
2579  // Check response validity
2580  if (!pLeagueClient->isSuccess())
2581  {
2583  return false;
2584  }
2585 
2586  // Check if league server approves. pInfo will have league info if this call is successful.
2587  if (!pLeagueClient->GetAuthCheckReply(&Message, Game.Parameters.League.getData(), pInfo))
2588  {
2589  LeagueShowError(FormatString(LoadResStr("IDS_MSG_LEAGUEJOINREFUSED"), pInfo->GetName(), Message.getData()).getData());
2590  return false;
2591  }
2592 
2593  return true;
2594 }
2595 
2597 {
2598  // league active?
2599  if (!pLeagueClient || !Game.Parameters.isLeague()) return;
2600  // only in running game
2601  if (!Game.IsRunning || Game.GameOver) return;
2602  // clients send notifications for their own players; host sends for the affected client players
2603  if (!isHost()) { if (!Clients.GetLocal()) return; iClientID = Clients.GetLocal()->getID(); }
2604  // clients only need notifications if they have players in the game
2605  const C4ClientPlayerInfos *pInfos = Game.PlayerInfos.GetInfoByClientID(iClientID);
2606  if (!pInfos) return;
2607  int32_t i=0; C4PlayerInfo *pInfo;
2608  while ((pInfo = pInfos->GetPlayerInfo(i++))) if (pInfo->IsJoined() && !pInfo->IsRemoved()) break;
2609  if (!pInfo) return;
2610  // Make sure league client is avilable
2612  // report the disconnect!
2613  LogF(LoadResStr("IDS_LEAGUE_LEAGUEREPORTINGUNEXPECTED"), (int) eReason);
2614  pLeagueClient->ReportDisconnect(*pInfos, eReason);
2615  // wait for the reply
2617  // display it
2618  const char *szMsg;
2619  StdStrBuf sMessage;
2620  if (pLeagueClient->GetReportDisconnectReply(&sMessage))
2621  szMsg = LoadResStr("IDS_MSG_LEAGUEUNEXPECTEDDISCONNEC");
2622  else
2623  szMsg = LoadResStr("IDS_ERR_LEAGUEERRORREPORTINGUNEXP");
2624  LogF(szMsg, sMessage.getData());
2625 }
2626 
2628 {
2629  // league client busy?
2630  if (!pLeagueClient || !pLeagueClient->isBusy()) return;
2631  // wait for it
2632  Log(LoadResStr("IDS_LEAGUE_WAITINGFORLASTLEAGUESERVE"));
2633  while (pLeagueClient->isBusy())
2634  if (!pLeagueClient->Execute(100))
2635  break;
2636  // if last request was an update request, process it
2639 }
2640 
2642 {
2643  // there's currently no functionality to surrender in the league
2644  // just stop responding so other clients will notify the disconnect
2645  DeinitLeague();
2646 }
2647 
2648 void C4Network2::LeagueShowError(const char *szMsg)
2649 {
2650  if (!Application.isEditor)
2651  {
2652  ::pGUI->ShowErrorMessage(szMsg);
2653  }
2654  else
2655  {
2656  LogF(LoadResStr("IDS_LGA_SERVERFAILURE"), szMsg);
2657  }
2658 }
2659 
2660 void C4Network2::Vote(C4ControlVoteType eType, bool fApprove, int32_t iData)
2661 {
2662  // Original vote?
2663  if (!GetVote(C4ClientIDUnknown, eType, iData))
2664  {
2665  // Too fast?
2666  if (time(nullptr) < (time_t) (iLastOwnVoting + C4NetMinVotingInterval))
2667  {
2668  Log(LoadResStr("IDS_TEXT_YOUCANONLYSTARTONEVOTINGE"));
2669  if ((eType == VT_Kick && iData == Game.Clients.getLocalID()) || eType == VT_Cancel)
2670  OpenSurrenderDialog(eType, iData);
2671  return;
2672  }
2673  // Save timestamp
2674  iLastOwnVoting = time(nullptr);
2675  }
2676  // Already voted? Ignore
2677  if (GetVote(::Control.ClientID(), eType, iData))
2678  return;
2679  // Set pause mode if this is the host
2680  if (isHost() && isRunning())
2681  {
2682  Pause();
2683  fPausedForVote = true;
2684  }
2685  // send vote control
2686  ::Control.DoInput(CID_Vote, new C4ControlVote(eType, fApprove, iData), CDT_Direct);
2687 }
2688 
2690 {
2691  // Save back timestamp
2692  if (!Votes.firstPkt())
2693  iVoteStartTime = time(nullptr);
2694  // Save vote back
2695  Votes.Add(CID_Vote, new C4ControlVote(Vote));
2696  // Set pause mode if this is the host
2697  if (isHost() && isRunning())
2698  {
2699  Pause();
2700  fPausedForVote = true;
2701  }
2702  // Check if the dialog should be opened
2703  OpenVoteDialog();
2704 }
2705 
2706 C4IDPacket *C4Network2::GetVote(int32_t iClientID, C4ControlVoteType eType, int32_t iData)
2707 {
2708  C4ControlVote *pVote;
2709  for (C4IDPacket *pPkt = Votes.firstPkt(); pPkt; pPkt = Votes.nextPkt(pPkt))
2710  if (pPkt->getPktType() == CID_Vote)
2711  if ((pVote = static_cast<C4ControlVote *>(pPkt->getPkt())))
2712  if (iClientID == C4ClientIDUnknown || pVote->getByClient() == iClientID)
2713  if (pVote->getType() == eType && pVote->getData() == iData)
2714  return pPkt;
2715  return nullptr;
2716 }
2717 
2718 void C4Network2::EndVote(C4ControlVoteType eType, bool fApprove, int32_t iData)
2719 {
2720  // Remove all vote packets
2721  C4IDPacket *pPkt; int32_t iOrigin = C4ClientIDUnknown;
2722  while ((pPkt = GetVote(C4ClientIDAll, eType, iData)))
2723  {
2724  if (iOrigin == C4ClientIDUnknown)
2725  iOrigin = static_cast<C4ControlVote *>(pPkt->getPkt())->getByClient();
2726  Votes.Delete(pPkt);
2727  }
2728  // Reset timestamp
2729  iVoteStartTime = time(nullptr);
2730  // Approved own voting? Reset voting block
2731  if (fApprove && iOrigin == Game.Clients.getLocalID())
2732  iLastOwnVoting = 0;
2733  // Dialog open?
2734  if (pVoteDialog)
2735  if (pVoteDialog->getVoteType() == eType && pVoteDialog->getVoteData() == iData)
2736  {
2737  // close
2738  delete pVoteDialog;
2739  pVoteDialog = nullptr;
2740  }
2741  // Did we try to kick ourself? Ask if we'd like to surrender
2742  bool fCancelVote = (eType == VT_Kick && iData == Game.Clients.getLocalID()) || eType == VT_Cancel;
2743  if (!fApprove && fCancelVote && iOrigin == Game.Clients.getLocalID())
2744  OpenSurrenderDialog(eType, iData);
2745  // Check if the dialog should be opened
2746  OpenVoteDialog();
2747  // Pause/unpause voting?
2748  if (fApprove && eType == VT_Pause)
2749  fPausedForVote = !iData;
2750  // No voting left? Reset pause.
2751  if (!Votes.firstPkt())
2752  if (fPausedForVote)
2753  {
2754  Start();
2755  fPausedForVote = false;
2756  }
2757 }
2758 
2760 {
2761  // Dialog already open?
2762  if (pVoteDialog) return;
2763  // No vote available?
2764  if (!Votes.firstPkt()) return;
2765  // Can't vote?
2767  if (!pPlayerInfos || !pPlayerInfos->GetPlayerCount() || !pPlayerInfos->GetJoinedPlayerCount())
2768  return;
2769  // Search a voting we have to vote on
2770  for (C4IDPacket *pPkt = Votes.firstPkt(); pPkt; pPkt = Votes.nextPkt(pPkt))
2771  {
2772  // Already voted on this matter?
2773  C4ControlVote *pVote = static_cast<C4ControlVote *>(pPkt->getPkt());
2774  if (!GetVote(::Control.ClientID(), pVote->getType(), pVote->getData()))
2775  {
2776  // Compose message
2777  C4Client *pSrcClient = Game.Clients.getClientByID(pVote->getByClient());
2778  StdStrBuf Msg; Msg.Format(LoadResStr("IDS_VOTE_WANTSTOALLOW"), pSrcClient ? pSrcClient->getName() : "???", pVote->getDesc().getData());
2779  Msg.AppendChar('|');
2780  Msg.Append(pVote->getDescWarning());
2781 
2782  // Open dialog
2783  pVoteDialog = new C4VoteDialog(Msg.getData(), pVote->getType(), pVote->getData(), false);
2785  pVoteDialog->Show(::pGUI, true);
2786 
2787  break;
2788  }
2789  }
2790 }
2791 
2793 {
2794  if (!pVoteDialog)
2795  {
2796  pVoteDialog = new C4VoteDialog(
2797  LoadResStr("IDS_VOTE_SURRENDERWARNING"), eType, iData, true);
2799  pVoteDialog->Show(::pGUI, true);
2800  }
2801 }
2802 
2803 
2805 {
2806  pVoteDialog = nullptr;
2807 }
2808 
2809 
2810 // *** C4VoteDialog
2811 
2812 C4VoteDialog::C4VoteDialog(const char *szText, C4ControlVoteType eVoteType, int32_t iVoteData, bool fSurrender)
2813  : MessageDialog(szText, LoadResStr("IDS_DLG_VOTING"), C4GUI::MessageDialog::btnYesNo, C4GUI::Ico_Confirm, C4GUI::MessageDialog::dsRegular, nullptr, true),
2814  eVoteType(eVoteType), iVoteData(iVoteData), fSurrender(fSurrender)
2815 {
2816 
2817 }
2818 
2819 void C4VoteDialog::OnClosed(bool fOK)
2820 {
2821  bool fAbortGame = false;
2822  // notify that this object will be deleted shortly
2824  // Was league surrender dialog
2825  if (fSurrender)
2826  {
2827  // League surrender accepted
2828  if (fOK)
2829  {
2830  // set game leave reason, although round results dialog isn't showing it ATM
2831  Game.RoundResults.EvaluateNetwork(C4RoundResults::NR_NetError, LoadResStr("IDS_ERR_YOUSURRENDEREDTHELEAGUEGA"));
2832  // leave game
2834  ::Network.Clear();
2835  // We have just league-surrendered. Abort the game - that is what we originally wanted.
2836  // Note: as we are losing league points and this is a relevant game, it would actually be
2837  // nice to show an evaluation dialog which tells us that we have lost and how many league
2838  // points we have lost. But until the evaluation dialog can actually do that, it is better
2839  // to abort completely.
2840  // Note2: The league dialog will never know that, because the game will usually not be over yet.
2841  // Scores are not calculated until after the game.
2842  fAbortGame = true;
2843  }
2844  }
2845  // Was normal vote dialog
2846  else
2847  {
2848  // Vote still active? Then vote.
2849  if (::Network.GetVote(C4ClientIDUnknown, eVoteType, iVoteData))
2850  ::Network.Vote(eVoteType, fOK, iVoteData);
2851  }
2852  // notify base class
2853  MessageDialog::OnClosed(fOK);
2854  // Abort game
2855  if (fAbortGame)
2856  Game.Abort(true);
2857 }
2858 
2859 
2860 /* Lobby countdown */
2861 
2862 void C4Network2::StartLobbyCountdown(int32_t iCountdownTime)
2863 {
2864  // abort previous
2866  // start new
2867  pLobbyCountdown = new C4GameLobby::Countdown(iCountdownTime);
2868 }
2869 
2871 {
2872  // aboert lobby countdown
2873  if (pLobbyCountdown)
2874  {
2876  delete pLobbyCountdown;
2877  pLobbyCountdown = nullptr;
2878  }
2879 }
2880 
2881 /* Streaming */
2882 
2884 {
2885  // Save back
2886  fStreaming = true;
2887  pStreamedRecord = pRecord;
2888  iLastStreamAttempt = time(nullptr);
2889 
2890  // Initialize compressor
2892  if (deflateInit(&StreamCompressor, 9) != Z_OK)
2893  return false;
2894 
2895  // Create stream buffer
2897  StreamCompressor.next_out = reinterpret_cast<BYTE*>(StreamingBuf.getMData());
2899 
2900  // Initialize HTTP client
2902  if (!pStreamer->Init())
2903  return false;
2905 
2906  return true;
2907 }
2908 
2910 {
2911  if (!fStreaming) return false;
2912 
2913  // Stream
2914  StreamIn(true);
2915 
2916  // Reset record pointer
2917  pStreamedRecord = nullptr;
2918 
2919  // Try to get rid of remaining data immediately
2920  iLastStreamAttempt = 0;
2921  StreamOut();
2922 
2923  return true;
2924 }
2925 
2927 {
2928  if (!fStreaming) return false;
2929 
2930  // Clear
2932  fStreaming = false;
2933  pStreamedRecord = nullptr;
2934  deflateEnd(&StreamCompressor);
2935  StreamingBuf.Clear();
2936  delete pStreamer;
2937  pStreamer = nullptr;
2938 
2939  // ... finalization?
2940  return true;
2941 }
2942 
2943 bool C4Network2::StreamIn(bool fFinish)
2944 {
2945  if (!pStreamedRecord) return false;
2946 
2947  // Get data from record
2948  const StdBuf &Data = pStreamedRecord->GetStreamingBuf();
2949  if (!fFinish)
2950  if (!Data.getSize() || !StreamCompressor.avail_out)
2951  return false;
2952 
2953  do
2954  {
2955 
2956  // Compress
2957  StreamCompressor.next_in = const_cast<BYTE *>(getBufPtr<BYTE>(Data));
2958  StreamCompressor.avail_in = Data.getSize();
2959  int ret = deflate(&StreamCompressor, fFinish ? Z_FINISH : Z_NO_FLUSH);
2960 
2961  // Anything consumed?
2962  unsigned int iInAmount = Data.getSize() - StreamCompressor.avail_in;
2963  if (iInAmount > 0)
2964  pStreamedRecord->ClearStreamingBuf(iInAmount);
2965 
2966  // Done?
2967  if (!fFinish || ret == Z_STREAM_END)
2968  break;
2969 
2970  // Error while finishing?
2971  if (ret != Z_OK)
2972  return false;
2973 
2974  // Enlarge buffer, if neccessary
2975  size_t iPending = getPendingStreamData();
2976  size_t iGrow = StreamingBuf.getSize();
2977  StreamingBuf.Grow(iGrow);
2978  StreamCompressor.avail_out += iGrow;
2979  StreamCompressor.next_out = getMBufPtr<BYTE>(StreamingBuf, iPending);
2980 
2981  }
2982  while (true);
2983 
2984  return true;
2985 }
2986 
2988 {
2989  // Streamer busy?
2990  if (!pStreamer || pStreamer->isBusy())
2991  return false;
2992 
2993  // Streamer done?
2994  if (pStreamer->isSuccess())
2995  {
2996 
2997  // Move new data to front of buffer
3000 
3001  // Free buffer space
3004 
3005  // Advance stream
3007 
3008  // Get input
3009  StreamIn(false);
3010  }
3011 
3012  // Clear streamer
3013  pStreamer->Clear();
3014 
3015  // Record is still running?
3016  if (pStreamedRecord)
3017  {
3018 
3019  // Enough available to send?
3021  return false;
3022 
3023  // Overflow protection
3025  return false;
3026 
3027  }
3028  // All data finished?
3029  else if (!getPendingStreamData())
3030  {
3031  // Then we're done.
3032  StopStreaming();
3033  return false;
3034  }
3035 
3036  // Set stream address
3037  StdStrBuf StreamAddr;
3038  StreamAddr.Copy(Game.Parameters.StreamAddress);
3039  StreamAddr.AppendFormat("pos=%d&end=%d", iCurrentStreamPosition, !pStreamedRecord);
3040  pStreamer->SetServer(StreamAddr.getData());
3041 
3042  // Send data
3043  size_t iStreamAmount = getPendingStreamData();
3044  iCurrentStreamAmount = iStreamAmount;
3045  iLastStreamAttempt = time(nullptr);
3046  return pStreamer->Query(StdBuf(StreamingBuf.getData(), iStreamAmount), false);
3047 }
3048 
3050 {
3051  // Streaming must be active and there must still be anything to stream
3052  return fStreaming;
3053 }
int32_t GetJoinedPlayerCount() const
char * GetFilename(char *szPath)
Definition: StdFile.cpp:45
const char * getData() const
Definition: StdBuf.h:442
void CheckStatusAck()
void RemoveDynamic()
class C4Network2HTTPClient * pStreamer
Definition: C4Network2.h:193
const int32_t C4ClientIDAll
C4Client * getLocal() const
Definition: C4Client.h:161
const char * GetInputText()
Definition: C4Gui.h:2421
class C4GameLobby::MainDlg * GetLobby() const
Definition: C4Network2.h:216
bool InitNetwork(C4Network2ResList *pNetResList)
void SetObserver(bool fnObserver)
Definition: C4Client.h:65
C4IDPacket * firstPkt() const
Definition: C4Control.h:78
bool IsRunning
Definition: C4Game.h:139
const unsigned int C4NetReferenceUpdateInterval
Definition: C4Network2.h:48
bool fStatusAck
Definition: C4Network2.h:140
void SetPassword(const char *szToPassword)
Definition: C4Network2.cpp:771
int getConnectionCount()
int32_t getTargetCtrlTick() const
Definition: C4Network2.h:82
bool CheckConn(const C4ClientCore &CCore, C4Network2IOConnection *pConn, C4Network2Client *pClient, StdStrBuf *szReply)
bool SendMsgToHost(C4NetIOPacket rPkt)
unsigned int iCurrentStreamPosition
Definition: C4Network2.h:194
bool Start()
Definition: C4Network2.cpp:505
bool isObserver() const
void SetReference(class C4Network2Reference *pReference)
void OnClientDisconnect(C4Network2Client *pClient)
int32_t iLobbyTimeout
Definition: C4Game.h:119
StdBuf StreamingBuf
Definition: C4Network2.h:190
char ScenarioFilename[_MAX_PATH+1]
Definition: C4Game.h:102
void HandleConnRe(const class C4PacketConnRe &Pkt, C4Network2IOConnection *pConn, C4Network2Client *pClient)
bool Auth(const C4PlayerInfo &PlrInfo, const char *szAccount, const char *szPassword, const char *szNewAccount=nullptr, const char *szNewPassword=nullptr, bool fRememberLogin=false)
Definition: C4League.cpp:405
const void * getData() const
Definition: StdBuf.h:99
void SetLocalID(int32_t iID)
Definition: C4Client.cpp:324
int32_t RandomSeed
Definition: C4Game.h:134
C4Config Config
Definition: C4Config.cpp:833
time_t iVoteStartTime
Definition: C4Network2.h:184
C4Network2Res::Ref AddByCore(const C4Network2ResCore &Core, bool fLoad=true)
const char * GetName() const
Definition: C4PlayerInfo.h:157
void RemoveConn(C4Network2IOConnection *pConn)
Definition: StdBuf.h:29
float Y
Definition: C4Facet.h:118
bool InitNetIO(bool fNoClientID, bool fHost)
uint32_t iLastLeagueUpdate
Definition: C4Network2.h:165
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
C4Network2Client * GetClientByID(int32_t iID) const
void SetCtrlMode(int32_t iCtrlMode)
Definition: C4Network2.cpp:818
void Init(C4ClientList *pClientList, bool fHost)
bool fStatusReached
Definition: C4Network2.h:140
bool GetUpdateReply(StdStrBuf *pMessage, C4ClientPlayerInfos *pPlayerLeagueInfos)
Definition: C4League.cpp:360
int32_t PortDiscovery
Definition: C4Config.h:153
void LeagueWaitNotBusy()
void AddVote(const C4ControlVote &Vote)
int32_t ControlTick
Definition: C4GameControl.h:89
C4Console Console
Definition: C4Globals.cpp:45
bool LogSilent(const char *szMessage, bool fConsole)
Definition: C4Log.cpp:114
bool isPasswordWrong() const
Definition: C4Network2IO.h:381
bool isHost() const
Definition: C4Network2.h:209
void SetGameStatus(const C4Network2Status &Status)
Definition: C4Network2.h:449
C4Network2Client * GetNextClient(C4Network2Client *pClient)
bool isFailed() const
Definition: C4Network2IO.h:291
Definition: StdAdaptors.h:762
bool isRunning() const
Definition: C4Network2.h:206
C4Network2IO NetIO
Definition: C4Network2.h:110
void AbortLobbyCountdown()
bool LeaguePlrAuthCheck(C4PlayerInfo *pInfo)
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:257
void SyncClearance()
Definition: C4Game.cpp:3129
void SetAllowObserve(bool fAllow)
Definition: C4Network2.cpp:812
const C4Network2Address & getAddr(int i) const
C4Game Game
Definition: C4Globals.cpp:52
const C4Network2Status & getStatus() const
Definition: C4Network2.h:445
void SetClientID(int32_t inClientID)
Definition: C4Network2.h:448
void OnDisconn(C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:835
bool Merge(const char *szFolders)
Definition: C4Group.cpp:1229
void SendPuncherPacket(const C4NetpuncherPacket &, C4NetIO::HostAddress::AddressFamily family)
uint32_t iLeagueUpdateDelay
Definition: C4Network2.h:165
C4Scenario C4S
Definition: C4Game.h:74
void Delete(C4IDPacket *pPkt)
Definition: C4Control.h:88
bool FinalInit()
Definition: C4Network2.cpp:537
void ResetCurrentAction()
Definition: C4League.h:217
void Clear()
Definition: StdBuf.h:466
#define b
bool fLobbyRunning
Definition: C4Network2.h:148
#define sprintf
Definition: Standard.h:164
const StdBuf & GetStreamingBuf() const
Definition: C4Record.h:263
#define C4CFN_Material
Definition: C4Components.h:25
C4Network2IOConnection * getMsgConn() const
bool ThreadLogS(const char *szMessage,...) GNUC_FORMAT_ATTRIBUTE_O
bool isLobbyActive() const
Definition: C4Network2.h:204
C4NetGameState
Definition: C4Network2.h:60
void SetLeagueProjectedGain(int32_t iProjectedGain)
Definition: C4PlayerInfo.h:143
void EvaluateNetwork(NetResult eResult, const char *szResultsString)
void EnforceLeagueRules(class C4Scenario *pScenario)
int32_t iNextClientID
Definition: C4Network2.h:155
void SetAuthID(const char *sznAuthID)
Definition: C4PlayerInfo.h:133
void SetCCore(const C4ClientCore &nCCore)
void OpenSurrenderDialog(C4ControlVoteType eType, int32_t iData)
const int C4NetStreamingInterval
Definition: C4Network2.h:58
const int C4NetDeactivationDelay
Definition: C4Network2.h:42
void Clear()
Definition: StdBuf.h:190
bool IsAborted()
Definition: C4Gui.h:2149
int getProtBCRate(C4Network2IOProtocol eProt) const
Definition: C4Network2IO.h:154
void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2Client *pClient)
bool isStreaming() const
bool ToggleAllowJoin()
Definition: C4Network2.cpp:758
void Set(C4NetGameState eState, int32_t iTargetCtrlTick)
Definition: C4Network2.cpp:81
bool BroadcastMsgToClients(const C4NetIOPacket &rPkt)
int32_t PortRefServer
Definition: C4Config.h:153
bool TextOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface *sfcDest, float iTx, float iTy, DWORD dwFCol=0xffffffff, BYTE byForm=ALeft, bool fDoMarkup=true)
Definition: C4Draw.cpp:567
StdCopyStrBuf League
bool isActivated() const
Definition: C4Client.h:110
void Add(C4PacketType eType, C4ControlPacket *pCtrl)
Definition: C4Control.h:82
void SetID(int32_t inID)
Definition: C4Client.h:62
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:207
bool InitNetwork(C4Network2ResList *pResList)
void OnStatusAck()
void OnPuncherConnect(C4NetIO::addr_t addr)
StdStrBuf ToString(int flags=0) const
Definition: C4NetIO.cpp:599
bool Connect(const C4NetIO::addr_t &addr, C4Network2IOProtocol eProt, const C4ClientCore &nCCore, const char *szPassword=nullptr)
void Close(bool fOK)
C4ControlVoteType
Definition: C4Control.h:559
bool CreateDynamic(bool fInit)
C4NetpuncherID getNetpuncherGameID() const
Definition: C4Network2.h:308
char PuncherAddress[CFG_MaxString+1]
Definition: C4Config.h:160
C4ScenarioParameterDefs ScenarioParameterDefs
Definition: C4Network2.h:437
void Vote(C4ControlVoteType eType, bool fApprove=true, int32_t iData=0)
const char * getPassword() const
Definition: C4Network2IO.h:364
void SetLeagueLoginData(const char *szServer, const char *szPlayerName, const char *szAccount, const char *szLoginToken)
Definition: C4Config.cpp:617
void SetDynamicCore(const C4Network2ResCore &Core)
Definition: C4Network2.h:450
virtual const char * GetError() const
Definition: C4NetIO.h:284
StdStrBuf getNetpuncherAddr() const
Definition: C4Network2.h:309
bool End(const C4Network2Reference &Ref, const char *szRecordName, const BYTE *pRecordSHA)
Definition: C4League.cpp:375
uint8_t BYTE
bool Send(const C4NetIOPacket &rPkt)
C4Network2IOProtocol
Definition: C4Network2IO.h:29
class C4VoteDialog * pVoteDialog
Definition: C4Network2.h:182
bool isOK() const
Definition: C4Network2IO.h:380
const int32_t C4ClientCoreDL_IDMatch
Definition: C4Client.h:30
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
void HandleStatus(const C4Network2Status &nStatus)
bool C4Group_UnpackDirectory(const char *szFilename)
Definition: C4Group.cpp:306
bool fAllowJoin
Definition: C4Network2.h:130
unsigned int iCurrentStreamAmount
Definition: C4Network2.h:194
StdCopyStrBuf MasterServerAddress
Definition: C4Network2.h:152
C4Client * getHost() const
Definition: C4Client.h:163
bool StreamIn(bool fFinish)
C4Network2Client * GetClient(const char *szName) const
const char * GetRevision() const
Definition: C4Application.h:72
void SetCtrlMode(int32_t iCtrlMode)
Definition: C4Network2.cpp:86
C4Record * pStreamedRecord
Definition: C4Network2.h:189
void Execute()
Definition: C4Network2.cpp:648
#define GETPKT(type, name)
void OnStatusReached()
C4NetpuncherID getNetpuncherGameID() const
#define _MAX_PATH
C4ClientList Clients
#define a
void SetExclusiveConnMode(bool fExclusiveConn)
void Grow(size_t iGrow)
Definition: StdBuf.h:171
class C4ScenarioParameterDefs & ScenarioParameterDefs
Definition: C4Game.h:75
bool IsReferenceNeeded()
int32_t getID() const
void UpdateMenus()
Definition: C4Console.cpp:504
void SetAddress(const sockaddr *addr)
Definition: C4NetIO.cpp:364
int32_t iDynamicTick
Definition: C4Network2.h:136
C4GraphicsResource GraphicsResource
const size_t C4NetStreamingMinBlockSize
Definition: C4Network2.h:56
void CtrlRemove(const C4Client *pClient, const char *szReason)
Definition: C4Client.cpp:333
bool CtrlReady(int32_t iTick)
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:93
const char * getFileName() const
Definition: C4Network2Res.h:94
C4GameParameters & Parameters
Definition: C4Game.h:67
void Add(StdSchedulerProc *pProc)
void SetDelOnClose(bool fToVal=true)
Definition: C4Gui.h:2190
const C4NetIO::addr_t & getPeerAddr() const
Definition: C4Network2IO.h:264
class C4LeagueClient * pLeagueClient
Definition: C4Network2.h:169
C4NetIO * MsgIO()
C4ClientPlayerInfos * GetIndexedInfo(int32_t iIndex) const
Definition: C4PlayerInfo.h:358
bool isHalfAccepted() const
Definition: C4Network2IO.h:286
bool GetAuthReply(StdStrBuf *pMessage, StdStrBuf *pAUID, StdStrBuf *pAccount, bool *pRegister, StdStrBuf *pLoginToken)
Definition: C4League.cpp:425
size_t getSize() const
Definition: StdBuf.h:101
bool IsShown()
Definition: C4Gui.h:2147
void HandleConn(const class C4PacketConn &Pkt, C4Network2IOConnection *pConn, C4Network2Client *pClient)
bool isChasing() const
bool isHost() const
Definition: C4Client.h:58
void StartLobbyCountdown(int32_t iCountdownTime)
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
void CopyClientList(const C4ClientList &rClients)
T Clamp(T bval, T lbound, T rbound)
Definition: Standard.h:44
void AppendFormat(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:190
const C4Network2ResCore & getDynamicCore() const
Definition: C4Network2.h:444
bool isConnected() const
C4LeagueDisconnectReason
Definition: C4Constants.h:142
bool InitHost(bool fLobby)
Definition: C4Network2.cpp:138
void OnSec1Timer() override
Definition: C4Network2.cpp:643
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
const C4Network2ResCore * getResCore() const
void AppendChar(char cChar)
Definition: StdBuf.h:588
void AllowJoin(bool fAllow)
Definition: C4Network2.cpp:801
bool isLoading() const
bool FlushMessages()
Definition: C4AppSDL.cpp:115
const C4NetIO::addr_t & getConnectAddr() const
Definition: C4Network2IO.h:265
~C4Network2() override
Definition: C4Network2.cpp:133
void OnDisconnect(C4Network2Client *pClient, C4Network2IOConnection *pConn)
void HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:962
void LeagueNotifyDisconnect(int32_t iClientID, enum C4LeagueDisconnectReason eReason)
C4Network2IOProtocol getProtocol() const
Definition: C4Network2IO.h:263
void DeactivateInactiveClients()
bool Init(int32_t iClientID, C4Network2IO *pIOClass)
const C4ClientCore & getLocalCore() const
Definition: C4Client.h:169
bool Show(Screen *pOnScreen, bool fCB)
bool ToggleClientListDlg()
Definition: C4Network2.cpp:765
void Punch(const C4NetIO::addr_t &)
int32_t GetBehind(int32_t iTick) const
int32_t ClientPerfStat(int32_t iClientID)
int32_t getAvgControlSendTime() const
const char * getFile() const
bool isObserver() const
Definition: C4Client.h:60
int32_t FrameCounter
Definition: C4Game.h:128
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1191
void OpenVoteDialog()
const int32_t C4ClientIDStart
Definition: C4Client.h:26
int32_t getByClient() const
Definition: C4Control.h:42
int32_t getID() const
Definition: C4Network2Res.h:86
bool isNetwork() const
Definition: C4GameControl.h:97
int GetScopeId() const
Definition: C4NetIO.cpp:296
bool LogSilentF(const char *strMessage,...)
Definition: C4Log.cpp:260
void HandleJoinData(const class C4PacketJoinData &rPkt)
InitialConnect(const std::vector< C4Network2Address > &Addrs, const C4ClientCore &HostCore, const char *Password)
Definition: C4Network2.cpp:313
C4PlayerInfo * GetPlayerInfo(int32_t iIndex) const
bool CheckAndReset()
Definition: StdScheduler.h:107
bool isObserver() const
Definition: C4Client.h:111
static std::vector< HostAddress > GetLocalAddresses()
Definition: C4NetIO.cpp:626
bool isActivated() const
Definition: C4Client.h:59
bool Close()
Definition: C4GameSave.cpp:447
C4Network2 Network
Definition: C4Globals.cpp:53
bool isPasswordNeeded() const
bool Pause()
Definition: C4Network2.cpp:513
const char * getMsg() const
Definition: C4Network2IO.h:382
time_t iLastStreamAttempt
Definition: C4Network2.h:188
void LeagueShowError(const char *szMsg)
const C4Network2ResCore & getCore() const
C4Network2IOProtocol getNetIOProt(C4NetIO *pNetIO)
void OnConnectFail(C4Network2IOConnection *pConn)
bool isAccepted() const
Definition: C4Network2IO.h:287
bool fDelayedActivateReq
Definition: C4Network2.h:178
bool LeagueUpdate()
bool GameOver
Definition: C4Game.h:114
bool Save(const char *szFilename)
Definition: C4GameSave.cpp:398
bool ShowMessageModal(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
void SetName(const char *sznName)
Definition: C4Client.h:63
bool Init(int32_t iLocalClientID=C4ClientIDHost)
Definition: C4Client.cpp:243
bool AllClientsReady() const
int32_t iCtrlMode
Definition: C4Network2.h:76
C4Network2Res::Ref RetrieveRes(const C4Network2ResCore &Core, int32_t iTimeout, const char *szResName, bool fWaitForCore=false)
const C4ClientCore & getCore() const
int32_t getLocalID() const
Definition: C4Client.h:171
int32_t FPS
Definition: C4Game.h:111
bool InitLeague(bool *pCancel)
void InvalidateReference()
void SetMsgConn(C4Network2IOConnection *pConn)
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
bool ShowErrorMessage(const char *szMessage)
void CheckPortsForCollisions()
Definition: C4Config.cpp:600
C4GraphicsSystem GraphicsSystem
Definition: C4Globals.cpp:51
bool IsJoined() const
Definition: C4PlayerInfo.h:166
bool LeagueStart(bool *pCancel)
bool Open(const char *szGroupName, bool fCreate=false)
Definition: C4Group.cpp:514
const char * AtNetworkPath(const char *szFilename)
Definition: C4Config.cpp:547
bool isFrozen() const
void SetStartCtrlTick(int32_t iTick)
Definition: C4Network2.h:451
const C4ClientCore & getCore() const
Definition: C4Client.h:104
const unsigned int C4NetMinVotingInterval
Definition: C4Network2.h:53
uint32_t iLastReferenceUpdate
Definition: C4Network2.h:164
size_t getPendingStreamData() const
Definition: C4Network2.h:300
void SetDataConn(C4Network2IOConnection *pConn)
bool AuthCheck(const C4PlayerInfo &PlrInfo)
Definition: C4League.cpp:455
void DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4ControlDeliveryType eDelivery)
C4GameResList GameRes
bool LeagueSignupEnable()
void Take(char *pnData)
Definition: StdBuf.h:457
void Abort(bool fApproved=false)
Definition: C4Game.cpp:3666
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
bool isNull() const
Definition: C4Network2Res.h:85
bool C4Group_CopyItem(const char *szSource, const char *szTarget1, bool fNoSort, bool fResetAttributes)
Definition: C4Group.cpp:100
const char * getStateName() const
Definition: C4Network2.cpp:55
bool EraseDirectory(const char *szDirName)
Definition: StdFile.cpp:761
const C4NetIO::EndpointAddress & GetSourceAddress() const
void OnClientConnect(C4Network2Client *pClient, C4Network2IOConnection *pConn)
void Clear()
Definition: C4Control.cpp:85
int getPingTime() const
Definition: C4Network2IO.h:273
bool isWaitedFor() const
int32_t GetPlayerCount() const
Definition: C4PlayerInfo.h:251
C4ConfigNetwork Network
Definition: C4Config.h:255
bool StartStreaming(C4Record *pRecord)
bool IsRemoved() const
Definition: C4PlayerInfo.h:164
bool LeaguePlrAuth(C4PlayerInfo *pInfo)
C4Network2ResCore ResDynamic
Definition: C4Network2.h:133
void HandleActivateReq(int32_t iTick, C4Network2Client *pClient)
bool isRemoved() const
void SetAcceptMode(bool fAcceptAll)
const int C4NetActivationReqInterval
Definition: C4Network2.h:40
bool GetAuthCheckReply(StdStrBuf *pMessage, const char *szLeague, class C4PlayerInfo *pPlrInfo)
Definition: C4League.cpp:471
const std::set< int > & getInterfaceIDs() const
C4Network2Status Status
Definition: C4Network2.h:122
bool Close()
Definition: C4Group.cpp:755
C4Draw * pDraw
Definition: C4Draw.cpp:42
void Clear()
Definition: C4Network2.cpp:711
bool fDynamicNeeded
Definition: C4Network2.h:137
bool isActivated() const
const char * getName() const
void HandleStatusAck(const C4Network2Status &nStatus, C4Network2Client *pClient)
const unsigned int C4NetChaseTargetUpdateInterval
Definition: C4Network2.h:45
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:227
void SetScopeId(int scopeId)
Definition: C4NetIO.cpp:289
bool fWrongPassword
Definition: C4Network2.h:175
void SendJoinData(C4Network2Client *pClient)
bool GetEndReply(StdStrBuf *pMessage, class C4RoundResultsPlayers *pRoundResults)
Definition: C4League.cpp:390
InitResult InitClient(const class C4Network2Reference &Ref, bool fObserver)
C4GameLobby::MainDlg * pLobby
Definition: C4Network2.h:147
C4Network2Res::Ref AddByFile(const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iResID=-1, const char *szResName=nullptr, bool fAllowUnloadable=false)
int32_t getID() const
Definition: C4Client.h:57
const int32_t & getClientID() const
Definition: C4Network2.h:443
C4Network2Res::Ref getRefRes(int32_t iResID)
void Value(const T &rStruct)
Definition: StdCompiler.h:161
const char * getRevision() const
Definition: C4Client.h:72
bool isEnabled() const
Definition: C4Network2.h:203
bool Update(const C4Network2Reference &Ref)
Definition: C4League.cpp:345
void OnConn(C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:827
StdCopyStrBuf NetpuncherAddr
Definition: C4Network2.h:198
const int32_t C4ClientIDUnknown
Definition: C4Client.h:24
bool fPausedForVote
Definition: C4Network2.h:183
C4TimeMilliseconds tLastActivateRequest
Definition: C4Network2.h:161
C4RoundResults & RoundResults
Definition: C4Game.h:73
C4Network2ResList ResList
Definition: C4Network2.h:113
int32_t getDiffLevel(const C4ClientCore &CCore2) const
Definition: C4Client.cpp:75
bool hasConn(C4Network2IOConnection *pConn)
C4NetGameState getState() const
Definition: C4Network2.h:80
C4ClientPlayerInfos * GetInfoByClientID(int32_t iClientID) const
Definition: C4PlayerInfo.h:361
bool FindTempResFileName(const char *szFilename, char *pTarget)
void New(size_t inSize)
Definition: StdBuf.h:146
uint32_t getOutPacketCounter() const
Definition: C4Network2IO.h:282
void EndVote(C4ControlVoteType eType, bool fApprove, int32_t iData)
const char * getServerName() const
C4NetIO * DataIO()
void LeagueSurrender()
int32_t LeagueServerSignUp
Definition: C4Config.h:151
int32_t getCtrlMode() const
Definition: C4Network2.h:81
int32_t getControlPreSend() const
void CompileFunc(StdCompiler *pComp, bool fReference)
Definition: C4Network2.cpp:106
void OnClientConnect(C4Client *pClient, C4Network2IOConnection *pConn)
int32_t MasterServerSignUp
Definition: C4Config.h:147
void SetLocalCCore(const C4ClientCore &CCore)
bool SendMsgToClient(int32_t iClient, C4NetIOPacket &&rPkt)
void Quit() override
void EvaluateLeague(const char *szResultMsg, bool fSuccess, const C4RoundResultsPlayers &rLeagueInfo)
C4Network2IOProtocol getProtocol() const
int32_t GetLeagueProjectedGain() const
Definition: C4PlayerInfo.h:191
StdStrBuf getDesc() const
Definition: C4Control.cpp:1675
bool FileExists(const char *szFileName)
int32_t iTargetCtrlTick
Definition: C4Network2.h:77
int32_t MasterReferencePeriod
Definition: C4Config.h:150
C4ControlVoteType getType() const
Definition: C4Control.h:580
const unsigned int C4NetMinLeagueUpdateInterval
Definition: C4Network2.h:49
bool Init(uint16_t iPort=addr_t::IPPORT_NONE) override
Definition: C4NetIO.cpp:806
void Remove(StdSchedulerProc *pProc)
void OnClientConnect(C4Network2IOConnection *pConn)
void SetStatus(C4Network2ClientStatus enStatus)
StdIntPackAdapt< T > mkIntPackAdapt(T &rVal)
Definition: StdAdaptors.h:759
bool ChangeGameStatus(C4NetGameState enState, int32_t iTargetCtrlTick, int32_t iCtrlMode=-1)
void CloseAllDialogs(bool fWithOK)
Definition: C4Gui.cpp:704
int getProtORate(C4Network2IOProtocol eProt) const
Definition: C4Network2IO.h:153
bool fLeagueEndSent
Definition: C4Network2.h:166
C4Network2ClientList Clients
Definition: C4Network2.h:116
const int C4NetResRetrieveTimeout
Definition: C4Network2.h:37
void SetTargetTick(int32_t iTargetCtrlTick)
Definition: C4Network2.cpp:91
C4PlayerInfoList & PlayerInfos
Definition: C4Game.h:71
int32_t getData() const
Definition: C4Control.h:582
C4Client * getClientByID(int32_t iID) const
Definition: C4Client.cpp:200
int32_t ClientNextControl(int32_t iClientID)
int getProtIRate(C4Network2IOProtocol eProt) const
Definition: C4Network2IO.h:152
void DrawStatus(C4TargetFacet &cgo)
bool StreamOut()
void FlashMessage(const char *szMessage)
bool Start(const C4Network2Reference &Ref)
Definition: C4League.cpp:298
int32_t getVoteData() const
Definition: C4Network2.h:403
int32_t ClientID() const
const char * getName() const
Definition: C4Client.h:107
bool ClientReady(int32_t iClientID, int32_t iTick)
std::unique_ptr< C4NetpuncherPacket > uptr
C4GameControlNetwork Network
Definition: C4GameControl.h:67
C4NetpuncherID NetpuncherGameID
Definition: C4Network2.h:197
bool LeagueUpdateProcessReply()
bool IsInfinite() const
bool FadeIn(Screen *pOnScreen)
C4IDPacket * nextPkt(C4IDPacket *pPkt) const
Definition: C4Control.h:79
bool isNull() const
Definition: StdBuf.h:441
void SetActivated(bool fnActivated)
Definition: C4Client.h:64
bool Init(int32_t iClientID, bool fHost, int32_t iStartTick, bool fActivated, C4Network2 *pNetwork)
bool RetrieveScenario(char *szScenario)
Definition: C4Network2.cpp:589
bool ReportDisconnect(const C4ClientPlayerInfos &rSendPlayerFBIDs, C4LeagueDisconnectReason eReason)
Definition: C4League.cpp:485
C4PlayerInfoList PlayerInfos
void NotifyUserIfInactive()
Definition: C4App.cpp:89
void Synchronize(bool fSavePlayerFiles)
Definition: C4Game.cpp:3135
bool fAllowObserve
Definition: C4Network2.h:130
int32_t ControlMode
Definition: C4Config.h:155
time_t iLastOwnVoting
Definition: C4Network2.h:184
bool UpdateHaltCtrls(bool fHalt)
Definition: C4ConsoleGUI.h:152
C4Network2Players Players
Definition: C4Network2.h:119
void OnGameSynchronized()
z_stream StreamCompressor
Definition: C4Network2.h:191
bool GetStartReply(StdStrBuf *pMessage, StdStrBuf *pLeague, StdStrBuf *pStreamingAddr, int32_t *pSeed, int32_t *pMaxPlayers)
Definition: C4League.cpp:312
void UpdateChaseTarget()
bool Active
Definition: C4Window.h:273
bool Log(const char *szMessage)
Definition: C4Log.cpp:192
bool DoLobby()
Definition: C4Network2.cpp:432
bool isLocal() const
C4ClientList & Clients
Definition: C4Game.h:69
bool GetLeagueLoginData(const char *szServer, const char *szPlayerName, StdStrBuf *pAccount, StdStrBuf *pLoginToken) const
Definition: C4Config.cpp:627
bool isPassworded() const
Definition: C4Network2.h:218
bool isLeague() const
int32_t NoRuntimeJoin
Definition: C4Config.h:142
uint32_t getPacketCount() const
Definition: C4Network2IO.h:433
StdCopyStrBuf StreamAddress
bool fHost
Definition: C4Network2.h:127
bool GetReportDisconnectReply(StdStrBuf *pMessage)
Definition: C4League.cpp:500
static bool ShowModal(const char *szPlayerName, const char *szLeagueName, const char *szLeagueServerName, StdStrBuf *psAccount, StdStrBuf *psPass, bool fWarnThirdParty, bool fRegister, bool *pfRememberLogin)
Definition: C4League.cpp:661
C4IDPacket * GetVote(int32_t iClientID, C4ControlVoteType eType, int32_t iData)
void InitPuncher()
const size_t C4NetStreamingMaxBlockSize
Definition: C4Network2.h:57
void ClearStreamingBuf(unsigned int iAmount)
Definition: C4Record.cpp:339
const int C4NetMaxBehind4Activation
Definition: C4Network2.h:41
C4Control Votes
Definition: C4Network2.h:181
void OnConnect(C4Network2Client *pClient, C4Network2IOConnection *pConn, const char *szMsg, bool fFirstConnection)
bool IsNull() const
Definition: C4NetIO.cpp:503
size_t getLength() const
Definition: StdBuf.h:445
void Move(size_t iFrom, size_t inSize, size_t iTo=0)
Definition: StdBuf.h:159
int32_t getNextControlTick() const
bool CreatePostMortem(class C4PacketPostMortem *pPkt)
bool CtrlTickReached(int32_t iTick)
bool HostConnect(const C4ClientCore &CCore, C4Network2IOConnection *pConn, StdStrBuf *szReply)
int32_t GetID() const
Definition: C4PlayerInfo.h:194
bool ShowModalDlg(Dialog *pDlg, bool fDestruct=true)
C4Network2Client * GetLocal() const
bool FinishStreaming()
bool hasUDP() const
Definition: C4Network2IO.h:108
EndpointAddress AsIPv4() const
Definition: C4NetIO.cpp:341
StdStrBuf sPassword
Definition: C4Network2.h:172
bool Init(int16_t iPortTCP, int16_t iPortUDP, int16_t iPortDiscovery=-1, int16_t iPortRefServer=-1, bool fBroadcast=false, bool enable_upnp=true)
void LeagueGameEvaluate(const char *szRecordName=nullptr, const BYTE *pRecordSHA=nullptr)
bool Execute(int, pollfd *) override
Definition: C4Network2.cpp:325
bool fStreaming
Definition: C4Network2.h:187
bool hasJoinData() const
int32_t HaltCount
Definition: C4Game.h:112
bool Sync()
Definition: C4Network2.cpp:520
int32_t EnableUPnP
Definition: C4Config.h:154
bool ScheduleProcs(int iTimeout=1000/36)
std::enable_if< std::is_pod< T >::value >::type ZeroMem(T *lpMem, size_t dwSize)
Definition: Standard.h:60
C4ControlVoteType getVoteType() const
Definition: C4Network2.h:402
bool StopStreaming()
C4LeagueAction getCurrentAction() const
Definition: C4League.h:216
uint32_t iLastChaseTargetUpdate
Definition: C4Network2.h:158
bool hasTCP() const
Definition: C4Network2IO.h:107
bool HandlePuncherPacket(C4NetpuncherPacket::uptr, C4NetIO::HostAddress::AddressFamily family)
Definition: C4Network2.cpp:971
const unsigned int C4NetVotingTimeout
Definition: C4Network2.h:52
const char * getName() const
Definition: C4Client.h:69
C4Surface * Surface
Definition: C4Facet.h:117
bool InitPuncher(C4NetIO::addr_t PuncherAddr)
#define DirSep
bool Query(const StdBuf &Data, bool fBinary)
void SetRunning(bool fnRunning, int32_t inTargetTick=-1)
StdCopyStrBuf LeagueAddress
bool isAutoAccepted() const
Definition: C4Network2IO.h:289
void SetDefaultPort(uint16_t port)
Definition: C4NetIO.cpp:536
void RequestActivate()
const char * getNetIOName(C4NetIO *pNetIO)
C4NetIO * getNetClass() const
Definition: C4Network2IO.h:262
C4NetGameState eState
Definition: C4Network2.h:75
const char * GetLeagueServerAddress()
Definition: C4Config.cpp:590
C4Network2Client * GetHost()
void SetCtrlMode(C4GameControlNetworkMode enMode)
void SetLocalID(int32_t iClientID)
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:250
bool isClosed() const
Definition: C4Network2IO.h:288
uint16_t GetPort() const
Definition: C4NetIO.cpp:542
bool SendMsg(C4NetIOPacket rPkt) const
void Copy()
Definition: StdBuf.h:467
int32_t getPresentPercent() const
int32_t PortTCP
Definition: C4Config.h:153
int32_t getVer() const
Definition: C4Network2IO.h:361
C4PacketBase * getPkt() const
Definition: C4PacketBase.h:255
bool LeagueEnd(const char *szRecordName=nullptr, const BYTE *pRecordSHA=nullptr)
bool Execute(int iMaxTime, pollfd *readyfds) override
void OnVoteDialogClosed()
void LeagueSignupDisable()
float X
Definition: C4Facet.h:118
const char * getDescription() const
Definition: C4Network2.cpp:68
int32_t getStartCtrlTick() const
Definition: C4Network2.h:446
void CheckStatusReached(bool fFromFinalInit=false)
StdStrBuf QueryClientPassword()
Definition: C4Network2.cpp:784
bool SetServer(const char *szServerAddress)
C4Client * getClient() const
void AddLocalAddrs(int16_t iPortTCP, int16_t iPortUDP)
Definition: C4Network2IO.h:414
C4Application Application
Definition: C4Globals.cpp:44
C4InteractiveThread InteractiveThread
Definition: C4Application.h:45
void DeinitLeague()
bool AddAddr(const C4Network2Address &addr, bool fAnnounce)
const int16_t C4NetStdPortPuncher
Definition: C4Network2.h:33
void SetProgress(int32_t iToProgress)
Definition: C4Gui.h:2377
static C4TimeMilliseconds Now()
void * getMData()
Definition: StdBuf.h:100
void ChangeToLocal()
C4GameParameters Parameters
Definition: C4Network2.h:440
int32_t ControlRate
Definition: C4GameControl.h:88
const C4NetIO::addr_t & getAddr() const
void Add(C4Client *pClient)
Definition: C4Client.cpp:181
C4GameParameters Parameters
void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:878
StdStrBuf getDescWarning() const
Definition: C4Control.cpp:1704
bool Join(C4ClientCore &CCore, C4Network2IOConnection *pConn, StdStrBuf *szReply)
bool isOpen() const
Definition: C4Network2IO.h:285
int32_t PortUDP
Definition: C4Config.h:153
const int32_t C4ClientIDHost
Definition: C4Client.h:25
class C4GameControlNetwork * pControl
Definition: C4Network2.h:144
StdStrBuf getNetpuncherAddr() const
bool isHost() const
unsigned int GetStreamingPos() const
Definition: C4Record.h:262
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
C4VoteDialog(const char *szText, C4ControlVoteType eVoteType, int32_t iVoteData, bool fSurrender)
C4GameLobby::Countdown * pLobbyCountdown
Definition: C4Network2.h:149
const C4ClientCore & getCCore() const
Definition: C4Network2IO.h:363
bool fChasing
Definition: C4Network2.h:141
void SendAddresses(C4Network2IOConnection *pConn)