PageRenderTime 74ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 1ms

/src/network/C4Network2.cpp

https://bitbucket.org/randrian/openclonk2
C++ | 2901 lines | 1977 code | 303 blank | 621 comment | 620 complexity | 7fd9b93aaacc6fedfca7a5dba0a3ac2e MD5 | raw file
Possible License(s): WTFPL, 0BSD, LGPL-2.1, CC-BY-3.0
  1. /*
  2. * OpenClonk, http://www.openclonk.org
  3. *
  4. * Copyright (c) 2004-2009 Peter Wortmann
  5. * Copyright (c) 2004-2009 Sven Eberhardt
  6. * Copyright (c) 2005-2006, 2009 Günther Brammer
  7. * Copyright (c) 2006 Florian Groß
  8. * Copyright (c) 2007-2008 Matthes Bender
  9. * Copyright (c) 2009 Nicolas Hake
  10. * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
  11. *
  12. * Portions might be copyrighted by other authors who have contributed
  13. * to OpenClonk.
  14. *
  15. * Permission to use, copy, modify, and/or distribute this software for any
  16. * purpose with or without fee is hereby granted, provided that the above
  17. * copyright notice and this permission notice appear in all copies.
  18. * See isc_license.txt for full license and disclaimer.
  19. *
  20. * "Clonk" is a registered trademark of Matthes Bender.
  21. * See clonk_trademark_license.txt for full license.
  22. */
  23. #include <C4Include.h>
  24. #include <C4Network2.h>
  25. #include <C4Version.h>
  26. #ifndef BIG_C4INCLUDE
  27. #include <C4Log.h>
  28. #include <C4Application.h>
  29. #include <C4Console.h>
  30. #include <C4GameSave.h>
  31. #include <C4RoundResults.h>
  32. #include <C4Game.h>
  33. #include <C4GraphicsSystem.h>
  34. #include <C4GraphicsResource.h>
  35. #include <C4GameControl.h>
  36. // lobby
  37. #include <C4Gui.h>
  38. #include <C4GameLobby.h>
  39. #include <C4Network2Dialogs.h>
  40. #include <C4League.h>
  41. #endif
  42. #ifdef _WIN32
  43. #include <direct.h>
  44. #endif
  45. #ifndef HAVE_WINSOCK
  46. #include <sys/socket.h>
  47. #include <netinet/in.h>
  48. #include <arpa/inet.h>
  49. #endif
  50. // compile options
  51. #ifdef _MSC_VER
  52. #pragma warning (disable: 4355)
  53. #endif
  54. // *** C4Network2Status
  55. C4Network2Status::C4Network2Status()
  56. : eState(GS_None), iTargetCtrlTick(-1)
  57. {
  58. }
  59. const char *C4Network2Status::getStateName() const
  60. {
  61. switch(eState)
  62. {
  63. case GS_None: return "none";
  64. case GS_Init: return "init";
  65. case GS_Lobby: return "lobby";
  66. case GS_Pause: return "pause";
  67. case GS_Go: return "go";
  68. }
  69. return "???";
  70. }
  71. const char *C4Network2Status::getDescription() const
  72. {
  73. switch(eState)
  74. {
  75. case GS_None: return LoadResStr("IDS_DESC_NOTINITED");
  76. case GS_Init: return LoadResStr("IDS_DESC_WAITFORHOST");
  77. case GS_Lobby: return LoadResStr("IDS_DESC_EXPECTING");
  78. case GS_Pause: return LoadResStr("IDS_DESC_GAMEPAUSED");
  79. case GS_Go: return LoadResStr("IDS_DESC_GAMERUNNING");
  80. }
  81. return LoadResStr("IDS_DESC_UNKNOWNGAMESTATE");
  82. }
  83. void C4Network2Status::Set(C4NetGameState enState, int32_t inTargetTick)
  84. {
  85. eState = enState; iTargetCtrlTick = inTargetTick;
  86. }
  87. void C4Network2Status::SetCtrlMode(int32_t inCtrlMode)
  88. {
  89. iCtrlMode = inCtrlMode;
  90. }
  91. void C4Network2Status::SetTargetTick(int32_t inTargetCtrlTick)
  92. {
  93. iTargetCtrlTick = inTargetCtrlTick;
  94. }
  95. void C4Network2Status::Clear()
  96. {
  97. eState = GS_None; iTargetCtrlTick = -1;
  98. }
  99. void C4Network2Status::CompileFunc(StdCompiler *pComp)
  100. {
  101. CompileFunc(pComp, false);
  102. }
  103. void C4Network2Status::CompileFunc(StdCompiler *pComp, bool fReference)
  104. {
  105. StdEnumEntry<C4NetGameState> GameStates[] =
  106. {
  107. { "None", GS_None },
  108. { "Init", GS_Init },
  109. { "Lobby", GS_Lobby },
  110. { "Paused", GS_Pause },
  111. { "Running", GS_Go },
  112. };
  113. pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(eState, GameStates), "State", GS_None));
  114. pComp->Value(mkNamingAdapt(mkIntPackAdapt(iCtrlMode), "CtrlMode", -1));
  115. if(!fReference)
  116. pComp->Value(mkNamingAdapt(mkIntPackAdapt(iTargetCtrlTick), "TargetTick", -1));
  117. }
  118. // *** C4Network2
  119. C4Network2::C4Network2()
  120. : Clients(&NetIO),
  121. fAllowJoin(false),
  122. iDynamicTick(-1), fDynamicNeeded(false),
  123. fStatusAck(false), fStatusReached(false),
  124. fChasing(false),
  125. pLobby(NULL), fLobbyRunning(false), pLobbyCountdown(NULL),
  126. pControl(NULL),
  127. iNextClientID(0),
  128. iLastActivateRequest(0),
  129. iLastChaseTargetUpdate(0),
  130. iLastReferenceUpdate(0),
  131. iLastLeagueUpdate(0),
  132. pLeagueClient(NULL),
  133. fDelayedActivateReq(false),
  134. pVoteDialog(NULL),
  135. fPausedForVote(false),
  136. iLastOwnVoting(0),
  137. fStreaming(NULL)
  138. {
  139. }
  140. C4Network2::~C4Network2()
  141. {
  142. Clear();
  143. }
  144. bool C4Network2::InitHost(bool fLobby)
  145. {
  146. if(isEnabled()) Clear();
  147. // initialize everything
  148. Status.Set(fLobby ? GS_Lobby : GS_Go, ::Control.ControlTick);
  149. Status.SetCtrlMode(Config.Network.ControlMode);
  150. fHost = true;
  151. fStatusAck = fStatusReached = true;
  152. fChasing = false;
  153. fAllowJoin = false;
  154. iNextClientID = C4ClientIDStart;
  155. // initialize client list
  156. Clients.Init(&Game.Clients, true);
  157. // initialize resource list
  158. if(!ResList.Init(Game.Clients.getLocalID(), &NetIO))
  159. { LogFatal("Network: failed to initialize resource list!"); Clear(); return false; }
  160. if(!Game.Parameters.InitNetwork(&ResList))
  161. return false;
  162. // create initial dynamic
  163. if(!CreateDynamic(true))
  164. return false;
  165. // initialize net i/o
  166. if(!InitNetIO(false, true))
  167. { Clear(); return false; }
  168. // init network control
  169. pControl = &::Control.Network;
  170. pControl->Init(C4ClientIDHost, true, ::Control.getNextControlTick(), true, this);
  171. // init league
  172. bool fCancel = true;
  173. if(!InitLeague(&fCancel) || !LeagueStart(&fCancel))
  174. {
  175. // deinit league
  176. DeinitLeague();
  177. // user cancelled?
  178. if(fCancel)
  179. return false;
  180. // in console mode, bail out
  181. #ifdef USE_CONSOLE
  182. return false;
  183. #endif
  184. }
  185. // allow connect
  186. NetIO.SetAcceptMode(true);
  187. // timer
  188. Application.Add(this);
  189. // ok
  190. return true;
  191. }
  192. C4Network2::InitResult C4Network2::InitClient(const C4Network2Reference &Ref, bool fObserver)
  193. {
  194. if(isEnabled()) Clear();
  195. // Get host core
  196. const C4ClientCore &HostCore = Ref.Parameters.Clients.getHost()->getCore();
  197. // repeat if wrong password
  198. fWrongPassword = Ref.isPasswordNeeded();
  199. StdStrBuf Password;
  200. for(;;)
  201. {
  202. // ask for password (again)?
  203. if(fWrongPassword)
  204. {
  205. Password.Take(QueryClientPassword());
  206. if(!Password.getLength())
  207. return IR_Error;
  208. fWrongPassword = false;
  209. }
  210. // copy addresses
  211. C4Network2Address Addrs[C4ClientMaxAddr];
  212. for(int i = 0; i < Ref.getAddrCnt(); i++)
  213. Addrs[i] = Ref.getAddr(i);
  214. // Try to connect to host
  215. if(InitClient(Addrs, Ref.getAddrCnt(), HostCore, Password.getData()) == IR_Fatal)
  216. return IR_Fatal;
  217. // success?
  218. if(isEnabled())
  219. break;
  220. // Retry only for wrong password
  221. if(!fWrongPassword)
  222. {
  223. LogSilent("Network: Could not connect!");
  224. return IR_Error;
  225. }
  226. }
  227. // initialize ressources
  228. if(!Game.Parameters.InitNetwork(&ResList))
  229. return IR_Fatal;
  230. // init league
  231. if(!InitLeague(NULL))
  232. {
  233. // deinit league
  234. DeinitLeague();
  235. return IR_Fatal;
  236. }
  237. // allow connect
  238. NetIO.SetAcceptMode(true);
  239. // timer
  240. Application.Add(this);
  241. // ok, success
  242. return IR_Success;
  243. }
  244. C4Network2::InitResult C4Network2::InitClient(const class C4Network2Address *pAddrs, int iAddrCount, const C4ClientCore &HostCore, const char *szPassword)
  245. {
  246. // initialization
  247. Status.Set(GS_Init, -1);
  248. fHost = false;
  249. fStatusAck = fStatusReached = true;
  250. fChasing = true;
  251. fAllowJoin = false;
  252. // initialize client list
  253. Game.Clients.Init(C4ClientIDUnknown);
  254. Clients.Init(&Game.Clients, false);
  255. // initialize resource list
  256. if(!ResList.Init(Game.Clients.getLocalID(), &NetIO))
  257. { LogFatal(LoadResStr("IDS_NET_ERR_INITRESLIST")); Clear(); return IR_Fatal; }
  258. // initialize net i/o
  259. if(!InitNetIO(true, false))
  260. { Clear(); return IR_Fatal; }
  261. // set network control
  262. pControl = &::Control.Network;
  263. // set exclusive connection mode
  264. NetIO.SetExclusiveConnMode(true);
  265. // try to connect host
  266. StdStrBuf strAddresses; int iSuccesses = 0;
  267. for(int i = 0; i < iAddrCount; i++)
  268. if(!pAddrs[i].isIPNull())
  269. {
  270. // connection
  271. if(!NetIO.Connect(pAddrs[i].getAddr(), pAddrs[i].getProtocol(), HostCore, szPassword))
  272. continue;
  273. // format for message
  274. if(strAddresses.getLength())
  275. strAddresses.Append(", ");
  276. strAddresses.Append(pAddrs[i].toString());
  277. iSuccesses++;
  278. }
  279. // no connection attempt running?
  280. if(!iSuccesses)
  281. { Clear(); return IR_Error; }
  282. // log
  283. StdStrBuf strMessage = FormatString(LoadResStr("IDS_NET_CONNECTHOST"), strAddresses.getData());
  284. Log(strMessage.getData());
  285. // show box
  286. C4GUI::MessageDialog *pDlg = NULL;
  287. if(::pGUI && !Console.Active)
  288. {
  289. // create & show
  290. pDlg = new C4GUI::MessageDialog(strMessage.getData(), LoadResStr("IDS_NET_JOINGAME"),
  291. C4GUI::MessageDialog::btnAbort, C4GUI::Ico_NetWait, C4GUI::MessageDialog::dsMedium);
  292. if(!pDlg->Show(::pGUI, true)) { Clear(); return IR_Fatal; }
  293. }
  294. // wait for connect / timeout / abort by user (host will change status on succesful connect)
  295. while(Status.getState() == GS_Init)
  296. {
  297. if(!Application.ScheduleProcs(100))
  298. { if(::pGUI && pDlg) delete pDlg; return IR_Fatal;}
  299. if(pDlg && pDlg->IsAborted())
  300. { if(::pGUI && pDlg) delete pDlg; return IR_Fatal; }
  301. }
  302. // Close dialog
  303. if(::pGUI && pDlg) delete pDlg;
  304. // error?
  305. if(!isEnabled())
  306. return IR_Error;
  307. // deactivate exclusive connection mode
  308. NetIO.SetExclusiveConnMode(false);
  309. return IR_Success;
  310. }
  311. bool C4Network2::DoLobby()
  312. {
  313. // shouldn't do lobby?
  314. if(!isEnabled() || (!isHost() && !isLobbyActive()))
  315. return true;
  316. // lobby runs
  317. fLobbyRunning = true;
  318. fAllowJoin = true;
  319. Log(LoadResStr("IDS_NET_LOBBYWAITING"));
  320. // client: lobby status reached, message to host
  321. if(!isHost())
  322. CheckStatusReached();
  323. // host: set lobby mode
  324. else
  325. ChangeGameStatus(GS_Lobby, 0);
  326. // determine lobby type
  327. bool fFullscreenLobby = !Console.Active && (lpDDraw->GetEngine() != GFXENGN_NOGFX);
  328. if(!fFullscreenLobby)
  329. {
  330. // console lobby - update console
  331. if (Console.Active) Console.UpdateMenus();
  332. // init lobby countdown if specified
  333. if (Game.iLobbyTimeout) StartLobbyCountdown(Game.iLobbyTimeout);
  334. // do console lobby
  335. while(isLobbyActive())
  336. if (!Application.ScheduleProcs())
  337. { Clear(); return false; }
  338. }
  339. else
  340. {
  341. // fullscreen lobby
  342. // init lobby dialog
  343. pLobby = new C4GameLobby::MainDlg(isHost());
  344. if (!pLobby->FadeIn(::pGUI)) { delete pLobby; pLobby = NULL; Clear(); return false; }
  345. // init lobby countdown if specified
  346. if (Game.iLobbyTimeout) StartLobbyCountdown(Game.iLobbyTimeout);
  347. // while state lobby: keep looping
  348. while(isLobbyActive() && ::pGUI && pLobby && pLobby->IsShown())
  349. if (!Application.ScheduleProcs())
  350. { Clear(); return false; }
  351. // check whether lobby was aborted; first checking ::pGUI
  352. // (because an external call to Game.Clear() would invalidate pLobby)
  353. if (!::pGUI) { pLobby = NULL; Clear(); return false; }
  354. if (pLobby && pLobby->IsAborted()) { delete pLobby; pLobby = NULL; Clear(); return false; }
  355. // deinit lobby
  356. if (pLobby && pLobby->IsShown()) pLobby->Close(true);
  357. delete pLobby; pLobby = NULL;
  358. // close any other dialogs
  359. if (::pGUI) ::pGUI->CloseAllDialogs(false);
  360. }
  361. // lobby end
  362. delete pLobbyCountdown; pLobbyCountdown = NULL;
  363. fLobbyRunning = false;
  364. fAllowJoin = !Config.Network.NoRuntimeJoin;
  365. // notify user that the lobby has ended (for people who tasked out)
  366. Application.NotifyUserIfInactive();
  367. // notify lobby end
  368. bool fGameGo = isEnabled();
  369. if (fGameGo) Log(LoadResStr("IDS_PRC_GAMEGO"));;
  370. // disabled?
  371. return fGameGo;
  372. }
  373. bool C4Network2::Start()
  374. {
  375. if(!isEnabled() || !isHost()) return false;
  376. // change mode: go
  377. ChangeGameStatus(GS_Go, ::Control.ControlTick);
  378. return true;
  379. }
  380. bool C4Network2::Pause()
  381. {
  382. if(!isEnabled() || !isHost()) return false;
  383. // change mode: pause
  384. return ChangeGameStatus(GS_Pause, ::Control.getNextControlTick());
  385. }
  386. bool C4Network2::Sync()
  387. {
  388. // host only
  389. if(!isEnabled() || !isHost()) return false;
  390. // already syncing the network?
  391. if(!fStatusAck)
  392. {
  393. // maybe we are already sync?
  394. if(fStatusReached) CheckStatusAck();
  395. return true;
  396. }
  397. // already sync?
  398. if(isFrozen()) return true;
  399. // ok, so let's do a sync: change in the same state we are already in
  400. return ChangeGameStatus(Status.getState(), ::Control.getNextControlTick());
  401. }
  402. bool C4Network2::FinalInit()
  403. {
  404. // check reach
  405. CheckStatusReached(true);
  406. // reached, waiting for ack?
  407. if(fStatusReached && !fStatusAck)
  408. {
  409. // wait for go acknowledgement
  410. Log(LoadResStr("IDS_NET_JOINREADY"));
  411. // any pending keyboard commands should not be routed to cancel the wait dialog - flish the message queue!
  412. if(!Application.FlushMessages()) return false;
  413. // show box
  414. C4GUI::Dialog *pDlg = NULL;
  415. if(::pGUI && !Console.Active)
  416. {
  417. // seperate dlgs for host/client
  418. if (isHost())
  419. pDlg = new C4Network2StartWaitDlg();
  420. else
  421. pDlg = new C4GUI::MessageDialog(LoadResStr("IDS_NET_WAITFORSTART"), LoadResStr("IDS_NET_CAPTION"),
  422. C4GUI::MessageDialog::btnAbort, C4GUI::Ico_NetWait, C4GUI::MessageDialog::dsSmall);
  423. // show it
  424. if(!pDlg->Show(::pGUI, true)) return false;
  425. }
  426. // wait for acknowledgement
  427. while(fStatusReached && !fStatusAck)
  428. {
  429. if(pDlg)
  430. {
  431. // execute
  432. if(!pDlg->Execute()) { delete pDlg; Clear(); return false; }
  433. // aborted?
  434. if(!::pGUI) { Clear(); return false;}
  435. if(pDlg->IsAborted()) { delete pDlg; Clear(); return false; }
  436. }
  437. else if(!Application.ScheduleProcs())
  438. { Clear(); return false; }
  439. }
  440. if(::pGUI && pDlg) delete pDlg;
  441. // log
  442. Log(LoadResStr("IDS_NET_START"));
  443. }
  444. // synchronize
  445. Game.SyncClearance();
  446. Game.Synchronize(false);
  447. // finished
  448. return isEnabled();
  449. }
  450. bool C4Network2::RetrieveScenario(char *szScenario)
  451. {
  452. // client only
  453. if(isHost()) return false;
  454. // wait for scenario
  455. C4Network2Res::Ref pScenario = RetrieveRes(*Game.Parameters.Scenario.getResCore(),
  456. C4NetResRetrieveTimeout, LoadResStr("IDS_NET_RES_SCENARIO"));
  457. if(!pScenario)
  458. return false;
  459. // wait for dynamic data
  460. C4Network2Res::Ref pDynamic = RetrieveRes(ResDynamic, C4NetResRetrieveTimeout, LoadResStr("IDS_NET_RES_DYNAMIC"));
  461. if(!pDynamic)
  462. return false;
  463. // create unpacked copy of scenario
  464. if(!ResList.FindTempResFileName(FormatString("Combined%d.c4s", Game.Clients.getLocalID()).getData(), szScenario) ||
  465. !C4Group_CopyItem(pScenario->getFile(), szScenario) ||
  466. !C4Group_UnpackDirectory(szScenario))
  467. return false;
  468. // create unpacked copy of dynamic data
  469. char szTempDynamic[_MAX_PATH + 1];
  470. if(!ResList.FindTempResFileName(pDynamic->getFile(), szTempDynamic) ||
  471. !C4Group_CopyItem(pDynamic->getFile(), szTempDynamic) ||
  472. !C4Group_UnpackDirectory(szTempDynamic))
  473. return false;
  474. // unpack Material.c4g if materials need to be merged
  475. StdStrBuf MaterialScenario, MaterialDynamic;
  476. MaterialScenario.Format("%s" DirSep C4CFN_Material, szScenario);
  477. MaterialDynamic.Format("%s" DirSep C4CFN_Material, szTempDynamic);
  478. if(FileExists(MaterialScenario.getData()) && FileExists(MaterialDynamic.getData()))
  479. if(!C4Group_UnpackDirectory(MaterialScenario.getData()) ||
  480. !C4Group_UnpackDirectory(MaterialDynamic.getData()))
  481. return false;
  482. // move all dynamic files to scenario
  483. C4Group ScenGrp;
  484. if(!ScenGrp.Open(szScenario) ||
  485. !ScenGrp.Merge(szTempDynamic))
  486. return false;
  487. ScenGrp.Close();
  488. // remove dynamic temp file
  489. EraseDirectory(szTempDynamic);
  490. // remove dynamic - isn't needed any more and will soon be out-of-date
  491. pDynamic->Remove();
  492. return true;
  493. }
  494. void C4Network2::OnSec1Timer()
  495. {
  496. Execute();
  497. }
  498. void C4Network2::Execute()
  499. {
  500. // client connections
  501. Clients.DoConnectAttempts();
  502. // status reached?
  503. CheckStatusReached();
  504. if(isHost())
  505. {
  506. // remove dynamic
  507. if(!ResDynamic.isNull() && ::Control.ControlTick > iDynamicTick)
  508. RemoveDynamic();
  509. // Set chase target
  510. UpdateChaseTarget();
  511. // check for inactive clients and deactivate them
  512. DeactivateInactiveClients();
  513. // reference
  514. if(!iLastReferenceUpdate || time(NULL) > (time_t) (iLastReferenceUpdate + C4NetReferenceUpdateInterval))
  515. if (NetIO.IsReferenceNeeded())
  516. {
  517. // create
  518. C4Network2Reference *pRef = new C4Network2Reference();
  519. pRef->InitLocal();
  520. // set
  521. NetIO.SetReference(pRef);
  522. iLastReferenceUpdate = time(NULL);
  523. }
  524. // league server reference
  525. if(!iLastLeagueUpdate || time(NULL) > (time_t) (iLastLeagueUpdate + iLeagueUpdateDelay))
  526. {
  527. LeagueUpdate();
  528. }
  529. // league update reply receive
  530. if(pLeagueClient && fHost && !pLeagueClient->isBusy() && pLeagueClient->getCurrentAction() == C4LA_Update)
  531. {
  532. LeagueUpdateProcessReply();
  533. }
  534. // voting timeout
  535. if(Votes.firstPkt() && time(NULL) > (time_t) (iVoteStartTime + C4NetVotingTimeout))
  536. {
  537. C4ControlVote *pVote = static_cast<C4ControlVote *>(Votes.firstPkt()->getPkt());
  538. ::Control.DoInput(
  539. CID_VoteEnd,
  540. new C4ControlVoteEnd(pVote->getType(), false, pVote->getData()),
  541. CDT_Sync);
  542. iVoteStartTime = time(NULL);
  543. }
  544. // record streaming
  545. if(fStreaming)
  546. {
  547. StreamIn(false);
  548. StreamOut();
  549. }
  550. }
  551. else
  552. {
  553. // request activate, if neccessary
  554. if(iLastActivateRequest) RequestActivate();
  555. }
  556. }
  557. void C4Network2::Clear()
  558. {
  559. // stop timer
  560. Application.Remove(this);
  561. // stop streaming
  562. StopStreaming();
  563. // clear league
  564. if(pLeagueClient)
  565. {
  566. LeagueEnd();
  567. DeinitLeague();
  568. }
  569. // stop lobby countdown
  570. delete pLobbyCountdown; pLobbyCountdown = NULL;
  571. // cancel lobby
  572. delete pLobby; pLobby = NULL;
  573. fLobbyRunning = false;
  574. // deactivate
  575. Status.Clear();
  576. fStatusAck = fStatusReached = true;
  577. // if control mode is network: change to local
  578. if(::Control.isNetwork())
  579. ::Control.ChangeToLocal();
  580. // clear all player infos
  581. Players.Clear();
  582. // remove all clients
  583. Clients.Clear();
  584. // close net classes
  585. NetIO.Clear();
  586. // clear ressources
  587. ResList.Clear();
  588. // clear password
  589. sPassword.Clear();
  590. // stuff
  591. fAllowJoin = false;
  592. iDynamicTick = -1; fDynamicNeeded = false;
  593. iLastActivateRequest = iLastChaseTargetUpdate = iLastReferenceUpdate = iLastLeagueUpdate = 0;
  594. fDelayedActivateReq = false;
  595. if(::pGUI) delete pVoteDialog; pVoteDialog = NULL;
  596. fPausedForVote = false;
  597. iLastOwnVoting = 0;
  598. // don't clear fPasswordNeeded here, it's needed by InitClient
  599. }
  600. bool C4Network2::ToggleAllowJoin()
  601. {
  602. // just toggle
  603. AllowJoin(!fAllowJoin);
  604. return true; // toggled
  605. }
  606. bool C4Network2::ToggleClientListDlg()
  607. {
  608. C4Network2ClientListDlg::Toggle();
  609. return true;
  610. }
  611. void C4Network2::SetPassword(const char *szToPassword)
  612. {
  613. bool fHadPassword = isPassworded();
  614. // clear password?
  615. if (!szToPassword || !*szToPassword)
  616. sPassword.Clear();
  617. else
  618. // no? then set it
  619. sPassword.Copy(szToPassword);
  620. // if the has-password-state has changed, the reference is invalidated
  621. if (fHadPassword != isPassworded()) InvalidateReference();
  622. }
  623. StdStrBuf C4Network2::QueryClientPassword()
  624. {
  625. // ask client for a password; return nothing if user canceled
  626. StdStrBuf sCaption; sCaption.Copy(LoadResStr("IDS_MSG_ENTERPASSWORD"));
  627. C4GUI::InputDialog *pInputDlg = new C4GUI::InputDialog(LoadResStr("IDS_MSG_ENTERPASSWORD"), sCaption.getData(), C4GUI::Ico_Ex_Locked, NULL, false);
  628. pInputDlg->SetDelOnClose(false);
  629. if (!::pGUI->ShowModalDlg(pInputDlg, false))
  630. {
  631. if (C4GUI::IsGUIValid()) delete pInputDlg;
  632. return StdStrBuf();
  633. }
  634. // copy to buffer
  635. StdStrBuf Buf; Buf.Copy(pInputDlg->GetInputText());
  636. delete pInputDlg;
  637. return Buf;
  638. }
  639. void C4Network2::AllowJoin(bool fAllow)
  640. {
  641. if(!isHost()) return;
  642. fAllowJoin = fAllow;
  643. if (Game.IsRunning)
  644. {
  645. ::GraphicsSystem.FlashMessage(LoadResStr(fAllowJoin ? "IDS_NET_RUNTIMEJOINFREE" : "IDS_NET_RUNTIMEJOINBARRED"));
  646. Config.Network.NoRuntimeJoin = !fAllowJoin;
  647. }
  648. }
  649. void C4Network2::SetAllowObserve(bool fAllow)
  650. {
  651. if(!isHost()) return;
  652. fAllowObserve = fAllow;
  653. }
  654. void C4Network2::SetCtrlMode(int32_t iCtrlMode)
  655. {
  656. if(!isHost()) return;
  657. // no change?
  658. if(iCtrlMode == Status.getCtrlMode()) return;
  659. // change game status
  660. ChangeGameStatus(Status.getState(), ::Control.ControlTick, iCtrlMode);
  661. }
  662. void C4Network2::OnConn(C4Network2IOConnection *pConn)
  663. {
  664. // Nothing to do atm... New pending connections are managed mainly by C4Network2IO
  665. // until they are accepted, see PID_Conn/PID_ConnRe handlers in HandlePacket.
  666. // Note this won't get called anymore because of this (see C4Network2IO::OnConn)
  667. }
  668. void C4Network2::OnDisconn(C4Network2IOConnection *pConn)
  669. {
  670. // could not establish host connection?
  671. if(Status.getState() == GS_Init && !isHost())
  672. {
  673. if(!NetIO.getConnectionCount())
  674. Clear();
  675. return;
  676. }
  677. // connection failed?
  678. if(pConn->isFailed())
  679. {
  680. // call handler
  681. OnConnectFail(pConn);
  682. return;
  683. }
  684. // search client
  685. C4Network2Client *pClient = Clients.GetClient(pConn);
  686. // not found? Search by ID (not associated yet, half-accepted connection)
  687. if(!pClient) pClient = Clients.GetClientByID(pConn->getClientID());
  688. // not found? ignore
  689. if(!pClient) return;
  690. // remove connection
  691. pClient->RemoveConn(pConn);
  692. // create post-mortem if needed
  693. C4PacketPostMortem PostMortem;
  694. if(pConn->CreatePostMortem(&PostMortem))
  695. {
  696. LogSilentF("Network: Sending %d packets for recovery (%d-%d)", PostMortem.getPacketCount(), pConn->getOutPacketCounter() - PostMortem.getPacketCount(), pConn->getOutPacketCounter() - 1);
  697. // This might fail because of this disconnect
  698. // (If it's the only host connection. We're toast then anyway.)
  699. if(!Clients.SendMsgToClient(pConn->getClientID(), MkC4NetIOPacket(PID_PostMortem, PostMortem)))
  700. assert(isHost() || !Clients.GetHost()->isConnected());
  701. }
  702. // call handler
  703. OnDisconnect(pClient, pConn);
  704. }
  705. void C4Network2::HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
  706. {
  707. // find associated client
  708. C4Network2Client *pClient = Clients.GetClient(pConn);
  709. if(!pClient) pClient = Clients.GetClientByID(pConn->getClientID());
  710. // local? ignore
  711. if(pClient && pClient->isLocal()) { pConn->Close(); return; }
  712. #define GETPKT(type, name) \
  713. assert(pPacket); const type &name = \
  714. /*dynamic_cast*/ static_cast<const type &>(*pPacket);
  715. switch(cStatus)
  716. {
  717. case PID_Conn: // connection request
  718. {
  719. if(!pConn->isOpen()) break;
  720. GETPKT(C4PacketConn, rPkt);
  721. HandleConn(rPkt, pConn, pClient);
  722. }
  723. break;
  724. case PID_ConnRe: // connection request reply
  725. {
  726. GETPKT(C4PacketConnRe, rPkt);
  727. HandleConnRe(rPkt, pConn, pClient);
  728. }
  729. break;
  730. case PID_JoinData:
  731. {
  732. // host->client only
  733. if(isHost() || !pClient || !pClient->isHost()) break;
  734. if(!pConn->isOpen()) break;
  735. // handle
  736. GETPKT(C4PacketJoinData, rPkt)
  737. HandleJoinData(rPkt);
  738. }
  739. break;
  740. case PID_Status: // status change
  741. {
  742. // by host only
  743. if(isHost() || !pClient || !pClient->isHost()) break;
  744. if(!pConn->isOpen()) break;
  745. // must be initialized
  746. if(Status.getState() == GS_Init) break;
  747. // handle
  748. GETPKT(C4Network2Status, rPkt);
  749. HandleStatus(rPkt);
  750. }
  751. break;
  752. case PID_StatusAck: // status change acknowledgement
  753. {
  754. // host->client / client->host only
  755. if(!pClient) break;
  756. if(!isHost() && !pClient->isHost()) break;
  757. // must be initialized
  758. if(Status.getState() == GS_Init) break;
  759. // handle
  760. GETPKT(C4Network2Status, rPkt);
  761. HandleStatusAck(rPkt, pClient);
  762. }
  763. break;
  764. case PID_ClientActReq: // client activation request
  765. {
  766. // client->host only
  767. if(!isHost() || !pClient || pClient->isHost()) break;
  768. // must be initialized
  769. if(Status.getState() == GS_Init) break;
  770. // handle
  771. GETPKT(C4PacketActivateReq, rPkt)
  772. HandleActivateReq(rPkt.getTick(), pClient);
  773. }
  774. break;
  775. }
  776. #undef GETPKT
  777. }
  778. void C4Network2::HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
  779. {
  780. // find associated client
  781. C4Network2Client *pClient = Clients.GetClient(pConn);
  782. if(!pClient) pClient = Clients.GetClientByID(pConn->getClientID());
  783. // forward directly to lobby
  784. if (pLobby) pLobby->HandlePacket(cStatus, pBasePkt, pClient);
  785. }
  786. void C4Network2::OnGameSynchronized()
  787. {
  788. // savegame needed?
  789. if(fDynamicNeeded)
  790. {
  791. // create dynamic
  792. bool fSuccess = CreateDynamic(false);
  793. // check for clients that still need join-data
  794. C4Network2Client *pClient = NULL;
  795. while(pClient = Clients.GetNextClient(pClient))
  796. if(!pClient->hasJoinData())
  797. if(fSuccess)
  798. // now we can provide join data: send it
  799. SendJoinData(pClient);
  800. else
  801. // join data could not be created: emergency kick
  802. Game.Clients.CtrlRemove(pClient->getClient(), LoadResStr("IDS_ERR_ERRORWHILECREATINGJOINDAT"));
  803. }
  804. }
  805. void C4Network2::DrawStatus(C4TargetFacet &cgo)
  806. {
  807. if(!isEnabled()) return;
  808. C4Network2Client *pLocal = Clients.GetLocal();
  809. StdStrBuf Stat;
  810. // local client status
  811. Stat.AppendFormat("Local: %s %s %s (ID %d)",
  812. pLocal->isObserver() ? "Observing" : pLocal->isActivated() ? "Active" : "Inactive", pLocal->isHost() ? "host" : "client",
  813. pLocal->getName(), pLocal->getID());
  814. // game status
  815. Stat.AppendFormat( "|Game Status: %s (tick %d)%s%s",
  816. Status.getStateName(), Status.getTargetCtrlTick(),
  817. fStatusReached ? " reached" : "", fStatusAck ? " ack" : "");
  818. // available protocols
  819. C4NetIO *pMsgIO = NetIO.MsgIO(), *pDataIO = NetIO.DataIO();
  820. if(pMsgIO && pDataIO)
  821. {
  822. C4Network2IOProtocol eMsgProt = NetIO.getNetIOProt(pMsgIO),
  823. eDataProt = NetIO.getNetIOProt(pDataIO);
  824. int32_t iMsgPort = 0, iDataPort = 0;
  825. switch(eMsgProt)
  826. {
  827. case P_TCP: iMsgPort = Config.Network.PortTCP; break;
  828. case P_UDP: iMsgPort = Config.Network.PortUDP; break;
  829. }
  830. switch(eDataProt)
  831. {
  832. case P_TCP: iDataPort = Config.Network.PortTCP; break;
  833. case P_UDP: iDataPort = Config.Network.PortUDP; break;
  834. }
  835. Stat.AppendFormat( "|Protocols: %s: %s (%d i%d o%d bc%d)",
  836. pMsgIO != pDataIO ? "Msg" : "Msg/Data",
  837. NetIO.getNetIOName(pMsgIO), iMsgPort,
  838. NetIO.getProtIRate(eMsgProt), NetIO.getProtORate(eMsgProt), NetIO.getProtBCRate(eMsgProt));
  839. if(pMsgIO != pDataIO)
  840. Stat.AppendFormat( ", Data: %s (%d i%d o%d bc%d)",
  841. NetIO.getNetIOName(pDataIO), iDataPort,
  842. NetIO.getProtIRate(eDataProt), NetIO.getProtORate(eDataProt), NetIO.getProtBCRate(eDataProt));
  843. }
  844. else
  845. Stat.Append("|Protocols: none");
  846. // some control statistics
  847. Stat.AppendFormat( "|Control: %s, Tick %d, Behind %d, Rate %d, PreSend %d, ACT: %d",
  848. Status.getCtrlMode() == CNM_Decentral ? "Decentral" : Status.getCtrlMode() == CNM_Central ? "Central" : "Async",
  849. ::Control.ControlTick, pControl->GetBehind(::Control.ControlTick),
  850. ::Control.ControlRate, pControl->getControlPreSend(), pControl->getAvgControlSendTime());
  851. // Streaming statistics
  852. if(fStreaming)
  853. Stat.AppendFormat( "|Streaming: %d waiting, %d in, %d out, %d sent",
  854. pStreamedRecord ? pStreamedRecord->GetStreamingBuf().getSize() : 0,
  855. pStreamedRecord ? pStreamedRecord->GetStreamingPos() : 0,
  856. getPendingStreamData(),
  857. iCurrentStreamPosition);
  858. // clients
  859. Stat.Append("|Clients:");
  860. for(C4Network2Client *pClient = Clients.GetNextClient(NULL); pClient; pClient = Clients.GetNextClient(pClient))
  861. {
  862. // ignore local
  863. if(pClient->isLocal()) continue;
  864. // client status
  865. const C4ClientCore &Core = pClient->getCore();
  866. const char *szClientStatus = "";
  867. switch(pClient->getStatus())
  868. {
  869. case NCS_Joining: szClientStatus = " (joining)"; break;
  870. case NCS_Chasing: szClientStatus = " (chasing)"; break;
  871. case NCS_NotReady: szClientStatus = " (!rdy)"; break;
  872. case NCS_Remove: szClientStatus = " (removed)"; break;
  873. }
  874. Stat.AppendFormat( "|- %s %s %s (ID %d) (wait %d ms, behind %d)%s%s",
  875. Core.isObserver() ? "Observing" : Core.isActivated() ? "Active" : "Inactive", Core.isHost() ? "host" : "client",
  876. Core.getName(), Core.getID(),
  877. pControl->ClientPerfStat(pClient->getID()),
  878. ::Control.ControlTick - pControl->ClientNextControl(pClient->getID()),
  879. szClientStatus,
  880. pClient->isActivated() && !pControl->ClientReady(pClient->getID(), ::Control.ControlTick) ? " (!ctrl)" : "");
  881. // connections
  882. if(pClient->isConnected())
  883. {
  884. Stat.AppendFormat( "| Connections: %s: %s (%s:%d p%d l%d)",
  885. pClient->getMsgConn() == pClient->getDataConn() ? "Msg/Data" : "Msg",
  886. NetIO.getNetIOName(pClient->getMsgConn()->getNetClass()),
  887. inet_ntoa(pClient->getMsgConn()->getPeerAddr().sin_addr),
  888. htons(pClient->getMsgConn()->getPeerAddr().sin_port),
  889. pClient->getMsgConn()->getPingTime(),
  890. pClient->getMsgConn()->getPacketLoss());
  891. if(pClient->getMsgConn() != pClient->getDataConn())
  892. Stat.AppendFormat( ", Data: %s (%s:%d p%d l%d)",
  893. NetIO.getNetIOName(pClient->getDataConn()->getNetClass()),
  894. inet_ntoa(pClient->getDataConn()->getPeerAddr().sin_addr),
  895. htons(pClient->getDataConn()->getPeerAddr().sin_port),
  896. pClient->getDataConn()->getPingTime(),
  897. pClient->getDataConn()->getPacketLoss());
  898. }
  899. else
  900. Stat.Append("| Not connected");
  901. }
  902. if(!Clients.GetNextClient(NULL))
  903. Stat.Append("| - none -");
  904. // draw
  905. Application.DDraw->TextOut(Stat.getData(), ::GraphicsResource.FontRegular, 1.0, cgo.Surface,cgo.X + 20,cgo.Y + 50);
  906. }
  907. bool C4Network2::InitNetIO(bool fNoClientID, bool fHost)
  908. {
  909. // clear
  910. NetIO.Clear();
  911. Config.Network.CheckPortsForCollisions();
  912. // discovery: disable for client
  913. int16_t iPortDiscovery = fHost ? Config.Network.PortDiscovery : -1;
  914. int16_t iPortRefServer = fHost ? Config.Network.PortRefServer : -1;
  915. // init subclass
  916. if(!NetIO.Init(Config.Network.PortTCP, Config.Network.PortUDP, iPortDiscovery, iPortRefServer, fHost))
  917. return false;
  918. // set core (unset ID if sepecified, has to be set later)
  919. C4ClientCore Core = Game.Clients.getLocalCore();
  920. if(fNoClientID) Core.SetID(C4ClientIDUnknown);
  921. NetIO.SetLocalCCore(Core);
  922. // safe addresses of local client
  923. Clients.GetLocal()->AddLocalAddrs(
  924. NetIO.hasTCP() ? Config.Network.PortTCP : -1,
  925. NetIO.hasUDP() ? Config.Network.PortUDP : -1);
  926. // ok
  927. return true;
  928. }
  929. void C4Network2::HandleConn(const C4PacketConn &Pkt, C4Network2IOConnection *pConn, C4Network2Client *pClient)
  930. {
  931. // security
  932. if(!pConn) return;
  933. // Handles a connect request (packet PID_Conn).
  934. // Check if this peer should be allowed to connect, make space for the new connection.
  935. // connection is closed?
  936. if(pConn->isClosed())
  937. return;
  938. // set up core
  939. const C4ClientCore &CCore = Pkt.getCCore();
  940. C4ClientCore NewCCore = CCore;
  941. // accept connection?
  942. StdStrBuf reply;
  943. bool fOK = false;
  944. // search client
  945. if(!pClient && Pkt.getCCore().getID() != C4ClientIDUnknown)
  946. pClient = Clients.GetClient(Pkt.getCCore());
  947. // check engine version
  948. bool fWrongPassword = false;
  949. if(Pkt.getVer() != C4XVERBUILD)
  950. {
  951. reply.Format("wrong engine (%d, I have %d)", Pkt.getVer(), C4XVERBUILD);
  952. fOK = false;
  953. }
  954. else
  955. {
  956. if(pClient)
  957. if(CheckConn(NewCCore, pConn, pClient, reply.getData()))
  958. {
  959. // accept
  960. if(!reply) reply = "connection accepted";
  961. fOK = true;
  962. }
  963. // client: host connection?
  964. if(!fOK && !isHost() && Status.getState() == GS_Init && !Clients.GetHost())
  965. if(HostConnect(NewCCore, pConn, reply.getData()))
  966. {
  967. // accept
  968. if(!reply) reply = "host connection accepted";
  969. fOK = true;
  970. }
  971. // host: client join? (NewCCore will be changed by Join()!)
  972. if(!fOK && isHost() && !pClient)
  973. {
  974. // check password
  975. if(!sPassword.isNull() && !SEqual(Pkt.getPassword(), sPassword.getData()))
  976. {
  977. reply = "wrong password";
  978. fWrongPassword = true;
  979. }
  980. // registered join only
  981. else if (Game.RegJoinOnly && !SLen(NewCCore.getCUID()))
  982. {
  983. reply = "registered join only";
  984. }
  985. // accept join
  986. else if(Join(NewCCore, pConn, reply.getData()))
  987. {
  988. // save core
  989. pConn->SetCCore(NewCCore);
  990. // accept
  991. if(!reply) reply = "join accepted";
  992. fOK = true;
  993. }
  994. }
  995. }
  996. // denied? set default reason
  997. if(!fOK && !reply) reply = "connection denied";
  998. // OK and already half accepted? Skip (double-checked: ok).
  999. if(fOK && pConn->isHalfAccepted())
  1000. return;
  1001. // send answer
  1002. C4PacketConnRe pcr(fOK, fWrongPassword, reply.getData());
  1003. if(!pConn->Send(MkC4NetIOPacket(PID_ConnRe, pcr)))
  1004. return;
  1005. // accepted?
  1006. if(fOK)
  1007. {
  1008. // set status
  1009. if(!pConn->isClosed())
  1010. pConn->SetHalfAccepted();
  1011. }
  1012. // denied? close
  1013. else
  1014. {
  1015. // log & close
  1016. LogSilentF("Network: connection by %s (%s:%d) blocked: %s", CCore.getName(), inet_ntoa(pConn->getPeerAddr().sin_addr), htons(pConn->getPeerAddr().sin_port), reply.getData());
  1017. pConn->Close();
  1018. }
  1019. }
  1020. bool C4Network2::CheckConn(const C4ClientCore &CCore, C4Network2IOConnection *pConn, C4Network2Client *pClient, const char *szReply)
  1021. {
  1022. if(!pConn || !pClient) return false;
  1023. // already connected? (shouldn't happen really)
  1024. if(pClient->hasConn(pConn))
  1025. { szReply = "already connected"; return true; }
  1026. // check core
  1027. if(CCore.getDiffLevel(pClient->getCore()) > C4ClientCoreDL_IDMatch)
  1028. { szReply = "wrong client core"; return false; }
  1029. // check address
  1030. if(pClient->isConnected() && pClient->getMsgConn()->getPeerAddr().sin_addr.s_addr != pConn->getPeerAddr().sin_addr.s_addr)
  1031. { szReply = "wrong address"; return false; }
  1032. // accept
  1033. return true;
  1034. }
  1035. bool C4Network2::HostConnect(const C4ClientCore &CCore, C4Network2IOConnection *pConn, const char *szReply)
  1036. {
  1037. if(!pConn) return false;
  1038. if(!CCore.isHost()) { szReply = "not host"; return false; }
  1039. // create client class for host
  1040. // (core is unofficial, see InitClient() - will be overwritten later in HandleJoinData)
  1041. C4Client *pClient = Game.Clients.Add(CCore);
  1042. if(!pClient) return false;
  1043. // accept
  1044. return true;
  1045. }
  1046. bool C4Network2::Join(C4ClientCore &CCore, C4Network2IOConnection *pConn, const char *szReply)
  1047. {
  1048. if(!pConn) return false;
  1049. // security
  1050. if(!isHost()) { szReply = "not host"; return false; }
  1051. if(!fAllowJoin && !fAllowObserve) { szReply = "join denied"; return false; }
  1052. if(CCore.getID() != C4ClientIDUnknown) { szReply = "join with set id not allowed"; return false; }
  1053. // find free client id
  1054. CCore.SetID(iNextClientID++);
  1055. // observer?
  1056. if(!fAllowJoin) CCore.SetObserver(true);
  1057. // deactivate - client will have to ask for activation.
  1058. CCore.SetActivated(false);
  1059. // Name already in use? Find unused one
  1060. if(Clients.GetClient(CCore.getName()))
  1061. {
  1062. char szNameTmpl[256+1], szNewName[256+1];
  1063. SCopy(CCore.getName(), szNameTmpl, 254); SAppend("%d", szNameTmpl, 256);
  1064. int32_t i = 1;
  1065. do
  1066. sprintf(szNewName, szNameTmpl, ++i);
  1067. while(Clients.GetClient(szNewName));
  1068. CCore.SetName(szNewName);
  1069. }
  1070. // join client
  1071. ::Control.DoInput(CID_ClientJoin, new C4ControlClientJoin(CCore), CDT_Direct);
  1072. // get client, set status
  1073. C4Network2Client *pClient = Clients.GetClient(CCore);
  1074. if(pClient) pClient->SetStatus(NCS_Joining);
  1075. // ok, client joined.
  1076. return true;
  1077. // Note that the connection isn't fully accepted at this point and won't be
  1078. // associated with the client. The new-created client is waiting for connect.
  1079. // Somewhat ironically, the connection may still timeout (resulting in an instant
  1080. // removal and maybe some funny message sequences).
  1081. // The final client initialization will be done at OnClientConnect.
  1082. }
  1083. void C4Network2::HandleConnRe(const C4PacketConnRe &Pkt, C4Network2IOConnection *pConn, C4Network2Client *pClient)
  1084. {
  1085. // Handle the connection request reply. After this handling, the connection should
  1086. // be either fully associated with a client (fully accepted) or closed.
  1087. // Note that auto-accepted connection have to processed here once, too, as the
  1088. // client must get associated with the connection. After doing so, the connection
  1089. // auto-accept flag will be reset to mark the connection fully accepted.
  1090. // security
  1091. if(!pConn) return;
  1092. if(!pClient) { pConn->Close(); return; }
  1093. // negative reply?
  1094. if(!Pkt.isOK())
  1095. {
  1096. // wrong password?
  1097. fWrongPassword = Pkt.isPasswordWrong();
  1098. // show message
  1099. LogSilentF("Network: connection to %s (%s:%d) refused: %s", pClient->getName(), inet_ntoa(pConn->getPeerAddr().sin_addr), htons(pConn->getPeerAddr().sin_port), Pkt.getMsg());
  1100. // close connection
  1101. pConn->Close();
  1102. return;
  1103. }
  1104. // connection is closed?
  1105. if(!pConn->isOpen())
  1106. return;
  1107. // already accepted? ignore
  1108. if(pConn->isAccepted() && !pConn->isAutoAccepted()) return;
  1109. // first connection?
  1110. bool fFirstConnection = !pClient->isConnected();
  1111. // accept connection
  1112. pConn->SetAccepted(); pConn->ResetAutoAccepted();
  1113. // add connection
  1114. pConn->SetCCore(pClient->getCore());
  1115. if(pConn->getNetClass() == NetIO.MsgIO()) pClient->SetMsgConn(pConn);
  1116. if(pConn->getNetClass() == NetIO.DataIO()) pClient->SetDataConn(pConn);
  1117. // add peer connect address to client address list
  1118. if(pConn->getConnectAddr().sin_addr.s_addr)
  1119. {
  1120. C4Network2Address Addr(pConn->getConnectAddr(), pConn->getProtocol());
  1121. pClient->AddAddr(Addr, Status.getState() != GS_Init);
  1122. }
  1123. // handle
  1124. OnConnect(pClient, pConn, Pkt.getMsg(), fFirstConnection);
  1125. }
  1126. void C4Network2::HandleStatus(const C4Network2Status &nStatus)
  1127. {
  1128. // set
  1129. Status = nStatus;
  1130. // log
  1131. LogSilentF("Network: going into status %s (tick %d)", Status.getStateName(), nStatus.getTargetCtrlTick());
  1132. // reset flags
  1133. fStatusReached = fStatusAck = false;
  1134. // check: reached?
  1135. CheckStatusReached();
  1136. }
  1137. void C4Network2::HandleStatusAck(const C4Network2Status &nStatus, C4Network2Client *pClient)
  1138. {
  1139. // security
  1140. if(!pClient->hasJoinData() || pClient->isRemoved()) return;
  1141. // status doesn't match?
  1142. if(nStatus.getState() != Status.getState() || nStatus.getTargetCtrlTick() < Status.getTargetCtrlTick())
  1143. return;
  1144. // host: wait until all clients are ready
  1145. if(isHost())
  1146. {
  1147. // check: target tick change?
  1148. if(!fStatusAck && nStatus.getTargetCtrlTick() > Status.getTargetCtrlTick())
  1149. // take the new status
  1150. ChangeGameStatus(nStatus.getState(), nStatus.getTargetCtrlTick());
  1151. // already acknowledged? Send another ack
  1152. if(fStatusAck)
  1153. pClient->SendMsg(MkC4NetIOPacket(PID_StatusAck, nStatus));
  1154. // mark as ready (will clear chase-flag)
  1155. pClient->SetStatus(NCS_Ready);
  1156. // check: everyone ready?
  1157. if(!fStatusAck && fStatusReached)
  1158. CheckStatusAck();
  1159. }
  1160. else
  1161. {
  1162. // target tick doesn't match? ignore
  1163. if(nStatus.getTargetCtrlTick() != Status.getTargetCtrlTick())
  1164. return;
  1165. // reached?
  1166. // can be ignored safely otherwise - when the status is reached, we will send
  1167. // status ack on which the host should generate another status ack (see above)
  1168. if(fStatusReached)
  1169. {
  1170. // client: set flags, call handler
  1171. fStatusAck = true; fChasing = false;
  1172. OnStatusAck();
  1173. }
  1174. }
  1175. }
  1176. void C4Network2::HandleActivateReq(int32_t iTick, C4Network2Client *pByClient)
  1177. {
  1178. if(!isHost()) return;
  1179. // not allowed or already activated? ignore
  1180. if(pByClient->isObserver() || pByClient->isActivated()) return;
  1181. // not joined completely yet? ignore
  1182. if(!pByClient->isWaitedFor()) return;
  1183. // check behind limit
  1184. if(isRunning())
  1185. {
  1186. // make a guess how much the client lags.
  1187. int32_t iLagFrames = BoundBy(pByClient->getMsgConn()->getPingTime() * Game.FPS / 500, 0, 100);
  1188. if(iTick < Game.FrameCounter - iLagFrames - C4NetMaxBehind4Activation)
  1189. return;
  1190. }
  1191. // activate him
  1192. ::Control.DoInput(CID_ClientUpdate,
  1193. new C4ControlClientUpdate(pByClient->getID(), CUT_Activate, true),
  1194. CDT_Sync);
  1195. }
  1196. void C4Network2::HandleJoinData(const C4PacketJoinData &rPkt)
  1197. {
  1198. // init only
  1199. if(Status.getState() != GS_Init)
  1200. { LogSilentF("Network: unexpected join data received!"); return; }
  1201. // get client ID
  1202. if(rPkt.getClientID() == C4ClientIDUnknown)
  1203. { LogSilentF("Network: host didn't set client ID!"); Clear(); return; }
  1204. // set local ID
  1205. ResList.SetLocalID(rPkt.getClientID());
  1206. Game.Parameters.Clients.SetLocalID(rPkt.getClientID());
  1207. // read and validate status
  1208. HandleStatus(rPkt.getStatus());
  1209. if(Status.getState() != GS_Lobby && Status.getState() != GS_Pause && Status.getState() != GS_Go)
  1210. { LogSilentF("Network: join data has bad game status: %s", Status.getStateName()); Clear(); return; }
  1211. // copy parameters
  1212. Game.Parameters = rPkt.Parameters;
  1213. // set local client
  1214. C4Client *pLocalClient = Game.Clients.getClientByID(rPkt.getClientID());
  1215. if(!pLocalClient)
  1216. { LogSilentF("Network: Could not find local client in join data!"); Clear(); return; }
  1217. // save back dynamic data
  1218. ResDynamic = rPkt.getDynamicCore();
  1219. iDynamicTick = rPkt.getStartCtrlTick();
  1220. // initialize control
  1221. ::Control.ControlRate = rPkt.Parameters.ControlRate;
  1222. pControl->Init(rPkt.getClientID(), false, rPkt.getStartCtrlTick(), pLocalClient->isActivated(), this);
  1223. pControl->CopyClientList(Game.Parameters.Clients);
  1224. // set local core
  1225. NetIO.SetLocalCCore(pLocalClient->getCore());
  1226. // add the resources to the network ressource list
  1227. Game.Parameters.GameRes.InitNetwork(&ResList);
  1228. // load dynamic
  1229. if(!ResList.AddByCore(ResDynamic))
  1230. { LogFatal("Network: can not not retrieve dynamic!"); Clear(); return; }
  1231. // load player ressources
  1232. Game.Parameters.PlayerInfos.LoadResources();
  1233. // send additional addresses
  1234. Clients.SendAddresses(NULL);
  1235. }
  1236. void C4Network2::OnConnect(C4Network2Client *pClient, C4Network2IOConnection *pConn, const char *szMsg, bool fFirstConnection)
  1237. {
  1238. // log
  1239. LogSilentF("Network: %s %s connected (%s:%d/%s) (%s)", pClient->isHost() ? "host" : "client",
  1240. pClient->getName(), inet_ntoa(pConn->getPeerAddr().sin_addr), htons(pConn->getPeerAddr().sin_port),
  1241. NetIO.getNetIOName(pConn->getNetClass()), szMsg ? szMsg : "");
  1242. // first connection for this peer? call special handler
  1243. if(fFirstConnection) OnClientConnect(pClient, pConn);
  1244. }
  1245. void C4Network2::OnConnectFail(C4Network2IOConnection *pConn)
  1246. {
  1247. LogSilentF("Network: %s connection to %s:%d failed!", NetIO.getNetIOName(pConn->getNetClass()),
  1248. inet_ntoa(pConn->getPeerAddr().sin_addr), htons(pConn->getPeerAddr().sin_port));
  1249. // maybe client connection failure
  1250. // (happens if the connection is not fully accepted and the client disconnects.
  1251. // See C4Network2::Join)
  1252. C4Network2Client *pClient = Clients.GetClientByID(pConn->getClientID());
  1253. if(pClient && !pClient->isConnected())
  1254. OnClientDisconnect(pClient);
  1255. }
  1256. void C4Network2::OnDisconnect(C4Network2Client *pClient, C4Network2IOConnection *pConn)
  1257. {
  1258. LogSilentF("Network: %s connection to %s (%s:%d) lost!", NetIO.getNetIOName(pConn->getNetClass()),
  1259. pClient->getName(), inet_ntoa(pConn->getPeerAddr().sin_addr), htons(pConn->getPeerAddr().sin_port));
  1260. // connection lost?
  1261. if(!pClient->isConnected())
  1262. OnClientDisconnect(pClient);
  1263. }
  1264. void C4Network2::OnClientConnect(C4Network2Client *pClient, C4Network2IOConnection *pConn)
  1265. {
  1266. // host: new client?
  1267. if(isHost())
  1268. {
  1269. // dynamic available?
  1270. if(!pClient->hasJoinData())
  1271. SendJoinData(pClient);
  1272. // notice lobby (doesn't do anything atm?)
  1273. C4GameLobby::MainDlg *pDlg = GetLobby();
  1274. if (isLobbyActive()) pDlg->OnClientConnect(pClient->getClient(), pConn);
  1275. }
  1276. // discover resources
  1277. ResList.OnClientConnect(pConn);
  1278. }
  1279. void C4Network2::OnClientDisconnect(C4Network2Client *pClient)
  1280. {
  1281. // league: Notify regular client disconnect within the game
  1282. if (pLeagueClient && (isHost() || pClient->isHost())) LeagueNotifyDisconnect(pClient->getID(), C4LDR_ConnectionFailed);
  1283. // host? Remove this client from the game.
  1284. if(isHost())
  1285. {
  1286. // log
  1287. LogSilentF(LoadResStr("IDS_NET_CLIENTDISCONNECTED"), pClient->getName()); // silent, because a duplicate message with disconnect reason will follow
  1288. // remove the client
  1289. Game.Clients.CtrlRemove(pClient->getClient(), LoadResStr("IDS_MSG_DISCONNECTED"));
  1290. // check status ack (disconnected client might be the last that was waited for)
  1291. CheckStatusAck();
  1292. // unreached pause/go? retry setting the state with current control tick
  1293. // (client might be the only one claiming to have the given control)
  1294. if(!fStatusReached)
  1295. if(Status.getState() == GS_Go || Status.getState() == GS_Pause)
  1296. ChangeGameStatus(Status.getState(), ::Control.ControlTick);
  1297. }
  1298. // host disconnected? Clear up
  1299. if(!isHost() && pClient->isHost())
  1300. {
  1301. StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_NET_HOSTDISCONNECTED"), pClient->getName());
  1302. Log(sMsg.getData());
  1303. // host connection lost: clear up everything
  1304. Game.RoundResults.EvaluateNetwork(C4RoundResults::NR_NetError, sMsg.getData());
  1305. Clear();
  1306. }
  1307. }
  1308. void C4Network2::SendJoinData(C4Network2Client *pClient)
  1309. {
  1310. if(pClient->hasJoinData()) return;
  1311. // host only, scenario must be available
  1312. assert(isHost());
  1313. // dynamic available?
  1314. if(ResDynamic.isNull() || iDynamicTick < ::Control.ControlTick)
  1315. {
  1316. fDynamicNeeded = true;
  1317. // add synchronization control (will callback, see C4Game::Synchronize)
  1318. ::Control.DoInput(CID_Synchronize, new C4ControlSynchronize(false, true), CDT_Sync);
  1319. return;
  1320. }
  1321. // save his client ID
  1322. C4PacketJoinData JoinData;
  1323. JoinData.SetClientID(pClient->getID());
  1324. // save status into packet
  1325. JoinData.SetGameStatus(Status);
  1326. // parameters
  1327. JoinData.Parameters = Game.Parameters;
  1328. // core join data
  1329. JoinData.SetStartCtrlTick(iDynamicTick);
  1330. JoinData.SetDynamicCore(ResDynamic);
  1331. // send
  1332. pClient->SendMsg(MkC4NetIOPacket(PID_JoinData, JoinData));
  1333. // send addresses
  1334. Clients.SendAddresses(pClient->getMsgConn());
  1335. // flag client (he will have to accept the network status sent next)
  1336. pClient->SetStatus(NCS_Chasing);
  1337. if(!iLastChaseTargetUpdate) iLastChaseTargetUpdate = time(NULL);
  1338. }
  1339. C4Network2Res::Ref C4Network2::RetrieveRes(const C4Network2ResCore &Core, int32_t iTimeoutLen, const char *szResName, bool fWaitForCore)
  1340. {
  1341. C4GUI::ProgressDialog *pDlg = NULL;
  1342. bool fLog = false;
  1343. int32_t iProcess = -1; uint32_t iTimeout = timeGetTime() + iTimeoutLen;
  1344. // wait for ressource
  1345. while(isEnabled())
  1346. {
  1347. // find ressource
  1348. C4Network2Res::Ref pRes = ResList.getRefRes(Core.getID());
  1349. // res not found?
  1350. if(!pRes)
  1351. if(Core.isNull())
  1352. {
  1353. // should wait for core?
  1354. if(!fWaitForCore) return NULL;
  1355. }
  1356. else
  1357. {
  1358. // start loading
  1359. pRes = ResList.AddByCore(Core);
  1360. }
  1361. // res found and loaded completely
  1362. else if(!pRes->isLoading())
  1363. {
  1364. // log
  1365. if(fLog) LogF(LoadResStr("IDS_NET_RECEIVED"), szResName, pRes->getCore().getFileName());
  1366. // return
  1367. if (pDlg) delete pDlg;
  1368. return pRes;
  1369. }
  1370. // check: progress?
  1371. if(pRes && pRes->getPresentPercent() != iProcess)
  1372. {
  1373. iProcess = pRes->getPresentPercent();
  1374. iTimeout = timeGetTime() + iTimeoutLen;
  1375. }
  1376. else
  1377. {
  1378. // if not: check timeout
  1379. if(timeGetTime() > iTimeout)
  1380. {
  1381. LogFatal(FormatString(LoadResStr("IDS_NET_ERR_RESTIMEOUT"), szResName).getData());
  1382. if (pDlg) delete pDlg;
  1383. return NULL;
  1384. }
  1385. }
  1386. // log
  1387. if(!fLog)
  1388. {
  1389. LogF(LoadResStr("IDS_NET_WAITFORRES"), szResName);
  1390. fLog = true;
  1391. }
  1392. // show progress dialog
  1393. if(!pDlg && !Console.Active && ::pGUI)
  1394. {
  1395. // create
  1396. pDlg = new C4GUI::ProgressDialog(FormatString(LoadResStr("IDS_NET_WAITFORRES"), szResName).getData(),
  1397. LoadResStr("IDS_NET_CAPTION"), 100, 0, C4GUI::Ico_NetWait);
  1398. // show dialog
  1399. if(!pDlg->Show(::pGUI, true)) { delete pDlg; return NULL; }
  1400. }
  1401. // wait
  1402. if(pDlg)
  1403. {
  1404. // set progress bar
  1405. pDlg->SetProgress(iProcess);
  1406. // execute (will do message handling)
  1407. if(!pDlg->Execute())
  1408. { if (pDlg) delete pDlg; return NULL; }
  1409. // aborted?
  1410. if(!::pGUI) return NULL;
  1411. if(pDlg->IsAborted()) break;
  1412. }
  1413. else
  1414. {
  1415. if(!Application.ScheduleProcs(iTimeout - timeGetTime()))
  1416. { return NULL; }
  1417. }
  1418. }
  1419. // aborted
  1420. if(!::pGUI) return NULL;
  1421. delete pDlg;
  1422. return NULL;
  1423. }
  1424. bool C4Network2::CreateDynamic(bool fInit)
  1425. {
  1426. if(!isHost()) return false;
  1427. // remove all existing dynamic data
  1428. RemoveDynamic();
  1429. // log
  1430. Log(LoadResStr("IDS_NET_SAVING"));
  1431. // compose file name
  1432. char szDynamicBase[_MAX_PATH+1], szDynamicFilename[_MAX_PATH+1];
  1433. sprintf(szDynamicBase, Config.AtNetworkPath("Dyn%s"), GetFilename(Game.ScenarioFilename), _MAX_PATH);
  1434. if(!ResList.FindTempResFileName(szDynamicBase, szDynamicFilename))
  1435. LogF(LoadResStr("IDS_NET_SAVE_ERR_CREATEDYNFILE"));
  1436. // save dynamic data
  1437. C4GameSaveNetwork SaveGame(fInit);
  1438. if (!SaveGame.Save(szDynamicFilename) || !SaveGame.Close())
  1439. { Log(LoadResStr("IDS_NET_SAVE_ERR_SAVEDYNFILE")); return false; }
  1440. // add ressource
  1441. C4Network2Res::Ref pRes = ResList.AddByFile(szDynamicFilename, true, NRT_Dynamic);
  1442. if(!pRes) { Log(LoadResStr("IDS_NET_SAVE_ERR_ADDDYNDATARES")); return false; }
  1443. // save
  1444. ResDynamic = pRes->getCore();
  1445. iDynamicTick = ::Control.getNextControlTick();
  1446. fDynamicNeeded = false;
  1447. // ok
  1448. return true;
  1449. }
  1450. void C4Network2::RemoveDynamic()
  1451. {
  1452. C4Network2Res::Ref pRes = ResList.getRefRes(ResDynamic.getID());
  1453. if(pRes) pRes->Remove();
  1454. ResDynamic.Clear();
  1455. iDynamicTick = -1;
  1456. }
  1457. bool C4Network2::isFrozen() const
  1458. {
  1459. // "frozen" means all clients are garantueed to be in the same tick.
  1460. // This is only the case if the game is not started yet (lobby) or the
  1461. // tick has been ensured (pause) and acknowledged by all joined clients.
  1462. // Note unjoined clients must be ignored here - they can't be faster than
  1463. // the host, anyway.
  1464. if(Status.getState() == GS_Lobby) return true;
  1465. if(Status.getState() == GS_Pause && fStatusAck) return true;
  1466. return false;
  1467. }
  1468. bool C4Network2::ChangeGameStatus(C4NetGameState enState, int32_t iTargetCtrlTick, int32_t iCtrlMode)
  1469. {
  1470. // change game status, announce. Can only be done by host.
  1471. if(!isHost()) return false;
  1472. // set status
  1473. Status.Set(enState, iTargetCtrlTick);
  1474. // update reference
  1475. InvalidateReference();
  1476. // control mode change?
  1477. if(iCtrlMode >= 0) Status.SetCtrlMode(iCtrlMode);
  1478. // log
  1479. LogSilentF("Network: going into status %s (tick %d)", Status.getStateName(), iTargetCtrlTick);
  1480. // set flags
  1481. Clients.ResetReady();
  1482. fStatusReached = fStatusAck = false;
  1483. // send new status to all clients
  1484. Clients.BroadcastMsgToClients(MkC4NetIOPacket(PID_Status, Status));
  1485. // check reach/ack
  1486. CheckStatusReached();
  1487. // ok
  1488. return true;
  1489. }
  1490. void C4Network2::CheckStatusReached(bool fFromFinalInit)
  1491. {
  1492. // already reached?
  1493. if(fStatusReached) return;
  1494. if(Status.getState() == GS_Lobby)
  1495. fStatusReached = fLobbyRunning;
  1496. // game go / pause: control must be initialized and target tick reached
  1497. else if(Status.getState() == GS_Go || Status.getState() == GS_Pause)
  1498. {
  1499. if(Game.IsRunning || fFromFinalInit)
  1500. {
  1501. // Make sure we have reached the tick and the control queue is empty (except for chasing)
  1502. if(::Control.CtrlTickReached(Status.getTargetCtrlTick()) &&
  1503. (fChasing || !pControl->CtrlReady(::Control.ControlTick)))
  1504. fStatusReached = true;
  1505. else
  1506. {
  1507. // run ctrl so the tick can be reached
  1508. pControl->SetRunning(true, Status.getTargetCtrlTick());
  1509. Game.HaltCount = 0;
  1510. Console.UpdateHaltCtrls(!! Game.HaltCount);
  1511. }
  1512. }
  1513. }
  1514. if(!fStatusReached) return;
  1515. // call handler
  1516. OnStatusReached();
  1517. // host?
  1518. if(isHost())
  1519. // all clients ready?
  1520. CheckStatusAck();
  1521. else
  1522. {
  1523. Status.SetTargetTick(::Control.ControlTick);
  1524. // send response to host
  1525. Clients.SendMsgToHost(MkC4NetIOPacket(PID_StatusAck, Status));
  1526. // do delayed activation request
  1527. if(fDelayedActivateReq)
  1528. {
  1529. fDelayedActivateReq = false;
  1530. RequestActivate();
  1531. }
  1532. }
  1533. }
  1534. void C4Network2::CheckStatusAck()
  1535. {
  1536. // host only
  1537. if(!isHost()) return;
  1538. // status must be reached and not yet acknowledged
  1539. if(!fStatusReached || fStatusAck) return;
  1540. // all clients ready?
  1541. if(fStatusAck = Clients.AllClientsReady())
  1542. {
  1543. // pause/go: check for sync control that can be executed
  1544. if(Status.getState() == GS_Go || Status.getState() == GS_Pause)
  1545. pControl->ExecSyncControl();
  1546. // broadcast ack
  1547. Clients.BroadcastMsgToClients(MkC4NetIOPacket(PID_StatusAck, Status));
  1548. // handle
  1549. OnStatusAck();
  1550. }
  1551. }
  1552. void C4Network2::OnStatusReached()
  1553. {
  1554. // stop ctrl, wait for ack
  1555. if(pControl->IsEnabled())
  1556. {
  1557. Console.UpdateHaltCtrls(!!++Game.HaltCount);
  1558. pControl->SetRunning(false);
  1559. }
  1560. }
  1561. void C4Network2::OnStatusAck()
  1562. {
  1563. // log it
  1564. LogSilentF("Network: status %s (tick %d) reached", Status.getStateName(), Status.getTargetCtrlTick());
  1565. // pause?
  1566. if(Status.getState() == GS_Pause)
  1567. {
  1568. // set halt-flag (show hold message)
  1569. Console.UpdateHaltCtrls(!!++Game.HaltCount);
  1570. }
  1571. // go?
  1572. if(Status.getState() == GS_Go)
  1573. {
  1574. // set mode
  1575. pControl->SetCtrlMode(static_cast<C4GameControlNetworkMode>(Status.getCtrlMode()));
  1576. // notify player list of reached status - will add some input to the queue
  1577. Players.OnStatusGoReached();
  1578. // start ctrl
  1579. pControl->SetRunning(true);
  1580. // reset halt-flag
  1581. Game.HaltCount = 0;
  1582. Console.UpdateHaltCtrls(!!Game.HaltCount);
  1583. }
  1584. }
  1585. void C4Network2::RequestActivate()
  1586. {
  1587. // neither observer nor activated?
  1588. if(Game.Clients.getLocal()->isObserver() || Game.Clients.getLocal()->isActivated())
  1589. {
  1590. iLastActivateRequest = 0;
  1591. return;
  1592. }
  1593. // host? just do it
  1594. if(fHost)
  1595. {
  1596. // activate him
  1597. ::Control.DoInput(CID_ClientUpdate,
  1598. new C4ControlClientUpdate(C4ClientIDHost, CUT_Activate, true),
  1599. CDT_Sync);
  1600. return;
  1601. }
  1602. // ensure interval
  1603. if(iLastActivateRequest && timeGetTime() < iLastActivateRequest + C4NetActivationReqInterval)
  1604. return;
  1605. // status not reached yet? May be chasing, let's delay this.
  1606. if(!fStatusReached)
  1607. {
  1608. fDelayedActivateReq = true;
  1609. return;
  1610. }
  1611. // request
  1612. Clients.SendMsgToHost(MkC4NetIOPacket(PID_ClientActReq, C4PacketActivateReq(Game.FrameCounter)));
  1613. // store time
  1614. iLastActivateRequest = timeGetTime();
  1615. }
  1616. void C4Network2::DeactivateInactiveClients()
  1617. {
  1618. // host only
  1619. if(!isHost()) return;
  1620. // update activity
  1621. Clients.UpdateClientActivity();
  1622. // find clients to deactivate
  1623. for(C4Network2Client *pClient = Clients.GetNextClient(NULL); pClient; pClient = Clients.GetNextClient(pClient))
  1624. if(!pClient->isLocal() && pClient->isActivated())
  1625. if(pClient->getLastActivity() + C4NetDeactivationDelay < Game.FrameCounter)
  1626. ::Control.DoInput(CID_ClientUpdate, new C4ControlClientUpdate(pClient->getID(), CUT_Activate, false), CDT_Sync);
  1627. }
  1628. void C4Network2::UpdateChaseTarget()
  1629. {
  1630. // no chasing clients?
  1631. C4Network2Client *pClient;
  1632. for(pClient = Clients.GetNextClient(NULL); pClient; pClient = Clients.GetNextClient(pClient))
  1633. if(pClient->isChasing())
  1634. break;
  1635. if(!pClient)
  1636. {
  1637. iLastChaseTargetUpdate = 0;
  1638. return;
  1639. }
  1640. // not time for an update?
  1641. if(!iLastChaseTargetUpdate || long(iLastChaseTargetUpdate + C4NetChaseTargetUpdateInterval) > time(NULL))
  1642. return;
  1643. // copy status, set current tick
  1644. C4Network2Status ChaseTarget = Status;
  1645. ChaseTarget.SetTargetTick(::Control.ControlTick);
  1646. // send to everyone involved
  1647. for(pClient = Clients.GetNextClient(NULL); pClient; pClient = Clients.GetNextClient(pClient))
  1648. if(pClient->isChasing())
  1649. pClient->SendMsg(MkC4NetIOPacket(PID_Status, ChaseTarget));
  1650. iLastChaseTargetUpdate = time(NULL);
  1651. }
  1652. void C4Network2::LeagueGameEvaluate(const char *szRecordName, const BYTE *pRecordSHA)
  1653. {
  1654. // already off?
  1655. if (!pLeagueClient) return;
  1656. // already evaluated?
  1657. if (fLeagueEndSent) return;
  1658. // end
  1659. LeagueEnd(szRecordName, pRecordSHA);
  1660. }
  1661. void C4Network2::LeagueSignupDisable()
  1662. {
  1663. // already off?
  1664. if (!pLeagueClient) return;
  1665. // no post-disable if league is active
  1666. if (pLeagueClient && Game.Parameters.isLeague()) return;
  1667. // signup end
  1668. LeagueEnd(); DeinitLeague();
  1669. }
  1670. bool C4Network2::LeagueSignupEnable()
  1671. {
  1672. // already running?
  1673. if (pLeagueClient) return true;
  1674. // Start it!
  1675. if (InitLeague(NULL) && LeagueStart(NULL)) return true;
  1676. // Failure :'(
  1677. DeinitLeague();
  1678. return false;
  1679. }
  1680. void C4Network2::InvalidateReference()
  1681. {
  1682. // Update both local and league reference as soon as possible
  1683. iLastReferenceUpdate = 0;
  1684. iLeagueUpdateDelay = C4NetMinLeagueUpdateInterval;
  1685. }
  1686. bool C4Network2::InitLeague(bool *pCancel)
  1687. {
  1688. if(fHost)
  1689. {
  1690. // Clear parameters
  1691. MasterServerAddress.Clear();
  1692. Game.Parameters.League.Clear();
  1693. Game.Parameters.LeagueAddress.Clear();
  1694. if (pLeagueClient) delete pLeagueClient; pLeagueClient = NULL;
  1695. // Not needed?
  1696. if(!Config.Network.MasterServerSignUp && !Config.Network.LeagueServerSignUp)
  1697. return true;
  1698. // Save address
  1699. MasterServerAddress = Config.Network.GetLeagueServerAddress();
  1700. if (Config.Network.LeagueServerSignUp)
  1701. {
  1702. Game.Parameters.LeagueAddress = MasterServerAddress;
  1703. // enforce some league rules
  1704. Game.Parameters.EnforceLeagueRules(&Game.C4S);
  1705. }
  1706. }
  1707. else
  1708. {
  1709. // Get league server from parameters
  1710. MasterServerAddress = Game.Parameters.LeagueAddress;
  1711. // Not needed?
  1712. if(!MasterServerAddress.getLength())
  1713. return true;
  1714. }
  1715. // Init
  1716. pLeagueClient = new C4LeagueClient();
  1717. if(!pLeagueClient->Init() ||
  1718. !pLeagueClient->SetServer(MasterServerAddress.getData()))
  1719. {
  1720. // Log message
  1721. StdStrBuf Message = FormatString(LoadResStr("IDS_NET_ERR_LEAGUEINIT"), pLeagueClient->GetError());
  1722. LogFatal(Message.getData());
  1723. // Clear league
  1724. delete pLeagueClient; pLeagueClient = NULL;
  1725. if(fHost)
  1726. Game.Parameters.LeagueAddress.Clear();
  1727. // Show message, allow abort
  1728. bool fResult = true;
  1729. if(::pGUI && !Console.Active)
  1730. fResult = ::pGUI->ShowMessageModal(Message.getData(), LoadResStr("IDS_NET_ERR_LEAGUE"),
  1731. (pCancel ? C4GUI::MessageDialog::btnOK : 0) | C4GUI::MessageDialog::btnAbort,
  1732. C4GUI::Ico_Error);
  1733. if (pCancel) *pCancel = fResult;
  1734. return false;
  1735. }
  1736. // Add to message loop
  1737. Application.Add(pLeagueClient);
  1738. // OK
  1739. return true;
  1740. }
  1741. void C4Network2::DeinitLeague()
  1742. {
  1743. // league clear
  1744. MasterServerAddress.Clear();
  1745. Game.Parameters.League.Clear();
  1746. Game.Parameters.LeagueAddress.Clear();
  1747. if (pLeagueClient)
  1748. {
  1749. Application.Remove(pLeagueClient);
  1750. delete pLeagueClient; pLeagueClient = NULL;
  1751. }
  1752. }
  1753. bool C4Network2::LeagueStart(bool *pCancel)
  1754. {
  1755. // Not needed?
  1756. if(!pLeagueClient || !fHost)
  1757. return true;
  1758. // Default
  1759. if(pCancel) *pCancel = true;
  1760. // Do update
  1761. C4Network2Reference Ref;
  1762. Ref.InitLocal();
  1763. if(!pLeagueClient->Start(Ref))
  1764. {
  1765. // Log message
  1766. StdStrBuf Message = FormatString(LoadResStr("IDS_NET_ERR_LEAGUE_STARTGAME"), pLeagueClient->GetError());
  1767. LogFatal(Message.getData());
  1768. // Show message
  1769. if(::pGUI && !Console.Active)
  1770. {
  1771. // Show option to cancel, if possible
  1772. bool fResult = ::pGUI->ShowMessageModal(
  1773. Message.getData(),
  1774. LoadResStr("IDS_NET_ERR_LEAGUE"),
  1775. pCancel ? (C4GUI::MessageDialog::btnOK | C4GUI::MessageDialog::btnAbort) : C4GUI::MessageDialog::btnOK,
  1776. C4GUI::Ico_Error);
  1777. if(pCancel)
  1778. *pCancel = !fResult;
  1779. }
  1780. // Failed
  1781. return false;
  1782. }
  1783. // We have an internet connection, so let's punch the master server here in order to open an udp port
  1784. C4NetIO::addr_t PuncherAddr;
  1785. if(ResolveAddress(Config.Network.PuncherAddress, &PuncherAddr, C4NetStdPortPuncher))
  1786. NetIO.Punch(PuncherAddr);
  1787. // Let's wait for response
  1788. StdStrBuf Message = FormatString(LoadResStr("IDS_NET_LEAGUE_REGGAME"), pLeagueClient->getServerName());
  1789. Log(Message.getData());
  1790. // Set up a dialog
  1791. C4GUI::MessageDialog *pDlg = NULL;
  1792. if(::pGUI && !Console.Active)
  1793. {
  1794. // create & show
  1795. pDlg = new C4GUI::MessageDialog(Message.getData(), LoadResStr("IDS_NET_LEAGUE_STARTGAME"),
  1796. C4GUI::MessageDialog::btnAbort, C4GUI::Ico_NetWait, C4GUI::MessageDialog::dsRegular);
  1797. if(!pDlg || !pDlg->Show(::pGUI, true)) return false;
  1798. }
  1799. // Wait for response
  1800. while(pLeagueClient->isBusy())
  1801. {
  1802. // Execute GUI
  1803. if(!Application.ScheduleProcs() ||
  1804. (pDlg && pDlg->IsAborted()))
  1805. {
  1806. // Clear up
  1807. if(::pGUI && pDlg) delete pDlg;
  1808. return false;
  1809. }
  1810. // Check if league server has responded
  1811. if(!pLeagueClient->Execute(100))
  1812. break;
  1813. }
  1814. // Close dialog
  1815. if(::pGUI && pDlg)
  1816. {
  1817. pDlg->Close(true);
  1818. delete pDlg;
  1819. }
  1820. // Error?
  1821. StdStrBuf LeagueServerMessage, League, StreamingAddr;
  1822. int32_t Seed = Game.RandomSeed, MaxPlayersLeague = 0;
  1823. if(!pLeagueClient->isSuccess() ||
  1824. !pLeagueClient->GetStartReply(&LeagueServerMessage, &League, &StreamingAddr, &Seed, &MaxPlayersLeague))
  1825. {
  1826. const char *pError = pLeagueClient->GetError() ? pLeagueClient->GetError() :
  1827. LeagueServerMessage.getLength() ? LeagueServerMessage.getData() :
  1828. LoadResStr("IDS_NET_ERR_LEAGUE_EMPTYREPLY");
  1829. StdStrBuf Message = FormatString(LoadResStr("IDS_NET_ERR_LEAGUE_REGGAME"), pError);
  1830. // Log message
  1831. Log(Message.getData());
  1832. // Show message
  1833. if(::pGUI && !Console.Active)
  1834. {
  1835. // Show option to cancel, if possible
  1836. bool fResult = ::pGUI->ShowMessageModal(
  1837. Message.getData(),
  1838. LoadResStr("IDS_NET_ERR_LEAGUE"),
  1839. pCancel ? (C4GUI::MessageDialog::btnOK | C4GUI::MessageDialog::btnAbort) : C4GUI::MessageDialog::btnOK,
  1840. C4GUI::Ico_Error);
  1841. if(pCancel)
  1842. *pCancel = !fResult;
  1843. }
  1844. // Failed
  1845. return false;
  1846. }
  1847. // Show message
  1848. if(LeagueServerMessage.getLength())
  1849. {
  1850. StdStrBuf Message = FormatString(LoadResStr("IDS_MSG_LEAGUEGAMESIGNUP"), pLeagueClient->getServerName(), LeagueServerMessage.getData());
  1851. // Log message
  1852. Log(Message.getData());
  1853. // Show message
  1854. if(::pGUI && !Console.Active)
  1855. {
  1856. // Show option to cancel, if possible
  1857. bool fResult = ::pGUI->ShowMessageModal(
  1858. Message.getData(),
  1859. LoadResStr("IDS_NET_ERR_LEAGUE"),
  1860. pCancel ? (C4GUI::MessageDialog::btnOK | C4GUI::MessageDialog::btnAbort) : C4GUI::MessageDialog::btnOK,
  1861. C4GUI::Ico_Error);
  1862. if(pCancel)
  1863. *pCancel = !fResult;
  1864. if(!fResult)
  1865. {
  1866. LeagueEnd(); DeinitLeague();
  1867. return false;
  1868. }
  1869. }
  1870. }
  1871. // Set game parameters for league game
  1872. Game.Parameters.League = League;
  1873. Game.RandomSeed = Seed;
  1874. if(MaxPlayersLeague)
  1875. Game.Parameters.MaxPlayers = MaxPlayersLeague;
  1876. if(!League.getLength())
  1877. {
  1878. Game.Parameters.LeagueAddress.Clear();
  1879. Game.Parameters.StreamAddress.Clear();
  1880. }
  1881. else
  1882. {
  1883. Game.Parameters.StreamAddress = StreamingAddr;
  1884. }
  1885. // All ok
  1886. fLeagueEndSent = false;
  1887. return true;
  1888. }
  1889. bool C4Network2::LeagueUpdate()
  1890. {
  1891. // Not needed?
  1892. if(!pLeagueClient || !fHost)
  1893. return true;
  1894. // League client currently busy?
  1895. if(pLeagueClient->isBusy())
  1896. return true;
  1897. // Create reference
  1898. C4Network2Reference Ref;
  1899. Ref.InitLocal();
  1900. // Do update
  1901. if(!pLeagueClient->Update(Ref))
  1902. {
  1903. // Log
  1904. LogF(LoadResStr("IDS_NET_ERR_LEAGUE_UPDATEGAME"), pLeagueClient->GetError());
  1905. return false;
  1906. }
  1907. // Timing
  1908. iLastLeagueUpdate = time(NULL);
  1909. iLeagueUpdateDelay = Config.Network.MasterReferencePeriod;
  1910. return true;
  1911. }
  1912. bool C4Network2::LeagueUpdateProcessReply()
  1913. {
  1914. // safety: A reply must be present
  1915. assert(pLeagueClient);
  1916. assert(fHost);
  1917. assert(!pLeagueClient->isBusy());
  1918. assert(pLeagueClient->getCurrentAction() == C4LA_Update);
  1919. // check reply success
  1920. C4ClientPlayerInfos PlayerLeagueInfos;
  1921. StdStrBuf LeagueServerMessage;
  1922. bool fSucc = pLeagueClient->isSuccess() && pLeagueClient->GetUpdateReply(&LeagueServerMessage, &PlayerLeagueInfos);
  1923. pLeagueClient->ResetCurrentAction();
  1924. if(!fSucc)
  1925. {
  1926. const char *pError = pLeagueClient->GetError() ? pLeagueClient->GetError() :
  1927. LeagueServerMessage.getLength() ? LeagueServerMessage.getData() :
  1928. LoadResStr("IDS_NET_ERR_LEAGUE_EMPTYREPLY");
  1929. StdStrBuf Message = FormatString(LoadResStr("IDS_NET_ERR_LEAGUE_UPDATEGAME"), pError);
  1930. // Show message - no dialog, because it's not really fatal and might happen in the running game
  1931. Log(Message.getData());
  1932. return false;
  1933. }
  1934. // evaluate reply: Transfer data to players
  1935. // Take round results
  1936. C4PlayerInfoList &TargetList = Game.PlayerInfos;
  1937. C4ClientPlayerInfos *pInfos; C4PlayerInfo *pInfo, *pResultInfo;
  1938. for(int iClient = 0; pInfos = TargetList.GetIndexedInfo(iClient); iClient++)
  1939. for(int iInfo = 0; pInfo = pInfos->GetPlayerInfo(iInfo); iInfo++)
  1940. if(pResultInfo = PlayerLeagueInfos.GetPlayerInfoByID(pInfo->GetID()))
  1941. {
  1942. int32_t iLeagueProjectedGain = pResultInfo->GetLeagueProjectedGain();
  1943. if (iLeagueProjectedGain != pInfo->GetLeagueProjectedGain())
  1944. {
  1945. pInfo->SetLeagueProjectedGain(iLeagueProjectedGain);
  1946. pInfos->SetUpdated();
  1947. }
  1948. }
  1949. // transfer info update to other clients
  1950. Players.SendUpdatedPlayers();
  1951. // if lobby is open, notify lobby of updated players
  1952. if (pLobby) pLobby->OnPlayersChange();
  1953. // OMFG SUCCESS!
  1954. return true;
  1955. }
  1956. bool C4Network2::LeagueEnd(const char *szRecordName, const BYTE *pRecordSHA)
  1957. {
  1958. C4RoundResultsPlayers RoundResults;
  1959. StdStrBuf sResultMessage;
  1960. bool fIsError = true;
  1961. // Not needed?
  1962. if(!pLeagueClient || !fHost || fLeagueEndSent)
  1963. return true;
  1964. // Make sure league client is available
  1965. LeagueWaitNotBusy();
  1966. // Try until either aborted or successful
  1967. const int MAX_RETRIES = 10;
  1968. for(int iRetry = 0; iRetry < MAX_RETRIES; iRetry++)
  1969. {
  1970. // Do update
  1971. C4Network2Reference Ref;
  1972. Ref.InitLocal();
  1973. if(!pLeagueClient->End(Ref, szRecordName, pRecordSHA))
  1974. {
  1975. // Log message
  1976. sResultMessage = FormatString(LoadResStr("IDS_NET_ERR_LEAGUE_FINISHGAME"), pLeagueClient->GetError());
  1977. Log(sResultMessage.getData());
  1978. // Show message, allow retry
  1979. if(!::pGUI || Console.Active) break;
  1980. bool fRetry = ::pGUI->ShowMessageModal(sResultMessage.getData(), LoadResStr("IDS_NET_ERR_LEAGUE"),
  1981. C4GUI::MessageDialog::btnRetryAbort, C4GUI::Ico_Error);
  1982. if (fRetry) continue;
  1983. break;
  1984. }
  1985. // Let's wait for response
  1986. StdStrBuf Message = FormatString(LoadResStr("IDS_NET_LEAGUE_SENDRESULT"), pLeagueClient->getServerName());
  1987. Log(Message.getData());
  1988. // Wait for response
  1989. while(pLeagueClient->isBusy())
  1990. {
  1991. // Check if league server has responded
  1992. if(!pLeagueClient->Execute(100))
  1993. break;
  1994. }
  1995. // Error?
  1996. StdStrBuf LeagueServerMessage;
  1997. if(!pLeagueClient->isSuccess() || !pLeagueClient->GetEndReply(&LeagueServerMessage, &RoundResults))
  1998. {
  1999. const char *pError = pLeagueClient->GetError() ? pLeagueClient->GetError() :
  2000. LeagueServerMessage.getLength() ? LeagueServerMessage.getData() :
  2001. LoadResStr("IDS_NET_ERR_LEAGUE_EMPTYREPLY");
  2002. sResultMessage.Take(FormatString(LoadResStr("IDS_NET_ERR_LEAGUE_SENDRESULT"), pError));
  2003. if(!::pGUI || Console.Active) continue;
  2004. // Only retry if we didn't get an answer from the league server
  2005. bool fRetry = !pLeagueClient->isSuccess();
  2006. fRetry = ::pGUI->ShowMessageModal(sResultMessage.getData(), LoadResStr("IDS_NET_ERR_LEAGUE"),
  2007. fRetry ? C4GUI::MessageDialog::btnRetryAbort : C4GUI::MessageDialog::btnAbort,
  2008. C4GUI::Ico_Error);
  2009. if (fRetry) continue;
  2010. }
  2011. else
  2012. {
  2013. // All OK!
  2014. sResultMessage.Copy(LoadResStr(Game.Parameters.isLeague() ? "IDS_MSG_LEAGUEEVALUATIONSUCCESSFU" : "IDS_MSG_INTERNETGAMEEVALUATED"));
  2015. fIsError = false;
  2016. }
  2017. // Done
  2018. break;
  2019. }
  2020. // Show message
  2021. Log(sResultMessage.getData());
  2022. // Take round results
  2023. Game.RoundResults.EvaluateLeague(sResultMessage.getData(), !fIsError, RoundResults);
  2024. // Send round results to other clients
  2025. C4PacketLeagueRoundResults LeagueUpdatePacket(sResultMessage.getData(), !fIsError, RoundResults);
  2026. Clients.BroadcastMsgToClients(MkC4NetIOPacket(PID_LeagueRoundResults, LeagueUpdatePacket));
  2027. // All done
  2028. fLeagueEndSent = true;
  2029. return true;
  2030. }
  2031. bool C4Network2::LeaguePlrAuth(C4PlayerInfo *pInfo)
  2032. {
  2033. // Not possible?
  2034. if(!pLeagueClient)
  2035. return false;
  2036. // Make sure league client is avilable
  2037. LeagueWaitNotBusy();
  2038. // Official league?
  2039. bool fOfficialLeague = SEqual(pLeagueClient->getServerName(), "clonk.de");
  2040. // Try to auth with WebCode, if it's an official league server and we have valid registration information
  2041. bool fWebCode = fOfficialLeague && *Config.GetRegistrationData("Cuid");
  2042. StdStrBuf Account, Password;
  2043. bool fRegister = false;
  2044. for(;;)
  2045. {
  2046. StdStrBuf NewAccount, NewPassword;
  2047. // Default authentication data
  2048. if (!Account.getLength()) Account.Copy(Config.GetRegistrationData("Cuid"));
  2049. // Try first auth with local CUID and WebCode
  2050. if(fWebCode)
  2051. {
  2052. // Default authentication data
  2053. Password.Copy(Config.GetRegistrationData("WebCode"));
  2054. };
  2055. // Ask for registration information
  2056. if(fRegister)
  2057. {
  2058. // Use local nick as default
  2059. NewAccount.Copy(Config.Network.Nick);
  2060. if(Config.Network.Nick.getLength() == 0)
  2061. NewAccount.Copy(Config.GetRegistrationData("Nick"));
  2062. if(!C4LeagueSignupDialog::ShowModal(pInfo->GetName(), "", pLeagueClient->getServerName(), &NewAccount, &NewPassword, !fOfficialLeague, true))
  2063. return false;
  2064. if(!NewPassword.getLength())
  2065. NewPassword.Copy(Password);
  2066. }
  2067. else if (!fWebCode)
  2068. {
  2069. // CUID is default for account, no default password
  2070. Password.Clear();
  2071. // ask for account
  2072. if(!C4LeagueSignupDialog::ShowModal(pInfo->GetName(), "", pLeagueClient->getServerName(), &Account, &Password, !fOfficialLeague, false))
  2073. return false;
  2074. }
  2075. // safety (modal dlg may have deleted network)
  2076. if (!pLeagueClient) return false;
  2077. // Send authentication request
  2078. if(!pLeagueClient->Auth(*pInfo, Account.getData(), Password.getData(), NewAccount.getLength() ? NewAccount.getData() : NULL, NewPassword.getLength() ? NewPassword.getData() : NULL))
  2079. return false;
  2080. // safety (modal dlg may have deleted network)
  2081. if (!pLeagueClient) return false;
  2082. // Wait for a response
  2083. StdStrBuf Message = FormatString(LoadResStr("IDS_MSG_TRYLEAGUESIGNUP"), pInfo->GetName(), Account.getData(), pLeagueClient->getServerName());
  2084. Log(Message.getData());
  2085. // Set up a dialog
  2086. C4GUI::MessageDialog *pDlg = NULL;
  2087. if(::pGUI && !Console.Active)
  2088. {
  2089. // create & show
  2090. pDlg = new C4GUI::MessageDialog(Message.getData(), LoadResStr("IDS_DLG_LEAGUESIGNUP"), C4GUI::MessageDialog::btnAbort, C4GUI::Ico_NetWait, C4GUI::MessageDialog::dsRegular);
  2091. if(!pDlg || !pDlg->Show(::pGUI, true)) return false;
  2092. }
  2093. // Wait for response
  2094. while(pLeagueClient->isBusy())
  2095. {
  2096. // Execute GUI
  2097. if(!Application.ScheduleProcs() ||
  2098. (pDlg && pDlg->IsAborted()))
  2099. {
  2100. // Clear up
  2101. if(::pGUI && pDlg) delete pDlg;
  2102. return false;
  2103. }
  2104. // Check if league server has responded
  2105. if(!pLeagueClient->Execute(0))
  2106. break;
  2107. }
  2108. // Close dialog
  2109. if(::pGUI && pDlg)
  2110. {
  2111. pDlg->Close(true);
  2112. delete pDlg;
  2113. }
  2114. // Success?
  2115. StdStrBuf AUID, AccountMaster; bool fUnregistered = false;
  2116. if(pLeagueClient->GetAuthReply(&Message, &AUID, &AccountMaster, &fUnregistered))
  2117. {
  2118. // Set AUID
  2119. pInfo->SetAuthID(AUID.getData());
  2120. // Show welcome message, if any
  2121. bool fSuccess;
  2122. if(Message.getLength())
  2123. fSuccess = ::pGUI->ShowMessageModal(
  2124. Message.getData(), LoadResStr("IDS_DLG_LEAGUESIGNUPCONFIRM"),
  2125. C4GUI::MessageDialog::btnOKAbort, C4GUI::Ico_Ex_League);
  2126. else if(AccountMaster.getLength())
  2127. fSuccess = ::pGUI->ShowMessageModal(
  2128. FormatString(LoadResStr("IDS_MSG_LEAGUEPLAYERSIGNUPAS"), pInfo->GetName(), AccountMaster.getData(), pLeagueClient->getServerName()).getData(), LoadResStr("IDS_DLG_LEAGUESIGNUPCONFIRM"),
  2129. C4GUI::MessageDialog::btnOKAbort, C4GUI::Ico_Ex_League);
  2130. else
  2131. fSuccess = ::pGUI->ShowMessageModal(
  2132. FormatString(LoadResStr("IDS_MSG_LEAGUEPLAYERSIGNUP"), pInfo->GetName(), pLeagueClient->getServerName()).getData(), LoadResStr("IDS_DLG_LEAGUESIGNUPCONFIRM"),
  2133. C4GUI::MessageDialog::btnOKAbort, C4GUI::Ico_Ex_League);
  2134. // Approved?
  2135. if(fSuccess)
  2136. // Done
  2137. return true;
  2138. else
  2139. // Sign-up was cancelled by user
  2140. ::pGUI->ShowMessageModal(FormatString(LoadResStr("IDS_MSG_LEAGUESIGNUPCANCELLED"), pInfo->GetName()).getData(), LoadResStr("IDS_DLG_LEAGUESIGNUP"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Notify);
  2141. }
  2142. else
  2143. {
  2144. // Error with first-time registration or manual password entry
  2145. if((!fWebCode && !fUnregistered) || fRegister)
  2146. {
  2147. LogF(LoadResStr("IDS_MSG_LEAGUESIGNUPERROR"), Message.getData());
  2148. ::pGUI->ShowMessageModal(FormatString(LoadResStr("IDS_MSG_LEAGUESERVERMSG"), Message.getData()).getData(), LoadResStr("IDS_DLG_LEAGUESIGNUPFAILED"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error);
  2149. // after a league server error message, always fall-through to try again
  2150. }
  2151. }
  2152. // Autommatic attempt?
  2153. if((fWebCode || fUnregistered) && !fRegister)
  2154. {
  2155. // No account yet? Try to register.
  2156. if(fUnregistered)
  2157. fRegister = true;
  2158. else
  2159. fWebCode = false;
  2160. }
  2161. // Try given account name as default next time
  2162. if(AccountMaster.getLength())
  2163. Account.Take(AccountMaster);
  2164. // safety (modal dlg may have deleted network)
  2165. if (!pLeagueClient) return false;
  2166. }
  2167. }
  2168. bool C4Network2::LeaguePlrAuthCheck(C4PlayerInfo *pInfo)
  2169. {
  2170. // Not possible?
  2171. if(!pLeagueClient)
  2172. return false;
  2173. // Make sure league client is available
  2174. LeagueWaitNotBusy();
  2175. // Ask league server to check the code
  2176. if(!pLeagueClient->AuthCheck(*pInfo))
  2177. return false;
  2178. // Log
  2179. StdStrBuf Message = FormatString(LoadResStr("IDS_MSG_LEAGUEJOINING"), pInfo->GetName());
  2180. Log(Message.getData());
  2181. // Wait for response
  2182. while(pLeagueClient->isBusy())
  2183. if(!pLeagueClient->Execute(100))
  2184. break;
  2185. // Check response validity
  2186. if (!pLeagueClient->isSuccess())
  2187. {
  2188. LeagueShowError(pLeagueClient->GetError());
  2189. return false;
  2190. }
  2191. // Check if league server approves. pInfo will have league info if this call is successful.
  2192. if(!pLeagueClient->GetAuthCheckReply(&Message, Game.Parameters.League.getData(), pInfo))
  2193. {
  2194. LeagueShowError(FormatString(LoadResStr("IDS_MSG_LEAGUEJOINREFUSED"), pInfo->GetName(), Message.getData()).getData());
  2195. return false;
  2196. }
  2197. return true;
  2198. }
  2199. void C4Network2::LeagueNotifyDisconnect(int32_t iClientID, C4LeagueDisconnectReason eReason)
  2200. {
  2201. // league active?
  2202. if (!pLeagueClient || !Game.Parameters.isLeague()) return;
  2203. // only in running game
  2204. if (!Game.IsRunning || Game.GameOver) return;
  2205. // clients send notifications for their own players; host sends for the affected client players
  2206. if (!isHost()) { if (!Clients.GetLocal()) return; iClientID = Clients.GetLocal()->getID(); }
  2207. // clients only need notifications if they have players in the game
  2208. const C4ClientPlayerInfos *pInfos = Game.PlayerInfos.GetInfoByClientID(iClientID);
  2209. if (!pInfos) return;
  2210. int32_t i=0; C4PlayerInfo *pInfo;
  2211. while (pInfo = pInfos->GetPlayerInfo(i++)) if (pInfo->IsJoined() && !pInfo->IsRemoved()) break;
  2212. if (!pInfo) return;
  2213. // Make sure league client is avilable
  2214. LeagueWaitNotBusy();
  2215. // report the disconnect!
  2216. LogF(LoadResStr("IDS_LEAGUE_LEAGUEREPORTINGUNEXPECTED"), (int) eReason);
  2217. pLeagueClient->ReportDisconnect(*pInfos, eReason);
  2218. // wait for the reply
  2219. LeagueWaitNotBusy();
  2220. // display it
  2221. const char *szMsg;
  2222. StdStrBuf sMessage;
  2223. if (pLeagueClient->GetReportDisconnectReply(&sMessage))
  2224. szMsg = LoadResStr("IDS_MSG_LEAGUEUNEXPECTEDDISCONNEC");
  2225. else
  2226. szMsg = LoadResStr("IDS_ERR_LEAGUEERRORREPORTINGUNEXP");
  2227. LogF(szMsg, sMessage.getData());
  2228. }
  2229. void C4Network2::LeagueWaitNotBusy()
  2230. {
  2231. // league client busy?
  2232. if (!pLeagueClient || !pLeagueClient->isBusy()) return;
  2233. // wait for it
  2234. Log(LoadResStr("IDS_LEAGUE_WAITINGFORLASTLEAGUESERVE"));
  2235. while(pLeagueClient->isBusy())
  2236. if(!pLeagueClient->Execute(100))
  2237. break;
  2238. // if last request was an update request, process it
  2239. if (pLeagueClient->getCurrentAction() == C4LA_Update)
  2240. LeagueUpdateProcessReply();
  2241. }
  2242. void C4Network2::LeagueSurrender()
  2243. {
  2244. // there's currently no functionality to surrender in the league
  2245. // just stop responding so other clients will notify the disconnect
  2246. DeinitLeague();
  2247. }
  2248. void C4Network2::LeagueShowError(const char *szMsg)
  2249. {
  2250. if (::pGUI && Application.isFullScreen)
  2251. {
  2252. ::pGUI->ShowErrorMessage(szMsg);
  2253. }
  2254. else
  2255. {
  2256. LogF(LoadResStr("IDS_LGA_SERVERFAILURE"), szMsg);
  2257. }
  2258. }
  2259. void C4Network2::Vote(C4ControlVoteType eType, bool fApprove, int32_t iData)
  2260. {
  2261. // Original vote?
  2262. if(!GetVote(C4ClientIDUnknown, eType, iData))
  2263. {
  2264. // Too fast?
  2265. if(time(NULL) < (time_t) (iLastOwnVoting + C4NetMinVotingInterval))
  2266. {
  2267. Log(LoadResStr("IDS_TEXT_YOUCANONLYSTARTONEVOTINGE"));
  2268. if(eType == VT_Kick || eType == VT_Cancel)
  2269. OpenSurrenderDialog(eType, iData);
  2270. return;
  2271. }
  2272. // Save timestamp
  2273. iLastOwnVoting = time(NULL);
  2274. }
  2275. // Already voted? Ignore
  2276. if(GetVote(::Control.ClientID(), eType, iData))
  2277. return;
  2278. // Set pause mode if this is the host
  2279. if(isHost() && isRunning())
  2280. {
  2281. Pause();
  2282. fPausedForVote = true;
  2283. }
  2284. // send vote control
  2285. ::Control.DoInput(CID_Vote, new C4ControlVote(eType, fApprove, iData), CDT_Direct);
  2286. }
  2287. void C4Network2::AddVote(const C4ControlVote &Vote)
  2288. {
  2289. // Save back timestamp
  2290. if(!Votes.firstPkt())
  2291. iVoteStartTime = time(NULL);
  2292. // Save vote back
  2293. Votes.Add(CID_Vote, new C4ControlVote(Vote));
  2294. // Set pause mode if this is the host
  2295. if(isHost() && isRunning())
  2296. {
  2297. Pause();
  2298. fPausedForVote = true;
  2299. }
  2300. // Check if the dialog should be opened
  2301. OpenVoteDialog();
  2302. }
  2303. C4IDPacket *C4Network2::GetVote(int32_t iClientID, C4ControlVoteType eType, int32_t iData)
  2304. {
  2305. C4ControlVote *pVote;
  2306. for(C4IDPacket *pPkt = Votes.firstPkt(); pPkt; pPkt = Votes.nextPkt(pPkt))
  2307. if(pPkt->getPktType() == CID_Vote)
  2308. if(pVote = static_cast<C4ControlVote *>(pPkt->getPkt()))
  2309. if(iClientID == C4ClientIDUnknown || pVote->getByClient() == iClientID)
  2310. if(pVote->getType() == eType && pVote->getData() == iData)
  2311. return pPkt;
  2312. return NULL;
  2313. }
  2314. void C4Network2::EndVote(C4ControlVoteType eType, bool fApprove, int32_t iData)
  2315. {
  2316. // Remove all vote packets
  2317. C4IDPacket *pPkt; int32_t iOrigin = C4ClientIDUnknown;
  2318. while(pPkt = GetVote(C4ClientIDAll, eType, iData))
  2319. {
  2320. if(iOrigin == C4ClientIDUnknown)
  2321. iOrigin = static_cast<C4ControlVote *>(pPkt->getPkt())->getByClient();
  2322. Votes.Delete(pPkt);
  2323. }
  2324. // Reset timestamp
  2325. iVoteStartTime = time(NULL);
  2326. // Approved own voting? Reset voting block
  2327. if(fApprove && iOrigin == Game.Clients.getLocalID())
  2328. iLastOwnVoting = 0;
  2329. // Dialog open?
  2330. if(pVoteDialog)
  2331. if(pVoteDialog->getVoteType() == eType && pVoteDialog->getVoteData() == iData)
  2332. {
  2333. // close
  2334. delete pVoteDialog;
  2335. pVoteDialog = NULL;
  2336. }
  2337. // Did we try to kick ourself? Ask if we'd like to surrender
  2338. if(!fApprove && (eType == VT_Kick || eType == VT_Cancel) && iOrigin == Game.Clients.getLocalID())
  2339. OpenSurrenderDialog(eType, iData);
  2340. // Check if the dialog should be opened
  2341. OpenVoteDialog();
  2342. // Pause/unpause voting?
  2343. if(fApprove && eType == VT_Pause)
  2344. fPausedForVote = !iData;
  2345. // No voting left? Reset pause.
  2346. if(!Votes.firstPkt())
  2347. if(fPausedForVote)
  2348. {
  2349. Start();
  2350. fPausedForVote = false;
  2351. }
  2352. }
  2353. void C4Network2::OpenVoteDialog()
  2354. {
  2355. // Dialog already open?
  2356. if(pVoteDialog) return;
  2357. // No GUI?
  2358. if(!::pGUI) return;
  2359. // No vote available?
  2360. if(!Votes.firstPkt()) return;
  2361. // Can't vote?
  2362. C4ClientPlayerInfos *pPlayerInfos = Game.PlayerInfos.GetInfoByClientID(Game.Clients.getLocalID());
  2363. if(!pPlayerInfos || !pPlayerInfos->GetPlayerCount() || !pPlayerInfos->GetJoinedPlayerCount())
  2364. return;
  2365. // Search a voting we have to vote on
  2366. for(C4IDPacket *pPkt = Votes.firstPkt(); pPkt; pPkt = Votes.nextPkt(pPkt))
  2367. {
  2368. // Already voted on this matter?
  2369. C4ControlVote *pVote = static_cast<C4ControlVote *>(pPkt->getPkt());
  2370. if(!GetVote(::Control.ClientID(), pVote->getType(), pVote->getData()))
  2371. {
  2372. // Compose message
  2373. C4Client *pSrcClient = Game.Clients.getClientByID(pVote->getByClient());
  2374. StdStrBuf Msg; Msg.Format(LoadResStr("IDS_VOTE_WANTSTOALLOW"), pSrcClient ? pSrcClient->getName() : "???", pVote->getDesc().getData());
  2375. Msg.AppendChar('|');
  2376. Msg.Append(pVote->getDescWarning());
  2377. // Open dialog
  2378. pVoteDialog = new C4VoteDialog(Msg.getData(), pVote->getType(), pVote->getData(), false);
  2379. pVoteDialog->SetDelOnClose();
  2380. pVoteDialog->Show(::pGUI, true);
  2381. break;
  2382. }
  2383. }
  2384. }
  2385. void C4Network2::OpenSurrenderDialog(C4ControlVoteType eType, int32_t iData)
  2386. {
  2387. if(!pVoteDialog)
  2388. {
  2389. pVoteDialog = new C4VoteDialog(
  2390. LoadResStr("IDS_VOTE_SURRENDERWARNING"), eType, iData, true);
  2391. pVoteDialog->SetDelOnClose();
  2392. pVoteDialog->Show(::pGUI, true);
  2393. }
  2394. }
  2395. void C4Network2::OnVoteDialogClosed()
  2396. {
  2397. pVoteDialog = NULL;
  2398. }
  2399. // *** C4VoteDialog
  2400. C4VoteDialog::C4VoteDialog(const char *szText, C4ControlVoteType eVoteType, int32_t iVoteData, bool fSurrender)
  2401. : eVoteType(eVoteType), iVoteData(iVoteData), fSurrender(fSurrender),
  2402. MessageDialog(szText, LoadResStr("IDS_DLG_VOTING"), C4GUI::MessageDialog::btnYesNo, C4GUI::Ico_Confirm, C4GUI::MessageDialog::dsRegular, NULL, true)
  2403. {
  2404. }
  2405. void C4VoteDialog::OnClosed(bool fOK)
  2406. {
  2407. bool fAbortGame = false;
  2408. // notify that this object will be deleted shortly
  2409. ::Network.OnVoteDialogClosed();
  2410. // Was league surrender dialog
  2411. if (fSurrender)
  2412. {
  2413. // League surrender accepted
  2414. if (fOK)
  2415. {
  2416. // set game leave reason, although round results dialog isn't showing it ATM
  2417. Game.RoundResults.EvaluateNetwork(C4RoundResults::NR_NetError, LoadResStr("IDS_ERR_YOUSURRENDEREDTHELEAGUEGA"));
  2418. // leave game
  2419. ::Network.LeagueSurrender();
  2420. ::Network.Clear();
  2421. // We have just league-surrendered. Abort the game - that is what we originally wanted.
  2422. // Note: as we are losing league points and this is a relevant game, it would actually be
  2423. // nice to show an evaluation dialog which tells us that we have lost and how many league
  2424. // points we have lost. But until the evaluation dialog can actually do that, it is better
  2425. // to abort completely.
  2426. // Note2: The league dialog will never know that, because the game will usually not be over yet.
  2427. // Scores are not calculated until after the game.
  2428. fAbortGame = true;
  2429. }
  2430. }
  2431. // Was normal vote dialog
  2432. else
  2433. {
  2434. // Vote still active? Then vote.
  2435. if (::Network.GetVote(C4ClientIDUnknown, eVoteType, iVoteData))
  2436. ::Network.Vote(eVoteType, fOK, iVoteData);
  2437. }
  2438. // notify base class
  2439. MessageDialog::OnClosed(fOK);
  2440. // Abort game
  2441. if (fAbortGame)
  2442. Game.Abort(true);
  2443. }
  2444. /* Lobby countdown */
  2445. void C4Network2::StartLobbyCountdown(int32_t iCountdownTime)
  2446. {
  2447. // abort previous
  2448. if (pLobbyCountdown) AbortLobbyCountdown();
  2449. // start new
  2450. pLobbyCountdown = new C4GameLobby::Countdown(iCountdownTime);
  2451. }
  2452. void C4Network2::AbortLobbyCountdown()
  2453. {
  2454. // aboert lobby countdown
  2455. if (pLobbyCountdown)
  2456. {
  2457. pLobbyCountdown->Abort();
  2458. delete pLobbyCountdown;
  2459. pLobbyCountdown = NULL;
  2460. }
  2461. }
  2462. /* Streaming */
  2463. bool C4Network2::StartStreaming(C4Record *pRecord)
  2464. {
  2465. // Save back
  2466. fStreaming = true;
  2467. pStreamedRecord = pRecord;
  2468. iLastStreamAttempt = time(NULL);
  2469. // Initialize compressor
  2470. ZeroMem(&StreamCompressor, sizeof(StreamCompressor));
  2471. if(deflateInit(&StreamCompressor, 9) != Z_OK)
  2472. return false;
  2473. // Create stream buffer
  2474. StreamingBuf.New(C4NetStreamingMaxBlockSize);
  2475. StreamCompressor.next_out = reinterpret_cast<BYTE*>(StreamingBuf.getMData());
  2476. StreamCompressor.avail_out = C4NetStreamingMaxBlockSize;
  2477. // Initialize HTTP client
  2478. pStreamer = new C4Network2HTTPClient();
  2479. if(!pStreamer->Init())
  2480. return false;
  2481. Application.Add(pStreamer);
  2482. return true;
  2483. }
  2484. bool C4Network2::FinishStreaming()
  2485. {
  2486. if(!fStreaming) return false;
  2487. // Stream
  2488. StreamIn(true);
  2489. // Reset record pointer
  2490. pStreamedRecord = NULL;
  2491. // Try to get rid of remaining data immediately
  2492. iLastStreamAttempt = 0;
  2493. StreamOut();
  2494. return true;
  2495. }
  2496. bool C4Network2::StopStreaming()
  2497. {
  2498. if(!fStreaming) return false;
  2499. // Clear
  2500. Application.Remove(pStreamer);
  2501. fStreaming = false;
  2502. pStreamedRecord = NULL;
  2503. deflateEnd(&StreamCompressor);
  2504. StreamingBuf.Clear();
  2505. delete pStreamer;
  2506. pStreamer = NULL;
  2507. // ... finalization?
  2508. return true;
  2509. }
  2510. bool C4Network2::StreamIn(bool fFinish)
  2511. {
  2512. if(!pStreamedRecord) return false;
  2513. // Get data from record
  2514. const StdBuf &Data = pStreamedRecord->GetStreamingBuf();
  2515. if(!fFinish)
  2516. if(!Data.getSize() || !StreamCompressor.avail_out)
  2517. return false;
  2518. do
  2519. {
  2520. // Compress
  2521. StreamCompressor.next_in = const_cast<BYTE *>(getBufPtr<BYTE>(Data));
  2522. StreamCompressor.avail_in = Data.getSize();
  2523. int ret = deflate(&StreamCompressor, fFinish ? Z_FINISH : Z_NO_FLUSH);
  2524. // Anything consumed?
  2525. unsigned int iInAmount = Data.getSize() - StreamCompressor.avail_in;
  2526. if(iInAmount > 0)
  2527. pStreamedRecord->ClearStreamingBuf(iInAmount);
  2528. // Done?
  2529. if(!fFinish || ret == Z_STREAM_END)
  2530. break;
  2531. // Error while finishing?
  2532. if(ret != Z_OK)
  2533. return false;
  2534. // Enlarge buffer, if neccessary
  2535. size_t iPending = getPendingStreamData();
  2536. size_t iGrow = StreamingBuf.getSize();
  2537. StreamingBuf.Grow(iGrow);
  2538. StreamCompressor.avail_out += iGrow;
  2539. StreamCompressor.next_out = getMBufPtr<BYTE>(StreamingBuf, iPending);
  2540. }
  2541. while(true);
  2542. return true;
  2543. }
  2544. bool C4Network2::StreamOut()
  2545. {
  2546. // Streamer busy?
  2547. if(!pStreamer || pStreamer->isBusy())
  2548. return false;
  2549. // Streamer done?
  2550. if(pStreamer->isSuccess())
  2551. {
  2552. // Move new data to front of buffer
  2553. if(getPendingStreamData() != iCurrentStreamAmount)
  2554. StreamingBuf.Move(iCurrentStreamAmount, getPendingStreamData() - iCurrentStreamAmount);
  2555. // Free buffer space
  2556. StreamCompressor.next_out -= iCurrentStreamAmount;
  2557. StreamCompressor.avail_out += iCurrentStreamAmount;
  2558. // Advance stream
  2559. iCurrentStreamPosition += iCurrentStreamAmount;
  2560. // Get input
  2561. StreamIn(false);
  2562. }
  2563. // Clear streamer
  2564. pStreamer->Clear();
  2565. // Record is still running?
  2566. if(pStreamedRecord)
  2567. {
  2568. // Enough available to send?
  2569. if(getPendingStreamData() < C4NetStreamingMinBlockSize)
  2570. return false;
  2571. // Overflow protection
  2572. if(iLastStreamAttempt && iLastStreamAttempt + C4NetStreamingInterval >= time(NULL))
  2573. return false;
  2574. }
  2575. // All data finished?
  2576. else if (!getPendingStreamData())
  2577. {
  2578. // Then we're done.
  2579. StopStreaming();
  2580. return false;
  2581. }
  2582. // Set stream address
  2583. StdStrBuf StreamAddr;
  2584. StreamAddr.Copy(Game.Parameters.StreamAddress);
  2585. StreamAddr.AppendFormat("pos=%d&end=%d", iCurrentStreamPosition, !pStreamedRecord);
  2586. pStreamer->SetServer(StreamAddr.getData());
  2587. // Send data
  2588. size_t iStreamAmount = getPendingStreamData();
  2589. iCurrentStreamAmount = iStreamAmount;
  2590. iLastStreamAttempt = time(NULL);
  2591. return pStreamer->Query(StdBuf(StreamingBuf.getData(), iStreamAmount), false);
  2592. }
  2593. bool C4Network2::isStreaming() const
  2594. {
  2595. // Streaming must be active and there must still be anything to stream
  2596. return fStreaming;
  2597. }
  2598. //C4Network2 Network;