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