OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4StartupNetDlg.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2006-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 // Startup screen for non-parameterized engine start: Network game selection dialog
17 
18 #include "C4Include.h"
19 #include "gui/C4StartupNetDlg.h"
20 
21 #include "game/C4Application.h"
22 #include "gui/C4UpdateDlg.h"
24 #include "game/C4Game.h"
25 #include "gui/C4ChatDlg.h"
27 #include "graphics/C4Draw.h"
29 
30 // ----------- C4StartupNetListEntry -----------------------------------------------------------------------
31 
33  : pNetDlg(pNetDlg), pList(pForListBox), pRefClient(nullptr), pRef(nullptr), fError(false), eQueryType(NRQT_Unknown), iTimeout(0), iInfoIconCount(0), iSortOrder(0), fIsSmall(false), fIsCollapsed(false), fIsEnabled(true), fIsImportant(false)
34 {
35  // calc height
36  int32_t iLineHgt = ::GraphicsResource.TextFont.GetLineHeight(), iHeight = iLineHgt * 2 + 4;
37  // add icons - normal icons use small size, only animated netgetref uses full size
38  rctIconLarge.Set(0, 0, iHeight, iHeight);
39  int32_t iSmallIcon = iHeight * 2 / 3; rctIconSmall.Set((iHeight - iSmallIcon)/2, (iHeight - iSmallIcon)/2, iSmallIcon, iSmallIcon);
40  pIcon = new C4GUI::Icon(rctIconSmall, C4GUI::Ico_Host);
41  AddElement(pIcon);
42  SetBounds(pIcon->GetBounds());
43  // add to listbox (will get resized horizontally and moved)
44  pForListBox->InsertElement(this, pInsertBefore);
45  // add status icons and text labels now that width is known
46  CStdFont *pUseFont = &(::GraphicsResource.TextFont);
47  int32_t iIconSize = pUseFont->GetLineHeight();
48  C4Rect rcIconRect = GetContainedClientRect();
49  int32_t iThisWdt = rcIconRect.Wdt;
50  rcIconRect.x = iThisWdt - iIconSize * (iInfoIconCount + 1);
51  rcIconRect.Wdt = rcIconRect.Hgt = iIconSize;
52  for (int32_t iIcon = 0; iIcon<MaxInfoIconCount; ++iIcon)
53  {
54  AddElement(pInfoIcons[iIcon] = new C4GUI::Icon(rcIconRect, C4GUI::Ico_None));
55  rcIconRect.x -= rcIconRect.Wdt;
56  }
57  C4Rect rcLabelBounds;
58  rcLabelBounds.x = iHeight+3;
59  rcLabelBounds.Hgt = iLineHgt;
60  for (int i=0; i<InfoLabelCount; ++i)
61  {
62  C4GUI::Label *pLbl;
63  rcLabelBounds.y = 1+i*(iLineHgt+2);
64  rcLabelBounds.Wdt = iThisWdt - rcLabelBounds.x - 1;
65  if (!i) rcLabelBounds.Wdt -= iLineHgt; // leave space for topright extra icon
66  AddElement(pLbl = pInfoLbl[i] = new C4GUI::Label("", rcLabelBounds, ALeft, C4GUI_CaptionFontClr));
67  // label will have collapsed due to no text: Repair it
68  pLbl->SetAutosize(false);
69  pLbl->SetBounds(rcLabelBounds);
70  }
71  // update small state, which will resize this to a small entry
72  UpdateSmallState();
73  // Set*-function will fill icon and text and calculate actual size
74 }
75 
77 {
78  ClearRef();
79 }
80 
82 {
83  typedef C4GUI::Window ParentClass;
84  // background if important and not selected
85  if (fIsImportant && !IsSelectedChild(this))
86  {
87  int32_t x1 = cgo.X+cgo.TargetX+rcBounds.x;
88  int32_t y1 = cgo.Y+cgo.TargetY+rcBounds.y;
90  }
91  // inherited
92  ParentClass::DrawElement(cgo);
93 }
94 
96 {
97  // del old ref data
98  if (pRefClient)
99  {
101  Thread.RemoveProc(pRefClient);
102  delete pRefClient; pRefClient = nullptr;
103  }
104  if (pRef) { delete pRef; pRef = nullptr; }
105  eQueryType = NRQT_Unknown;
106  iTimeout = iRequestTimeout = 0;
107  fError = false;
108  sError.Clear();
109  int32_t i;
110  for (i=0; i<InfoLabelCount; ++i) sInfoText[i].Clear();
111  InvalidateStatusIcons();
112  sRefClientAddress.Clear();
113  fIsEnabled = true;
114  fIsImportant = false;
115 }
116 
117 const char *C4StartupNetListEntry::GetQueryTypeName(QueryType eQueryType)
118 {
119  switch (eQueryType)
120  {
121  case NRQT_GameDiscovery: return LoadResStr("IDS_NET_QUERY_LOCALNET");
122  case NRQT_Masterserver: return LoadResStr("IDS_NET_QUERY_MASTERSRV");
123  case NRQT_DirectJoin: return LoadResStr("IDS_NET_QUERY_DIRECTJOIN");
124  default: return "";
125  };
126 }
127 
128 void C4StartupNetListEntry::SetRefQuery(const char *szAddress, enum QueryType eQueryType)
129 {
130  // safety: clear previous
131  ClearRef();
132  // setup layout
133  const_cast<C4Facet &>(reinterpret_cast<const C4Facet &>(pIcon->GetFacet()))
135  pIcon->SetAnimated(true, 1);
136  pIcon->SetBounds(rctIconLarge);
137  // init a new ref client to query
138  sRefClientAddress.Copy(szAddress);
139  this->eQueryType = eQueryType;
140  pRefClient = new C4Network2RefClient();
141  if (!pRefClient->Init() || !pRefClient->SetServer(szAddress))
142  {
143  // should not happen
144  sInfoText[0].Clear();
145  SetError(pRefClient->GetError(), TT_RefReqWait);
146  return;
147  }
148  // set info
149  sInfoText[0].Format(LoadResStr("IDS_NET_CLIENTONNET"), GetQueryTypeName(eQueryType), pRefClient->getServerName());
150  sInfoText[1].Copy(LoadResStr("IDS_NET_INFOQUERY"));
151  UpdateSmallState(); UpdateText();
153  // masterserver: always on top
154  if (eQueryType == NRQT_Masterserver)
155  iSortOrder = 100;
156  // register proc
158  Thread.AddProc(pRefClient);
159  // start querying!
160  QueryReferences();
161 }
162 
163 bool C4StartupNetListEntry::QueryReferences()
164 {
165  // begin querying
166  if (!pRefClient->QueryReferences())
167  {
168  SetError(pRefClient->GetError(), TT_RefReqWait);
169  return false;
170  }
171  // set up timeout
172  iRequestTimeout = time(nullptr) + C4NetRefRequestTimeout;
173  return true;
174 }
175 
177 {
178  // update entries
179  // if the return value is false, this entry will be deleted
180  // timer running?
181  if (iTimeout) if (time(nullptr) >= iTimeout)
182  {
183  // timeout!
184  // for internet servers, this means refresh needed - search anew!
185  if (pRefClient && eQueryType == NRQT_Masterserver)
186  {
187  fError = false;
188  sError.Clear();
189  pIcon->SetFacet(C4Startup::Get()->Graphics.fctNetGetRef);
190  pIcon->SetAnimated(true, 1);
191  pIcon->SetBounds(rctIconLarge);
192  sInfoText[1].Copy(LoadResStr("IDS_NET_INFOQUERY"));
193  iTimeout = 0;
194  QueryReferences();
195  // always keep item even if query failed
196  return true;
197  }
198  // any other item is just removed - return value marks this
199  return false;
200  }
201  // failed without a timer. Nothing to be done about it.
202  if (fError) return true;
203  // updates need to be done for references being retrieved only
204  if (!pRefClient) return true;
205  // check if it has arrived
206  if (pRefClient->isBusy())
207  // still requesting - but do not wait forever
208  if (time(nullptr) >= iRequestTimeout)
209  {
210  SetError(LoadResStr("IDS_NET_ERR_REFREQTIMEOUT"), TT_RefReqWait);
211  pRefClient->Cancel("Timeout");
212  }
213  return true;
214 }
215 
217 {
218  // wrong type / still busy?
219  if (!pRefClient || pRefClient->isBusy())
220  return true;
221  // successful?
222  if (!pRefClient->isSuccess())
223  {
224  // couldn't get references
225  SetError(pRefClient->GetError(), TT_RefReqWait);
226  return true;
227  }
228  // Ref getting done!
229  pIcon->SetAnimated(false, 1);
230  // Get reference information from client
231  C4Network2Reference **ppNewRefs=nullptr; int32_t iNewRefCount;
232  if (!pRefClient->GetReferences(ppNewRefs, iNewRefCount))
233  {
234  // References could be retrieved but not read
235  SetError(LoadResStr("IDS_NET_ERR_REFINVALID"), TT_RefReqWait);
236  delete [] ppNewRefs;
237  return true;
238  }
239  if (!iNewRefCount)
240  {
241  // References retrieved but no game open: Inform user
242  sInfoText[1].Copy(LoadResStr("IDS_NET_INFONOGAME"));
243  UpdateText();
244  }
245  else
246  {
247  // Grab references, count players
248  C4StartupNetListEntry *pNewRefEntry = this; int iPlayerCount = 0;
249  for (int i = 0; i < iNewRefCount; i++)
250  {
251  pNewRefEntry = AddReference(ppNewRefs[i], pNewRefEntry->GetNextLower(ppNewRefs[i]->getSortOrder()));
252  iPlayerCount += ppNewRefs[i]->Parameters.PlayerInfos.GetActivePlayerCount(false);
253  }
254  // Count player
255  sInfoText[1].Format(LoadResStr("IDS_NET_INFOGAMES"), (int) iNewRefCount, iPlayerCount);
256  UpdateText();
257  }
258  delete [] ppNewRefs;
259  // special masterserver handling
260  if (eQueryType == NRQT_Masterserver)
261  {
262  // masterserver: schedule next query
263  sInfoText[1].Format(LoadResStr("IDS_NET_INFOGAMES"), (int) iNewRefCount);
264  SetTimeout(TT_Masterserver);
265  return true;
266  }
267  // non-masterserver
268  if (iNewRefCount)
269  {
270  // this item has been "converted" into the references - remove without further feedback
271  return false;
272  }
273  else
274  {
275  // no ref found on custom adress: Schedule re-check
276  SetTimeout(TT_RefReqWait);
277  }
278  return true;
279 }
280 
282 {
283  // search list for the next element of a lower sort order
284  for (C4GUI::Element *pElem = pList->GetFirst(); pElem; pElem = pElem->GetNext())
285  {
286  C4StartupNetListEntry *pEntry = static_cast<C4StartupNetListEntry *>(pElem);
287  if (pEntry->iSortOrder < sortOrder)
288  return pElem;
289  }
290  // none found: insert at start
291  return nullptr;
292 }
293 
294 
295 void C4StartupNetListEntry::UpdateCollapsed(bool fToCollapseValue)
296 {
297  // if collapsed state changed, update the text
298  if (fIsCollapsed == fToCollapseValue) return;
299  fIsCollapsed = fToCollapseValue;
300  UpdateSmallState();
301 }
302 
303 void C4StartupNetListEntry::UpdateSmallState()
304 {
305  // small view: Always collapsed if there is no extended text
306  bool fNewIsSmall = !sInfoText[2].getLength() || fIsCollapsed;
307  if (fNewIsSmall == fIsSmall) return;
308  fIsSmall = fNewIsSmall;
309  for (int i=2; i<InfoLabelCount; ++i) pInfoLbl[i]->SetVisibility(!fIsSmall);
310  UpdateEntrySize();
311 }
312 
313 void C4StartupNetListEntry::UpdateEntrySize()
314 {
315  if(fVisible) {
316  // restack all labels by their size
317  int32_t iLblCnt = (fIsSmall ? 2 : InfoLabelCount), iY=1;
318  for (int i=0; i<iLblCnt; ++i)
319  {
320  C4Rect rcBounds = pInfoLbl[i]->GetBounds();
321  rcBounds.y = iY;
322  iY += rcBounds.Hgt + 2;
323  pInfoLbl[i]->SetBounds(rcBounds);
324  }
325  // resize this control
326  GetBounds().Hgt = iY-1;
327  } else GetBounds().Hgt = 0;
328  UpdateSize();
329 }
330 
331 void C4StartupNetListEntry::UpdateText()
332 {
333  bool fRestackElements=false;
334  CStdFont *pUseFont = &(::GraphicsResource.TextFont);
335  // adjust icons
336  int32_t sx=iInfoIconCount*pUseFont->GetLineHeight();
337  int32_t i;
338  for (i=iInfoIconCount; i<MaxInfoIconCount; ++i)
339  {
340  pInfoIcons[i]->SetIcon(C4GUI::Ico_None);
341  pInfoIcons[i]->SetToolTip(nullptr);
342  }
343  // text to labels
344  for (i=0; i<InfoLabelCount; ++i)
345  {
346  int iAvailableWdt = GetClientRect().Wdt - pInfoLbl[i]->GetBounds().x - 1;
347  if (!i) iAvailableWdt -= sx;
348  StdStrBuf BrokenText;
349  pUseFont->BreakMessage(sInfoText[i].getData(), iAvailableWdt, &BrokenText, true);
350  int32_t iHgt, iWdt;
351  if (pUseFont->GetTextExtent(BrokenText.getData(), iWdt, iHgt, true))
352  {
353  if ((pInfoLbl[i]->GetBounds().Hgt != iHgt) || (pInfoLbl[i]->GetBounds().Wdt != iAvailableWdt))
354  {
355  C4Rect rcBounds = pInfoLbl[i]->GetBounds();
356  rcBounds.Wdt = iAvailableWdt;
357  rcBounds.Hgt = iHgt;
358  pInfoLbl[i]->SetBounds(rcBounds);
359  fRestackElements = true;
360  }
361  }
362  pInfoLbl[i]->SetText(BrokenText.getData());
363  pInfoLbl[i]->SetColor(fIsEnabled ? C4GUI_MessageFontClr : C4GUI_InactMessageFontClr);
364  }
365  if (fRestackElements) UpdateEntrySize();
366 }
367 
369  bool fChange = fToValue != fVisible;
371  if(fChange) UpdateEntrySize();
372 }
373 
374 void C4StartupNetListEntry::AddStatusIcon(C4GUI::Icons eIcon, const char *szToolTip)
375 {
376  // safety
377  if (iInfoIconCount==MaxInfoIconCount) return;
378  // set icon to the left of the existing icons to the desired data
379  pInfoIcons[iInfoIconCount]->SetIcon(eIcon);
380  pInfoIcons[iInfoIconCount]->SetToolTip(szToolTip);
381  ++iInfoIconCount;
382 }
383 
385 {
386  // safety: clear previous
387  ClearRef();
388  // set info
389  this->pRef = pRef;
390  int32_t iIcon = pRef->getIcon();
391  if (!Inside<int32_t>(iIcon, 0, C4StartupScenSel_IconCount-1)) iIcon = C4StartupScenSel_DefaultIcon_Scenario;
392  pIcon->SetFacet(C4Startup::Get()->Graphics.fctScenSelIcons.GetPhase(iIcon));
393  pIcon->SetAnimated(false, 0);
394  pIcon->SetBounds(rctIconSmall);
395  int32_t iPlrCnt = pRef->isEditor() ? pRef->Parameters.PlayerInfos.GetActivePlayerCount(false) : pRef->Parameters.Clients.getClientCnt();
396  C4Client *pHost = pRef->Parameters.Clients.getHost();
397  sInfoText[0].Format(LoadResStr("IDS_NET_REFONCLIENT"), pRef->getTitle(), pHost ? pHost->getName() : "unknown");
398  if (pRef->isEditor())
399  {
400  sInfoText[1].Format(LoadResStr("IDS_NET_INFOEDITOR"),
401  (int)iPlrCnt,
402  StdStrBuf(pRef->getGameStatus().getDescription(), true).getData());
403  }
404  else
405  {
406  sInfoText[1].Format(LoadResStr("IDS_NET_INFOPLRSGOALDESC"),
407  (int)iPlrCnt,
408  (int)pRef->Parameters.MaxPlayers,
409  pRef->getGameGoalString().getData(),
410  StdStrBuf(pRef->getGameStatus().getDescription(), true).getData());
411  }
412  if (pRef->getTime() > 0)
413  {
414  StdStrBuf strDuration; strDuration.Format("%02d:%02d:%02d", pRef->getTime()/3600, (pRef->getTime() % 3600) / 60, pRef->getTime() % 60);
415  sInfoText[1].Append(" - "); sInfoText[1].Append(strDuration);
416  }
417  sInfoText[2].Format(LoadResStr("IDS_DESC_VERSION"), pRef->getGameVersion().GetString().getData());
418  sInfoText[3].Format("%s: %s", LoadResStr("IDS_CTL_COMMENT"), pRef->getComment());
419  StdStrBuf sAddress;
420  for (int i=0; i<pRef->getAddrCnt(); ++i)
421  {
422  if (i) sAddress.Append(", ");
423  sAddress.Append(pRef->getAddr(i).toString());
424  }
425  // editor reference
426  if (pRef->isEditor())
427  AddStatusIcon(C4GUI::Ico_Editor, LoadResStr("IDS_CNS_CONSOLE"));
428  // password
429  if (pRef->isPasswordNeeded())
430  AddStatusIcon(C4GUI::Ico_Ex_LockedFrontal, LoadResStr("IDS_NET_INFOPASSWORD"));
431  // league
432  if (pRef->Parameters.isLeague())
433  AddStatusIcon(C4GUI::Ico_Ex_League, pRef->Parameters.getLeague());
434  // lobby active
435  if (pRef->getGameStatus().isLobbyActive())
436  AddStatusIcon(C4GUI::Ico_Lobby, LoadResStr("IDS_DESC_EXPECTING"));
437  // game running
438  if (pRef->getGameStatus().isPastLobby())
439  AddStatusIcon(C4GUI::Ico_GameRunning, LoadResStr("IDS_NET_INFOINPROGR"));
440  // runtime join
441  if (pRef->isJoinAllowed() && pRef->getGameStatus().isPastLobby()) // A little workaround to determine RuntimeJoin...
442  AddStatusIcon(C4GUI::Ico_RuntimeJoin, LoadResStr("IDS_NET_RUNTIMEJOINFREE"));
443  // official server
444  if (pRef->isOfficialServer() && !Config.Network.UseAlternateServer) // Offical server icon is only displayed if references are obtained from official league server
445  {
446  fIsImportant = true;
447  AddStatusIcon(C4GUI::Ico_OfficialServer, LoadResStr("IDS_NET_OFFICIALSERVER"));
448  }
449  // list participating player/client names
450  if (pRef->isEditor())
451  {
452  sInfoText[4].Format("%s%s", LoadResStr("IDS_DESC_CLIENTS"), iPlrCnt ? pRef->Parameters.Clients.GetAllClientNames().getData() : LoadResStr("IDS_CTL_NONE"));
453  }
454  else
455  {
456  sInfoText[4].Format("%s: %s", LoadResStr("IDS_CTL_PLAYER"), iPlrCnt ? pRef->Parameters.PlayerInfos.GetActivePlayerNames(false).getData() : LoadResStr("IDS_CTL_NONE"));
457  }
458  // disabled if join is not possible for some reason
459  C4GameVersion verThis;
460  if (!pRef->isJoinAllowed() || !(pRef->getGameVersion() == verThis))
461  {
462  fIsEnabled = false;
463  }
464  // store sort order
465  iSortOrder = pRef->getSortOrder();
466  // all references expire after a while
467  SetTimeout(TT_Reference);
468  UpdateSmallState(); UpdateText();
469 }
470 
471 void C4StartupNetListEntry::SetError(const char *szErrorText, TimeoutType eTimeout)
472 {
473  // set error message
474  fError = true;
475  sInfoText[1].Copy(szErrorText);
476  for (int i=2; i<InfoLabelCount; ++i) sInfoText[i].Clear();
477  InvalidateStatusIcons();
478  UpdateSmallState(); UpdateText();
479  pIcon->SetIcon(C4GUI::Ico_Close);
480  pIcon->SetAnimated(false, 0);
481  pIcon->SetBounds(rctIconSmall);
482  SetTimeout(eTimeout);
483 }
484 
485 void C4StartupNetListEntry::SetTimeout(TimeoutType eTimeout)
486 {
487  int iTime = 0;
488  switch (eTimeout)
489  {
491  case TT_Reference: iTime = C4NetReferenceTimeout; break;
492  case TT_Masterserver: iTime = C4NetMasterServerQueryInterval; break;
493  };
494  if (!iTime) return;
495  iTimeout = time(nullptr) + iTime;
496 }
497 
498 C4StartupNetListEntry *C4StartupNetListEntry::AddReference(C4Network2Reference *pAddRef, C4GUI::Element *pInsertBefore)
499 {
500  // check list whether the same reference has been added already
501  for (C4GUI::Element *pElem = pList->GetFirst(); pElem; pElem = pElem->GetNext())
502  {
503  C4StartupNetListEntry *pEntry = static_cast<C4StartupNetListEntry *>(pElem);
504  // match to existing reference entry:
505  // * same host (checking for same name and nick)
506  // * at least one match in address and port
507  // * the incoming reference is newer than (or same as) the current one
508  if ( pEntry->IsSameHost(pAddRef)
509  && pEntry->IsSameAddress(pAddRef)
510  && (pEntry->GetReference()->getStartTime() <= pAddRef->getStartTime()) )
511  {
512  // update existing entry
513  pEntry->SetReference(pAddRef);
514  return pEntry;
515  }
516  }
517  // no update - just add
518  C4StartupNetListEntry *pNewRefEntry = new C4StartupNetListEntry(pList, pInsertBefore, pNetDlg);
519  pNewRefEntry->SetReference(pAddRef);
520  pNetDlg->OnReferenceEntryAdd(pNewRefEntry);
521  return pNewRefEntry;
522 }
523 
525 {
526  // not if ref has not been retrieved yet
527  if (!pRef) return false;
528  C4Client *pHost1 = pRef->Parameters.Clients.getHost();
529  C4Client *pHost2 = pRef2->Parameters.Clients.getHost();
530  if (!pHost1 || !pHost2) return false;
531  // check
532  return SEqual(pHost1->getCUID(), pHost2->getCUID()) && SEqual(pHost1->getName(), pHost2->getName());
533 }
534 
536 {
537  // not if ref has not been retrieved yet
538  if (!pRef) return false;
539  // check all of our addresses
540  for (int i = 0; i < pRef->getAddrCnt(); i++)
541  // against all of the other ref's addresses
542  for (int j = 0; j < pRef2->getAddrCnt(); j++)
543  // at least one match!
544  if (pRef->getAddr(i) == pRef2->getAddr(j))
545  return true;
546  // no match
547  return false;
548 }
549 
550 bool C4StartupNetListEntry::IsSameRefQueryAddress(const char *szJoinaddress)
551 {
552  // only unretrieved references
553  if (!pRefClient) return false;
554  // if request failed, create a duplicate anyway in case the game is opened now
555  // except masterservers, which would re-search some time later anyway
556  if (fError && eQueryType != NRQT_Masterserver) return false;
557  // check equality of address
558  // do it the simple way for now
559  return SEqualNoCase(sRefClientAddress.getData(), szJoinaddress);
560 }
561 
562 bool C4StartupNetListEntry::KeywordMatch(const char *szMatch)
563 {
564  // only finished references
565  if (!pRef) return false;
566  if(SSearchNoCase(pRef->getTitle(),szMatch)) return true;
567  C4Client *pHost = pRef->Parameters.Clients.getHost();
568  if(pHost && SSearchNoCase(pHost->getName(),szMatch)) return true;
569  if(SSearchNoCase(pRef->getComment(),szMatch)) return true;
570  return false;
571 }
572 
574 {
575  // only unresolved references
576  if (!pRefClient) return nullptr;
577  // not masterservers (cannot join directly on clonk.de)
578  if (eQueryType == NRQT_Masterserver) return nullptr;
579  // return join address
580  return pRefClient->getServerName();
581 }
582 
584 {
585  C4Network2Reference *pOldRef = pRef;
586  pRef = nullptr;
587  return pOldRef;
588 }
589 
590 
591 
592 
593 // ----------- C4StartupNetDlg ---------------------------------------------------------------------------------
594 
595 C4StartupNetDlg::C4StartupNetDlg() : C4StartupDlg(LoadResStr("IDS_DLG_NETSTART")), pChatTitleLabel(nullptr), pMasterserverClient(nullptr), fIsCollapsed(false), fUpdatingList(false), iGameDiscoverInterval(0), tLastRefresh(0), fUpdateCheckPending(false)
596 {
597  // ctor
598  // key bindings
600  keys.push_back(C4KeyCodeEx(K_BACK)); keys.push_back(C4KeyCodeEx(K_LEFT));
601  pKeyBack = new C4KeyBinding(keys, "StartupNetBack", KEYSCOPE_Gui,
603  pKeyRefresh = new C4KeyBinding(C4KeyCodeEx(K_F5), "StartupNetReload", KEYSCOPE_Gui,
605 
606  // screen calculations
607  UpdateSize();
608  int32_t iIconSize = C4GUI_IconExWdt;
609  int32_t iButtonWidth,iCaptionFontHgt, iSideSize = std::max<int32_t>(GetBounds().Wdt/6, iIconSize);
610  int32_t iButtonHeight = C4GUI_ButtonHgt, iButtonIndent = GetBounds().Wdt/40;
611  ::GraphicsResource.CaptionFont.GetTextExtent("<< BACK", iButtonWidth, iCaptionFontHgt, true);
612  iButtonWidth *= 3;
613  C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
614  C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(caMain.GetHeight()/7),0,0);
615  int32_t iButtonAreaWdt = caButtonArea.GetWidth()*7/8;
616  iButtonWidth = std::min<int32_t>(iButtonWidth, (iButtonAreaWdt - 8 * iButtonIndent)/4);
617  iButtonIndent = (iButtonAreaWdt - 4 * iButtonWidth) / 8;
618  C4GUI::ComponentAligner caButtons(caButtonArea.GetCentered(iButtonAreaWdt, iButtonHeight),iButtonIndent,0);
619  C4GUI::ComponentAligner caLeftBtnArea(caMain.GetFromLeft(iSideSize), std::min<int32_t>(caMain.GetWidth()/20, (iSideSize-C4GUI_IconExWdt)/2), caMain.GetHeight()/40);
620  C4GUI::ComponentAligner caConfigArea(caMain.GetFromRight(iSideSize), std::min<int32_t>(caMain.GetWidth()/20, (iSideSize-C4GUI_IconExWdt)/2), caMain.GetHeight()/40);
621 
622  // left button area: Switch between chat and game list
624  {
625  btnGameList = new C4GUI::CallbackButton<C4StartupNetDlg, C4GUI::IconButton>(C4GUI::Ico_Ex_GameList, caLeftBtnArea.GetFromTop(iIconSize, iIconSize), '\0', &C4StartupNetDlg::OnBtnGameList);
626  btnGameList->SetToolTip(LoadResStr("IDS_DESC_SHOWSAVAILABLENETWORKGAME"));
627  btnGameList->SetText(LoadResStr("IDS_BTN_GAMES"));
628  AddElement(btnGameList);
629  btnChat = new C4GUI::CallbackButton<C4StartupNetDlg, C4GUI::IconButton>(C4GUI::Ico_Ex_Chat, caLeftBtnArea.GetFromTop(iIconSize, iIconSize), '\0', &C4StartupNetDlg::OnBtnChat);
630  btnChat->SetToolTip(LoadResStr("IDS_DESC_CONNECTSTOANIRCCHATSERVER"));
631  btnChat->SetText(LoadResStr("IDS_BTN_CHAT"));
632  AddElement(btnChat);
633  }
634  else btnChat = nullptr;
635 
636  // main area: Tabular to switch between game list and chat
637  pMainTabular = new C4GUI::Tabular(caMain.GetAll(), C4GUI::Tabular::tbNone);
638  pMainTabular->SetDrawDecoration(false);
639  pMainTabular->SetSheetMargin(0);
640  AddElement(pMainTabular);
641 
642  // main area: game selection sheet
643  C4GUI::Tabular::Sheet *pSheetGameList = pMainTabular->AddSheet(nullptr);
644  C4GUI::ComponentAligner caGameList(pSheetGameList->GetContainedClientRect(), 0,0, false);
646  pGameListLbl = new C4GUI::WoodenLabel(LoadResStr("IDS_NET_GAMELIST"), caGameList.GetFromTop(iCaptHgt), C4GUI_Caption2FontClr, &::GraphicsResource.TextFont, ALeft);
647  // search field
648  C4GUI::WoodenLabel *pSearchLbl;
649  const char *szSearchLblText = LoadResStr("IDS_NET_MSSEARCH");
650  int32_t iSearchWdt=100, iSearchHgt;
651  ::GraphicsResource.TextFont.GetTextExtent(szSearchLblText, iSearchWdt, iSearchHgt, true);
652  C4GUI::ComponentAligner caSearch(caGameList.GetFromTop(iSearchHgt), 0,0);
653  pSearchLbl = new C4GUI::WoodenLabel(szSearchLblText, caSearch.GetFromLeft(iSearchWdt+10), C4GUI_Caption2FontClr, &::GraphicsResource.TextFont);
654  const char *szSearchTip = LoadResStr("IDS_NET_MSSEARCH_DESC");
655  pSearchLbl->SetToolTip(szSearchTip);
656  pSheetGameList->AddElement(pSearchLbl);
657  pSearchFieldEdt = new C4GUI::CallbackEdit<C4StartupNetDlg>(caSearch.GetAll(), this, &C4StartupNetDlg::OnSearchFieldEnter);
658  pSearchFieldEdt->SetToolTip(szSearchTip);
659  pSheetGameList->AddElement(pSearchFieldEdt);
660  pSheetGameList->AddElement(pGameListLbl);
661  pGameSelList = new C4GUI::ListBox(caGameList.GetFromTop(caGameList.GetHeight() - iCaptHgt));
662  pGameSelList->SetDecoration(true, nullptr, true, true);
663  pGameSelList->UpdateElementPositions();
666  pSheetGameList->AddElement(pGameSelList);
667  C4GUI::ComponentAligner caIP(caGameList.GetAll(), 0,0);
668  C4GUI::WoodenLabel *pIPLbl;
669  const char *szIPLblText = LoadResStr("IDS_NET_IP");
670  int32_t iIPWdt=100, Q;
671  ::GraphicsResource.TextFont.GetTextExtent(szIPLblText, iIPWdt, Q, true);
672  pIPLbl = new C4GUI::WoodenLabel(szIPLblText, caIP.GetFromLeft(iIPWdt+10), C4GUI_Caption2FontClr, &::GraphicsResource.TextFont);
673  const char *szIPTip = LoadResStr("IDS_NET_IP_DESC");
674  pIPLbl->SetToolTip(szIPTip);
675  pSheetGameList->AddElement(pIPLbl);
676  pJoinAddressEdt = new C4GUI::CallbackEdit<C4StartupNetDlg>(caIP.GetAll(), this, &C4StartupNetDlg::OnJoinAddressEnter);
677  pJoinAddressEdt->SetToolTip(szIPTip);
678  pSheetGameList->AddElement(pJoinAddressEdt);
679 
680  // main area: chat sheet
682  {
683  C4GUI::Tabular::Sheet *pSheetChat = pMainTabular->AddSheet(nullptr);
684  C4GUI::ComponentAligner caChat(pSheetChat->GetContainedClientRect(), 0,0, false);
685  pSheetChat->AddElement(pChatTitleLabel = new C4GUI::WoodenLabel("", caChat.GetFromTop(iCaptHgt), C4GUI_Caption2FontClr, &::GraphicsResource.TextFont, ALeft, false));
686  C4GUI::GroupBox *pChatGroup = new C4GUI::GroupBox(caChat.GetAll());
688  pChatGroup->SetMargin(2);
689  pSheetChat->AddElement(pChatGroup);
690  pChatCtrl = new C4ChatControl(&Application.IRCClient);
691  pChatCtrl->SetBounds(pChatGroup->GetContainedClientRect());
693  StdStrBuf sCurrTitle; sCurrTitle.Ref(pChatCtrl->GetTitle()); OnChatTitleChange(sCurrTitle);
694  pChatGroup->AddElement(pChatCtrl);
695  }
696 
697  // config area
699  btnInternet->SetToolTip(LoadResStr("IDS_DLGTIP_SEARCHINTERNETGAME"));
700  btnInternet->SetText(LoadResStr("IDS_CTL_INETSERVER"));
701  AddElement(btnInternet);
703  btnRecord->SetToolTip(LoadResStr("IDS_DLGTIP_RECORD"));
704  btnRecord->SetText(LoadResStr("IDS_CTL_RECORD"));
705  AddElement(btnRecord);
706 #ifdef WITH_AUTOMATIC_UPDATE
707  btnUpdate = new C4GUI::CallbackButton<C4StartupNetDlg, C4GUI::IconButton>(C4GUI::Ico_Ex_Update, caConfigArea.GetFromTop(iIconSize, iIconSize), '\0', &C4StartupNetDlg::OnBtnUpdate);
708  btnUpdate->SetVisibility(false); // update only available if masterserver notifies it
709  btnUpdate->SetToolTip(LoadResStr("IDS_DLGTIP_UPDATE"));
710  btnUpdate->SetText(LoadResStr("IDS_CTL_UPDATE"));
711  AddElement(btnUpdate);
712 #endif
713 
714  // button area
716  AddElement(btn = new C4GUI::CallbackButton<C4StartupNetDlg>(LoadResStr("IDS_BTN_BACK"), caButtons.GetFromLeft(iButtonWidth), &C4StartupNetDlg::OnBackBtn));
717  btn->SetToolTip(LoadResStr("IDS_DLGTIP_BACKMAIN"));
718  AddElement(btnRefresh = new C4GUI::CallbackButton<C4StartupNetDlg>(LoadResStr("IDS_BTN_RELOAD"), caButtons.GetFromLeft(iButtonWidth), &C4StartupNetDlg::OnRefreshBtn));
719  btnRefresh->SetToolTip(LoadResStr("IDS_NET_RELOAD_DESC"));
720  AddElement(btnJoin = new C4GUI::CallbackButton<C4StartupNetDlg>(LoadResStr("IDS_NET_JOINGAME_BTN"), caButtons.GetFromLeft(iButtonWidth), &C4StartupNetDlg::OnJoinGameBtn));
721  btnJoin->SetToolTip(LoadResStr("IDS_NET_JOINGAME_DESC"));
722  AddElement(btn = new C4GUI::CallbackButton<C4StartupNetDlg>(LoadResStr("IDS_NET_NEWGAME"), caButtons.GetFromLeft(iButtonWidth), &C4StartupNetDlg::OnCreateGameBtn));
723  btn->SetToolTip(LoadResStr("IDS_NET_NEWGAME_DESC"));
724 
725  // initial dlg mode
726  UpdateDlgMode();
727 
728  // initial focus
730 
731  // initialize discovery
732  DiscoverClient.Init(Config.Network.PortDiscovery);
733  DiscoverClient.StartDiscovery();
734  iGameDiscoverInterval = C4NetGameDiscoveryInterval;
735 
736  // register timer
737  Application.Add(this);
738 
739  // register as receiver of reference notifies
741 
742 }
743 
745 {
746  // disable notifies
748  Application.InteractiveThread.RemoveProc(&pUpdateClient);
749 
750  DiscoverClient.Close();
751  Application.Remove(this);
752  if (pMasterserverClient) delete pMasterserverClient;
753  // dtor
754  delete pKeyBack;
755  delete pKeyRefresh;
756 }
757 
759 {
760  // draw background
761  typedef C4GUI::FullscreenDialog Base;
762  Base::DrawElement(cgo);
763 }
764 
766 {
767  // callback when shown: Start searching for games
770  UpdateList();
771  UpdateUpdateButton(); // in case update check was finished before callback registration
772  UpdateMasterserver();
773  OnSec1Timer();
774  tLastRefresh = time(0);
775  // also update chat
776  if (pChatCtrl) pChatCtrl->OnShown();
777 }
778 
780 {
781  // dlg abort: return to main screen
782  if (pMasterserverClient) { delete pMasterserverClient; pMasterserverClient=nullptr; }
783  if (!fOK) DoBack();
784 }
785 
787 {
788  // default control depends on whether dlg is in chat or game list mode
789  if (GetDlgMode() == SNDM_Chat && pChatCtrl)
790  // chat mode: Chat input edit
791  return pChatCtrl->GetDefaultControl();
792  else
793  // game list mode: No default control, because it would move focus away from IP input edit
794  return nullptr;
795 }
796 
798 {
799  // default control depends on whether dlg is in chat or game list mode
800  if (GetDlgMode() == SNDM_Chat && pChatCtrl)
801  // chat mode: Chat input edit
802  return pChatCtrl->GetDefaultControl();
803  else
804  // game list mode: Game list box
805  return pGameSelList;
806 }
807 
809 {
810  // switch to game list dialog
811  pMainTabular->SelectSheet(SNDM_GameList, true);
812  UpdateDlgMode();
813 }
814 
816 {
817  // toggle chat / game list
818  if (pChatCtrl)
819  {
820  if (pMainTabular->GetActiveSheetIndex() == SNDM_GameList)
821  {
822  pMainTabular->SelectSheet(SNDM_Chat, true);
823  pChatCtrl->OnShown();
824  UpdateDlgMode();
825  }
826  else
827  {
828  pMainTabular->SelectSheet(SNDM_GameList, true);
829  UpdateDlgMode();
830  }
831  }
832 }
833 
835 {
836  // toggle masterserver game search
838  UpdateMasterserver();
839 }
840 
842 {
843  // toggle league signup flag
844  bool fCheck = !Game.Record;
845  Game.Record = fCheck;
846  Config.General.DefRec = fCheck;
848 }
849 
850 #ifdef WITH_AUTOMATIC_UPDATE
851 void C4StartupNetDlg::OnBtnUpdate(C4GUI::Control *btn)
852 {
853  // do update
854  if (!C4UpdateDlg::DoUpdate(UpdateURL.getData(), GetScreen()))
855  {
856  GetScreen()->ShowMessage(LoadResStr("IDS_MSG_UPDATEFAILED"), LoadResStr("IDS_TYPE_UPDATE"), C4GUI::Ico_Ex_Update);
857  }
858 }
859 #endif
860 
861 void C4StartupNetDlg::UpdateMasterserver()
862 {
863  // update button icon to current state
865  // creates masterserver object if masterserver is enabled; destroy otherwise
866  if (!Config.Network.MasterServerSignUp == !pMasterserverClient) return;
868  {
869  delete pMasterserverClient;
870  pMasterserverClient = nullptr;
871  }
872  else
873  {
874  pMasterserverClient = new C4StartupNetListEntry(pGameSelList, nullptr, this);
875  StdStrBuf strVersion; strVersion.Format("%d.%d", C4XVER1, C4XVER2);
876  StdStrBuf strQuery; strQuery.Format("%s?version=%s&platform=%s", Config.Network.GetLeagueServerAddress(), strVersion.getData(), C4_OS);
877  pMasterserverClient->SetRefQuery(strQuery.getData(), C4StartupNetListEntry::NRQT_Masterserver);
878  }
879 }
880 
881 void C4StartupNetDlg::UpdateList(bool fGotReference)
882 {
883  // recursion check
884  if (fUpdatingList) return;
885  fUpdatingList = true;
886  pGameSelList->FreezeScrolling();
887  // Games display mask
888  const char *szGameMask = pSearchFieldEdt->GetText();
889  if (!szGameMask) szGameMask = "";
890  // Update all child entries
891  bool fAnyRemoval = false;
892  C4GUI::Element *pElem, *pNextElem = pGameSelList->GetFirst();
893  while ((pElem=pNextElem))
894  {
895  pNextElem = pElem->GetNext(); // determine next exec element now - execution
896  C4StartupNetListEntry *pEntry = static_cast<C4StartupNetListEntry *>(pElem);
897  // do item updates
898  if(pEntry->GetReference()) pEntry->SetVisibility(pEntry->KeywordMatch(szGameMask));
899  bool fKeepEntry = true;
900  if (fGotReference)
901  fKeepEntry = pEntry->OnReference();
902  if (fKeepEntry)
903  fKeepEntry = pEntry->Execute();
904  // remove?
905  if (!fKeepEntry)
906  {
907  // entry wishes to be removed
908  // if the selected entry is being removed, the next entry should be selected (which might be the ref for a finished refquery)
909  if (pGameSelList->GetSelectedItem() == pEntry)
910  if (pEntry->GetNext())
911  {
912  pGameSelList->SelectEntry(pEntry->GetNext(), false);
913  }
914  delete pEntry;
915  fAnyRemoval = true; // setting any removal will also update collapsed state of all entries; so no need to do updates because of selection change here
916  }
917  }
918 
919  // Add LAN games
920  C4NetIO::addr_t Discover;
921  while (DiscoverClient.PopDiscover(Discover))
922  {
923  StdStrBuf Address(Discover.ToString());
924  AddReferenceQuery(Address.getData(), C4StartupNetListEntry::NRQT_GameDiscovery);
925  }
926 
927  // check whether view needs to be collapsed or uncollapsed
928  if (fIsCollapsed && fAnyRemoval)
929  {
930  // try uncollapsing
931  fIsCollapsed = false;
932  UpdateCollapsed();
933  // if scrolling is still necessary, the view will be collapsed again immediately
934  }
935  if (!fIsCollapsed && pGameSelList->IsScrollingNecessary())
936  {
937  fIsCollapsed = true;
938  UpdateCollapsed();
939  }
940 
941  fUpdatingList = false;
942  // done; selection might have changed
943  pGameSelList->UnFreezeScrolling();
944  UpdateSelection(false);
945 }
946 
947 void C4StartupNetDlg::UpdateCollapsed()
948 {
949  // update collapsed state for all child entries
950  for (C4GUI::Element *pElem = pGameSelList->GetFirst(); pElem; pElem = pElem->GetNext())
951  {
952  C4StartupNetListEntry *pEntry = static_cast<C4StartupNetListEntry *>(pElem);
953  pEntry->UpdateCollapsed(fIsCollapsed && pElem != pGameSelList->GetSelectedItem());
954  }
955 }
956 
957 void C4StartupNetDlg::UpdateSelection(bool fUpdateCollapsed)
958 {
959  // not during list updates - list update call will do this
960  if (fUpdatingList) return;
961  // in collapsed view, updating the selection may uncollapse something
962  if (fIsCollapsed && fUpdateCollapsed) UpdateCollapsed();
963 }
964 
965 void C4StartupNetDlg::UpdateDlgMode()
966 {
967  DlgMode eMode = GetDlgMode();
968  // buttons for game joining only visible in game list mode
969  btnInternet->SetVisibility(eMode == SNDM_GameList);
970  btnRecord->SetVisibility(eMode == SNDM_GameList);
971  btnJoin->SetVisibility(eMode == SNDM_GameList);
972  btnRefresh->SetVisibility(eMode == SNDM_GameList);
973  // focus update
974  if (!GetFocus()) SetFocus(GetDlgModeFocusControl(), false);
975 }
976 
977 C4StartupNetDlg::DlgMode C4StartupNetDlg::GetDlgMode()
978 {
979  // dlg mode determined by active tabular sheet
980  if (pMainTabular->GetActiveSheetIndex() == SNDM_Chat) return SNDM_Chat; else return SNDM_GameList;
981 }
982 
983 void C4StartupNetDlg::OnThreadEvent(C4InteractiveEventType eEvent, void *pEventData)
984 {
985  UpdateUpdateButton();
986  UpdateList(true);
987 }
988 
989 void C4StartupNetDlg::UpdateUpdateButton()
990 {
991  if (!fUpdateCheckPending) return;
992  if(!pUpdateClient.isSuccess() || pUpdateClient.isBusy()) return;
993 
994  pUpdateClient.SetNotify(nullptr);
995 
996  StdCopyStrBuf versionInfo;
997 
998  pUpdateClient.GetVersion(&versionInfo);
999  pUpdateClient.GetUpdateURL(&UpdateURL);
1000 
1001 #ifdef WITH_AUTOMATIC_UPDATE
1002  btnUpdate->SetVisibility(C4UpdateDlg::IsValidUpdate(versionInfo.getData()));
1003 #endif
1004  fUpdateCheckPending = false;
1005 }
1006 
1008 {
1009  // OK in chat mode? Forward to chat control
1010  if (GetDlgMode() == SNDM_Chat) return pChatCtrl->DlgEnter();
1011  // OK on editbox with text enetered: Add the specified IP for reference retrieval
1012  if (GetFocus() == pJoinAddressEdt)
1013  {
1014  const char *szDirectJoinAddress = pJoinAddressEdt->GetText();
1015  if (szDirectJoinAddress && *szDirectJoinAddress)
1016  {
1017  // First do some acrobatics to avoid trying to resolve addresses with leading
1018  // or trailing whitespace, which is easily pasted in with an IP address.
1019  // We can trivially skip whitespace at the beginning, but we need a copy to
1020  // omit whitespace at the end.
1021  while (std::isspace(*szDirectJoinAddress))
1022  // skip whitespace at the beginning
1023  ++szDirectJoinAddress;
1024  if (!*szDirectJoinAddress)
1025  // entry empty, apart from whitespace
1026  return true;
1027  const char *szDirectJoinAddressEnd = szDirectJoinAddress + std::strlen(szDirectJoinAddress) - 1;
1028  while (std::isspace(*szDirectJoinAddressEnd))
1029  // skip whitespace at the end
1030  --szDirectJoinAddressEnd;
1031  if (*++szDirectJoinAddressEnd)
1032  {
1033  // Make a temporary copy of the part that is not trailing whitespace, if any
1034  std::string strDirectJoinAddressStripped(szDirectJoinAddress, szDirectJoinAddressEnd - szDirectJoinAddress);
1035  AddReferenceQuery(strDirectJoinAddressStripped.c_str(), C4StartupNetListEntry::NRQT_DirectJoin);
1036  }
1037  else
1038  AddReferenceQuery(szDirectJoinAddress, C4StartupNetListEntry::NRQT_DirectJoin);
1039  // Switch focus to list so another OK joins the specified address
1040  SetFocus(pGameSelList, true);
1041  return true;
1042  }
1043  }
1044  if (GetFocus() == pSearchFieldEdt)
1045  {
1046  UpdateList();
1047  return true;
1048  }
1049  // get currently selected item
1050  C4GUI::Element *pSelection = pGameSelList->GetSelectedItem();
1051  StdCopyStrBuf strNoJoin(LoadResStr("IDS_NET_NOJOIN"));
1052  if (!pSelection)
1053  {
1054  // no ref selected: Oh noes!
1056  LoadResStr("IDS_NET_NOJOIN_NOREF"),
1057  strNoJoin.getData(),
1060  return true;
1061  }
1062  C4StartupNetListEntry *pRefEntry = static_cast<C4StartupNetListEntry *>(pSelection);
1063  const char *szError;
1064  if ((szError = pRefEntry->GetError()))
1065  {
1066  // erroneous ref selected: Oh noes!
1068  FormatString(LoadResStr("IDS_NET_NOJOIN_BADREF"), szError).getData(),
1069  strNoJoin.getData(),
1072  return true;
1073  }
1074  C4Network2Reference *pRef = pRefEntry->GetReference();
1075  const char *szDirectJoinAddress = pRefEntry->GetJoinAddress();
1076  if (!pRef && !(szDirectJoinAddress && *szDirectJoinAddress))
1077  {
1078  // something strange has been selected (e.g., a masterserver entry). Error.
1080  LoadResStr("IDS_NET_NOJOIN_NOREF"),
1081  strNoJoin.getData(),
1084  return true;
1085  }
1086  // check if join to this reference is possible at all
1087  if (pRef)
1088  {
1089  // version mismatch
1090  C4GameVersion verThis;
1091  if (!(pRef->getGameVersion() == verThis))
1092  {
1094  FormatString(LoadResStr("IDS_NET_NOJOIN_BADVER"),
1095  pRef->getGameVersion().GetString().getData(),
1096  verThis.GetString().getData()).getData(),
1097  strNoJoin.getData(),
1100  return true;
1101  }
1102  // no runtime join
1103  if (!pRef->isJoinAllowed())
1104  {
1105  if (!::pGUI->ShowMessageModal(
1106  LoadResStr("IDS_NET_NOJOIN_NORUNTIME"),
1107  strNoJoin.getData(),
1110  {
1111  return true;
1112  }
1113  }
1114  }
1115  // OK; joining!
1116  if (pRef->isEditor())
1117  {
1118  bool success = false;
1119  // Editor mode join: Serialize reference to temp file and join on that
1120  // (could pass through environment, but that's hard to do platform-independent
1121  // (QProcessEnvironment? But then there's a Qt dependency in the network init code))
1122  StdStrBuf tmpfn(Config.AtTempPath("ocjoin"), true);
1123  MakeTempFilename(&tmpfn);
1124  StdBuf join_data = DecompileToBuf<StdCompilerBinWrite>(*pRef);
1125  if (join_data.getSize())
1126  {
1127  if (join_data.SaveToFile(tmpfn.getData()))
1128  {
1129  if (RestartApplication({"--editor", FormatString("--join=%s%s", C4Game::DirectJoinFilePrefix, tmpfn.getData()).getData()})) // hope for no " in temp path
1130  {
1131  // Application.Quit() has been called. Will quit after returning from this callback.
1132  // The temp file will be deleted by the new instance
1133  success = true;
1134  }
1135  else
1136  {
1137  EraseFile(tmpfn.getData());
1138  }
1139  }
1140  }
1141  if (!success)
1142  {
1143  C4GUI::TheScreen.ShowErrorMessage(LoadResStr("IDS_ERR_STARTEDITOR"));
1144  }
1145  return true;
1146  }
1147  else
1148  {
1149  // Player mode join
1150  // Take over reference
1151  pRefEntry->GrabReference();
1152  // Set join parameters
1153  *Game.ScenarioFilename = '\0';
1154  if (szDirectJoinAddress) SCopy(szDirectJoinAddress, Game.DirectJoinAddress, _MAX_PATH); else *Game.DirectJoinAddress = '\0';
1155  SCopy("Objects.ocd", Game.DefinitionFilenames);
1156  Game.NetworkActive = true;
1157  Game.fObserve = false;
1158  Game.pJoinReference.reset(pRef);
1159  // start with this set!
1161  return true;
1162  }
1163 }
1164 
1166 {
1167  // abort dialog: Back to main
1169  return true;
1170 }
1171 
1173 {
1174  // check min refresh timer
1175  time_t tNow = time(0);
1176  if (tLastRefresh && tNow < tLastRefresh + C4NetMinRefreshInterval)
1177  {
1178  // avoid hammering on refresh key
1179  C4GUI::GUISound("UI::Error");
1180  return;
1181  }
1182  tLastRefresh = tNow;
1183  // empty list of all old entries
1184  fUpdatingList = true;
1185  while (pGameSelList->GetFirst()) delete pGameSelList->GetFirst();
1186  pMasterserverClient=nullptr;
1187  // (Re-)Start discovery
1188  if (!DiscoverClient.StartDiscovery())
1189  {
1190  StdCopyStrBuf strNoDiscovery(LoadResStr("IDS_NET_NODISCOVERY"));
1192  FormatString(LoadResStr("IDS_NET_NODISCOVERY_DESC"), DiscoverClient.GetError()).getData(),
1193  strNoDiscovery.getData(),
1196  }
1197  iGameDiscoverInterval = C4NetGameDiscoveryInterval;
1198  // restart masterserver query
1199  UpdateMasterserver();
1200  // done; update stuff
1201  fUpdatingList = false;
1202  UpdateList();
1203 }
1204 
1206 {
1208 }
1209 
1211 {
1212  // no updates if dialog is inactive (e.g., because a join password dlg is shown!)
1213  if (!IsActive(true))
1214  return;
1215 
1216  // Execute discovery
1217  if (!iGameDiscoverInterval--)
1218  {
1219  DiscoverClient.StartDiscovery();
1220  iGameDiscoverInterval = C4NetGameDiscoveryInterval;
1221  }
1222  DiscoverClient.Execute(0);
1223 
1224  UpdateList(false);
1225 }
1226 
1227 void C4StartupNetDlg::AddReferenceQuery(const char *szAddress, C4StartupNetListEntry::QueryType eQueryType)
1228 {
1229  // Check for an active reference query to the same address
1230  for (C4GUI::Element *pElem = pGameSelList->GetFirst(); pElem; pElem = pElem->GetNext())
1231  {
1232  C4StartupNetListEntry *pEntry = static_cast<C4StartupNetListEntry *>(pElem);
1233  // same address
1234  if (pEntry->IsSameRefQueryAddress(szAddress))
1235  {
1236  // nothing to do, xcept maybe select it
1237  if (eQueryType == C4StartupNetListEntry::NRQT_DirectJoin) pGameSelList->SelectEntry(pEntry, true);
1238  return;
1239  }
1240  }
1241  // No reference from same host found - create a new entry
1242  C4StartupNetListEntry *pEntry = new C4StartupNetListEntry(pGameSelList, nullptr, this);
1243  pEntry->SetRefQuery(szAddress, eQueryType);
1244  if (eQueryType == C4StartupNetListEntry::NRQT_DirectJoin)
1245  pGameSelList->SelectEntry(pEntry, true);
1246  else if (fIsCollapsed)
1247  pEntry->UpdateCollapsed(true);
1248 }
1249 
1251 {
1252  // collapse the new entry if desired
1253  if (fIsCollapsed && pEntry != pGameSelList->GetSelectedItem())
1254  pEntry->UpdateCollapsed(true);
1255 }
1256 
1258 {
1259 #ifdef WITH_AUTOMATIC_UPDATE
1260  StdStrBuf strVersion; strVersion.Format("%d.%d", C4XVER1, C4XVER2);
1261  StdStrBuf strQuery; strQuery.Format("%s?version=%s&platform=%s&action=version", Config.Network.UpdateServerAddress, strVersion.getData(), C4_OS);
1262 
1263  if (pUpdateClient.Init() && pUpdateClient.SetServer(strQuery.getData()) && pUpdateClient.QueryUpdateURL())
1264  {
1265  pUpdateClient.SetNotify(&Application.InteractiveThread);
1266  Application.InteractiveThread.AddProc(&pUpdateClient);
1267  }
1268  fUpdateCheckPending = true;
1269 #endif
1270 }
1271 
1273 {
1274  // update label
1275  if (pChatTitleLabel) pChatTitleLabel->SetText(FormatString("%s - %s", LoadResStr("IDS_DLG_CHAT"), sNewTitle.getData()).getData());
1276 }
const char * getData() const
Definition: StdBuf.h:450
int32_t GetHeight() const
Definition: C4Gui.h:2801
void SetText(const char *szToText)
Definition: C4GuiButton.cpp:55
void OnBtnGameList(C4GUI::Control *btn)
bool Execute()
const int C4NetRefRequestTimeout
#define C4GUI_CaptionFontClr
Definition: C4Gui.h:37
#define C4GUI_StandardBGColor
Definition: C4Gui.h:66
char ScenarioFilename[_MAX_PATH+1]
Definition: C4Game.h:104
bool IsScrollingNecessary()
Definition: C4Gui.h:1582
C4Config Config
Definition: C4Config.cpp:837
int GetLineHeight() const
Definition: C4FontLoader.h:132
void GUISound(const char *szSound)
Definition: C4Gui.cpp:1178
Definition: StdBuf.h:37
float Y
Definition: C4Facet.h:120
#define C4GUI_ButtonHgt
Definition: C4Gui.h:111
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:122
void GetAll(C4Rect &rcOut)
Definition: C4Gui.cpp:1128
void OnChatTitleChange(const StdStrBuf &sNewTitle)
std::unique_ptr< C4Network2Reference > pJoinReference
Definition: C4Game.h:110
bool IsSameRefQueryAddress(const char *szJoinAddress)
bool KeywordMatch(const char *szMatch)
const int32_t C4StartupScenSel_IconCount
int32_t PortDiscovery
Definition: C4Config.h:154
StdStrBuf GetString() const
Definition: C4GameVersion.h:32
virtual void DrawElement(C4TargetFacet &cgo)
#define C4GUI_MessageFontClr
Definition: C4Gui.h:43
void SetAnimated(bool fEnabled, int iDelay)
void SetMargin(int32_t iNewMargin)
Definition: C4Gui.h:1019
const char * GetJoinAddress()
const C4Network2Address & getAddr(int i) const
#define C4GUI_InactMessageFontClr
Definition: C4Gui.h:45
C4Game Game
Definition: C4Globals.cpp:52
void UpdateCollapsed(bool fToCollapseValue)
C4StartupNetListEntry(C4GUI::ListBox *pForListBox, C4GUI::Element *pInsertBefore, class C4StartupNetDlg *pNetDlg)
StdStrBuf GetAllClientNames() const
Definition: C4Client.cpp:249
void Clear()
Definition: StdBuf.h:474
virtual void OnShown()
C4ConfigGeneral General
Definition: C4Config.h:252
bool DlgEnter()
Definition: C4ChatDlg.cpp:786
void OnSelDblClick(class C4GUI::Element *pEl)
static constexpr const char * DirectJoinFilePrefix
Definition: C4Game.h:293
virtual void OnShown()
Definition: C4Gui.h:2208
bool EraseFile(const char *szFilename)
Definition: StdFile.cpp:495
const char * getComment() const
C4GUI::Edit::InputResult OnJoinAddressEnter(C4GUI::Edit *edt, bool fPasting, bool fPastingMore)
void SetFacet(const C4Facet &fct)
Definition: C4Gui.h:612
void SetToolTip(const char *szNewTooltip, bool is_immediate=false)
Definition: C4Gui.cpp:410
#define C4GUI_IconExWdt
Definition: C4Gui.h:95
void UpdateElementPositions()
const int C4NetErrorRefTimeout
bool InsertElement(Element *pChild, Element *pInsertBefore, int32_t iIndent=0)
void SetSheetMargin(int32_t iMargin)
Definition: C4Gui.h:1713
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:177
StdStrBuf ToString(int flags=0) const
Definition: C4NetIO.cpp:605
C4Network2Status getGameStatus() const
const char * GetText()
Definition: C4Gui.h:1338
void SetVisibility(bool fToValue)
Definition: C4Rect.h:29
virtual const char * GetError() const
Definition: C4NetIO.h:285
bool GetTextExtent(const char *szText, int32_t &rsx, int32_t &rsy, bool fCheckMarkup=true)
const char * getLeague()
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:181
virtual C4Rect & GetClientRect()
Definition: C4Gui.h:864
void AddElement(Element *pChild)
C4Client * getHost() const
Definition: C4Client.h:163
Element * GetFirst()
Definition: C4Gui.h:1571
#define _MAX_PATH
virtual void UpdateSize()
Definition: C4Gui.cpp:186
C4ClientList Clients
void SetSelectionChangeCallbackFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1548
C4Rect GetContainedClientRect()
Definition: C4Gui.h:448
C4GraphicsResource GraphicsResource
void SetDecoration(bool fDrawBG, ScrollBarFacets *pToGfx, bool fAutoScroll, bool fDrawBorder=false)
Definition: C4Gui.h:1566
void SelectSheet(int32_t iIndex, bool fByUser)
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:97
void Add(StdSchedulerProc *pProc)
void SetReference(C4Network2Reference *pNewRef)
C4GUI::Edit::InputResult OnSearchFieldEnter(C4GUI::Edit *edt, bool fPasting, bool fPastingMore)
bool GetVersion(StdStrBuf *pVersion)
const char * SSearchNoCase(const char *szString, const char *szIndex)
Definition: Standard.cpp:348
void ClearCallback(C4InteractiveEventType eEvent, Callback *pnNetworkCallback)
size_t getSize() const
Definition: StdBuf.h:109
void SetColor(DWORD dwToClr, bool fMakeReadableOnBlack=true)
Definition: C4Gui.h:505
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
Element * GetSelectedItem()
Definition: C4Gui.h:1580
void Cancel(const char *szReason)
void SetRefQuery(const char *szAddress, QueryType eQueryType)
virtual void SetVisibility(bool fToValue)
bool GetUpdateURL(StdStrBuf *pUpdateURL)
int32_t Wdt
Definition: C4Rect.h:32
bool PopDiscover(C4NetIO::addr_t &Discover)
std::vector< C4KeyCodeEx > CodeList
int32_t getTime() const
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1194
const int C4NetReferenceTimeout
bool Init(uint16_t iPort=C4NetIO::addr_t::IPPORT_NONE)
void SetAutosize(bool fToVal)
Definition: C4Gui.h:507
virtual bool Execute(int iMaxTime=TO_INF, pollfd *=0)
Definition: C4NetIO.cpp:2060
bool isPasswordNeeded() const
static int32_t GetDefaultHeight(CStdFont *pUseFont=nullptr)
int32_t y
Definition: C4Rect.h:32
bool ShowMessageModal(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
const char * getTitle() const
bool isPastLobby() const
Definition: C4Network2.h:88
void ClearRef()
const int C4NetMasterServerQueryInterval
bool GetReferences(C4Network2Reference **&rpReferences, int32_t &rRefCount)
const int C4NetGameDiscoveryInterval
bool ShowErrorMessage(const char *szMessage)
void DrawBoxDw(C4Surface *sfcDest, int iX1, int iY1, int iX2, int iY2, DWORD dwClr)
Definition: C4Draw.cpp:849
int32_t DefRec
Definition: C4Config.h:48
C4Rect rcBounds
Definition: C4Gui.h:385
bool OnReference()
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:527
virtual bool IsSelectedChild(Element *pChild)
Definition: C4Gui.h:837
C4ConfigNetwork Network
Definition: C4Config.h:256
void OnBackBtn(C4GUI::Control *btn)
int32_t getSortOrder() const
class C4StartupDlg * SwitchDialog(DialogID eToDlg, bool fFade=true, const char *szSubDialog=nullptr)
Definition: C4Startup.cpp:139
C4Draw * pDraw
Definition: C4Draw.cpp:45
C4Network2Reference * GetReference() const
#define C4GUI_ImportantBGColor
Definition: C4Gui.h:60
void OnCreateGameBtn(C4GUI::Control *btn)
C4InteractiveEventType
StdStrBuf GetActivePlayerNames(bool fCountInvisible, int32_t iAtClientID=-1) const
virtual void OnShown()
Definition: C4ChatDlg.cpp:421
bool NetworkActive
Definition: C4Game.h:124
virtual void DrawElement(C4TargetFacet &cgo)
bool AddProc(StdSchedulerProc *pProc)
C4Rect & GetBounds()
Definition: C4Gui.h:445
int32_t GetWidth() const
Definition: C4Gui.h:2800
const char * getServerName() const
bool SaveToFile(const char *szFile) const
Definition: StdBuf.cpp:60
std::tuple< std::string, int > BreakMessage(const char *szMsg, int iWdt, bool fCheckMarkup, float fZoom=1.0f)
bool fObserve
Definition: C4Game.h:122
Icons
Definition: C4Gui.h:637
int32_t getStartTime() const
int32_t MasterServerSignUp
Definition: C4Config.h:148
C4Network2Reference * GrabReference()
void SetColors(uint32_t dwFrameClr, uint32_t dwTitleClr, uint32_t dwBackClr=0xffffffff)
Definition: C4Gui.h:1017
void RemoveProc(StdSchedulerProc *pProc)
virtual C4GUI::Control * GetDefaultControl()
bool isJoinAllowed() const
void OnRefreshBtn(C4GUI::Control *btn)
void SelectEntry(Element *pNewSel, bool fByUser)
int32_t x
Definition: C4Rect.h:32
void Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt)
Definition: C4Rect.cpp:86
bool ShowMessage(const char *szMessage, const char *szCaption, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
void Ref(const char *pnData)
Definition: StdBuf.h:463
void Remove(StdSchedulerProc *pProc)
void OnBtnInternet(C4GUI::Control *btn)
void OnBtnChat(C4GUI::Control *btn)
int32_t getClientCnt() const
Definition: C4Client.cpp:241
C4GUI::Element * GetNextLower(int32_t sortOrder)
Element * GetNext() const
Definition: C4Gui.h:449
float TargetX
Definition: C4Facet.h:167
void SetCallback(C4InteractiveEventType eEvent, Callback *pnNetworkCallback)
void OnBtnRecord(C4GUI::Control *btn)
void SetFocus(Control *pCtrl, bool fByMouse)
const C4FacetSurface & GetFacet() const
Definition: C4Gui.h:610
const char * getCUID() const
Definition: C4Client.h:108
const char * GetTitle()
Definition: C4ChatDlg.h:140
static bool IsChatEnabled()
Definition: C4ChatDlg.cpp:1006
int32_t getIcon() const
bool IsSameHost(const C4Network2Reference *pRef2)
const int C4NetMinRefreshInterval
const char * getName() const
Definition: C4Client.h:107
void SetBounds(const C4Rect &rcNewBound)
Definition: C4Gui.h:446
bool isLobbyActive() const
Definition: C4Network2.h:87
void SetNotify(class C4InteractiveThread *pnNotify)
#define C4GUI_Caption2FontClr
Definition: C4Gui.h:38
void UnFreezeScrolling()
Definition: C4Gui.h:1563
#define C4_OS
StdStrBuf toString() const
C4PlayerInfoList PlayerInfos
const int ALeft
Definition: C4Surface.h:43
bool isOfficialServer() const
bool fVisible
Definition: C4Gui.h:383
static C4Startup * Get()
Definition: C4Startup.h:133
bool Record
Definition: C4Game.h:125
bool isLeague() const
bool RestartApplication(std::vector< const char * > parameters)
char DefinitionFilenames[20 *_MAX_PATH+1]
Definition: C4Game.h:107
const int32_t C4StartupScenSel_DefaultIcon_Scenario
virtual bool Init(uint16_t iPort=addr_t::IPPORT_NONE)
Definition: C4NetIO.cpp:824
bool IsSameAddress(const C4Network2Reference *pRef2)
virtual void OnClosed(bool fOK)
bool GetFromRight(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1096
QueryType
Sheet * AddSheet(const char *szTitle, int32_t icoTitle=Ico_None)
size_t getLength() const
Definition: StdBuf.h:453
void SetIcon(Icons eUseIcon)
virtual class C4GUI::Control * GetDefaultControl()
Definition: C4ChatDlg.cpp:426
virtual Screen * GetScreen()
Definition: C4Gui.cpp:290
const char * AtTempPath(const char *szFilename)
Definition: C4Config.cpp:544
C4GUI::Control * GetDlgModeFocusControl()
void OnReferenceEntryAdd(C4StartupNetListEntry *pEntry)
void OnSelChange(class C4GUI::Element *pEl)
int32_t Hgt
Definition: C4Rect.h:32
C4FacetID fctNetGetRef
Definition: C4Startup.h:74
void MakeTempFilename(char *szFilename)
Definition: StdFile.cpp:333
int32_t GetActivePlayerCount(bool fCountInvisible) const
~C4StartupNetListEntry()
char DirectJoinAddress[_MAX_PATH+1]
Definition: C4Game.h:108
C4Surface * Surface
Definition: C4Facet.h:119
bool GetFromLeft(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1079
float TargetY
Definition: C4Facet.h:167
void SetDrawDecoration(bool fToVal)
Definition: C4Gui.h:1714
int32_t UseAlternateServer
Definition: C4Config.h:153
const char * GetError()
Screen TheScreen
Definition: C4Gui.cpp:1057
const C4GameVersion & getGameVersion() const
const char * GetLeagueServerAddress()
Definition: C4Config.cpp:594
void Copy()
Definition: StdBuf.h:475
bool IsActive(bool fForKeyboard)
float X
Definition: C4Facet.h:120
C4StartupGraphics Graphics
Definition: C4Startup.h:100
void SetIcon(Icons icoNewIconIndex)
const char * getDescription() const
Definition: C4Network2.cpp:75
bool SetServer(const char *szServerAddress)
C4Application Application
Definition: C4Globals.cpp:44
virtual bool Close()
Definition: C4NetIO.cpp:2001
bool GetCentered(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1136
C4InteractiveThread InteractiveThread
Definition: C4Application.h:45
bool GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut)
Definition: C4Gui.cpp:1112
void FreezeScrolling()
Definition: C4Gui.h:1562
C4Network2IRCClient & IRCClient
Definition: C4Application.h:47
void OnJoinGameBtn(C4GUI::Control *btn)
C4GameParameters Parameters
virtual void UpdateSize()
void OpenGame(const char *scenario=0)
void SetText(const char *szToText, bool fAllowHotkey=true)
Definition: C4GuiLabels.cpp:74
int32_t GetActiveSheetIndex()
void SetTitleChangeCB(C4GUI::BaseInputCallback *pNewCB)
Definition: C4ChatDlg.cpp:383
Control * GetFocus()
Definition: C4Gui.h:2115
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277
void SetSelectionDblClickFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1553
StdStrBuf getGameGoalString() const