| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) RedWolf Design |
| 5 | * Copyright (c) 2006, Sven2 |
| 6 | * Copyright (c) 2017-2020, The LegacyClonk Team and contributors |
| 7 | * |
| 8 | * Distributed under the terms of the ISC license; see accompanying file |
| 9 | * "COPYING" for details. |
| 10 | * |
| 11 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 12 | * See accompanying file "TRADEMARK" for details. |
| 13 | * |
| 14 | * To redistribute this file separately, substitute the full license texts |
| 15 | * for the above references. |
| 16 | */ |
| 17 | |
| 18 | // Startup screen for non-parameterized engine start: Network game selection dialog |
| 19 | |
| 20 | #pragma once |
| 21 | |
| 22 | #include "C4Startup.h" |
| 23 | #include "C4Network2Discover.h" |
| 24 | #include "C4Network2Reference.h" |
| 25 | #include "C4UpdateDlg.h" |
| 26 | |
| 27 | const int C4NetMasterServerQueryInterval = 30; // seconds after last and beforenew game query to master server |
| 28 | const int C4NetRefRequestTimeout = 12; // seconds after which the reference request is interrupted |
| 29 | const int C4NetReferenceTimeout = 42; // seconds after which references are removed from the list (C4NetRefRequestTimeout + C4NetMasterServerQueryInterval) |
| 30 | const int C4NetErrorRefTimeout = 10; // seconds after which failed reference requestsare removed |
| 31 | const int C4NetGameDiscoveryInterval = 30; // seconds |
| 32 | const int C4NetMinRefreshInterval = 1; // seconds; minimum time between refreshes |
| 33 | |
| 34 | class C4StartupNetListEntry : public C4GUI::Window |
| 35 | { |
| 36 | public: |
| 37 | C4StartupNetListEntry(C4GUI::ListBox *pForListBox, C4GUI::Element *pInsertBefore, class C4StartupNetDlg *pNetDlg); |
| 38 | ~C4StartupNetListEntry(); |
| 39 | |
| 40 | enum QueryType // where the game ref is coming from |
| 41 | { |
| 42 | NRQT_Unknown, // unknown source |
| 43 | NRQT_GameDiscovery, // by UDP broadcast in local network |
| 44 | NRQT_Masterserver, // by internet masterserver |
| 45 | NRQT_DirectJoin, // by user-entered address |
| 46 | }; |
| 47 | |
| 48 | enum TimeoutType |
| 49 | { |
| 50 | TT_RefReqWait, // C4NetMasterServerQueryInterval for masterserver; C4NetErrorRefTimeout for other ref clients |
| 51 | TT_Reference, // C4NetReferenceTimeout |
| 52 | TT_Masterserver, // C4NetMasterServerQueryInterval |
| 53 | TT_Refresh, // Refresh as soon as possible |
| 54 | }; |
| 55 | |
| 56 | enum { InfoLabelCount = 5, MaxInfoIconCount = 10, }; |
| 57 | |
| 58 | private: |
| 59 | C4StartupNetDlg *pNetDlg; |
| 60 | C4GUI::ListBox *pList; |
| 61 | StdStrBuf sRefClientAddress; // set during reference retrieval: reference server address |
| 62 | C4Network2RefClient *pRefClient; // set during reference retrieval: reference request client |
| 63 | C4Network2Reference *pRef; // set for retrieved references |
| 64 | bool fError; // if set, the label was changed to an error message and no more updates are done |
| 65 | StdStrBuf sError; |
| 66 | QueryType eQueryType; // valid if pRefClient is set: Where the ref query is originating |
| 67 | |
| 68 | time_t iTimeout; // lifetime: If set, marks time when the item is removed or should re-query |
| 69 | time_t iRequestTimeout; // if this times out while the request remains busy, the ref client assumes that the tcp socket blocked upon request and aborts it |
| 70 | int32_t iNumFails; // number of times this reference has retried connecting |
| 71 | |
| 72 | C4GUI::Icon *pIcon; // scenario icon |
| 73 | C4GUI::Label *pInfoLbl[InfoLabelCount]; // info labels for reference or query |
| 74 | C4GUI::Icon *pInfoIcons[MaxInfoIconCount]; // right-aligned status icons at topright position |
| 75 | int32_t iInfoLink; // if !=-1, index of the label that should be turned into a clickable link (for MotD-link) |
| 76 | int32_t iInfoIconCount; |
| 77 | int32_t iSortOrder; |
| 78 | bool fIsSmall; // set if the item is in collapsed state |
| 79 | bool fIsCollapsed; // set if the item is forced to collapsed state |
| 80 | bool fIsEnabled; // if not set, the item is grayed out |
| 81 | bool fIsImportant; // if set, the item is presented in yellow (lower priority than fIsEnabled) |
| 82 | C4Rect rctIconSmall; // bounds for small icon |
| 83 | C4Rect rctIconLarge; // bounds for large icon |
| 84 | |
| 85 | StdStrBuf sInfoText[InfoLabelCount]; |
| 86 | |
| 87 | bool QueryReferences(); |
| 88 | static const char *GetQueryTypeName(QueryType eQueryType); // name of QueryType values |
| 89 | void SetError(const char *szErrorText, TimeoutType eTimeout); // change secondary label to error label, mark error and set a removal timer |
| 90 | void SetTimeout(TimeoutType eTimeout); |
| 91 | C4StartupNetListEntry *AddReference(C4Network2Reference *pAddRef, C4GUI::Element *pInsertBefore); // add a reference list item to the same list |
| 92 | void InvalidateStatusIcons() { iInfoIconCount = 0; } // schedule all current status icons for removal when UpdateText is called next |
| 93 | void AddStatusIcon(C4GUI::Icons eIcon, const char *szToolTip); // add a status icon with the specified tooltip |
| 94 | |
| 95 | void UpdateSmallState(); |
| 96 | void UpdateEntrySize(); |
| 97 | void UpdateText(); // strings to labels |
| 98 | |
| 99 | protected: |
| 100 | virtual int32_t GetListItemTopSpacing() override { return fIsCollapsed ? 5 : 10; } |
| 101 | virtual void DrawElement(C4FacetEx &cgo) override; |
| 102 | |
| 103 | C4GUI::Element *GetNextLower(int32_t sortOrder); // returns the element before which this element should be inserted |
| 104 | |
| 105 | public: |
| 106 | void ClearRef(); // del any ref/refclient/error data |
| 107 | void SetRefQuery(const char *szAddress, QueryType eQueryType); // start loading references fromt he specified address |
| 108 | void SetReference(C4Network2Reference *pNewRef); // replace the reference |
| 109 | |
| 110 | bool Execute(); // update stuff - if false is returned, the item is to be removed |
| 111 | void OnRequestFailed(); // called on connection error or timeout |
| 112 | bool OnReference(); // like Execute(), but only called if some reference was received |
| 113 | void UpdateCollapsed(bool fToCollapseValue); |
| 114 | |
| 115 | const char *GetError() { return fError ? sError.getData() : nullptr; } // return error message, if any is set |
| 116 | C4Network2Reference *GrabReference(); // grab the reference so it won't be deleted when this item is removed |
| 117 | C4Network2Reference *GetReference() const { return pRef; } // have a look at the reference |
| 118 | bool IsSameHost(const C4Network2Reference *pRef2); // check whether the reference was created by the same host as this one |
| 119 | bool IsSameAddress(const C4Network2Reference *pRef2); // check whether there is at least one matching address (address and port) |
| 120 | bool IsSameRefQueryAddress(const char *szJoinAddress); // check whether reference query was created with same host address |
| 121 | const char *GetJoinAddress(); // ref queries only: Get direct join address |
| 122 | }; |
| 123 | |
| 124 | // startup dialog: Network game selection |
| 125 | class C4StartupNetDlg : public C4StartupDlg, private C4InteractiveThread::Callback |
| 126 | { |
| 127 | public: |
| 128 | C4StartupNetDlg(); |
| 129 | ~C4StartupNetDlg(); |
| 130 | |
| 131 | private: |
| 132 | enum DlgMode { SNDM_GameList = 0, SNDM_Chat = 1 }; |
| 133 | C4GUI::Tabular *pMainTabular; // main tabular control: Contains game selection list and chat control |
| 134 | C4GUI::ListBox *pGameSelList; // game selection listbox |
| 135 | C4KeyBinding *pKeyRefresh, *pKeyBack, *pKeyForward; |
| 136 | C4GUI::CallbackButton<C4StartupNetDlg, C4GUI::IconButton> *btnGameList, *btnChat; // left side buttons |
| 137 | C4GUI::CallbackButton<C4StartupNetDlg, C4GUI::IconButton> *btnInternet, *btnRecord; // right side buttons |
| 138 | C4GUI::Button *btnJoin, *btnRefresh; |
| 139 | C4GUI::Edit *pJoinAddressEdt; |
| 140 | class C4ChatControl *pChatCtrl; |
| 141 | C4GUI::WoodenLabel *pChatTitleLabel; |
| 142 | C4StartupNetListEntry *pMasterserverClient; // set if masterserver query is enabled: Checks clonk.de for new games |
| 143 | bool fIsCollapsed; // set if the number of games in the list requires them to be displayed in a condensed format |
| 144 | bool fUpdatingList; // set during list update - prevent selection update calls |
| 145 | |
| 146 | C4Sec1TimerCallback<C4StartupNetDlg> *pSec1Timer; // engine timer hook for list updates |
| 147 | |
| 148 | C4Network2IODiscoverClient DiscoverClient; // disocver client to search for local hosts |
| 149 | |
| 150 | int iGameDiscoverInterval; // next time until a game discovery is started |
| 151 | time_t tLastRefresh; // time of last refresh |
| 152 | bool fIgnoreUpdate; // set after user pressed "No" on a server redirect once |
| 153 | |
| 154 | protected: |
| 155 | virtual bool HasBackground() override { return true; } |
| 156 | virtual void DrawElement(C4FacetEx &cgo) override; |
| 157 | |
| 158 | virtual C4GUI::Control *GetDefaultControl() override; // get Auto-Focus control |
| 159 | C4GUI::Control *GetDlgModeFocusControl(); // get control to be focused when main tabular sheet changes |
| 160 | |
| 161 | virtual bool OnEnter() override { DoOK(); return true; } |
| 162 | virtual bool OnEscape() override { DoBack(); return true; } |
| 163 | bool KeyBack() { return DoBack(); } |
| 164 | bool KeyRefresh() { DoRefresh(); return true; } |
| 165 | bool KeyForward() { DoOK(); return true; } |
| 166 | |
| 167 | virtual void OnShown() override; // callback when shown: Start searching for games |
| 168 | virtual void OnClosed(bool fOK) override; // callback when dlg got closed: Return to main screen |
| 169 | void OnBackBtn(C4GUI::Control *btn) { DoBack(); } |
| 170 | void OnRefreshBtn(C4GUI::Control *btn) { DoRefresh(); } |
| 171 | void OnCreateGameBtn(C4GUI::Control *btn) { CreateGame(); } |
| 172 | void OnJoinGameBtn(C4GUI::Control *btn) { DoOK(); } |
| 173 | void OnSelChange(class C4GUI::Element *pEl) { UpdateSelection(fUpdateCollapsed: true); } |
| 174 | void OnSelDblClick(class C4GUI::Element *pEl) { DoOK(); } |
| 175 | void OnBtnGameList(C4GUI::Control *btn); |
| 176 | void OnBtnChat(C4GUI::Control *btn); |
| 177 | void OnBtnInternet(C4GUI::Control *btn); |
| 178 | void OnBtnRecord(C4GUI::Control *btn); |
| 179 | void OnBtnUpdate(C4GUI::Control *btn) { C4UpdateDlg::CheckForUpdates(pScreen: GetScreen()); } |
| 180 | |
| 181 | C4GUI::InputResult OnJoinAddressEnter(C4GUI::Edit *edt, bool fPasting, bool fPastingMore) |
| 182 | { |
| 183 | DoOK(); return C4GUI::IR_Abort; |
| 184 | } |
| 185 | |
| 186 | void OnChatTitleChange(const StdStrBuf &sNewTitle); |
| 187 | |
| 188 | private: |
| 189 | static bool IsOpen(C4StartupNetDlg *instance); |
| 190 | void UpdateMasterserver(); // creates masterserver object if masterserver is enabled; destroy otherwise |
| 191 | void UpdateList(bool fGotReference = false); |
| 192 | void UpdateCollapsed(); |
| 193 | void UpdateSelection(bool fUpdateCollapsed); |
| 194 | void UpdateDlgMode(); // update button visibility after switching between game sel list and chat |
| 195 | |
| 196 | void AddReferenceQuery(const char *szAddress, C4StartupNetListEntry::QueryType eQueryType); // add a ref searcher entry and start searching |
| 197 | |
| 198 | DlgMode GetDlgMode(); |
| 199 | |
| 200 | // callback from C4Network2ReferenceClient |
| 201 | void OnThreadEvent(C4InteractiveEventType, const std::any &) override; |
| 202 | |
| 203 | public: |
| 204 | bool DoOK(); // join currently selected item |
| 205 | bool DoBack(); // abort dialog |
| 206 | void DoRefresh(); // restart network search |
| 207 | void CreateGame(); // switch to scenario selection in network mode |
| 208 | |
| 209 | void OnReferenceEntryAdd(C4StartupNetListEntry *pEntry); |
| 210 | |
| 211 | void OnSec1Timer(); // idle proc: update list |
| 212 | |
| 213 | void SetIgnoreUpdate(bool to_val) { fIgnoreUpdate = to_val; } |
| 214 | bool GetIgnoreUpdate() const { return fIgnoreUpdate; } |
| 215 | }; |
| 216 | |