1 /* 2 * OpenClonk, http://www.openclonk.org 3 * 4 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ 5 * Copyright (c) 2009-2016, The OpenClonk Team and contributors 6 * 7 * Distributed under the terms of the ISC license; see accompanying file 8 * "COPYING" for details. 9 * 10 * "Clonk" is a registered trademark of Matthes Bender, used with permission. 11 * See accompanying file "TRADEMARK" for details. 12 * 13 * To redistribute this file separately, substitute the full license texts 14 * for the above references. 15 */ 16 #ifndef INC_C4Network2 17 #define INC_C4Network2 18 19 #include "control/C4Control.h" 20 #include "control/C4GameParameters.h" 21 #include "gui/C4Gui.h" 22 #include "network/C4NetIO.h" 23 #include "network/C4Network2Client.h" 24 #include "network/C4Network2IO.h" 25 #include "network/C4Network2Players.h" 26 #include "network/C4Network2Res.h" 27 28 // standard ports 29 const int16_t C4NetStdPortTCP = 11112, 30 C4NetStdPortUDP = 11113, 31 C4NetStdPortDiscovery = 11114, 32 C4NetStdPortRefServer = 11111, 33 C4NetStdPortPuncher = 11115, 34 C4NetStdPortHTTP = 80; 35 36 // resource retrieve wait timeout 37 const int C4NetResRetrieveTimeout = 100000; // (ms) 38 39 // client (de)activation 40 const int C4NetActivationReqInterval = 5000, // (ms) 41 C4NetMaxBehind4Activation = 20, // (ticks) 42 C4NetDeactivationDelay = 500; // (ticks) 43 44 // client chase 45 const unsigned int C4NetChaseTargetUpdateInterval = 5; // (s) 46 47 // reference 48 const unsigned int C4NetReferenceUpdateInterval = 120; // (s) 49 const unsigned int C4NetMinLeagueUpdateInterval = 1; // (s) 50 51 // voting 52 const unsigned int C4NetVotingTimeout = 10; // (s) 53 const unsigned int C4NetMinVotingInterval = 120; // (s) 54 55 // streaming 56 const size_t C4NetStreamingMinBlockSize = 10 * 1024; 57 const size_t C4NetStreamingMaxBlockSize = 20 * 1024; 58 const int C4NetStreamingInterval = 30; // (s) 59 60 enum C4NetGameState 61 { 62 GS_None, // network not active 63 GS_Init, // connecting to host, waiting for join data 64 GS_Lobby, // lobby mode 65 GS_Pause, // game paused 66 GS_Go // game running 67 }; 68 69 class C4Network2Status : public C4PacketBase 70 { 71 public: 72 C4Network2Status(); 73 74 protected: 75 C4NetGameState eState{GS_None}; 76 int32_t iCtrlMode; 77 int32_t iTargetCtrlTick{-1}; 78 79 public: getState()80 C4NetGameState getState() const { return eState; } getCtrlMode()81 int32_t getCtrlMode() const { return iCtrlMode; } getTargetCtrlTick()82 int32_t getTargetCtrlTick() const { return iTargetCtrlTick; } 83 const char *getStateName() const; 84 const char *getDescription() const; 85 isEnabled()86 bool isEnabled() const { return eState != GS_None; } isLobbyActive()87 bool isLobbyActive() const { return eState == GS_Lobby; } isPastLobby()88 bool isPastLobby() const { return eState > GS_Lobby; } isPaused()89 bool isPaused() const { return eState == GS_Pause; } isRunning()90 bool isRunning() const { return eState == GS_Go; } 91 92 void Set(C4NetGameState eState, int32_t iTargetCtrlTick); 93 void SetCtrlMode(int32_t iCtrlMode); 94 void SetTargetTick(int32_t iTargetCtrlTick); 95 void Clear(); 96 97 void CompileFunc(StdCompiler *pComp, bool fReference); 98 void CompileFunc(StdCompiler *pComp) override; 99 }; 100 101 class C4Network2 : private C4ApplicationSec1Timer 102 { 103 friend class C4Network2IO; 104 public: 105 C4Network2(); 106 ~C4Network2() override; 107 108 public: 109 // network i/o class 110 C4Network2IO NetIO; 111 112 // resource list 113 C4Network2ResList ResList; 114 115 // client list 116 C4Network2ClientList Clients; 117 118 // player list 119 C4Network2Players Players; 120 121 // game status 122 C4Network2Status Status; 123 124 protected: 125 126 // role 127 bool fHost; 128 129 // options 130 bool fAllowJoin{false}, fAllowObserve; 131 132 // join resource 133 C4Network2ResCore ResDynamic; 134 135 // resources 136 int32_t iDynamicTick{-1}; 137 bool fDynamicNeeded{false}; 138 139 // game status flags 140 bool fStatusAck{false}, fStatusReached{false}; 141 bool fChasing{false}; 142 143 // control 144 class C4GameControlNetwork *pControl{nullptr}; 145 146 // lobby 147 C4GameLobby::MainDlg *pLobby{nullptr}; 148 bool fLobbyRunning{false}; 149 C4GameLobby::Countdown *pLobbyCountdown{nullptr}; 150 151 // master server used 152 StdCopyStrBuf MasterServerAddress; 153 154 // clients 155 int32_t iNextClientID{0}; 156 157 // chase 158 uint32_t iLastChaseTargetUpdate{0}; 159 160 // time of last activation request. 161 C4TimeMilliseconds tLastActivateRequest; 162 163 // reference 164 uint32_t iLastReferenceUpdate{0}; 165 uint32_t iLastLeagueUpdate{0}, iLeagueUpdateDelay; 166 bool fLeagueEndSent; 167 168 // league 169 class C4LeagueClient *pLeagueClient{nullptr}; 170 171 // game password 172 StdStrBuf sPassword; 173 174 // connection failed because password needed? 175 bool fWrongPassword; 176 177 // delayed activation request? 178 bool fDelayedActivateReq{false}; 179 180 // voting 181 C4Control Votes; 182 class C4VoteDialog *pVoteDialog{nullptr}; 183 bool fPausedForVote{false}; 184 time_t iVoteStartTime, iLastOwnVoting{0}; 185 186 // streaming 187 bool fStreaming{false}; 188 time_t iLastStreamAttempt; 189 C4Record *pStreamedRecord; 190 StdBuf StreamingBuf; 191 z_stream StreamCompressor; 192 193 class C4Network2HTTPClient *pStreamer; 194 unsigned int iCurrentStreamAmount, iCurrentStreamPosition; 195 196 // puncher 197 C4NetpuncherID NetpuncherGameID; 198 StdCopyStrBuf NetpuncherAddr; 199 200 public: 201 202 // data access isEnabled()203 bool isEnabled() const { return Status.isEnabled(); } isLobbyActive()204 bool isLobbyActive()const { return Status.isLobbyActive(); } isPastLobby()205 bool isPastLobby() const { return Status.isPastLobby(); } isRunning()206 bool isRunning() const { return Status.isRunning() && isStatusAck(); } isPaused()207 bool isPaused() const { return Status.isPaused() && isStatusAck(); } isPausing()208 bool isPausing() const { return Status.isPaused() && !fStatusAck; } isHost()209 bool isHost() const { return fHost; } isStatusAck()210 bool isStatusAck() const { return fStatusAck; } 211 bool isFrozen() const; 212 isJoinAllowed()213 bool isJoinAllowed() const { return fAllowJoin; } isObservingAllowed()214 bool isObservingAllowed() const { return fAllowObserve; } 215 GetLobby()216 class C4GameLobby::MainDlg *GetLobby() const { return pLobby; } // lobby publication GetPassword()217 const char *GetPassword() const { return sPassword.getData(); } // Oh noez, now the password is public! isPassworded()218 bool isPassworded() const { return !sPassword.isNull(); } 219 220 // initialization result type 221 enum InitResult 222 { 223 IR_Success, IR_Error, IR_Fatal 224 }; 225 226 // initialization 227 bool InitHost(bool fLobby); 228 InitResult InitClient(const class C4Network2Reference &Ref, bool fObserver); 229 InitResult InitClient(const std::vector<class C4Network2Address>& Addrs, const class C4ClientCore &HostCore, const char *szPassword = nullptr); 230 bool DoLobby(); 231 bool Start(); 232 bool Pause(); 233 bool Sync(); 234 bool FinalInit(); 235 236 bool RetrieveScenario(char *szScenario); 237 C4Network2Res::Ref RetrieveRes(const C4Network2ResCore &Core, int32_t iTimeout, const char *szResName, bool fWaitForCore = false); 238 239 // idle processes 240 void OnSec1Timer() override; 241 void Execute(); 242 243 // termination 244 void Clear(); 245 246 // some options 247 bool ToggleAllowJoin(); 248 bool ToggleClientListDlg(); 249 void AllowJoin(bool fAllow); 250 void SetAllowObserve(bool fAllow); 251 void SetCtrlMode(int32_t iCtrlMode); 252 void SetPassword(const char *szToPassword); 253 StdStrBuf QueryClientPassword(); // ask client for a password; empty if user canceled 254 255 // interface for C4Network2IO 256 void OnConn(C4Network2IOConnection *pConn); 257 void OnDisconn(C4Network2IOConnection *pConn); 258 void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn); 259 void HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn); 260 bool HandlePuncherPacket(C4NetpuncherPacket::uptr, C4NetIO::HostAddress::AddressFamily family); 261 void OnPuncherConnect(C4NetIO::addr_t addr); 262 263 // runtime join stuff 264 void OnGameSynchronized(); 265 266 // status 267 void DrawStatus(C4TargetFacet &cgo); 268 269 // client activation 270 void RequestActivate(); 271 void DeactivateInactiveClients(); // host 272 273 // league 274 void LeagueGameEvaluate(const char *szRecordName = nullptr, const BYTE *pRecordSHA = nullptr); 275 void LeagueSignupDisable(); // if "internet game" button is switched off in lobby: Remove from league server 276 bool LeagueSignupEnable(); // if "internet game" button is switched on in lobby: (re)Add to league server 277 void InvalidateReference(); // forces a recreation and re-send of the game reference in the next execution cycle 278 bool LeaguePlrAuth(C4PlayerInfo *pInfo); // client: get authentication for a player from the league server 279 bool LeaguePlrAuthCheck(C4PlayerInfo *pInfo); // host: check AUID of player info with league server 280 void LeagueNotifyDisconnect(int32_t iClientID, enum C4LeagueDisconnectReason eReason); // 281 void LeagueWaitNotBusy(); // block until league serveris no longer busy. Process update reply if last message was an update 282 void LeagueSurrender(); // forfeit in league - just fake a disconnect 283 void LeagueShowError(const char *szMsg); // show league error msg box in fullscreen; just log in console 284 285 // voting 286 void Vote(C4ControlVoteType eType, bool fApprove = true, int32_t iData = 0); 287 void AddVote(const C4ControlVote &Vote); 288 C4IDPacket *GetVote(int32_t iClientID, C4ControlVoteType eType, int32_t iData); 289 void EndVote(C4ControlVoteType eType, bool fApprove, int32_t iData); 290 void OpenVoteDialog(); 291 void OpenSurrenderDialog(C4ControlVoteType eType, int32_t iData); 292 void OnVoteDialogClosed(); 293 294 // lobby countdown 295 void StartLobbyCountdown(int32_t iCountdownTime); 296 void AbortLobbyCountdown(); isLobbyCountDown()297 bool isLobbyCountDown() { return pLobbyCountdown != nullptr; } 298 299 // streaming getPendingStreamData()300 size_t getPendingStreamData() const { return StreamingBuf.getSize() - StreamCompressor.avail_out; } 301 bool isStreaming() const; 302 bool StartStreaming(C4Record *pRecord); 303 bool FinishStreaming(); 304 bool StopStreaming(); 305 306 // netpuncher 307 C4NetpuncherID::value& getNetpuncherGameID(C4NetIO::HostAddress::AddressFamily family); getNetpuncherGameID()308 C4NetpuncherID getNetpuncherGameID() const { return NetpuncherGameID; }; getNetpuncherAddr()309 StdStrBuf getNetpuncherAddr() const { return NetpuncherAddr; } 310 311 protected: 312 313 using C4ApplicationSec1Timer::Execute; // to avoid "virtual ... is hidden" warning 314 315 // net i/o initialization 316 bool InitNetIO(bool fNoClientID, bool fHost); 317 318 // handling of own packets 319 void HandleConn(const class C4PacketConn &Pkt, C4Network2IOConnection *pConn, C4Network2Client *pClient); 320 bool CheckConn(const C4ClientCore &CCore, C4Network2IOConnection *pConn, C4Network2Client *pClient, StdStrBuf * szReply); 321 bool HostConnect(const C4ClientCore &CCore, C4Network2IOConnection *pConn, StdStrBuf *szReply); 322 bool Join(C4ClientCore &CCore, C4Network2IOConnection *pConn, StdStrBuf *szReply); 323 void HandleConnRe(const class C4PacketConnRe &Pkt, C4Network2IOConnection *pConn, C4Network2Client *pClient); 324 void HandleStatus(const C4Network2Status &nStatus); 325 void HandleStatusAck(const C4Network2Status &nStatus, C4Network2Client *pClient); 326 void HandleActivateReq(int32_t iTick, C4Network2Client *pClient); 327 void HandleJoinData(const class C4PacketJoinData &rPkt); 328 329 // events 330 void OnConnect(C4Network2Client *pClient, C4Network2IOConnection *pConn, const char *szMsg, bool fFirstConnection); 331 void OnConnectFail(C4Network2IOConnection *pConn); 332 void OnDisconnect(C4Network2Client *pClient, C4Network2IOConnection *pConn); 333 void OnClientConnect(C4Network2Client *pClient, C4Network2IOConnection *pConn); 334 void OnClientDisconnect(C4Network2Client *pClient); 335 336 void SendJoinData(C4Network2Client *pClient); 337 338 // resource list 339 bool CreateDynamic(bool fInit); 340 void RemoveDynamic(); 341 342 // status changes 343 bool PauseGame(bool fAutoContinue); 344 bool ChangeGameStatus(C4NetGameState enState, int32_t iTargetCtrlTick, int32_t iCtrlMode = -1); 345 void CheckStatusReached(bool fFromFinalInit = false); 346 void CheckStatusAck(); 347 void OnStatusReached(); 348 void OnStatusAck(); 349 350 // chase 351 void UpdateChaseTarget(); 352 353 // league 354 bool InitLeague(bool *pCancel); 355 void DeinitLeague(); 356 bool LeagueStart(bool *pCancel); 357 bool LeagueUpdate(); 358 bool LeagueUpdateProcessReply(); 359 bool LeagueEnd(const char *szRecordName = nullptr, const BYTE *pRecordSHA = nullptr); 360 361 // streaming 362 bool StreamIn(bool fFinish); 363 bool StreamOut(); 364 365 // puncher 366 void InitPuncher(); 367 368 // Manages delayed initial connection attempts. 369 class InitialConnect : protected CStdTimerProc 370 { 371 static const uint32_t DELAY = 100; // ms to wait for each batch 372 static const int ADDR_PER_TRY = 4; // how many connection attempts to start each time 373 374 const std::vector<C4Network2Address>& Addrs; 375 std::vector<C4Network2Address>::const_iterator CurrentAddr; 376 const C4ClientCore& HostCore; 377 const char *Password; 378 379 void TryNext(); 380 void Done(); 381 382 public: 383 InitialConnect(const std::vector<C4Network2Address>& Addrs, const C4ClientCore& HostCore, const char *Password); 384 ~InitialConnect() override; 385 bool Execute(int, pollfd *) override; 386 }; 387 }; 388 389 extern C4Network2 Network; 390 391 class C4VoteDialog : public C4GUI::MessageDialog 392 { 393 public: 394 C4VoteDialog(const char *szText, C4ControlVoteType eVoteType, int32_t iVoteData, bool fSurrender); 395 396 private: 397 C4ControlVoteType eVoteType; 398 int32_t iVoteData; 399 bool fSurrender; 400 401 public: getVoteType()402 C4ControlVoteType getVoteType() const { return eVoteType; } getVoteData()403 int32_t getVoteData() const { return iVoteData; } 404 405 private: OnEnter()406 bool OnEnter() override { UserClose(false); return true; } // the vote dialog defaults to "No" [MarkFra] 407 void OnClosed(bool fOK) override; 408 409 // true for dialogs that receive full keyboard and mouse input even in shared mode IsExclusiveDialog()410 bool IsExclusiveDialog() override { return true; } 411 }; 412 413 // * Packets * 414 415 class C4PacketJoinData : public C4PacketBase 416 { 417 public: 418 C4PacketJoinData() = default; 419 420 protected: 421 422 // the client ID 423 int32_t iClientID; 424 425 // Dynamic data to boot 426 C4Network2ResCore Dynamic; 427 428 // network status 429 C4Network2Status GameStatus; 430 431 // control tick 432 int32_t iStartCtrlTick; 433 434 public: 435 436 // scenario parameter definitions (so the client can see them in the lobby) 437 C4ScenarioParameterDefs ScenarioParameterDefs; 438 439 // the game parameters 440 C4GameParameters Parameters; 441 442 public: getClientID()443 const int32_t &getClientID() const { return iClientID; } getDynamicCore()444 const C4Network2ResCore&getDynamicCore() const { return Dynamic; } getStatus()445 const C4Network2Status &getStatus() const { return GameStatus; } getStartCtrlTick()446 int32_t getStartCtrlTick() const { return iStartCtrlTick; } 447 SetClientID(int32_t inClientID)448 void SetClientID(int32_t inClientID) { iClientID = inClientID; } SetGameStatus(const C4Network2Status & Status)449 void SetGameStatus(const C4Network2Status &Status) { GameStatus = Status; } SetDynamicCore(const C4Network2ResCore & Core)450 void SetDynamicCore(const C4Network2ResCore &Core) { Dynamic = Core; } SetStartCtrlTick(int32_t iTick)451 void SetStartCtrlTick(int32_t iTick) { iStartCtrlTick = iTick; } 452 453 void CompileFunc(StdCompiler *pComp) override; 454 }; 455 456 class C4PacketActivateReq : public C4PacketBase 457 { 458 public: iTick(iTick)459 C4PacketActivateReq(int32_t iTick = -1) : iTick(iTick) { } 460 461 protected: 462 int32_t iTick; 463 464 public: getTick()465 int32_t getTick() const { return iTick; } 466 467 void CompileFunc(StdCompiler *pComp) override; 468 }; 469 470 #endif 471