OpenTTD Source 20251005-master-ga617d009cc
network_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "../stdafx.h"
11#include "../strings_func.h"
12#include "../fios.h"
13#include "network_client.h"
14#include "network_gui.h"
15#include "network_gamelist.h"
16#include "network.h"
17#include "network_base.h"
18#include "network_content.h"
19#include "network_server.h"
20#include "network_coordinator.h"
21#include "network_survey.h"
22#include "../gui.h"
23#include "network_udp.h"
24#include "../window_func.h"
25#include "../gfx_func.h"
26#include "../dropdown_type.h"
27#include "../dropdown_func.h"
28#include "../querystring_gui.h"
29#include "../sortlist_type.h"
30#include "../company_func.h"
31#include "../command_func.h"
32#include "../core/geometry_func.hpp"
33#include "../genworld.h"
34#include "../map_type.h"
35#include "../zoom_func.h"
36#include "../sprite.h"
37#include "../settings_internal.h"
38#include "../company_cmd.h"
39#include "../timer/timer.h"
40#include "../timer/timer_window.h"
41#include "../timer/timer_game_calendar.h"
42#include "../textfile_gui.h"
43#include "../stringfilter_type.h"
44#include "../core/string_consumer.hpp"
45
46#include "../widgets/network_widget.h"
47
48#include "table/strings.h"
49#include "../table/sprites.h"
50
51#ifdef __EMSCRIPTEN__
52# include <emscripten.h>
53#endif
54
55#include "../safeguards.h"
56
57static void ShowNetworkStartServerWindow();
58
60static CompanyID _admin_company_id = CompanyID::Invalid();
61
70
71static DropDownList BuildVisibilityDropDownList()
72{
73 DropDownList list;
74
75 list.push_back(MakeDropDownListStringItem(STR_NETWORK_SERVER_VISIBILITY_LOCAL, SERVER_GAME_TYPE_LOCAL));
76 list.push_back(MakeDropDownListStringItem(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY, SERVER_GAME_TYPE_INVITE_ONLY));
77 list.push_back(MakeDropDownListStringItem(STR_NETWORK_SERVER_VISIBILITY_PUBLIC, SERVER_GAME_TYPE_PUBLIC));
78
79 return list;
80}
81
83typedef int ServerListPosition;
84static const ServerListPosition SLP_INVALID = -1;
85
88 static const uint MINIMUM_NAME_WIDTH_BEFORE_NEW_HEADER = 150;
89public:
91 {
92 auto leaf = std::make_unique<NWidgetLeaf>(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_NAME, WidgetData{.string = STR_NETWORK_SERVER_LIST_GAME_NAME}, STR_NETWORK_SERVER_LIST_GAME_NAME_TOOLTIP);
93 leaf->SetResize(1, 0);
94 leaf->SetFill(1, 0);
95 this->Add(std::move(leaf));
96
97 this->Add(std::make_unique<NWidgetLeaf>(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_CLIENTS, WidgetData{.string = STR_NETWORK_SERVER_LIST_CLIENTS_CAPTION}, STR_NETWORK_SERVER_LIST_CLIENTS_CAPTION_TOOLTIP));
98 this->Add(std::make_unique<NWidgetLeaf>(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_MAPSIZE, WidgetData{.string = STR_NETWORK_SERVER_LIST_MAP_SIZE_CAPTION}, STR_NETWORK_SERVER_LIST_MAP_SIZE_CAPTION_TOOLTIP));
99 this->Add(std::make_unique<NWidgetLeaf>(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_DATE, WidgetData{.string = STR_NETWORK_SERVER_LIST_DATE_CAPTION}, STR_NETWORK_SERVER_LIST_DATE_CAPTION_TOOLTIP));
100 this->Add(std::make_unique<NWidgetLeaf>(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_YEARS, WidgetData{.string = STR_NETWORK_SERVER_LIST_PLAY_TIME_CAPTION}, STR_NETWORK_SERVER_LIST_PLAY_TIME_CAPTION_TOOLTIP));
101
102 leaf = std::make_unique<NWidgetLeaf>(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_INFO, WidgetData{.string = STR_EMPTY}, STR_NETWORK_SERVER_LIST_INFO_ICONS_TOOLTIP);
103 leaf->SetFill(0, 1);
104 this->Add(std::move(leaf));
105 }
106
107 void SetupSmallestSize(Window *w) override
108 {
109 this->smallest_y = 0; // Biggest child.
110 this->fill_x = 1;
111 this->fill_y = 0;
112 this->resize_x = 1; // We only resize in this direction
113 this->resize_y = 0; // We never resize in this direction
114
115 /* First initialise some variables... */
116 for (const auto &child_wid : this->children) {
117 child_wid->SetupSmallestSize(w);
118 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
119 }
120
121 /* ... then in a second pass make sure the 'current' sizes are set. Won't change for most widgets. */
122 for (const auto &child_wid : this->children) {
123 child_wid->current_x = child_wid->smallest_x;
124 child_wid->current_y = this->smallest_y;
125 }
126
127 this->smallest_x = this->children.front()->smallest_x + this->children.back()->smallest_x; // First and last are always shown, rest not
128 this->ApplyAspectRatio();
129 }
130
131 void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
132 {
133 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
134
135 this->pos_x = x;
136 this->pos_y = y;
137 this->current_x = given_width;
138 this->current_y = given_height;
139
140 given_width -= this->children.back()->smallest_x;
141 /* The first and last widget are always visible, determine which other should be visible */
142 if (this->children.size() > 2) {
143 auto first = std::next(std::begin(this->children));
144 auto last = std::prev(std::end(this->children));
145 for (auto it = first; it != last; ++it) {
146 auto &child_wid = *it;
147 if (given_width > ScaleGUITrad(MINIMUM_NAME_WIDTH_BEFORE_NEW_HEADER) + child_wid->smallest_x && (*std::prev(it))->current_x != 0) {
148 given_width -= child_wid->smallest_x;
149 child_wid->current_x = child_wid->smallest_x; /* Make visible. */
150 } else {
151 child_wid->current_x = 0; /* Make invisible. */
152 }
153 }
154 }
155
156 /* All remaining space goes to the first (name) widget */
157 this->children.front()->current_x = given_width;
158
159 /* Now assign the widgets to their rightful place */
160 uint position = 0; // Place to put next child relative to origin of the container.
161 auto assign_position = [&](const std::unique_ptr<NWidgetBase> &child_wid) {
162 if (child_wid->current_x != 0) {
163 child_wid->AssignSizePosition(sizing, x + position, y, child_wid->current_x, this->current_y, rtl);
164 position += child_wid->current_x;
165 }
166 };
167
168 if (rtl) {
169 std::for_each(std::rbegin(this->children), std::rend(this->children), assign_position);
170 } else {
171 std::for_each(std::begin(this->children), std::end(this->children), assign_position);
172 }
173 }
174};
175
176class NetworkGameWindow : public Window {
177protected:
178 /* Runtime saved values */
179 static Listing last_sorting;
180
181 /* Constants for sorting servers */
182 static const std::initializer_list<GUIGameServerList::SortFunction * const> sorter_funcs;
183 static const std::initializer_list<GUIGameServerList::FilterFunction * const> filter_funcs;
184
185 NetworkGame *server = nullptr;
188 ServerListPosition list_pos = SLP_INVALID;
189 Scrollbar *vscroll = nullptr;
192 bool searched_internet = false;
193
194 Dimension lock{};
196
203 {
204 if (!this->servers.NeedRebuild()) return;
205
206 /* Create temporary array of games to use for listing */
207 this->servers.clear();
208
209 bool found_current_server = false;
210 bool found_last_joined = false;
211 for (const auto &ngl : _network_game_list) {
212 this->servers.push_back(ngl.get());
213 if (ngl.get() == this->server) {
214 found_current_server = true;
215 }
216 if (ngl.get() == this->last_joined) {
217 found_last_joined = true;
218 }
219 }
220 /* A refresh can cause the current server to be delete; so unselect. */
221 if (!found_last_joined) {
222 this->last_joined = nullptr;
223 }
224 if (!found_current_server) {
225 this->server = nullptr;
226 this->list_pos = SLP_INVALID;
227 }
228
229 /* Apply the filter condition immediately, if a search string has been provided. */
230 StringFilter sf;
231 sf.SetFilterTerm(this->filter_editbox.text.GetText());
232
233 if (!sf.IsEmpty()) {
234 this->servers.SetFilterState(true);
235 this->servers.Filter(sf);
236 } else {
237 this->servers.SetFilterState(false);
238 }
239
240 this->servers.RebuildDone();
241 this->vscroll->SetCount(this->servers.size());
242
243 /* Sort the list of network games as requested. */
244 this->servers.Sort();
245 this->UpdateListPos();
246 }
247
249 static bool NGameNameSorter(NetworkGame * const &a, NetworkGame * const &b)
250 {
251 int r = StrNaturalCompare(a->info.server_name, b->info.server_name, true); // Sort by name (natural sorting).
252 if (r == 0) r = a->connection_string.compare(b->connection_string);
253
254 return r < 0;
255 }
256
262 static bool NGameClientSorter(NetworkGame * const &a, NetworkGame * const &b)
263 {
264 /* Reverse as per default we are interested in most-clients first */
265 int r = a->info.clients_on - b->info.clients_on;
266
267 if (r == 0) r = a->info.clients_max - b->info.clients_max;
268 if (r == 0) return NGameNameSorter(a, b);
269
270 return r < 0;
271 }
272
274 static bool NGameMapSizeSorter(NetworkGame * const &a, NetworkGame * const &b)
275 {
276 /* Sort by the area of the map. */
277 int r = (a->info.map_height) * (a->info.map_width) - (b->info.map_height) * (b->info.map_width);
278
279 if (r == 0) r = a->info.map_width - b->info.map_width;
280 return (r != 0) ? r < 0 : NGameClientSorter(a, b);
281 }
282
284 static bool NGameCalendarDateSorter(NetworkGame * const &a, NetworkGame * const &b)
285 {
286 auto r = a->info.calendar_date - b->info.calendar_date;
287 return (r != 0) ? r < 0 : NGameClientSorter(a, b);
288 }
289
291 static bool NGameTicksPlayingSorter(NetworkGame * const &a, NetworkGame * const &b)
292 {
293 if (a->info.ticks_playing == b->info.ticks_playing) {
294 return NGameClientSorter(a, b);
295 }
296 return a->info.ticks_playing < b->info.ticks_playing;
297 }
298
303 static bool NGameAllowedSorter(NetworkGame * const &a, NetworkGame * const &b)
304 {
305 /* The servers we do not know anything about (the ones that did not reply) should be at the bottom) */
306 int r = a->info.server_revision.empty() - b->info.server_revision.empty();
307
308 /* Reverse default as we are interested in version-compatible clients first */
309 if (r == 0) r = b->info.version_compatible - a->info.version_compatible;
310 /* The version-compatible ones are then sorted with NewGRF compatible first, incompatible last */
311 if (r == 0) r = b->info.compatible - a->info.compatible;
312 /* Passworded servers should be below unpassworded servers */
313 if (r == 0) r = a->info.use_password - b->info.use_password;
314
315 /* Finally sort on the number of clients of the server in reverse order. */
316 return (r != 0) ? r < 0 : NGameClientSorter(b, a);
317 }
318
321 {
322 if (this->servers.Sort()) this->UpdateListPos();
323 }
324
327 {
328 auto it = std::ranges::find(this->servers, this->server);
329 if (it == std::end(this->servers)) {
330 this->list_pos = SLP_INVALID;
331 } else {
332 this->list_pos = static_cast<ServerListPosition>(std::distance(std::begin(this->servers), it));
333 }
334 }
335
336 static bool NGameSearchFilter(NetworkGame * const *item, StringFilter &sf)
337 {
338 assert(item != nullptr);
339 assert((*item) != nullptr);
340
341 sf.ResetState();
342 sf.AddLine((*item)->info.server_name);
343 return sf.GetState();
344 }
345
352 void DrawServerLine(const NetworkGame *cur_item, int y, bool highlight) const
353 {
354 Rect name = this->GetWidget<NWidgetBase>(WID_NG_NAME)->GetCurrentRect();
355 Rect info = this->GetWidget<NWidgetBase>(WID_NG_INFO)->GetCurrentRect();
356
357 /* show highlighted item with a different colour */
358 if (highlight) {
359 Rect r = {std::min(name.left, info.left), y, std::max(name.right, info.right), y + (int)this->resize.step_height - 1};
361 }
362
363 /* Offset to vertically position text. */
365
366 info = info.Shrink(WidgetDimensions::scaled.framerect);
367 name = name.Shrink(WidgetDimensions::scaled.framerect);
368 DrawString(name.left, name.right, y + text_y_offset, cur_item->info.server_name, TC_BLACK);
369
370 /* only draw details if the server is online */
371 if (cur_item->status == NGLS_ONLINE) {
372 if (const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(WID_NG_CLIENTS); nwid->current_x != 0) {
373 Rect clients = nwid->GetCurrentRect();
374 DrawString(clients.left, clients.right, y + text_y_offset,
375 GetString(STR_NETWORK_SERVER_LIST_GENERAL_ONLINE, cur_item->info.clients_on, cur_item->info.clients_max, cur_item->info.companies_on, cur_item->info.companies_max),
376 TC_FROMSTRING, SA_HOR_CENTER);
377 }
378
379 if (const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(WID_NG_MAPSIZE); nwid->current_x != 0) {
380 /* map size */
381 Rect mapsize = nwid->GetCurrentRect();
382 DrawString(mapsize.left, mapsize.right, y + text_y_offset,
383 GetString(STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT, cur_item->info.map_width, cur_item->info.map_height),
384 TC_FROMSTRING, SA_HOR_CENTER);
385 }
386
387 if (const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(WID_NG_DATE); nwid->current_x != 0) {
388 /* current date */
389 Rect date = nwid->GetCurrentRect();
390 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(cur_item->info.calendar_date);
391 DrawString(date.left, date.right, y + text_y_offset,
392 GetString(STR_JUST_INT, ymd.year),
393 TC_BLACK, SA_HOR_CENTER);
394 }
395
396 if (const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(WID_NG_YEARS); nwid->current_x != 0) {
397 /* play time */
398 Rect years = nwid->GetCurrentRect();
399 const auto play_time = cur_item->info.ticks_playing / Ticks::TICKS_PER_SECOND;
400 DrawString(years.left, years.right, y + text_y_offset,
401 GetString(STR_NETWORK_SERVER_LIST_PLAY_TIME_SHORT, play_time / 60 / 60, (play_time / 60) % 60),
402 TC_BLACK, SA_HOR_CENTER);
403 }
404
405 /* Set top and bottom of info rect to current row. */
406 info.top = y;
407 info.bottom = y + this->resize.step_height - 1;
408
409 bool rtl = _current_text_dir == TD_RTL;
410
411 /* draw a lock if the server is password protected */
412 if (cur_item->info.use_password) DrawSpriteIgnorePadding(SPR_LOCK, PAL_NONE, info.WithWidth(this->lock.width, rtl), SA_CENTER);
413
414 /* draw red or green icon, depending on compatibility with server */
415 PaletteID pal = cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED);
416 DrawSpriteIgnorePadding(SPR_BLOT, pal, info.WithWidth(this->blot.width, !rtl), SA_CENTER);
417 }
418 }
419
428 {
429 if (this->list_pos == SLP_INVALID) return; // no server selected
430 this->vscroll->ScrollTowards(this->list_pos);
431 }
432
433public:
435 {
436 this->CreateNestedTree();
437 this->vscroll = this->GetScrollbar(WID_NG_SCROLLBAR);
439
441 this->name_editbox.text.Assign(_settings_client.network.client_name);
442
444 this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
446
447 /* As the Game Coordinator doesn't support "websocket" servers yet, we
448 * let "os/emscripten/pre.js" hardcode a list of servers people can
449 * join. This means the serverlist is curated for now, but it is the
450 * best we can offer. */
451#ifdef __EMSCRIPTEN__
452 EM_ASM(if (window["openttd_server_list"]) openttd_server_list());
453#endif
454
455 this->last_joined = NetworkAddServer(_settings_client.network.last_joined, false);
456 this->server = this->last_joined;
457
458 this->servers.SetListing(this->last_sorting);
459 this->servers.SetSortFuncs(NetworkGameWindow::sorter_funcs);
460 this->servers.SetFilterFuncs(NetworkGameWindow::filter_funcs);
461 this->servers.ForceRebuild();
462 }
463
465 {
466 this->last_sorting = this->servers.GetListing();
467 }
468
469 void OnInit() override
470 {
471 this->lock = GetScaledSpriteSize(SPR_LOCK);
472 this->blot = GetScaledSpriteSize(SPR_BLOT);
473 }
474
475 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
476 {
477 switch (widget) {
478 case WID_NG_MATRIX:
479 fill.height = resize.height = std::max<uint>(this->blot.height, GetCharacterHeight(FS_NORMAL)) + padding.height;
480 size.height = 12 * resize.height;
481 break;
482
484 size.height = std::max<uint>(this->blot.height, GetCharacterHeight(FS_NORMAL)) + WidgetDimensions::scaled.matrix.Vertical();
485 break;
486
488 size.width = NWidgetScrollbar::GetVerticalDimension().width;
489 break;
490
491 case WID_NG_NAME:
492 size.width += 2 * Window::SortButtonWidth(); // Make space for the arrow
493 break;
494
495 case WID_NG_CLIENTS: {
496 size.width += 2 * Window::SortButtonWidth(); // Make space for the arrow
497 auto max_clients = GetParamMaxValue(MAX_CLIENTS);
498 auto max_companies = GetParamMaxValue(MAX_COMPANIES);
499 size = maxdim(size, GetStringBoundingBox(GetString(STR_NETWORK_SERVER_LIST_GENERAL_ONLINE, max_clients, max_clients, max_companies, max_companies)));
500 break;
501 }
502
503 case WID_NG_MAPSIZE: {
504 size.width += 2 * Window::SortButtonWidth(); // Make space for the arrow
505 auto max_map_size = GetParamMaxValue(MAX_MAP_SIZE);
506 size = maxdim(size, GetStringBoundingBox(GetString(STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT, max_map_size, max_map_size)));
507 break;
508 }
509
510 case WID_NG_DATE:
511 case WID_NG_YEARS:
512 size.width += 2 * Window::SortButtonWidth(); // Make space for the arrow
513 size = maxdim(size, GetStringBoundingBox(GetString(STR_JUST_INT, GetParamMaxValue(5))));
514 break;
515
516 case WID_NG_INFO:
517 size.width = this->lock.width + WidgetDimensions::scaled.hsep_normal + this->blot.width + padding.width;
518 size.height = std::max(this->lock.height, this->blot.height) + padding.height;
519 break;
520 }
521 }
522
523 void DrawWidget(const Rect &r, WidgetID widget) const override
524 {
525 switch (widget) {
526 case WID_NG_MATRIX: {
527 uint16_t y = r.top;
528
529 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->servers);
530 for (auto it = first; it != last; ++it) {
531 const NetworkGame *ngl = *it;
532 this->DrawServerLine(ngl, y, ngl == this->server);
533 y += this->resize.step_height;
534 }
535 break;
536 }
537
539 /* Draw the last joined server, if any */
540 if (this->last_joined != nullptr) this->DrawServerLine(this->last_joined, r.top, this->last_joined == this->server);
541 break;
542
543 case WID_NG_DETAILS:
544 this->DrawDetails(r);
545 break;
546
547 case WID_NG_NAME:
548 case WID_NG_CLIENTS:
549 case WID_NG_MAPSIZE:
550 case WID_NG_DATE:
551 case WID_NG_YEARS:
552 case WID_NG_INFO:
553 if (widget - WID_NG_NAME == this->servers.SortType()) this->DrawSortButtonState(widget, this->servers.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
554 break;
555 }
556 }
557
558
559 void OnPaint() override
560 {
561 if (this->servers.NeedRebuild()) {
563 }
564 if (this->servers.NeedResort()) {
565 this->SortNetworkGameList();
566 }
567
568 NetworkGame *sel = this->server;
569 /* 'Refresh' button invisible if no server selected */
570 this->SetWidgetDisabledState(WID_NG_REFRESH, sel == nullptr);
571 /* 'Join' button disabling conditions */
572 this->SetWidgetDisabledState(WID_NG_JOIN, sel == nullptr || // no Selected Server
573 sel->status != NGLS_ONLINE || // Server offline
574 sel->info.clients_on >= sel->info.clients_max || // Server full
575 !sel->info.compatible); // Revision mismatch
576
577 this->SetWidgetLoweredState(WID_NG_REFRESH, sel != nullptr && sel->refreshing);
578
579 /* 'NewGRF Settings' button invisible if no NewGRF is used */
580 bool changed = false;
581 changed |= this->GetWidget<NWidgetStacked>(WID_NG_NEWGRF_SEL)->SetDisplayedPlane(sel == nullptr || sel->status != NGLS_ONLINE || sel->info.grfconfig.empty() ? SZSP_NONE : 0);
582 changed |= this->GetWidget<NWidgetStacked>(WID_NG_NEWGRF_MISSING_SEL)->SetDisplayedPlane(sel == nullptr || sel->status != NGLS_ONLINE || sel->info.grfconfig.empty() || !sel->info.version_compatible || sel->info.compatible ? SZSP_NONE : 0);
583 if (changed) {
584 this->ReInit();
585 return;
586 }
587
588#ifdef __EMSCRIPTEN__
593#endif
594
595 this->DrawWidgets();
596 }
597
598 StringID GetHeaderString() const
599 {
600 if (this->server == nullptr) return STR_NETWORK_SERVER_LIST_GAME_INFO;
601 switch (this->server->status) {
602 case NGLS_OFFLINE: return STR_NETWORK_SERVER_LIST_SERVER_OFFLINE;
603 case NGLS_ONLINE: return STR_NETWORK_SERVER_LIST_GAME_INFO;
604 case NGLS_FULL: return STR_NETWORK_SERVER_LIST_SERVER_FULL;
605 case NGLS_BANNED: return STR_NETWORK_SERVER_LIST_SERVER_BANNED;
606 case NGLS_TOO_OLD: return STR_NETWORK_SERVER_LIST_SERVER_TOO_OLD;
607 default: NOT_REACHED();
608 }
609 }
610
611 void DrawDetails(const Rect &r) const
612 {
613 NetworkGame *sel = this->server;
614
615 Rect tr = r.Shrink(WidgetDimensions::scaled.frametext);
616 StringID header_msg = this->GetHeaderString();
617 int header_height = GetStringHeight(header_msg, tr.Width()) +
618 (sel == nullptr ? 0 : GetStringHeight(sel->info.server_name, tr.Width())) +
619 WidgetDimensions::scaled.frametext.Vertical();
620
621 /* Height for the title banner */
622 Rect hr = r.WithHeight(header_height).Shrink(WidgetDimensions::scaled.frametext);
623 tr.top += header_height;
624
625 /* Draw the right menu */
626 /* Create the nice darker rectangle at the details top */
627 GfxFillRect(r.WithHeight(header_height).Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(COLOUR_LIGHT_BLUE, SHADE_NORMAL));
628 hr.top = DrawStringMultiLine(hr, header_msg, TC_FROMSTRING, SA_HOR_CENTER);
629 if (sel == nullptr) return;
630
631 hr.top = DrawStringMultiLine(hr, sel->info.server_name, TC_ORANGE, SA_HOR_CENTER); // game name
632 if (sel->status != NGLS_ONLINE) {
633 tr.top = DrawStringMultiLine(tr, header_msg, TC_FROMSTRING, SA_HOR_CENTER);
634 } else { // show game info
635 tr.top = DrawStringMultiLine(tr, GetString(STR_NETWORK_SERVER_LIST_CLIENTS, sel->info.clients_on, sel->info.clients_max, sel->info.companies_on, sel->info.companies_max));
636
637 tr.top = DrawStringMultiLine(tr, GetString(STR_NETWORK_SERVER_LIST_LANDSCAPE, STR_CLIMATE_TEMPERATE_LANDSCAPE + to_underlying(sel->info.landscape))); // landscape
638
639 tr.top = DrawStringMultiLine(tr, GetString(STR_NETWORK_SERVER_LIST_MAP_SIZE, sel->info.map_width, sel->info.map_height)); // map size
640
641 tr.top = DrawStringMultiLine(tr, GetString(STR_NETWORK_SERVER_LIST_SERVER_VERSION, sel->info.server_revision)); // server version
642
643 StringID invite_or_address = sel->connection_string.starts_with("+") ? STR_NETWORK_SERVER_LIST_INVITE_CODE : STR_NETWORK_SERVER_LIST_SERVER_ADDRESS;
644 tr.top = DrawStringMultiLine(tr, GetString(invite_or_address, sel->connection_string)); // server address / invite code
645
646 tr.top = DrawStringMultiLine(tr, GetString(STR_NETWORK_SERVER_LIST_START_DATE, sel->info.calendar_start)); // start date
647
648 tr.top = DrawStringMultiLine(tr, GetString(STR_NETWORK_SERVER_LIST_CURRENT_DATE, sel->info.calendar_date)); // current date
649
650 const auto play_time = sel->info.ticks_playing / Ticks::TICKS_PER_SECOND;
651 tr.top = DrawStringMultiLine(tr, GetString(STR_NETWORK_SERVER_LIST_PLAY_TIME, play_time / 60 / 60, (play_time / 60) % 60)); // play time
652
653 if (sel->info.gamescript_version != -1) {
654 tr.top = DrawStringMultiLine(tr, GetString(STR_NETWORK_SERVER_LIST_GAMESCRIPT, sel->info.gamescript_name, sel->info.gamescript_version)); // gamescript name and version
655 }
656
658
659 if (!sel->info.compatible) {
660 DrawStringMultiLine(tr, sel->info.version_compatible ? STR_NETWORK_SERVER_LIST_GRF_MISMATCH : STR_NETWORK_SERVER_LIST_VERSION_MISMATCH, TC_FROMSTRING, SA_HOR_CENTER); // server mismatch
661 } else if (sel->info.clients_on == sel->info.clients_max) {
662 /* Show: server full, when clients_on == max_clients */
663 DrawStringMultiLine(tr, STR_NETWORK_SERVER_LIST_SERVER_FULL, TC_FROMSTRING, SA_HOR_CENTER); // server full
664 } else if (sel->info.use_password) {
665 DrawStringMultiLine(tr, STR_NETWORK_SERVER_LIST_PASSWORD, TC_FROMSTRING, SA_HOR_CENTER); // password warning
666 }
667 }
668 }
669
670 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
671 {
672 switch (widget) {
673 case WID_NG_NAME: // Sort by name
674 case WID_NG_CLIENTS: // Sort by connected clients
675 case WID_NG_MAPSIZE: // Sort by map size
676 case WID_NG_DATE: // Sort by date
677 case WID_NG_YEARS: // Sort by years
678 case WID_NG_INFO: // Connectivity (green dot)
679 if (this->servers.SortType() == widget - WID_NG_NAME) {
680 this->servers.ToggleSortOrder();
681 if (this->list_pos != SLP_INVALID) this->list_pos = (ServerListPosition)this->servers.size() - this->list_pos - 1;
682 } else {
683 this->servers.SetSortType(widget - WID_NG_NAME);
684 this->servers.ForceResort();
685 this->SortNetworkGameList();
686 }
688 this->SetDirty();
689 break;
690
691 case WID_NG_MATRIX: { // Show available network games
692 auto it = this->vscroll->GetScrolledItemFromWidget(this->servers, pt.y, this, WID_NG_MATRIX);
693 this->server = (it != this->servers.end()) ? *it : nullptr;
694 this->list_pos = (server == nullptr) ? SLP_INVALID : it - this->servers.begin();
695 this->SetDirty();
696
697 if (click_count > 1 && !this->IsWidgetDisabled(WID_NG_JOIN)) this->OnClick(pt, WID_NG_JOIN, 1);
698 break;
699 }
700
701 case WID_NG_LASTJOINED: {
702 if (this->last_joined != nullptr) {
703 this->server = this->last_joined;
704
705 /* search the position of the newly selected server */
706 this->UpdateListPos();
708 this->SetDirty();
709
710 if (click_count > 1 && !this->IsWidgetDisabled(WID_NG_JOIN)) this->OnClick(pt, WID_NG_JOIN, 1);
711 }
712 break;
713 }
714
717 this->searched_internet = true;
718 break;
719
722 break;
723
724 case WID_NG_ADD: // Add a server
727 STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS,
728 NETWORK_HOSTNAME_PORT_LENGTH, // maximum number of characters including '\0'
730 break;
731
732 case WID_NG_START: // Start server
733 ShowNetworkStartServerWindow();
734 break;
735
736 case WID_NG_JOIN: // Join Game
737 if (this->server != nullptr) {
739 }
740 break;
741
742 case WID_NG_REFRESH: // Refresh
743 if (this->server != nullptr && !this->server->refreshing) NetworkQueryServer(this->server->connection_string);
744 break;
745
746 case WID_NG_NEWGRF: // NewGRF Settings
747 if (this->server != nullptr) ShowNewGRFSettings(false, false, false, this->server->info.grfconfig);
748 break;
749
750 case WID_NG_NEWGRF_MISSING: // Find missing content online
751 if (this->server != nullptr) ShowMissingContentWindow(this->server->info.grfconfig);
752 break;
753 }
754 }
755
761 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
762 {
763 this->servers.ForceRebuild();
764 this->SetDirty();
765 }
766
767 EventState OnKeyPress([[maybe_unused]] char32_t key, uint16_t keycode) override
768 {
770
771 /* handle up, down, pageup, pagedown, home and end */
772 if (this->vscroll->UpdateListPositionOnKeyPress(this->list_pos, keycode) == ES_HANDLED) {
773 if (this->list_pos == SLP_INVALID) return ES_HANDLED;
774
775 this->server = this->servers[this->list_pos];
776
777 /* Scroll to the new server if it is outside the current range. */
779
780 /* redraw window */
781 this->SetDirty();
782 return ES_HANDLED;
783 }
784
785 if (this->server != nullptr) {
786 if (keycode == WKC_DELETE) { // Press 'delete' to remove servers
787 NetworkGameListRemoveItem(this->server);
788 if (this->server == this->last_joined) this->last_joined = nullptr;
789 this->server = nullptr;
790 this->list_pos = SLP_INVALID;
791 }
792 }
793
794 return state;
795 }
796
797 void OnEditboxChanged(WidgetID wid) override
798 {
799 switch (wid) {
800 case WID_NG_FILTER: {
801 this->servers.ForceRebuild();
804 this->SetDirty();
805 break;
806 }
807
808 case WID_NG_CLIENT:
809 /* Validation of the name will happen once the user tries to join or start a game, as getting
810 * error messages while typing (e.g. when you clear the name) defeats the purpose of the check. */
811 _settings_client.network.client_name = this->name_editbox.text.GetText();
812 break;
813 }
814 }
815
816 void OnQueryTextFinished(std::optional<std::string> str) override
817 {
818 if (!str.has_value() || str->empty()) return;
819
820 _settings_client.network.connect_to_ip = std::move(*str);
822 NetworkRebuildHostList();
823 }
824
825 void OnResize() override
826 {
827 this->vscroll->SetCapacityFromWidget(this, WID_NG_MATRIX);
828 }
829
831 const IntervalTimer<TimerWindow> refresh_interval = {std::chrono::seconds(30), [this](uint) {
832 if (!this->searched_internet) return;
833
835 }};
836};
837
838Listing NetworkGameWindow::last_sorting = {false, 5};
839const std::initializer_list<GUIGameServerList::SortFunction * const> NetworkGameWindow::sorter_funcs = {
840 &NGameNameSorter,
841 &NGameClientSorter,
842 &NGameMapSizeSorter,
843 &NGameCalendarDateSorter,
844 &NGameTicksPlayingSorter,
845 &NGameAllowedSorter
846};
847
848const std::initializer_list<GUIGameServerList::FilterFunction * const> NetworkGameWindow::filter_funcs = {
849 &NGameSearchFilter
850};
851
852static std::unique_ptr<NWidgetBase> MakeResizableHeader()
853{
854 return std::make_unique<NWidgetServerListHeader>();
855}
856
857static constexpr NWidgetPart _nested_network_game_widgets[] = {
858 /* TOP */
860 NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
861 NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE), SetStringTip(STR_NETWORK_SERVER_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
862 NWidget(WWT_DEFSIZEBOX, COLOUR_LIGHT_BLUE),
863 EndContainer(),
864 NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NG_MAIN),
867 /* LEFT SIDE */
870 NWidget(WWT_TEXT, INVALID_COLOUR, WID_NG_FILTER_LABEL), SetStringTip(STR_LIST_FILTER_TITLE),
871 NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, WID_NG_FILTER), SetMinimalSize(251, 0), SetFill(1, 0), SetResize(1, 0),
872 SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
873 EndContainer(),
876 NWidgetFunction(MakeResizableHeader),
877 NWidget(WWT_MATRIX, COLOUR_LIGHT_BLUE, WID_NG_MATRIX), SetResize(1, 1), SetFill(1, 0),
878 SetMatrixDataTip(1, 0, STR_NETWORK_SERVER_LIST_CLICK_GAME_TO_SELECT), SetScrollbar(WID_NG_SCROLLBAR),
879 EndContainer(),
880 NWidget(NWID_VSCROLLBAR, COLOUR_LIGHT_BLUE, WID_NG_SCROLLBAR),
881 EndContainer(),
883 NWidget(WWT_TEXT, INVALID_COLOUR, WID_NG_LASTJOINED_LABEL), SetFill(1, 0),
884 SetStringTip(STR_NETWORK_SERVER_LIST_LAST_JOINED_SERVER), SetResize(1, 0),
886 NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NG_LASTJOINED), SetFill(1, 0), SetResize(1, 0),
887 SetToolTip(STR_NETWORK_SERVER_LIST_CLICK_TO_SELECT_LAST_TOOLTIP),
888 EndContainer(),
889 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NG_LASTJOINED_SPACER), SetFill(0, 0),
890 EndContainer(),
891 EndContainer(),
892 EndContainer(),
893 /* RIGHT SIDE */
896 NWidget(WWT_TEXT, INVALID_COLOUR, WID_NG_CLIENT_LABEL), SetStringTip(STR_NETWORK_SERVER_LIST_PLAYER_NAME),
897 NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, WID_NG_CLIENT), SetMinimalSize(151, 0), SetFill(1, 0), SetResize(1, 0),
898 SetStringTip(STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE, STR_NETWORK_SERVER_LIST_ENTER_NAME_TOOLTIP),
899 EndContainer(),
901 NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NG_DETAILS), SetMinimalSize(140, 0), SetMinimalTextLines(15, 0), SetResize(0, 1),
902 EndContainer(),
905 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_NEWGRF_MISSING), SetFill(1, 0), SetStringTip(STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON, STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_TOOLTIP),
906 EndContainer(),
907 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NG_NEWGRF_SEL),
908 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_NEWGRF), SetFill(1, 0), SetStringTip(STR_MAPGEN_NEWGRF_SETTINGS, STR_MAPGEN_NEWGRF_SETTINGS_TOOLTIP),
909 EndContainer(),
911 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_JOIN), SetFill(1, 0), SetStringTip(STR_NETWORK_SERVER_LIST_JOIN_GAME),
912 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_REFRESH), SetFill(1, 0), SetStringTip(STR_NETWORK_SERVER_LIST_REFRESH, STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP),
913 EndContainer(),
914 EndContainer(),
915 EndContainer(),
916 EndContainer(),
917 EndContainer(),
918 /* BOTTOM */
920 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_SEARCH_INTERNET), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET, STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP),
921 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_SEARCH_LAN), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN, STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP),
922 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_ADD), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_NETWORK_SERVER_LIST_ADD_SERVER, STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP),
923 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_START), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_NETWORK_SERVER_LIST_START_SERVER, STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP),
924 EndContainer(),
925 EndContainer(),
926 /* Resize button. */
928 NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
929 NWidget(WWT_RESIZEBOX, COLOUR_LIGHT_BLUE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
930 EndContainer(),
931 EndContainer(),
932};
933
934static WindowDesc _network_game_window_desc(
935 WDP_CENTER, "list_servers", 1000, 730,
937 {},
938 _nested_network_game_widgets
939);
940
941void ShowNetworkGameWindow()
942{
943 static bool first = true;
945
946 /* Only show once */
947 if (first) {
948 first = false;
949 /* Add all servers from the config file to our list. */
950 for (const auto &iter : _network_host_list) {
951 NetworkAddServer(iter);
952 }
953 }
954
955 new NetworkGameWindow(_network_game_window_desc);
956}
957
961
963 {
965
967 this->name_editbox.text.Assign(_settings_client.network.server_name);
968
970 }
971
972 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
973 {
974 switch (widget) {
976 return GetString(STR_NETWORK_SERVER_VISIBILITY_LOCAL + _settings_client.network.server_game_type);
977
979 return GetString(STR_NETWORK_START_SERVER_CLIENTS_SELECT, _settings_client.network.max_clients);
980
982 return GetString(STR_NETWORK_START_SERVER_COMPANIES_SELECT, _settings_client.network.max_companies);
983
984 default:
985 return this->Window::GetWidgetString(widget, stringid);
986 }
987 }
988
989 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
990 {
991 switch (widget) {
993 size = maxdim(maxdim(GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_LOCAL), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_PUBLIC)), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY));
994 size.width += padding.width;
995 size.height += padding.height;
996 break;
997 }
998 }
999
1000 void DrawWidget(const Rect &r, WidgetID widget) const override
1001 {
1002 switch (widget) {
1003 case WID_NSS_SETPWD:
1004 /* If password is set, draw red '*' next to 'Set password' button. */
1005 if (!_settings_client.network.server_password.empty()) DrawString(r.right + WidgetDimensions::scaled.framerect.left, this->width - WidgetDimensions::scaled.framerect.right, r.top, "*", TC_RED);
1006 }
1007 }
1008
1009 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1010 {
1011 switch (widget) {
1012 case WID_NSS_CANCEL: // Cancel button
1013 ShowNetworkGameWindow();
1014 break;
1015
1016 case WID_NSS_SETPWD: // Set password button
1017 this->widget_id = WID_NSS_SETPWD;
1018 ShowQueryString(_settings_client.network.server_password, STR_NETWORK_START_SERVER_SET_PASSWORD, NETWORK_PASSWORD_LENGTH, this, CS_ALPHANUMERAL, {});
1019 break;
1020
1021 case WID_NSS_CONNTYPE_BTN: // Connection type
1022 ShowDropDownList(this, BuildVisibilityDropDownList(), _settings_client.network.server_game_type, WID_NSS_CONNTYPE_BTN);
1023 break;
1024
1025 case WID_NSS_CLIENTS_BTND: case WID_NSS_CLIENTS_BTNU: // Click on up/down button for number of clients
1026 case WID_NSS_COMPANIES_BTND: case WID_NSS_COMPANIES_BTNU: // Click on up/down button for number of companies
1027 /* Don't allow too fast scrolling. */
1028 if (!this->flags.Test(WindowFlag::Timeout) || this->timeout_timer <= 1) {
1029 this->HandleButtonClick(widget);
1030 this->SetDirty();
1031 switch (widget) {
1032 default: NOT_REACHED();
1035 break;
1038 break;
1039 }
1040 }
1041 _left_button_clicked = false;
1042 break;
1043
1044 case WID_NSS_CLIENTS_TXT: // Click on number of clients
1046 ShowQueryString(GetString(STR_JUST_INT, _settings_client.network.max_clients), STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS, 4, this, CS_NUMERAL, {});
1047 break;
1048
1049 case WID_NSS_COMPANIES_TXT: // Click on number of companies
1051 ShowQueryString(GetString(STR_JUST_INT, _settings_client.network.max_companies), STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES, 3, this, CS_NUMERAL, {});
1052 break;
1053
1054 case WID_NSS_GENERATE_GAME: // Start game
1055 if (!CheckServerName()) return;
1056 _is_network_server = true;
1057 if (_ctrl_pressed) {
1059 } else {
1061 }
1062 break;
1063
1064 case WID_NSS_LOAD_GAME:
1065 if (!CheckServerName()) return;
1066 _is_network_server = true;
1068 break;
1069
1071 if (!CheckServerName()) return;
1072 _is_network_server = true;
1074 break;
1075
1077 if (!CheckServerName()) return;
1078 _is_network_server = true;
1080 break;
1081 }
1082 }
1083
1084 void OnDropdownSelect(WidgetID widget, int index, int) override
1085 {
1086 switch (widget) {
1089 break;
1090 default:
1091 NOT_REACHED();
1092 }
1093
1094 this->SetDirty();
1095 }
1096
1097 bool CheckServerName()
1098 {
1099 std::string str{this->name_editbox.text.GetText()};
1100 if (!NetworkValidateServerName(str)) return false;
1101
1102 SetSettingValue(GetSettingFromName("network.server_name")->AsStringSetting(), std::move(str));
1103 return true;
1104 }
1105
1110
1111 void OnQueryTextFinished(std::optional<std::string> str) override
1112 {
1113 if (!str.has_value()) return;
1114
1115 if (this->widget_id == WID_NSS_SETPWD) {
1116 _settings_client.network.server_password = std::move(*str);
1117 } else {
1118 auto value = ParseInteger<int32_t>(*str, 10, true);
1119 if (!value.has_value()) return;
1120 this->SetWidgetDirty(this->widget_id);
1121 switch (this->widget_id) {
1122 default: NOT_REACHED();
1124 case WID_NSS_COMPANIES_TXT: _settings_client.network.max_companies = Clamp(*value, 1, MAX_COMPANIES); break;
1125 }
1126 }
1127
1128 this->SetDirty();
1129 }
1130};
1131
1132static constexpr NWidgetPart _nested_network_start_server_window_widgets[] = {
1134 NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
1135 NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE), SetStringTip(STR_NETWORK_START_SERVER_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1136 EndContainer(),
1137 NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NSS_BACKGROUND),
1141 /* Game name widgets */
1142 NWidget(WWT_TEXT, INVALID_COLOUR, WID_NSS_GAMENAME_LABEL), SetFill(1, 0), SetStringTip(STR_NETWORK_START_SERVER_NEW_GAME_NAME),
1143 NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, WID_NSS_GAMENAME), SetFill(1, 0), SetStringTip(STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE, STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP),
1144 EndContainer(),
1145
1148 NWidget(WWT_TEXT, INVALID_COLOUR, WID_NSS_CONNTYPE_LABEL), SetFill(1, 0), SetStringTip(STR_NETWORK_START_SERVER_VISIBILITY_LABEL),
1149 NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_BTN), SetFill(1, 0), SetToolTip(STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP),
1150 EndContainer(),
1152 NWidget(NWID_SPACER), SetFill(1, 1),
1153 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NSS_SETPWD), SetFill(1, 0), SetStringTip(STR_NETWORK_START_SERVER_SET_PASSWORD, STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP),
1154 EndContainer(),
1155 EndContainer(),
1156
1159 NWidget(WWT_TEXT, INVALID_COLOUR, WID_NSS_CLIENTS_LABEL), SetFill(1, 0), SetStringTip(STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS),
1161 NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, WID_NSS_CLIENTS_BTND), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetFill(0, 1), SetSpriteTip(SPR_ARROW_DOWN, STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP),
1162 NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, WID_NSS_CLIENTS_TXT), SetFill(1, 0), SetToolTip(STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP),
1163 NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, WID_NSS_CLIENTS_BTNU), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetFill(0, 1), SetSpriteTip(SPR_ARROW_UP, STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP),
1164 EndContainer(),
1165 EndContainer(),
1166
1168 NWidget(WWT_TEXT, INVALID_COLOUR, WID_NSS_COMPANIES_LABEL), SetFill(1, 0), SetStringTip(STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES),
1170 NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, WID_NSS_COMPANIES_BTND), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetFill(0, 1), SetSpriteTip(SPR_ARROW_DOWN, STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP),
1171 NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, WID_NSS_COMPANIES_TXT), SetFill(1, 0), SetToolTip(STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP),
1172 NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, WID_NSS_COMPANIES_BTNU), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetFill(0, 1), SetSpriteTip(SPR_ARROW_UP, STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP),
1173 EndContainer(),
1174 EndContainer(),
1175 EndContainer(),
1176 EndContainer(),
1177
1179 /* 'generate game' and 'load game' buttons */
1181 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NSS_GENERATE_GAME), SetStringTip(STR_INTRO_NEW_GAME, STR_INTRO_TOOLTIP_NEW_GAME), SetFill(1, 0),
1182 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NSS_LOAD_GAME), SetStringTip(STR_INTRO_LOAD_GAME, STR_INTRO_TOOLTIP_LOAD_GAME), SetFill(1, 0),
1183 EndContainer(),
1184
1185 /* 'play scenario' and 'play heightmap' buttons */
1187 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NSS_PLAY_SCENARIO), SetStringTip(STR_INTRO_PLAY_SCENARIO, STR_INTRO_TOOLTIP_PLAY_SCENARIO), SetFill(1, 0),
1188 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NSS_PLAY_HEIGHTMAP), SetStringTip(STR_INTRO_PLAY_HEIGHTMAP, STR_INTRO_TOOLTIP_PLAY_HEIGHTMAP), SetFill(1, 0),
1189 EndContainer(),
1190 EndContainer(),
1191
1193 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NSS_CANCEL), SetStringTip(STR_BUTTON_CANCEL), SetMinimalSize(128, 12),
1194 EndContainer(),
1195 EndContainer(),
1196 EndContainer(),
1197};
1198
1199static WindowDesc _network_start_server_window_desc(
1200 WDP_CENTER, {}, 0, 0,
1202 {},
1203 _nested_network_start_server_window_widgets
1204);
1205
1206static void ShowNetworkStartServerWindow()
1207{
1208 if (!NetworkValidateOurClientName()) return;
1209
1211
1212 new NetworkStartServerWindow(_network_start_server_window_desc);
1213}
1214
1215/* The window below gives information about the connected clients
1216 * and also makes able to kick them (if server) and stuff like that. */
1217
1218extern void DrawCompanyIcon(CompanyID cid, int x, int y);
1219
1220static constexpr NWidgetPart _nested_client_list_widgets[] = {
1222 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1223 NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_NETWORK_CLIENT_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1224 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1225 NWidget(WWT_STICKYBOX, COLOUR_GREY),
1226 EndContainer(),
1227 NWidget(WWT_PANEL, COLOUR_GREY),
1229 NWidget(WWT_FRAME, COLOUR_GREY), SetStringTip(STR_NETWORK_CLIENT_LIST_SERVER), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
1231 NWidget(WWT_TEXT, INVALID_COLOUR), SetStringTip(STR_NETWORK_CLIENT_LIST_SERVER_NAME),
1232 NWidget(WWT_TEXT, INVALID_COLOUR, WID_CL_SERVER_NAME), SetFill(1, 0), SetResize(1, 0), SetToolTip(STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1233 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_SERVER_NAME_EDIT), SetAspect(WidgetDimensions::ASPECT_RENAME), SetSpriteTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP),
1234 EndContainer(),
1238 NWidget(WWT_TEXT, INVALID_COLOUR), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY),
1239 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CL_SERVER_VISIBILITY), SetToolTip(STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP),
1240 EndContainer(),
1242 NWidget(WWT_TEXT, INVALID_COLOUR), SetStringTip(STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE),
1243 NWidget(WWT_TEXT, INVALID_COLOUR, WID_CL_SERVER_INVITE_CODE), SetFill(1, 0), SetResize(1, 0), SetToolTip(STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1244 EndContainer(),
1246 NWidget(WWT_TEXT, INVALID_COLOUR), SetStringTip(STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE),
1247 NWidget(WWT_TEXT, INVALID_COLOUR, WID_CL_SERVER_CONNECTION_TYPE), SetFill(1, 0), SetResize(1, 0), SetToolTip(STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1248 EndContainer(),
1249 EndContainer(),
1250 EndContainer(),
1251 EndContainer(),
1252 NWidget(WWT_FRAME, COLOUR_GREY), SetStringTip(STR_NETWORK_CLIENT_LIST_PLAYER),
1254 NWidget(WWT_TEXT, INVALID_COLOUR), SetStringTip(STR_NETWORK_CLIENT_LIST_PLAYER_NAME),
1255 NWidget(WWT_TEXT, INVALID_COLOUR, WID_CL_CLIENT_NAME), SetFill(1, 0), SetResize(1, 0), SetToolTip(STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
1256 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_CLIENT_NAME_EDIT), SetAspect(WidgetDimensions::ASPECT_RENAME), SetSpriteTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP),
1257 EndContainer(),
1258 EndContainer(),
1259 EndContainer(),
1260 EndContainer(),
1264 EndContainer(),
1266 NWidget(WWT_PANEL, COLOUR_GREY),
1267 NWidget(WWT_TEXT, INVALID_COLOUR, WID_CL_CLIENT_COMPANY_COUNT), SetFill(1, 0), SetResize(1, 0), SetPadding(WidgetDimensions::unscaled.framerect), SetAlignment(SA_CENTER), SetToolTip(STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP),
1268 EndContainer(),
1269 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1270 EndContainer(),
1271};
1272
1273static WindowDesc _client_list_desc(
1274 WDP_AUTO, "list_clients", 220, 300,
1276 {},
1277 _nested_client_list_widgets
1278);
1279
1284enum DropDownAdmin : uint8_t {
1285 DD_CLIENT_ADMIN_KICK,
1286 DD_CLIENT_ADMIN_BAN,
1287 DD_COMPANY_ADMIN_RESET,
1288};
1289
1294static void AdminClientKickCallback(Window *, bool confirmed)
1295{
1296 if (confirmed) NetworkServerKickClient(_admin_client_id, {});
1297}
1298
1303static void AdminClientBanCallback(Window *, bool confirmed)
1304{
1305 if (confirmed) NetworkServerKickOrBanIP(_admin_client_id, true, {});
1306}
1307
1312static void AdminCompanyResetCallback(Window *, bool confirmed)
1313{
1314 if (confirmed) {
1317 }
1318}
1319
1328public:
1331 Colours colour;
1333 uint height;
1334 uint width;
1335
1336 ButtonCommon(SpriteID sprite, StringID tooltip, Colours colour, bool disabled = false) :
1337 sprite(sprite),
1339 colour(colour),
1341 {
1343 this->height = d.height + WidgetDimensions::scaled.framerect.Vertical();
1344 this->width = d.width + WidgetDimensions::scaled.framerect.Horizontal();
1345 }
1346 virtual ~ButtonCommon() = default;
1347
1351 virtual void OnClick(struct NetworkClientListWindow *w, Point pt) = 0;
1352};
1353
1357template <typename T>
1358class Button : public ButtonCommon {
1359private:
1360 typedef void (*ButtonCallback)(struct NetworkClientListWindow *w, Point pt, T id);
1361 T id;
1363
1364public:
1365 Button(SpriteID sprite, StringID tooltip, Colours colour, T id, ButtonCallback proc, bool disabled = false) :
1367 id(id),
1368 proc(proc)
1369 {
1370 assert(proc != nullptr);
1371 }
1372
1373 void OnClick(struct NetworkClientListWindow *w, Point pt) override
1374 {
1375 if (this->disabled) return;
1376
1377 this->proc(w, pt, this->id);
1378 }
1379};
1380
1383
1388public:
1389 std::vector<std::unique_ptr<ButtonCommon>> buttons{};
1390
1391 virtual ~ButtonLine() = default;
1392
1397 virtual void Draw(Rect r) const = 0;
1398
1399 template <typename T, typename...TArgs>
1400 T &AddButton(TArgs &&... args)
1401 {
1402 auto &button = this->buttons.emplace_back(std::make_unique<T>(std::forward<TArgs &&>(args)...));
1403 return static_cast<T &>(*button);
1404 }
1405
1412 ButtonCommon *GetButton(Rect r, const Point &pt) const
1413 {
1414 bool rtl = _current_text_dir == TD_RTL;
1415 for (auto &button : this->buttons) {
1416 if (r.WithWidth(button->width, !rtl).Contains(pt)) return button.get();
1417 r = r.Indent(button->width + WidgetDimensions::scaled.hsep_normal, !rtl);
1418 }
1419 return nullptr;
1420 }
1421
1428 virtual std::optional<EncodedString> GetTooltip(Rect r, const Point &pt) const
1429 {
1430 ButtonCommon *button = this->GetButton(r, pt);
1431 if (button == nullptr) return {};
1432 return GetEncodedString(button->tooltip);
1433 }
1434
1435protected:
1442 {
1443 bool rtl = _current_text_dir == TD_RTL;
1444 for (auto &button : buttons) {
1445 Rect br = r.CentreToHeight(button->height).WithWidth(button->width, !rtl);
1446 DrawFrameRect(br, button->colour, {});
1447 DrawSpriteIgnorePadding(button->sprite, PAL_NONE, br, SA_CENTER);
1448 if (button->disabled) {
1450 }
1451 r = r.Indent(button->width + WidgetDimensions::scaled.hsep_normal, !rtl);
1452 }
1453 return r;
1454 }
1455};
1456
1458public:
1459 CompanyButtonLine(CompanyID company_id) : company_id(company_id) {}
1460
1461 void Draw(Rect r) const override
1462 {
1463 bool rtl = _current_text_dir == TD_RTL;
1464 r = this->DrawButtons(r);
1465
1466 Dimension d = GetScaledSpriteSize(SPR_COMPANY_ICON);
1467 PaletteID pal = Company::IsValidID(this->company_id) ? GetCompanyPalette(this->company_id) : PALETTE_TO_GREY;
1468 DrawSpriteIgnorePadding(SPR_COMPANY_ICON, pal, r.WithWidth(d.width, rtl), SA_CENTER);
1469
1471 if (this->company_id == COMPANY_SPECTATOR) {
1472 DrawString(tr, STR_NETWORK_CLIENT_LIST_SPECTATORS, TC_SILVER);
1473 } else if (this->company_id == COMPANY_NEW_COMPANY) {
1474 DrawString(tr, STR_NETWORK_CLIENT_LIST_NEW_COMPANY, TC_WHITE);
1475 } else {
1476 DrawString(tr, GetString(STR_COMPANY_NAME, this->company_id, this->company_id), TC_SILVER);
1477 }
1478 };
1479
1480private:
1481 CompanyID company_id;
1482};
1483
1485public:
1486 ClientButtonLine(ClientPoolID client_pool_id) : client_pool_id(client_pool_id) {}
1487
1488 void Draw(Rect r) const override
1489 {
1490 const NetworkClientInfo *ci = NetworkClientInfo::GetIfValid(this->client_pool_id);
1491 if (ci == nullptr) return;
1492
1493 bool rtl = _current_text_dir == TD_RTL;
1494 r = this->DrawButtons(r);
1495
1497
1498 SpriteID player_icon = 0;
1499 if (ci->client_id == _network_own_client_id) {
1500 player_icon = SPR_PLAYER_SELF;
1501 } else if (ci->client_id == CLIENT_ID_SERVER) {
1502 player_icon = SPR_PLAYER_HOST;
1503 }
1504
1505 if (player_icon != 0) {
1506 Dimension d = GetScaledSpriteSize(player_icon);
1507 DrawSpriteIgnorePadding(player_icon, PALETTE_TO_GREY, r.WithWidth(d.width, rtl), SA_CENTER);
1508 tr = tr.Indent(d.width + WidgetDimensions::scaled.hsep_normal, rtl);
1509 }
1510
1511 DrawString(tr, GetString(STR_JUST_RAW_STRING, ci->client_name), TC_BLACK);
1512 }
1513
1514 std::optional<EncodedString> GetTooltip(Rect r, const Point &pt) const override
1515 {
1516 bool rtl = _current_text_dir == TD_RTL;
1517 Dimension d = GetScaledSpriteSize(SPR_PLAYER_SELF);
1518
1519 if (r.WithWidth(d.width, rtl).Contains(pt)) {
1520 const NetworkClientInfo *ci = NetworkClientInfo::GetIfValid(this->client_pool_id);
1521 if (ci != nullptr) {
1522 if (ci->client_id == _network_own_client_id) {
1523 return GetEncodedString(STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP);
1524 } else if (ci->client_id == CLIENT_ID_SERVER) {
1525 return GetEncodedString(STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP);
1526 }
1527 }
1528 }
1529
1530 return this->ButtonLine::GetTooltip(r, pt);
1531 }
1532
1533private:
1534 ClientPoolID client_pool_id;
1535};
1536
1541private:
1543
1545 CompanyID dd_company_id = CompanyID::Invalid();
1546
1547 Scrollbar *vscroll = nullptr;
1548 uint line_height = 0;
1549 int hover_index = -1;
1550
1551 std::vector<std::unique_ptr<ButtonLine>> buttons{};
1552
1559 static void OnClickCompanyChat([[maybe_unused]] NetworkClientListWindow *w, [[maybe_unused]] Point pt, CompanyID company_id)
1560 {
1561 ShowNetworkChatQueryWindow(DESTTYPE_TEAM, company_id.base());
1562 }
1563
1570 static void OnClickCompanyJoin([[maybe_unused]] NetworkClientListWindow *w, [[maybe_unused]] Point pt, CompanyID company_id)
1571 {
1572 if (_network_server) {
1575 } else {
1576 NetworkClientRequestMove(company_id);
1577 }
1578 }
1579
1585 static void OnClickCompanyNew([[maybe_unused]] NetworkClientListWindow *w, [[maybe_unused]] Point pt, CompanyID)
1586 {
1588 }
1589
1596 static void OnClickClientAdmin([[maybe_unused]] NetworkClientListWindow *w, [[maybe_unused]] Point pt, ClientID client_id)
1597 {
1598 DropDownList list;
1599 list.push_back(MakeDropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK, DD_CLIENT_ADMIN_KICK));
1600 list.push_back(MakeDropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN, DD_CLIENT_ADMIN_BAN));
1601
1602 Rect wi_rect;
1603 wi_rect.left = pt.x;
1604 wi_rect.right = pt.x;
1605 wi_rect.top = pt.y;
1606 wi_rect.bottom = pt.y;
1607
1608 w->dd_client_id = client_id;
1609 ShowDropDownListAt(w, std::move(list), -1, WID_CL_MATRIX, wi_rect, COLOUR_GREY, true);
1610 }
1611
1618 static void OnClickCompanyAdmin([[maybe_unused]] NetworkClientListWindow *w, [[maybe_unused]] Point pt, CompanyID company_id)
1619 {
1620 DropDownList list;
1621 list.push_back(MakeDropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET, DD_COMPANY_ADMIN_RESET, NetworkCompanyHasClients(company_id)));
1622
1623 Rect wi_rect;
1624 wi_rect.left = pt.x;
1625 wi_rect.right = pt.x;
1626 wi_rect.top = pt.y;
1627 wi_rect.bottom = pt.y;
1628
1629 w->dd_company_id = company_id;
1630 ShowDropDownListAt(w, std::move(list), -1, WID_CL_MATRIX, wi_rect, COLOUR_GREY, true);
1631 }
1638 static void OnClickClientChat([[maybe_unused]] NetworkClientListWindow *w, [[maybe_unused]] Point pt, ClientID client_id)
1639 {
1641 }
1642
1643 static void OnClickClientAuthorize([[maybe_unused]] NetworkClientListWindow *w, [[maybe_unused]] Point pt, ClientID client_id)
1644 {
1647 }
1648
1655 void RebuildListCompany(CompanyID company_id, CompanyID client_playas, bool can_join_company)
1656 {
1657 ButtonLine &company_line = *this->buttons.emplace_back(std::make_unique<CompanyButtonLine>(company_id));
1658 if (_network_server) company_line.AddButton<CompanyButton>(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP, COLOUR_RED, company_id, &NetworkClientListWindow::OnClickCompanyAdmin, company_id == COMPANY_SPECTATOR);
1659 ButtonCommon &chat_button = company_line.AddButton<CompanyButton>(SPR_CHAT, company_id == COMPANY_SPECTATOR ? STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP : STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyChat);
1660 if (can_join_company) company_line.AddButton<CompanyButton>(SPR_JOIN, STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyJoin, company_id != COMPANY_SPECTATOR && Company::Get(company_id)->is_ai);
1661
1662 bool has_players = false;
1663 for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) {
1664 if (ci->client_playas != company_id) continue;
1665 has_players = true;
1666
1667 ButtonLine &line = *this->buttons.emplace_back(std::make_unique<ClientButtonLine>(ci->index));
1668 if (_network_server) line.AddButton<ClientButton>(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP, COLOUR_RED, ci->client_id, &NetworkClientListWindow::OnClickClientAdmin, _network_own_client_id == ci->client_id);
1669 if (_network_own_client_id != ci->client_id) line.AddButton<ClientButton>(SPR_CHAT, STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP, COLOUR_ORANGE, ci->client_id, &NetworkClientListWindow::OnClickClientChat);
1670 if (_network_own_client_id != ci->client_id && client_playas != COMPANY_SPECTATOR && !ci->CanJoinCompany(client_playas)) line.AddButton<ClientButton>(SPR_JOIN, STR_NETWORK_CLIENT_LIST_COMPANY_AUTHORIZE_TOOLTIP, COLOUR_GREEN, ci->client_id, &NetworkClientListWindow::OnClickClientAuthorize);
1671 }
1672
1673 /* Disable the chat button when there are players in this company. */
1674 chat_button.disabled = !has_players;
1675 }
1676
1681 {
1683 CompanyID client_playas = own_ci == nullptr ? COMPANY_SPECTATOR : own_ci->client_playas;
1684
1685 this->buttons.clear();
1686
1687 /* As spectator, show a line to create a new company. */
1688 if (client_playas == COMPANY_SPECTATOR && !NetworkMaxCompaniesReached()) {
1689 ButtonLine &line = *this->buttons.emplace_back(std::make_unique<CompanyButtonLine>(COMPANY_NEW_COMPANY));
1690 line.AddButton<CompanyButton>(SPR_JOIN, STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP, COLOUR_ORANGE, COMPANY_SPECTATOR, &NetworkClientListWindow::OnClickCompanyNew);
1691 }
1692
1693 if (client_playas != COMPANY_SPECTATOR) {
1694 this->RebuildListCompany(client_playas, client_playas, false);
1695 }
1696
1697 /* Companies */
1698 for (const Company *c : Company::Iterate()) {
1699 if (c->index == client_playas) continue;
1700
1701 this->RebuildListCompany(c->index, client_playas, (own_ci != nullptr && c->allow_list.Contains(own_ci->public_key)) || _network_server);
1702 }
1703
1704 /* Spectators */
1705 this->RebuildListCompany(COMPANY_SPECTATOR, client_playas, client_playas != COMPANY_SPECTATOR);
1706
1707 this->vscroll->SetCount(this->buttons.size());
1708 }
1709
1710public:
1712 {
1713 this->CreateNestedTree();
1714 this->vscroll = this->GetScrollbar(WID_CL_SCROLLBAR);
1715 this->OnInvalidateData();
1716 this->FinishInitNested(window_number);
1717 }
1718
1719 void OnInit() override
1720 {
1721 this->RebuildList();
1722 }
1723
1724 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1725 {
1726 this->RebuildList();
1727
1728 /* Currently server information is not synced to clients, so we cannot show it on clients. */
1729 this->GetWidget<NWidgetStacked>(WID_CL_SERVER_SELECTOR)->SetDisplayedPlane(_network_server ? 0 : SZSP_HORIZONTAL);
1731 }
1732
1733 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1734 {
1735 switch (widget) {
1736 case WID_CL_SERVER_NAME:
1737 case WID_CL_CLIENT_NAME: {
1738 std::string str;
1739 if (widget == WID_CL_SERVER_NAME) {
1741 } else {
1743 str = GetString(STR_JUST_RAW_STRING, own_ci != nullptr ? own_ci->client_name : _settings_client.network.client_name);
1744 }
1745 size = GetStringBoundingBox(str);
1746 size.width = std::min(size.width, static_cast<uint>(ScaleGUITrad(200))); // By default, don't open the window too wide.
1747 break;
1748 }
1749
1751 size = maxdim(maxdim(GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_LOCAL), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_PUBLIC)), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY));
1752 size.width += padding.width;
1753 size.height += padding.height;
1754 break;
1755
1756 case WID_CL_MATRIX: {
1757 uint height = std::max({GetScaledSpriteSize(SPR_COMPANY_ICON).height, GetScaledSpriteSize(SPR_JOIN).height, GetScaledSpriteSize(SPR_ADMIN).height, GetScaledSpriteSize(SPR_CHAT).height});
1759 this->line_height = std::max(height, (uint)GetCharacterHeight(FS_NORMAL)) + padding.height;
1760
1761 resize.width = 1;
1762 fill.height = resize.height = this->line_height;
1763 size.height = std::max(size.height, 5 * this->line_height);
1764 break;
1765 }
1766 }
1767 }
1768
1769 void OnResize() override
1770 {
1771 this->vscroll->SetCapacityFromWidget(this, WID_CL_MATRIX);
1772 }
1773
1774 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1775 {
1776 switch (widget) {
1777 case WID_CL_SERVER_NAME:
1779
1781 return GetString(STR_NETWORK_SERVER_VISIBILITY_LOCAL + _settings_client.network.server_game_type);
1782
1785
1787 return GetString(STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN + _network_server_connection_type);
1788
1789 case WID_CL_CLIENT_NAME: {
1791 return own_ci != nullptr ? own_ci->client_name : _settings_client.network.client_name;
1792 }
1793
1795 return GetString(STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT, NetworkClientInfo::GetNumItems(), Company::GetNumItems(), NetworkMaxCompaniesAllowed());
1796
1797 default:
1798 return this->Window::GetWidgetString(widget, stringid);
1799 }
1800 }
1801
1802 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1803 {
1804 switch (widget) {
1806 if (!_network_server) break;
1807
1810 break;
1811
1815 ShowQueryString(own_ci != nullptr ? own_ci->client_name : _settings_client.network.client_name, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION, NETWORK_CLIENT_NAME_LENGTH, this, CS_ALPHANUMERAL, QueryStringFlag::LengthIsInChars);
1816 break;
1817 }
1819 if (!_network_server) break;
1820
1822 break;
1823
1824 case WID_CL_MATRIX: {
1825 auto it = this->vscroll->GetScrolledItemFromWidget(this->buttons, pt.y, this, WID_CL_MATRIX);
1826 if (it == std::end(this->buttons)) break;
1827
1828 Rect r = this->GetWidget<NWidgetBase>(WID_CL_MATRIX)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
1829 ButtonCommon *button = (*it)->GetButton(r, pt);
1830 if (button == nullptr) break;
1831
1832 button->OnClick(this, pt);
1833 break;
1834 }
1835 }
1836 }
1837
1838 bool OnTooltip([[maybe_unused]] Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
1839 {
1840 switch (widget) {
1841 case WID_CL_MATRIX: {
1842 auto it = this->vscroll->GetScrolledItemFromWidget(this->buttons, pt.y, this, WID_CL_MATRIX);
1843 if (it == std::end(this->buttons)) break;
1844
1845 Rect r = this->GetWidget<NWidgetBase>(WID_CL_MATRIX)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
1846 auto tooltip = (*it)->GetTooltip(r, pt);
1847 if (!tooltip.has_value()) break;
1848
1849 GuiShowTooltips(this, std::move(*tooltip), close_cond);
1850 return true;
1851 };
1852 }
1853
1854 return false;
1855 }
1856
1857 void OnDropdownClose(Point pt, WidgetID widget, int index, int click_result, bool instant_close) override
1858 {
1859 /* If you close the dropdown outside the list, don't take any action. */
1860 if (widget == WID_CL_MATRIX) return;
1861
1862 Window::OnDropdownClose(pt, widget, index, click_result, instant_close);
1863 }
1864
1865 void OnDropdownSelect(WidgetID widget, int index, int) override
1866 {
1867 switch (widget) {
1869 if (!_network_server) break;
1870
1873 break;
1874
1875 case WID_CL_MATRIX: {
1876 QueryCallbackProc *callback = nullptr;
1877
1878 EncodedString text;
1879 switch (index) {
1880 case DD_CLIENT_ADMIN_KICK:
1882 callback = AdminClientKickCallback;
1883 text = GetEncodedString(STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK, NetworkClientInfo::GetByClientID(_admin_client_id)->client_name);
1884 break;
1885
1886 case DD_CLIENT_ADMIN_BAN:
1888 callback = AdminClientBanCallback;
1889 text = GetEncodedString(STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN, NetworkClientInfo::GetByClientID(_admin_client_id)->client_name);
1890 break;
1891
1892 case DD_COMPANY_ADMIN_RESET:
1894 callback = AdminCompanyResetCallback;
1895 text = GetEncodedString(STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET, _admin_company_id);
1896 break;
1897
1898 default:
1899 NOT_REACHED();
1900 }
1901
1902 assert(callback != nullptr);
1903
1904 /* Always ask confirmation for all admin actions. */
1905 ShowQuery(
1906 GetEncodedString(STR_NETWORK_CLIENT_LIST_ASK_CAPTION),
1907 std::move(text),
1908 this, callback);
1909
1910 break;
1911 }
1912
1913 default:
1914 NOT_REACHED();
1915 }
1916
1917 this->SetDirty();
1918 }
1919
1920 void OnQueryTextFinished(std::optional<std::string> str) override
1921 {
1922 if (!str.has_value()) return;
1923
1924 switch (this->query_widget) {
1925 default: NOT_REACHED();
1926
1928 if (!_network_server) break;
1929
1930 SetSettingValue(GetSettingFromName("network.server_name")->AsStringSetting(), *str);
1931 this->InvalidateData();
1932 break;
1933 }
1934
1936 SetSettingValue(GetSettingFromName("network.client_name")->AsStringSetting(), *str);
1937 this->InvalidateData();
1938 break;
1939 }
1940 }
1941 }
1942
1943 void DrawWidget(const Rect &r, WidgetID widget) const override
1944 {
1945 switch (widget) {
1946 case WID_CL_MATRIX: {
1947 Rect ir = r.WithHeight(this->line_height).Shrink(WidgetDimensions::scaled.framerect, RectPadding::zero);
1948
1949 if (this->hover_index >= 0) {
1950 Rect br = r.WithHeight(this->line_height).Translate(0, this->hover_index * this->line_height);
1952 }
1953
1954 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->buttons);
1955 for (auto it = first; it != last; ++it) {
1956 (*it)->Draw(ir);
1957 ir = ir.Translate(0, this->line_height);
1958 }
1959
1960 break;
1961 }
1962 }
1963 }
1964
1965 void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override
1966 {
1967 if (widget != WID_CL_MATRIX) {
1968 if (this->hover_index != -1) {
1969 this->hover_index = -1;
1971 }
1972 } else {
1973 int index = this->GetRowFromWidget(pt.y, widget, 0, -1);
1974 if (index != this->hover_index) {
1975 this->hover_index = index;
1977 }
1978 }
1979 }
1980};
1981
1982void ShowClientList()
1983{
1984 AllocateWindowDescFront<NetworkClientListWindow>(_client_list_desc, 0);
1985}
1986
1991
1993 std::shared_ptr<NetworkAuthenticationPasswordRequest> request{};
1994
1996 {
1999 }
2000
2001 void DrawWidget(const Rect &r, WidgetID widget) const override
2002 {
2003 switch (widget) {
2004 case WID_NJS_PROGRESS_BAR: {
2005 /* Draw the % complete with a bar and a text */
2007 Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
2008 uint8_t progress; // used for progress bar
2009 switch (_network_join_status) {
2010 case NETWORK_JOIN_STATUS_CONNECTING:
2011 case NETWORK_JOIN_STATUS_AUTHORIZING:
2012 case NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO:
2013 progress = 10; // first two stages 10%
2014 break;
2015 case NETWORK_JOIN_STATUS_WAITING:
2016 progress = 15; // third stage is 15%
2017 break;
2018 case NETWORK_JOIN_STATUS_DOWNLOADING:
2019 if (_network_join_bytes_total == 0) {
2020 progress = 15; // We don't have the final size yet; the server is still compressing!
2021 break;
2022 }
2023 [[fallthrough]];
2024
2025 default: // Waiting is 15%, so the remaining downloading of the map is maximum 70%
2026 progress = 15 + _network_join_bytes * (100 - 15) / _network_join_bytes_total;
2027 break;
2028 }
2029 DrawFrameRect(ir.WithWidth(ir.Width() * progress / 100, _current_text_dir == TD_RTL), COLOUR_MAUVE, {});
2030 DrawString(ir.left, ir.right, CentreBounds(ir.top, ir.bottom, GetCharacterHeight(FS_NORMAL)), STR_NETWORK_CONNECTING_1 + _network_join_status, TC_FROMSTRING, SA_HOR_CENTER);
2031 break;
2032 }
2033
2035 switch (_network_join_status) {
2036 case NETWORK_JOIN_STATUS_WAITING:
2037 DrawStringMultiLine(r, GetString(STR_NETWORK_CONNECTING_WAITING, _network_join_waiting), TC_FROMSTRING, SA_CENTER);
2038 break;
2039
2040 case NETWORK_JOIN_STATUS_DOWNLOADING:
2041 if (_network_join_bytes_total == 0) {
2042 DrawStringMultiLine(r, GetString(STR_NETWORK_CONNECTING_DOWNLOADING_1, _network_join_bytes), TC_FROMSTRING, SA_CENTER);
2043 } else {
2044 DrawStringMultiLine(r, GetString(STR_NETWORK_CONNECTING_DOWNLOADING_2, _network_join_bytes, _network_join_bytes_total), TC_FROMSTRING, SA_CENTER);
2045 }
2046 break;
2047
2048 default:
2049 break;
2050 }
2051 break;
2052 }
2053 }
2054
2055 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2056 {
2057 switch (widget) {
2059 /* Account for the statuses */
2060 for (uint i = 0; i < NETWORK_JOIN_STATUS_END; i++) {
2061 size = maxdim(size, GetStringBoundingBox(STR_NETWORK_CONNECTING_1 + i));
2062 }
2063 /* For the number of waiting (other) players */
2064 size = maxdim(size, GetStringBoundingBox(GetString(STR_NETWORK_CONNECTING_WAITING, GetParamMaxValue(MAX_CLIENTS))));
2065 /* We need some spacing for the 'border' */
2068 break;
2069
2070 case WID_NJS_PROGRESS_TEXT: {
2071 /* Account for downloading ~ 10 MiB */
2072 uint64_t max_digits = GetParamMaxDigits(8);
2073 size = maxdim(size, GetStringBoundingBox(GetString(STR_NETWORK_CONNECTING_DOWNLOADING_1, max_digits, max_digits)));
2074 size = maxdim(size, GetStringBoundingBox(GetString(STR_NETWORK_CONNECTING_DOWNLOADING_1, max_digits, max_digits)));
2075 break;
2076 }
2077 }
2078 }
2079
2080 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2081 {
2082 if (widget == WID_NJS_CANCELOK) { // Disconnect button
2084 SwitchToMode(SM_MENU);
2085 ShowNetworkGameWindow();
2086 }
2087 }
2088
2089 void OnQueryTextFinished(std::optional<std::string> str) override
2090 {
2091 if (!str.has_value() || str->empty() || this->request == nullptr) {
2093 return;
2094 }
2095
2096 this->request->Reply(*str);
2097 }
2098};
2099
2100static constexpr NWidgetPart _nested_network_join_status_window_widgets[] = {
2101 NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_NETWORK_CONNECTING_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2102 NWidget(WWT_PANEL, COLOUR_GREY),
2104 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NJS_PROGRESS_BAR), SetFill(1, 0),
2105 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NJS_PROGRESS_TEXT), SetFill(1, 0), SetMinimalSize(350, 0),
2106 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NJS_CANCELOK), SetMinimalSize(101, 12), SetStringTip(STR_NETWORK_CONNECTION_DISCONNECT), SetFill(1, 0),
2107 EndContainer(),
2108 EndContainer(),
2109};
2110
2111static WindowDesc _network_join_status_window_desc(
2112 WDP_CENTER, {}, 0, 0,
2115 _nested_network_join_status_window_widgets
2116);
2117
2118void ShowJoinStatusWindow()
2119{
2121 new NetworkJoinStatusWindow(_network_join_status_window_desc);
2122}
2123
2124void ShowNetworkNeedPassword(std::shared_ptr<NetworkAuthenticationPasswordRequest> request)
2125{
2127 if (w == nullptr) return;
2128 w->request = std::move(request);
2129
2130 ShowQueryString({}, STR_NETWORK_NEED_GAME_PASSWORD_CAPTION, NETWORK_PASSWORD_LENGTH, w, CS_ALPHANUMERAL, {});
2131}
2132
2139 std::string token{};
2140
2141 NetworkAskRelayWindow(WindowDesc &desc, Window *parent, std::string_view server_connection_string, std::string &&relay_connection_string, std::string &&token) :
2142 Window(desc),
2145 token(std::move(token))
2146 {
2147 this->parent = parent;
2148 this->InitNested(0);
2149 }
2150
2151 void Close(int data = 0) override
2152 {
2154 this->Window::Close();
2155 }
2156
2157 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2158 {
2159 if (widget == WID_NAR_TEXT) {
2160 size = GetStringBoundingBox(GetString(STR_NETWORK_ASK_RELAY_TEXT, this->server_connection_string, this->relay_connection_string));
2161 }
2162 }
2163
2164 void DrawWidget(const Rect &r, WidgetID widget) const override
2165 {
2166 if (widget == WID_NAR_TEXT) {
2167 DrawStringMultiLine(r, GetString(STR_NETWORK_ASK_RELAY_TEXT, this->server_connection_string, this->relay_connection_string), TC_FROMSTRING, SA_CENTER);
2168 }
2169 }
2170
2171 void FindWindowPlacementAndResize(int, int, bool) override
2172 {
2173 /* Position query window over the calling window, ensuring it's within screen bounds. */
2174 this->left = Clamp(parent->left + (parent->width / 2) - (this->width / 2), 0, _screen.width - this->width);
2175 this->top = Clamp(parent->top + (parent->height / 2) - (this->height / 2), 0, _screen.height - this->height);
2176 this->SetDirty();
2177 }
2178
2179 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2180 {
2181 switch (widget) {
2182 case WID_NAR_NO:
2184 this->Close(NRWCD_HANDLED);
2185 break;
2186
2187 case WID_NAR_YES_ONCE:
2188 _network_coordinator_client.StartTurnConnection(this->token);
2189 this->Close(NRWCD_HANDLED);
2190 break;
2191
2192 case WID_NAR_YES_ALWAYS:
2194 _network_coordinator_client.StartTurnConnection(this->token);
2195 this->Close(NRWCD_HANDLED);
2196 break;
2197 }
2198 }
2199};
2200
2201static constexpr NWidgetPart _nested_network_ask_relay_widgets[] = {
2203 NWidget(WWT_CLOSEBOX, COLOUR_RED),
2204 NWidget(WWT_CAPTION, COLOUR_RED, WID_NAR_CAPTION), SetStringTip(STR_NETWORK_ASK_RELAY_CAPTION),
2205 EndContainer(),
2206 NWidget(WWT_PANEL, COLOUR_RED),
2210 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NAR_NO), SetMinimalSize(71, 12), SetFill(1, 1), SetStringTip(STR_NETWORK_ASK_RELAY_NO),
2211 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NAR_YES_ONCE), SetMinimalSize(71, 12), SetFill(1, 1), SetStringTip(STR_NETWORK_ASK_RELAY_YES_ONCE),
2212 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NAR_YES_ALWAYS), SetMinimalSize(71, 12), SetFill(1, 1), SetStringTip(STR_NETWORK_ASK_RELAY_YES_ALWAYS),
2213 EndContainer(),
2214 EndContainer(),
2215 EndContainer(),
2216};
2217
2218static WindowDesc _network_ask_relay_desc(
2219 WDP_CENTER, {}, 0, 0,
2222 _nested_network_ask_relay_widgets
2223);
2224
2231void ShowNetworkAskRelay(std::string_view server_connection_string, std::string &&relay_connection_string, std::string &&token)
2232{
2234
2235 Window *parent = GetMainWindow();
2236 new NetworkAskRelayWindow(_network_ask_relay_desc, parent, server_connection_string, std::move(relay_connection_string), std::move(token));
2237}
2238
2244 Window(desc)
2245 {
2246 this->parent = parent;
2247 this->InitNested(0);
2248 }
2249
2250 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2251 {
2252 if (widget == WID_NAS_TEXT) {
2253 size = GetStringBoundingBox(STR_NETWORK_ASK_SURVEY_TEXT);
2254 }
2255 }
2256
2257 void DrawWidget(const Rect &r, WidgetID widget) const override
2258 {
2259 if (widget == WID_NAS_TEXT) {
2260 DrawStringMultiLine(r, STR_NETWORK_ASK_SURVEY_TEXT, TC_BLACK, SA_CENTER);
2261 }
2262 }
2263
2264 void FindWindowPlacementAndResize(int, int, bool) override
2265 {
2266 /* Position query window over the calling window, ensuring it's within screen bounds. */
2267 this->left = Clamp(parent->left + (parent->width / 2) - (this->width / 2), 0, _screen.width - this->width);
2268 this->top = Clamp(parent->top + (parent->height / 2) - (this->height / 2), 0, _screen.height - this->height);
2269 this->SetDirty();
2270 }
2271
2272 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2273 {
2274 switch (widget) {
2275 case WID_NAS_PREVIEW:
2276 ShowSurveyResultTextfileWindow(this);
2277 break;
2278
2279 case WID_NAS_LINK:
2280 OpenBrowser(NETWORK_SURVEY_DETAILS_LINK);
2281 break;
2282
2283 case WID_NAS_NO:
2285 this->Close();
2286 break;
2287
2288 case WID_NAS_YES:
2290 this->Close();
2291 break;
2292 }
2293 }
2294};
2295
2296static constexpr NWidgetPart _nested_network_ask_survey_widgets[] = {
2298 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2299 NWidget(WWT_CAPTION, COLOUR_GREY, WID_NAS_CAPTION), SetStringTip(STR_NETWORK_ASK_SURVEY_CAPTION),
2300 EndContainer(),
2301 NWidget(WWT_PANEL, COLOUR_GREY),
2305 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NAS_PREVIEW), SetMinimalSize(71, 12), SetFill(1, 1), SetStringTip(STR_NETWORK_ASK_SURVEY_PREVIEW),
2306 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NAS_LINK), SetMinimalSize(71, 12), SetFill(1, 1), SetStringTip(STR_NETWORK_ASK_SURVEY_LINK),
2307 EndContainer(),
2309 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NAS_NO), SetMinimalSize(71, 12), SetFill(1, 1), SetStringTip(STR_NETWORK_ASK_SURVEY_NO),
2310 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NAS_YES), SetMinimalSize(71, 12), SetFill(1, 1), SetStringTip(STR_NETWORK_ASK_SURVEY_YES),
2311 EndContainer(),
2312 EndContainer(),
2313 EndContainer(),
2314};
2315
2316static WindowDesc _network_ask_survey_desc(
2317 WDP_CENTER, {}, 0, 0,
2320 _nested_network_ask_survey_widgets
2321);
2322
2327{
2328 /* If we can't send a survey, don't ask the question. */
2329 if constexpr (!NetworkSurveyHandler::IsSurveyPossible()) return;
2330
2332
2333 Window *parent = GetMainWindow();
2334 new NetworkAskSurveyWindow(_network_ask_survey_desc, parent);
2335}
2336
2340
2342 {
2343 this->ConstructWindow();
2344
2345 auto result = _survey.CreatePayload(NetworkSurveyHandler::Reason::PREVIEW, true);
2346 this->LoadText(result);
2347 this->InvalidateData();
2348 }
2349};
2350
2351void ShowSurveyResultTextfileWindow(Window *parent)
2352{
2355}
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Button shown for either a company or client in the client-list.
Colours colour
The colour of the button.
bool disabled
Is the button disabled?
uint width
Calculated width of the button.
virtual void OnClick(struct NetworkClientListWindow *w, Point pt)=0
OnClick handler for when the button is pressed.
SpriteID sprite
The sprite to use on the button.
uint height
Calculated height of the button.
StringID tooltip
The tooltip of the button.
Base interface for a network client list line.
virtual std::optional< EncodedString > GetTooltip(Rect r, const Point &pt) const
Get tooptip for a given point on the line.
Rect DrawButtons(Rect r) const
Draw the buttons for this line.
std::vector< std::unique_ptr< ButtonCommon > > buttons
Buttons for this line.
ButtonCommon * GetButton(Rect r, const Point &pt) const
Get the button at a given point on the line.
virtual void Draw(Rect r) const =0
Draw the button line.
Template version of Button, with callback support.
T id
ID this button belongs to.
void OnClick(struct NetworkClientListWindow *w, Point pt) override
OnClick handler for when the button is pressed.
ButtonCallback proc
Callback proc to call when button is pressed.
void(* ButtonCallback)(struct NetworkClientListWindow *w, Point pt, T id)
Callback function to call on click.
void Draw(Rect r) const override
Draw the button line.
std::optional< EncodedString > GetTooltip(Rect r, const Point &pt) const override
Get tooptip for a given point on the line.
void ConnectFailure(std::string_view token, uint8_t tracking_number)
Callback from a Connecter to let the Game Coordinator know the connection failed.
void GetListing()
Request a listing of all public servers.
void Draw(Rect r) const override
Draw the button line.
Container for an encoded string, created by GetEncodedString.
List template of 'things' T to sort in a GUI.
bool Filter(FilterFunction *decide, F filter_data)
Filter the list.
void RebuildDone()
Notify the sortlist that the rebuild is done.
void SetListing(Listing l)
Import sort conditions.
void SetFilterState(bool state)
Enable or disable the filter.
bool IsDescSortOrder() const
Check if the sort order is descending.
void ToggleSortOrder()
Toggle the sort order Since that is the worst condition for the sort function reverse the list here.
void SetFilterFuncs(std::span< FilterFunction *const > n_funcs)
Hand the filter function pointers to the GUIList.
bool NeedRebuild() const
Check if a rebuild is needed.
void ForceRebuild()
Force that a rebuild is needed.
bool Sort(Comp compare)
Sort the list.
void ForceResort()
Force a resort next Sort call Reset the resort timer if used too.
uint8_t SortType() const
Get the sorttype of the list.
Listing GetListing() const
Export current sort conditions.
void SetSortFuncs(std::span< SortFunction *const > n_funcs)
Hand the sort function pointers to the GUIList.
bool NeedResort()
Check if a resort is needed next loop If used the resort timer will decrease every call till 0.
void SetSortType(uint8_t n_type)
Set the sorttype of the list.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
Baseclass for nested widgets.
uint resize_x
Horizontal resize step (0 means not resizable).
uint fill_x
Horizontal fill stepsize (from initial size, 0 means not resizable).
uint smallest_x
Smallest horizontal size of the widget in a filled window.
uint current_x
Current horizontal size (after resizing).
int pos_y
Vertical position of top-left corner of the widget in the window.
int pos_x
Horizontal position of top-left corner of the widget in the window.
uint smallest_y
Smallest vertical size of the widget in a filled window.
uint fill_y
Vertical fill stepsize (from initial size, 0 means not resizable).
uint resize_y
Vertical resize step (0 means not resizable).
uint current_y
Current vertical size (after resizing).
Baseclass for container widgets.
void Add(std::unique_ptr< NWidgetBase > &&wid)
Append widget wid to container.
Definition widget.cpp:1292
std::vector< std::unique_ptr< NWidgetBase > > children
Child widgets in container.
Full blown container to make it behave exactly as we want :)
static const uint MINIMUM_NAME_WIDTH_BEFORE_NEW_HEADER
Minimum width before adding a new header.
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
void OnPaint() override
The window must be repainted.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
QueryString filter_editbox
Editbox for filter on servers.
Scrollbar * vscroll
Vertical scrollbar of the list of servers.
void BuildGUINetworkGameList()
Dimension of compatibility icon.
static bool NGameNameSorter(NetworkGame *const &a, NetworkGame *const &b)
Sort servers by name.
static bool NGameTicksPlayingSorter(NetworkGame *const &a, NetworkGame *const &b)
Sort servers by the number of ticks the game is running.
void UpdateListPos()
Set this->list_pos to match this->server.
void OnInit() override
Notification that the nested widget tree gets initialized.
const IntervalTimer< TimerWindow > refresh_interval
Refresh the online servers on a regular interval.
static bool NGameAllowedSorter(NetworkGame *const &a, NetworkGame *const &b)
Sort servers by joinability.
NetworkGame * server
Selected server.
NetworkGame * last_joined
The last joined server.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void DrawServerLine(const NetworkGame *cur_item, int y, bool highlight) const
Draw a single server line.
void OnResize() override
Called after the window got resized.
static bool NGameCalendarDateSorter(NetworkGame *const &a, NetworkGame *const &b)
Sort servers by calendar date.
void ScrollToSelectedServer()
Scroll the list up or down to the currently selected server.
void OnEditboxChanged(WidgetID wid) override
The text in an editbox has been edited.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
ServerListPosition list_pos
Position of the selected server.
static bool NGameClientSorter(NetworkGame *const &a, NetworkGame *const &b)
Sort servers by the amount of clients online on a server.
void SortNetworkGameList()
Sort the server list.
Dimension blot
Dimension of lock icon.
static bool NGameMapSizeSorter(NetworkGame *const &a, NetworkGame *const &b)
Sort servers by map size.
GUIGameServerList servers
List with game servers.
bool searched_internet
Did we ever press "Search Internet" button?
QueryString name_editbox
Client name editbox.
EventState OnKeyPress(char32_t key, uint16_t keycode) override
A key has been pressed.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
@ PREVIEW
User is previewing the survey result.
Scrollbar data structure.
void SetCount(size_t num)
Sets the number of elements in the list.
auto GetScrolledItemFromWidget(Tcontainer &container, int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Return an iterator pointing to the element of a scrolled widget that a user clicked in.
void SetCapacityFromWidget(Window *w, WidgetID widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget.
Definition widget.cpp:2499
EventState UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
Update the given list position as if it were on this scroll bar when the given keycode was pressed.
Definition widget.cpp:2446
void ScrollTowards(size_type position)
Scroll towards the given position; if the item is visible nothing happens, otherwise it will be shown...
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
static constexpr TimerGameTick::Ticks TICKS_PER_SECOND
Estimation of how many ticks fit in a single second.
static YearMonthDay ConvertDateToYMD(Date date)
Converts a Date to a Year, Month & Day.
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:40
RectPadding frametext
Padding inside frame with text.
Definition window_gui.h:41
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
int vsep_wide
Wide vertical spacing.
Definition window_gui.h:60
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
RectPadding matrix
Padding of WWT_MATRIX items.
Definition window_gui.h:42
int hsep_normal
Normal horizontal spacing.
Definition window_gui.h:61
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:38
PaletteID GetCompanyPalette(CompanyID company)
Get the palette for recolouring with a company colour.
CompanyID _current_company
Company currently doing an action.
@ CCA_DELETE
Delete a company.
@ CCA_NEW
Create a new company.
static constexpr CompanyID COMPANY_SPECTATOR
The client is spectating.
@ CALCA_ADD
Create a public key.
static constexpr CompanyID COMPANY_NEW_COMPANY
The client wants a new company.
@ CRR_NONE
Dummy reason for actions that don't need one.
@ CRR_MANUAL
The company is manually removed.
static const uint NETWORK_CLIENT_NAME_LENGTH
The maximum length of a client's name, in bytes including '\0'.
Definition config.h:58
static const uint NETWORK_HOSTNAME_PORT_LENGTH
The maximum length of the host name + port, in bytes including '\0'. The extra six is ":" + port numb...
Definition config.h:55
static const uint NETWORK_NAME_LENGTH
The maximum length of the server name and map name, in bytes including '\0'.
Definition config.h:53
static const std::string NETWORK_SURVEY_DETAILS_LINK
Link with more details & privacy statement of the survey.
Definition config.h:30
static const uint NETWORK_PASSWORD_LENGTH
The maximum length of the password, in bytes including '\0'.
Definition config.h:57
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:415
void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, WidgetID button, Rect wi_rect, Colours wi_colour, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:397
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23)
Definition enum_type.hpp:17
@ SLO_LOAD
File is being loaded.
Definition fileio_type.h:54
@ FT_SCENARIO
old or new scenario
Definition fileio_type.h:20
@ FT_HEIGHTMAP
heightmap file
Definition fileio_type.h:21
@ FT_SAVEGAME
old or new savegame
Definition fileio_type.h:19
void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop)
Launch save/load dialog in the given mode.
Definition fios_gui.cpp:999
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:87
static const uint32_t GENERATE_NEW_SEED
Create a new random seed.
Definition genworld.h:25
void StartNewGameWithoutGUI(uint32_t seed)
Start a normal game without the GUI.
void ShowGenerateLandscape()
Start with a normal game.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Definition gfx.cpp:713
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:895
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:666
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
bool _left_button_clicked
Is left mouse button clicked?
Definition gfx.cpp:43
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:783
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:116
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:390
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:389
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:398
@ SA_VERT_CENTER
Vertically center the text.
Definition gfx_type.h:394
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:346
constexpr NWidgetPart SetMatrixDataTip(uint32_t cols, uint32_t rows, StringID tip={})
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
constexpr NWidgetPart NWidgetFunction(NWidgetFunctionType *func_ptr)
Obtain a nested widget (sub)tree from an external source.
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetSpriteTip(SpriteID sprite, StringID tip={})
Widget part function for setting the sprite and tooltip.
constexpr NWidgetPart SetPIP(uint8_t pre, uint8_t inter, uint8_t post)
Widget part function for setting a pre/inter/post spaces.
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
constexpr NWidgetPart SetStringTip(StringID string, StringID tip={})
Widget part function for setting the string and tooltip.
constexpr NWidgetPart SetAspect(float ratio, AspectFlags flags=AspectFlag::ResizeX)
Widget part function for setting the aspect ratio.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart SetResizeWidgetTypeTip(ResizeWidgetValues widget_type, StringID tip)
Widget part function for setting the resize widget type and tooltip.
constexpr NWidgetPart SetToolTip(StringID tip)
Widget part function for setting tooltip and clearing the widget data.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FS_NORMAL)
Widget part function for setting the minimal text lines.
constexpr NWidgetPart SetAlignment(StringAlignment align)
Widget part function for setting the alignment of text/images.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
constexpr NWidgetPart SetPIPRatio(uint8_t ratio_pre, uint8_t ratio_inter, uint8_t ratio_post)
Widget part function for setting a pre/inter/post ratio.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:966
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1547
static const uint MAX_MAP_SIZE
Maximal map size = 4096.
Definition map_type.h:40
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
void GuiShowTooltips(Window *parent, EncodedString &&text, TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition misc_gui.cpp:688
void ShowQuery(EncodedString &&caption, EncodedString &&message, Window *parent, QueryCallbackProc *callback, bool focus)
Show a confirmation window with standard 'yes' and 'no' buttons The window is aligned to the centre o...
void ShowQueryString(std::string_view str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
bool _is_network_server
Does this client wants to be a network-server?
Definition network.cpp:71
StringList _network_host_list
The servers we know.
Definition network.cpp:76
void NetworkQueryServer(std::string_view connection_string)
Query a server to fetch the game-info.
Definition network.cpp:671
void NetworkDisconnect(bool close_admins)
We want to disconnect from the host/clients.
Definition network.cpp:1000
bool _network_server
network-server is active
Definition network.cpp:68
NetworkGame * NetworkAddServer(std::string_view connection_string, bool manually, bool never_expire)
Validates an address entered as a string and adds the server to the list.
Definition network.cpp:693
bool NetworkClientConnectGame(std::string_view connection_string, CompanyID default_company, const std::string &join_server_password)
Join a client to the server at with the given connection string.
Definition network.cpp:786
ClientID _network_own_client_id
Our client identifier.
Definition network.cpp:72
void NetworkUpdateServerGameType()
The setting server_game_type was updated; possibly we need to take some action.
Definition network.cpp:1028
bool NetworkValidateServerName(std::string &server_name)
Trim the given server name in place, i.e.
Definition network.cpp:859
Basic functions/variables used all over the place.
Base core network types and some helper functions to access them.
void ShowNetworkChatQueryWindow(DestType type, int dest)
Show the chat window.
bool NetworkValidateOurClientName()
Convenience method for NetworkValidateClientName on _settings_client.network.client_name.
bool NetworkMaxCompaniesReached()
Check if max_companies has been reached on the server (local check only).
uint NetworkMaxCompaniesAllowed()
Get the maximum number of companies that are allowed by the server.
std::string _network_server_name
The current name of the server you are on.
void NetworkClientRequestMove(CompanyID company_id)
Notify the server of this client wanting to be moved to another company.
Client part of the network protocol.
Part of the network protocol handling content distribution.
void ShowMissingContentWindow(const GRFConfigList &list)
Show the content list window with all missing grfs from the given list.
ConnectionType _network_server_connection_type
What type of connection the Game Coordinator detected we are on.
ClientNetworkCoordinatorSocketHandler _network_coordinator_client
The connection to the Game Coordinator.
std::string _network_server_invite_code
Our invite code as indicated by the Game Coordinator.
Part of the network protocol handling Game Coordinator requests.
void NetworkServerDoMove(ClientID client_id, CompanyID company_id)
Handle the tid-bits of moving a client from one company to another.
bool NetworkCompanyHasClients(CompanyID company)
Check whether a particular company has clients.
void NetworkServerKickClient(ClientID client_id, std::string_view reason)
Kick a single client.
uint NetworkServerKickOrBanIP(ClientID client_id, bool ban, std::string_view reason)
Ban, or kick, everyone joined from the given client's IP.
void NetworkGameListRemoveItem(NetworkGame *remove)
Remove an item from the gamelist linked list.
std::vector< std::unique_ptr< NetworkGame > > _network_game_list
Game list of this client.
Handling of the list of games.
@ NGLS_ONLINE
Server is online.
@ NGLS_FULL
Server is full and cannot be queried.
@ NGLS_TOO_OLD
Server is too old to query.
@ NGLS_OFFLINE
Server is offline (or cannot be queried).
@ NGLS_BANNED
You are banned from this server.
static void AdminCompanyResetCallback(Window *, bool confirmed)
Callback function for admin command to reset company.
NetworkJoinStatus _network_join_status
The status of joining.
static void AdminClientKickCallback(Window *, bool confirmed)
Callback function for admin command to kick client.
void UpdateNetworkGameWindow()
Update the network new window because a new server is found on the network.
uint8_t _network_join_waiting
The number of clients waiting in front of us.
DropDownAdmin
The possibly entries in a DropDown for an admin.
static void AdminClientBanCallback(Window *, bool confirmed)
Callback function for admin command to ban client.
void DrawCompanyIcon(CompanyID cid, int x, int y)
Draw the icon of a company.
uint32_t _network_join_bytes_total
The total number of bytes to download.
void ShowNetworkAskRelay(std::string_view server_connection_string, std::string &&relay_connection_string, std::string &&token)
Show a modal confirmation window with "no" / "yes, once" / "yes, always" buttons.
uint32_t _network_join_bytes
The number of bytes we already downloaded.
void ShowNetworkAskSurvey()
Show a modal confirmation window with "no" / "preview" / "yes" buttons.
static CompanyID _admin_company_id
For what company a confirmation window is open.
static ClientID _admin_client_id
For what client a confirmation window is open.
GUIs related to networking.
@ NRWCD_UNHANDLED
Relay request is unhandled.
Definition network_gui.h:42
@ NRWCD_HANDLED
Relay request is handled, either by user or by timeout.
Definition network_gui.h:43
NetworkJoinStatus
Status of the clients during joining.
Server part of the network protocol.
Part of the network protocol handling opt-in survey.
@ DESTTYPE_CLIENT
Send message/notice to only a certain client (Private)
@ DESTTYPE_TEAM
Send message/notice to everyone playing the same company (Team)
static const uint MAX_CLIENTS
How many clients can we have.
ClientID
'Unique' identifier to be given to clients
@ INVALID_CLIENT_ID
Client is not part of anything.
@ CLIENT_ID_SERVER
Servers always have this ID.
ServerGameType
Game type the server can be using.
void NetworkUDPSearchGame()
Find all servers.
Sending and receiving UDP messages.
@ WID_NJS_PROGRESS_BAR
Simple progress bar.
@ WID_NJS_PROGRESS_TEXT
Text explaining what is happening.
@ WID_NJS_CANCELOK
Cancel / OK button.
@ WID_NG_CLIENT
Panel with editbox to set client name.
@ WID_NG_CLIENTS
'Clients' button.
@ WID_NG_SCROLLBAR
Scrollbar of matrix.
@ WID_NG_YEARS
'Years' button.
@ WID_NG_FILTER_LABEL
Label in front of the filter/search edit box.
@ WID_NG_NEWGRF_MISSING_SEL
Selection widget for the above button.
@ WID_NG_SEARCH_INTERNET
'Search internet server' button.
@ WID_NG_NEWGRF_MISSING
'Find missing NewGRF online' button.
@ WID_NG_LASTJOINED_SPACER
Spacer after last joined server panel.
@ WID_NG_DATE
'Date' button.
@ WID_NG_LASTJOINED
Info about the last joined server.
@ WID_NG_MATRIX
Panel with list of games.
@ WID_NG_CLIENT_LABEL
Label in front of client name edit box.
@ WID_NG_NEWGRF_SEL
Selection 'widget' to hide the NewGRF settings.
@ WID_NG_JOIN
'Join game' button.
@ WID_NG_SEARCH_LAN
'Search LAN server' button.
@ WID_NG_MAPSIZE
'Map size' button.
@ WID_NG_START
'Start server' button.
@ WID_NG_DETAILS
Panel with game details.
@ WID_NG_INFO
Third button in the game list panel.
@ WID_NG_NAME
'Name' button.
@ WID_NG_ADD
'Add server' button.
@ WID_NG_MAIN
Main panel.
@ WID_NG_REFRESH
'Refresh server' button.
@ WID_NG_FILTER
Panel with the edit box to enter the search text.
@ WID_NG_LASTJOINED_LABEL
Label "Last joined server:".
@ WID_NG_NEWGRF
'NewGRF Settings' button.
ClientListWidgets
Widgets of the NetworkClientListWindow class.
@ WID_CL_CLIENT_NAME_EDIT
Edit button for client name.
@ WID_CL_SERVER_NAME
Server name.
@ WID_CL_SERVER_INVITE_CODE
Invite code for this server.
@ WID_CL_SERVER_NAME_EDIT
Edit button for server name.
@ WID_CL_CLIENT_NAME
Client name.
@ WID_CL_SERVER_SELECTOR
Selector to hide the server frame.
@ WID_CL_SCROLLBAR
Scrollbar for company/client list.
@ WID_CL_CLIENT_COMPANY_COUNT
Count of clients and companies.
@ WID_CL_SERVER_CONNECTION_TYPE
The type of connection the Game Coordinator detected for this server.
@ WID_CL_SERVER_VISIBILITY
Server visibility.
@ WID_CL_MATRIX
Company/client list.
@ WID_NSS_COMPANIES_BTNU
'Max companies' uparrow.
@ WID_NSS_COMPANIES_LABEL
Label for 'max companies'.
@ WID_NSS_CLIENTS_LABEL
Label for 'max clients'.
@ WID_NSS_CLIENTS_TXT
'Max clients' text.
@ WID_NSS_CLIENTS_BTND
'Max clients' downarrow.
@ WID_NSS_GAMENAME
Background for editbox to set game name.
@ WID_NSS_CANCEL
'Cancel' button.
@ WID_NSS_PLAY_HEIGHTMAP
Play heightmap button.
@ WID_NSS_GAMENAME_LABEL
Label for the game name.
@ WID_NSS_LOAD_GAME
Load game button.
@ WID_NSS_GENERATE_GAME
New game button.
@ WID_NSS_CONNTYPE_LABEL
Label for 'connection type'.
@ WID_NSS_CONNTYPE_BTN
'Connection type' droplist button.
@ WID_NSS_COMPANIES_BTND
'Max companies' downarrow.
@ WID_NSS_SETPWD
'Set password' button.
@ WID_NSS_CLIENTS_BTNU
'Max clients' uparrow.
@ WID_NSS_COMPANIES_TXT
'Max companies' text.
@ WID_NSS_BACKGROUND
Background of the window.
@ WID_NSS_PLAY_SCENARIO
Play scenario button.
@ WID_NAR_YES_ALWAYS
"Yes, always" button.
@ WID_NAR_TEXT
Text in the window.
@ WID_NAR_YES_ONCE
"Yes, once" button.
@ WID_NAR_NO
"No" button.
@ WID_NAR_CAPTION
Caption of the window.
@ WID_NAS_YES
"Yes" button.
@ WID_NAS_LINK
"Details & Privacy" button.
@ WID_NAS_TEXT
Text in the window.
@ WID_NAS_CAPTION
Caption of the window.
@ WID_NAS_NO
"No" button.
@ WID_NAS_PREVIEW
"Preview" button.
void ShowNewGRFSettings(bool editable, bool show_params, bool exec_changes, GRFConfigList &config)
Setup the NewGRF gui.
@ SM_MENU
Switch to game intro menu.
Definition openttd.h:33
PixelColour GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:388
constexpr PixelColour GREY_SCALE(uint8_t level)
Return the colour for a particular greyscale level.
static constexpr PixelColour PC_GREY
Grey palette colour.
bool SetSettingValue(const IntSettingDesc *sd, int32_t value, bool force_newgame)
Top function to save the new value of an element of the Settings struct.
static const SettingDesc * GetSettingFromName(std::string_view name, const SettingTable &settings)
Given a name of setting, return a setting description from the table.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
int StrNaturalCompare(std::string_view s1, std::string_view s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Definition string.cpp:425
@ CS_NUMERAL
Only numeric ones.
Definition string_type.h:26
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
Definition string_type.h:25
uint64_t GetParamMaxValue(uint64_t max_value, uint min_count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:236
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:218
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_RTL
Text is written right-to-left by default.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
NetworkSettings network
settings related to the network
Dimensions (a width and height) of a rectangle in 2D.
Information about GRF, used in the game and (part of it) in savegames.
Data structure describing how to show the list (what sort direction and criteria).
Partial widget specification to allow NWidgets to be written nested.
Window used for asking the user if he is okay using a relay server.
void FindWindowPlacementAndResize(int, int, bool) override
Resize window towards the default size.
std::string relay_connection_string
The relay server we want to connect to.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
std::string token
The token for this connection.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
std::string server_connection_string
The game server we want to connect to.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Window used for asking if the user wants to participate in the automated survey.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
void FindWindowPlacementAndResize(int, int, bool) override
Resize window towards the default size.
Container for all information known about a client.
static NetworkClientInfo * GetByClientID(ClientID client_id)
Return the CI given it's client-identifier.
Definition network.cpp:118
CompanyID client_playas
As which company is this client playing (CompanyID)
ClientID client_id
Client identifier (same as ClientState->client_id)
std::string client_name
Name of the client.
std::string public_key
The public key of the client.
Main handle for clientlist.
Scrollbar * vscroll
Vertical scrollbar of this window.
void OnResize() override
Called after the window got resized.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
ClientListWidgets query_widget
During a query this tracks what widget caused the query.
CompanyID dd_company_id
During admin dropdown, track which company this was for.
void OnMouseOver(Point pt, WidgetID widget) override
The mouse is currently moving over the window or has just moved outside of the window.
int hover_index
Index of the current line we are hovering over, or -1 if none.
bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
Event to display a custom tooltip.
std::vector< std::unique_ptr< ButtonLine > > buttons
Per line which buttons are available.
static void OnClickCompanyNew(NetworkClientListWindow *w, Point pt, CompanyID)
Create new company button is clicked.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
uint line_height
Current lineheight of each entry in the matrix.
void OnInit() override
Notification that the nested widget tree gets initialized.
static void OnClickCompanyAdmin(NetworkClientListWindow *w, Point pt, CompanyID company_id)
Admin button on a Company is clicked.
static void OnClickCompanyChat(NetworkClientListWindow *w, Point pt, CompanyID company_id)
Chat button on a Company is clicked.
ClientID dd_client_id
During admin dropdown, track which client this was for.
static void OnClickClientChat(NetworkClientListWindow *w, Point pt, ClientID client_id)
Chat button on a Client is clicked.
void RebuildListCompany(CompanyID company_id, CompanyID client_playas, bool can_join_company)
Part of RebuildList() to create the information for a single company.
static void OnClickClientAdmin(NetworkClientListWindow *w, Point pt, ClientID client_id)
Admin button on a Client is clicked.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
void OnDropdownClose(Point pt, WidgetID widget, int index, int click_result, bool instant_close) override
A dropdown window associated to this window has been closed.
static void OnClickCompanyJoin(NetworkClientListWindow *w, Point pt, CompanyID company_id)
Join button on a Company is clicked.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void RebuildList()
Rebuild the list, meaning: calculate the lines needed and what buttons go on which line.
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
bool version_compatible
Can we connect to this server or not? (based on server_revision)
bool compatible
Can we connect to this server or not? (based on server_revision and grf_match.
Structure with information shown in the game list (GUI)
bool refreshing
Whether this server is being queried.
std::string connection_string
Address of the server.
NetworkGameInfo info
The game information of this server.
NetworkGameStatus status
Stats of the server.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
TimerGameCalendar::Date calendar_start
When the game started.
std::string server_revision
The version number the server is using (e.g.: 'r304' or 0.5.0)
bool use_password
Is this server passworded?
uint8_t clients_max
Max clients allowed on server.
GRFConfigList grfconfig
List of NewGRF files used.
uint16_t map_height
Map height.
std::string server_name
Server name.
uint16_t map_width
Map width.
TimerGameTick::TickCounter ticks_playing
Amount of ticks the game has been running unpaused.
LandscapeType landscape
The used landscape.
uint8_t companies_max
Max companies allowed on server.
std::string gamescript_name
Name of the gamescript.
TimerGameCalendar::Date calendar_date
Current calendar date.
int gamescript_version
Version of the gamescript.
uint8_t companies_on
How many started companies do we have.
uint8_t clients_on
Current count of clients on server.
uint8_t max_clients
maximum amount of clients
uint8_t max_companies
maximum amount of companies
std::string client_name
name of the player (as client)
ServerGameType server_game_type
Server type: local / public / invite-only.
UseRelayService use_relay_service
Use relay service?
std::string connect_to_ip
default for the "Add server" query
std::string last_joined
Last joined server.
std::string server_name
name of the server
std::string server_password
password for joining this server
ParticipateSurvey participate_survey
Participate in the automated survey.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
WidgetID widget_id
The widget that has the pop-up input menu.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
QueryString name_editbox
Server name editbox.
void OnTimeout() override
Called when this window's timeout has been reached.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
static size_t GetNumItems()
Returns number of valid items in the pool.
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * GetIfValid(auto index)
Returns Titem with given index.
Data stored about a string that can be modified in the GUI.
int cancel_button
Widget button of parent window to simulate when pressing CANCEL in OSK.
static const int ACTION_CLEAR
Clear editbox.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Rect CentreToHeight(int height) const
Centre a vertical dimension within this Rect.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
bool Contains(const Point &pt) const
Test if a point falls inside this Rect.
uint step_height
Step-size of height resize changes.
Definition window_gui.h:212
String filter and state.
bool IsEmpty() const
Check whether any filter words were entered.
void SetFilterTerm(std::string_view str)
Set the term to filter on.
void ResetState()
Reset the matching state to process a new item.
bool GetState() const
Get the matching state of the current item.
Window for displaying the textfile of a survey result.
const GRFConfig * grf_config
View the textfile of this GRFConfig.
std::string_view GetText() const
Get the current text.
Definition textbuf.cpp:284
void Assign(std::string_view text)
Copy a string into the textbuffer.
Definition textbuf.cpp:420
Window for displaying a textfile.
void LoadText(std::string_view buf)
Load a text into the textfile viewer.
TextfileType file_type
Type of textfile to view.
Container with the data associated to a single widget.
High level window description.
Definition window_gui.h:167
Number to differentiate different windows of the same class.
Data structure for an opened window.
Definition window_gui.h:273
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:978
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1102
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:815
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1789
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:320
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:766
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3216
Window * parent
Parent window.
Definition window_gui.h:328
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:556
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:504
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:798
ResizeInfo resize
Resize information.
Definition window_gui.h:314
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1779
void CloseChildWindowById(WindowClass wc, WindowNumber number) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1090
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:485
bool IsWidgetDisabled(WidgetID widget_index) const
Gets the enabled/disabled status of a widget.
Definition window_gui.h:410
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:441
virtual void OnDropdownClose(Point pt, WidgetID widget, int index, int click_result, bool instant_close)
A dropdown window associated to this window has been closed.
Definition window.cpp:286
int left
x position of left edge of the window
Definition window_gui.h:309
void RaiseWidgetsWhenLowered(Args... widgets)
Raises the widgets and sets widgets dirty that are lowered.
Definition window_gui.h:536
int top
y position of top edge of the window
Definition window_gui.h:310
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
Definition window.cpp:212
void HandleButtonClick(WidgetID widget)
Do all things to make a button look clicked and mark it to be unclicked in a few ticks.
Definition window.cpp:595
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1802
WindowFlags flags
Window flags.
Definition window_gui.h:300
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:313
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:381
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:312
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:302
@ CONNECTION_TYPE_UNKNOWN
The Game Coordinator hasn't informed us yet what type of connection we have.
@ AcceptUnchanged
return success even when the text didn't change
@ LengthIsInChars
the length of the string is counted in characters
void QueryCallbackProc(Window *, bool)
Callback procedure for the ShowQuery method.
Definition textbuf_gui.h:27
TextfileType
Additional text files accompanying Tar archives.
@ TFT_SURVEY_RESULT
Survey result (preview)
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:289
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_IMGBTN
(Toggle) Button with image
Definition widget_type.h:42
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:71
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:63
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:67
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:40
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:58
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:51
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:53
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:77
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:69
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:61
@ WWT_FRAME
Frame.
Definition widget_type.h:52
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:38
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:60
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:57
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:62
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:50
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:72
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ SZSP_NONE
Display plane with zero size in both directions (none filling and resizing).
@ EqualSize
Containers should keep all their (resizing) children equally large.
SizingType
Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition()
@ RWV_HIDE_BEVEL
Bevel of resize box is hidden.
Definition widget_type.h:30
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1193
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1180
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1205
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3276
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1151
@ Modal
The window is a modal child of some other window, meaning the parent is 'inactive'.
@ BorderOnly
Draw border only, no background.
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
@ Timeout
Window timeout counter.
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:218
@ SBS_UP
Sort descending.
Definition window_gui.h:219
@ WDP_CENTER
Center the window.
Definition window_gui.h:145
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:144
int WidgetID
Widget ID.
Definition window_type.h:20
@ WN_NETWORK_STATUS_WINDOW_JOIN
Network join status.
Definition window_type.h:44
@ WN_NETWORK_WINDOW_GAME
Network game window.
Definition window_type.h:40
@ WN_NETWORK_WINDOW_START
Network start server.
Definition window_type.h:42
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
@ WC_NETWORK_ASK_RELAY
Network ask relay window; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:50
@ WC_CLIENT_LIST
Client list; Window numbers:
@ WC_NETWORK_WINDOW
Network window; Window numbers:
@ WC_TEXTFILE
textfile; Window numbers:
@ WC_NETWORK_STATUS_WINDOW
Network status window; Window numbers:
@ WC_NETWORK_ASK_SURVEY
Network ask survey window; Window numbers: