From 526635942451479bee66e9eb61c50f91ae48a7dd Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 18 Apr 2021 09:54:47 +0200 Subject: [PATCH] Feature: rework in-game Online Players GUI The GUI now more clearly shows some basic information about the server you joined, your client name (and the ability to change it), and what players are in which company. It also contains useful buttons to press to join companies, chat with other people, and for admins to kick/ban people. Additionally, renamed "advertised" to "visibility"; this has to do with future additions, but also because it is more clear in wording. --- media/baseset/openttd.grf | Bin 510358 -> 510525 bytes media/baseset/openttd/openttdgui.nfo | 5 +- media/baseset/openttd/openttdgui.png | Bin 43899 -> 44464 bytes src/company_cmd.cpp | 4 +- src/lang/english.txt | 41 +- src/network/network.cpp | 5 +- src/network/network_client.cpp | 10 +- src/network/network_gui.cpp | 645 +++++++++++++++++++++------ src/network/network_server.cpp | 7 +- src/table/sprites.h | 6 +- src/toolbar_gui.cpp | 14 +- src/widgets/network_widget.h | 11 +- 12 files changed, 573 insertions(+), 175 deletions(-) diff --git a/media/baseset/openttd.grf b/media/baseset/openttd.grf index a63bfc4e295e77a34ac22387778c534dcc1f2c3d..c4511f55ea9f87c7dd7e3e4f73e79e404801d053 100644 GIT binary patch delta 217 zcmbO>S$^*nc@`Fi|92Z%T3HxdS(sW`m|Iy`wz9AqlriqzZd%6LIH$gWfsu=c0R)(V z_yzL?wii6ijEsygfW!@MW`2HtAdCM6?*Z-yybE|b%7 diff --git a/media/baseset/openttd/openttdgui.nfo b/media/baseset/openttd/openttdgui.nfo index eb1313365e..9a13fa8000 100644 --- a/media/baseset/openttd/openttdgui.nfo +++ b/media/baseset/openttd/openttdgui.nfo @@ -4,7 +4,7 @@ // 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 . // -1 * 0 0C "OpenTTD GUI graphics" - -1 * 3 05 15 \b 186 // OPENTTD_SPRITE_COUNT + -1 * 3 05 15 \b 189 // OPENTTD_SPRITE_COUNT -1 sprites/openttdgui.png 8bpp 66 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 146 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 226 8 64 31 -31 7 normal @@ -191,3 +191,6 @@ -1 sprites/openttdgui_convert_tram.png 8bpp 24 0 32 32 0 0 normal -1 sprites/openttdgui.png 8bpp 513 440 10 10 0 0 normal -1 sprites/openttdgui.png 8bpp 526 440 10 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 539 440 12 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 553 440 12 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 567 440 12 10 0 0 normal diff --git a/media/baseset/openttd/openttdgui.png b/media/baseset/openttd/openttdgui.png index 1fc02aa61e30774b4e69fd08b21f51ef69a74a00..dc0976a971647fa6adad1ed1506030aaf2c60bf8 100644 GIT binary patch delta 41971 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 delta 41353 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 YWbVTEdU}UG1pHhyxnf*o?EnA( diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index ab9e7e12ef..c3483a9bfe 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -571,8 +571,8 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY) GeneratePresidentName(c); SetWindowDirty(WC_GRAPH_LEGEND, 0); - SetWindowClassesDirty(WC_CLIENT_LIST_POPUP); - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowClassesData(WC_CLIENT_LIST_POPUP); + InvalidateWindowData(WC_CLIENT_LIST, 0); InvalidateWindowData(WC_LINKGRAPH_LEGEND, 0); BuildOwnerLegend(); InvalidateWindowData(WC_SMALLMAP, 0, 1); diff --git a/src/lang/english.txt b/src/lang/english.txt index ff12f122af..13cde55d95 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1991,6 +1991,9 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :Earring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Private +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Player name: @@ -2053,10 +2056,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}The game STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Set password STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protect your game with a password if you don't want it to be publicly accessible -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Advertised -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Choose between an advertised (internet) and a not advertised (Local Area Network, LAN) game -STR_NETWORK_START_SERVER_UNADVERTISED :No -STR_NETWORK_START_SERVER_ADVERTISED :Yes +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibility +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Whether other people can see your server in the public listing STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximum number of clients: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Choose the maximum number of clients. Not all slots need to be filled @@ -2118,19 +2119,37 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Disconne STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server is protected. Enter password STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Company is protected. Enter password -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Client list # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Client list -STR_NETWORK_COMPANY_LIST_SPECTATE :Spectate +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Online players STR_NETWORK_COMPANY_LIST_NEW_COMPANY :New company -# Network client list +# Network client list popup for clients STR_NETWORK_CLIENTLIST_KICK :Kick STR_NETWORK_CLIENTLIST_BAN :Ban -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Speak to all -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Speak to company -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Private message + +# Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multiplayer +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Name +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Name of the server you are playing on +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Edit the name of your server +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Name of the server +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibility +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Whether other people can see your server in the public listing +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Player +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Name +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Your player name +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Edit your player name +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Your player name +STR_NETWORK_CLIENT_LIST_PLAYER_HOST :{WHITE}(host) {BLACK}{RAW_STRING} +STR_NETWORK_CLIENT_LIST_PLAYER_SELF :{WHITE}(you) {BLACK}{RAW_STRING} +STR_NETWORK_CLIENT_LIST_ADMIN_TOOLTIP :{BLACK}Administrative actions to perform for this client +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Join this company +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Send a message to this player +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Send a message to all players of this company +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send a message to all spectators +STR_NETWORK_CLIENT_LIST_SPECTATORS :Spectators STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client diff --git a/src/network/network.cpp b/src/network/network.cpp index 97eadb9768..1e0838684e 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -522,9 +522,10 @@ void ParseGameConnectionString(const char **company, const char **port, char *co /* Register the login */ _network_clients_connected++; - SetWindowDirty(WC_CLIENT_LIST, 0); ServerNetworkGameSocketHandler *cs = new ServerNetworkGameSocketHandler(s); cs->client_address = address; // Save the IP of the client + + InvalidateWindowData(WC_CLIENT_LIST, 0); } /** @@ -713,7 +714,7 @@ void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const c static void NetworkInitGameInfo() { if (StrEmpty(_settings_client.network.server_name)) { - seprintf(_settings_client.network.server_name, lastof(_settings_client.network.server_name), "Unnamed Server"); + strecpy(_settings_client.network.server_name, "Unnamed Server", lastof(_settings_client.network.server_name)); } /* The server is a client too */ diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 6156dc4863..d5fe64b11d 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -647,7 +647,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac ci->client_playas = playas; strecpy(ci->client_name, name, lastof(ci->client_name)); - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } @@ -666,7 +666,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac strecpy(ci->client_name, name, lastof(ci->client_name)); - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } @@ -1043,7 +1043,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Pack delete ci; } - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } @@ -1062,7 +1062,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_QUIT(Packet *p) DEBUG(net, 0, "Unknown client (%d) is leaving the game", client_id); } - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); /* If we come here it means we could not locate the client.. strange :s */ return NETWORK_RECV_STATUS_OKAY; @@ -1079,7 +1079,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_JOIN(Packet *p) NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, ci->client_name); } - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index dd25ad5bc1..fcfeded5a5 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -21,6 +21,7 @@ #include "network_udp.h" #include "../window_func.h" #include "../gfx_func.h" +#include "../widgets/dropdown_type.h" #include "../widgets/dropdown_func.h" #include "../querystring_gui.h" #include "../sortlist_type.h" @@ -30,6 +31,8 @@ #include "../map_type.h" #include "../guitimer_func.h" #include "../zoom_func.h" +#include "../sprite.h" +#include "../settings_internal.h" #include "../widgets/network_widget.h" @@ -38,21 +41,24 @@ #include "../stringfilter_type.h" -#include "../safeguards.h" - #ifdef __EMSCRIPTEN__ # include #endif +#include + +#include "../safeguards.h" + static void ShowNetworkStartServerWindow(); static void ShowNetworkLobbyWindow(NetworkGameList *ngl); /** - * Advertisement options in the start server window + * Visibility of the server. Public servers advertise, where private servers + * do not. */ -static const StringID _connection_types_dropdown[] = { - STR_NETWORK_START_SERVER_UNADVERTISED, - STR_NETWORK_START_SERVER_ADVERTISED, +static const StringID _server_visibility_dropdown[] = { + STR_NETWORK_SERVER_VISIBILITY_PRIVATE, + STR_NETWORK_SERVER_VISIBILITY_PUBLIC, INVALID_STRING_ID }; @@ -985,7 +991,7 @@ struct NetworkStartServerWindow : public Window { { switch (widget) { case WID_NSS_CONNTYPE_BTN: - SetDParam(0, _connection_types_dropdown[_settings_client.network.server_advertise]); + SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]); break; case WID_NSS_CLIENTS_TXT: @@ -1006,7 +1012,7 @@ struct NetworkStartServerWindow : public Window { { switch (widget) { case WID_NSS_CONNTYPE_BTN: - *size = maxdim(GetStringBoundingBox(_connection_types_dropdown[0]), GetStringBoundingBox(_connection_types_dropdown[1])); + *size = maxdim(GetStringBoundingBox(_server_visibility_dropdown[0]), GetStringBoundingBox(_server_visibility_dropdown[1])); size->width += padding.width; size->height += padding.height; break; @@ -1036,7 +1042,7 @@ struct NetworkStartServerWindow : public Window { break; case WID_NSS_CONNTYPE_BTN: // Connection type - ShowDropDownMenu(this, _connection_types_dropdown, _settings_client.network.server_advertise, WID_NSS_CONNTYPE_BTN, 0, 0); // do it for widget WID_NSS_CONNTYPE_BTN + ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_NSS_CONNTYPE_BTN, 0, 0); // do it for widget WID_NSS_CONNTYPE_BTN break; case WID_NSS_CLIENTS_BTND: case WID_NSS_CLIENTS_BTNU: // Click on up/down button for number of clients @@ -1175,8 +1181,8 @@ static const NWidgetPart _nested_network_start_server_window_widgets[] = { NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 6, 10), NWidget(NWID_VERTICAL), SetPIP(0, 1, 0), - NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_ADVERTISED_LABEL, STR_NULL), - NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_BTN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP), + NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_VISIBILITY_LABEL, STR_NULL), + NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_BTN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP), EndContainer(), NWidget(NWID_VERTICAL), SetPIP(0, 1, 0), NWidget(NWID_SPACER), SetFill(1, 1), @@ -1592,21 +1598,6 @@ static void ClientList_Ban(const NetworkClientInfo *ci) NetworkServerKickOrBanIP(ci->client_id, true, nullptr); } -static void ClientList_SpeakToClient(const NetworkClientInfo *ci) -{ - ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, ci->client_id); -} - -static void ClientList_SpeakToCompany(const NetworkClientInfo *ci) -{ - ShowNetworkChatQueryWindow(DESTTYPE_TEAM, ci->client_playas); -} - -static void ClientList_SpeakToAll(const NetworkClientInfo *ci) -{ - ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0); -} - /** Popup selection window to chose an action to perform */ struct NetworkClientListPopupWindow : Window { /** Container for actions that can be executed. */ @@ -1639,15 +1630,6 @@ struct NetworkClientListPopupWindow : Window { const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id); - if (_network_own_client_id != ci->client_id) { - this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT, &ClientList_SpeakToClient); - } - - if (Company::IsValidID(ci->client_playas) || ci->client_playas == COMPANY_SPECTATOR) { - this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY, &ClientList_SpeakToCompany); - } - this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, &ClientList_SpeakToAll); - /* A server can kick clients (but not himself). */ if (_network_server && _network_own_client_id != ci->client_id) { this->AddAction(STR_NETWORK_CLIENTLIST_KICK, &ClientList_Kick); @@ -1671,7 +1653,7 @@ struct NetworkClientListPopupWindow : Window { } d.height *= (uint)this->actions.size(); - d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT + 4 + 4; // Give the list a bit of padding on both sides. d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; *size = d; } @@ -1690,7 +1672,7 @@ struct NetworkClientListPopupWindow : Window { colour = TC_BLACK; } - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, action.name, colour); + DrawString(r.left + WD_FRAMERECT_LEFT + 4, r.right - WD_FRAMERECT_RIGHT - 4, y, action.name, colour); y += FONT_HEIGHT_NORMAL; } } @@ -1731,167 +1713,554 @@ static void PopupClientList(ClientID client_id, int x, int y) static const NWidgetPart _nested_client_list_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_CL_PANEL), SetMinimalSize(250, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM), SetResize(1, 1), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CL_SERVER_SELECTOR), + NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER, STR_NULL), SetPadding(4, 4, 0, 4), SetPIP(0, 2, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_NAME, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_SERVER_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CL_SERVER_VISIBILITY), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP), + EndContainer(), + EndContainer(), + EndContainer(), + NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER, STR_NULL), SetPadding(4, 4, 4, 4), SetPIP(0, 2, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER_NAME, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_CLIENT_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_CLIENT_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP), + EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_CL_MATRIX), SetMinimalSize(180, 0), SetResize(1, 1), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_NULL), SetScrollbar(WID_CL_SCROLLBAR), + NWidget(NWID_VERTICAL), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_CL_SCROLLBAR), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), + EndContainer(), + EndContainer(), }; static WindowDesc _client_list_desc( - WDP_AUTO, "list_clients", 0, 0, + WDP_AUTO, "list_clients", 220, 300, WC_CLIENT_LIST, WC_NONE, 0, _nested_client_list_widgets, lengthof(_nested_client_list_widgets) ); +/** + * Button shown for either a company or client in the client-list. + * + * These buttons are dynamic and strongly depends on which company/client + * what buttons are available. This class allows dynamically creating them + * as the current Widget system does not. + */ +class ButtonCommon { +public: + SpriteID sprite; ///< The sprite to use on the button. + StringID tooltip; ///< The tooltip of the button. + Colours colour; ///< The colour of the button. + bool disabled; ///< Is the button disabled? + uint height; ///< Calculated height of the button. + uint width; ///< Calculated width of the button. + + ButtonCommon(SpriteID sprite, StringID tooltip, Colours colour) : + sprite(sprite), + tooltip(tooltip), + colour(colour), + disabled(false) + { + Dimension d = GetSpriteSize(sprite); + this->height = d.height + ScaleGUITrad(WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM); + this->width = d.width + ScaleGUITrad(WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT); + } + virtual ~ButtonCommon() {} + + /** + * OnClick handler for when the button is pressed. + */ + virtual void OnClick(struct NetworkClientListWindow *w, Point pt) = 0; +}; + +/** + * Template version of Button, with callback support. + */ +template +class Button : public ButtonCommon { +private: + typedef void (*ButtonCallback)(struct NetworkClientListWindow *w, Point pt, T id); ///< Callback function to call on click. + T id; ///< ID this button belongs to. + ButtonCallback proc; ///< Callback proc to call when button is pressed. + +public: + Button(SpriteID sprite, StringID tooltip, Colours colour, T id, ButtonCallback proc) : + ButtonCommon(sprite, tooltip, colour), + id(id), + proc(proc) + { + assert(proc != nullptr); + } + + void OnClick(struct NetworkClientListWindow *w, Point pt) override + { + if (this->disabled) return; + + this->proc(w, pt, this->id); + } +}; + +using CompanyButton = Button; +using ClientButton = Button; + /** * Main handle for clientlist */ struct NetworkClientListWindow : Window { - int selected_item; +private: + ClientListWidgets query_widget; ///< During a query this tracks what widget caused the query. + CompanyID join_company; ///< During query for company password, this stores what company we wanted to join. - uint server_client_width; - uint line_height; + Scrollbar *vscroll; ///< Vertical scrollbar of this window. + uint line_height; ///< Current lineheight of each entry in the matrix. + uint line_count; ///< Amount of lines in the matrix. - Dimension icon_size; + std::map>> buttons; ///< Per line which buttons are available. - NetworkClientListWindow(WindowDesc *desc, WindowNumber window_number) : - Window(desc), - selected_item(-1) + static const int CLIENT_OFFSET_LEFT = 12; ///< Offset of client entries compared to company entries. + + /** + * Chat button on a Company is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param company_id The company this button was assigned to. + */ + static void OnClickCompanyChat(NetworkClientListWindow *w, Point pt, CompanyID company_id) { - this->InitNested(window_number); + ShowNetworkChatQueryWindow(DESTTYPE_TEAM, company_id); } /** - * Finds the amount of clients and set the height correct + * Join button on a Company is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param company_id The company this button was assigned to. */ - bool CheckClientListHeight() + static void OnClickCompanyJoin(NetworkClientListWindow *w, Point pt, CompanyID company_id) { - int num = 0; + if (_network_server) { + NetworkServerDoMove(CLIENT_ID_SERVER, company_id); + MarkWholeScreenDirty(); + } else if (NetworkCompanyIsPassworded(company_id)) { + w->query_widget = WID_CL_COMPANY_JOIN; + w->join_company = company_id; + ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION, NETWORK_PASSWORD_LENGTH, w, CS_ALPHANUMERAL, QSF_PASSWORD); + } else { + NetworkClientRequestMove(company_id); + } + } - /* Should be replaced with a loop through all clients */ + /** + * Admin button on a Client is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param client_id The client this button was assigned to. + */ + static void OnClickClientAdmin(NetworkClientListWindow *w, Point pt, ClientID client_id) + { + PopupClientList(client_id, pt.x + w->left, pt.y + w->top); + } + + /** + * Chat button on a Client is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param client_id The client this button was assigned to. + */ + static void OnClickClientChat(NetworkClientListWindow *w, Point pt, ClientID client_id) + { + ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, client_id); + } + + /** + * Part of RebuildList() to create the information for a single company. + * @param company_id The company to build the list for. + * @param own_ci The NetworkClientInfo of the client itself. + */ + void RebuildListCompany(CompanyID company_id, const NetworkClientInfo *own_ci) + { + ButtonCommon *chat_button = new 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); + + this->buttons[line_count].emplace_back(chat_button); + if (own_ci->client_playas != company_id) this->buttons[line_count].emplace_back(new CompanyButton(SPR_JOIN, STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyJoin)); + + this->line_count += 1; + + bool has_players = false; for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - if (ci->client_playas != COMPANY_INACTIVE_CLIENT) num++; + if (ci->client_playas != company_id) continue; + has_players = true; + + if (_network_own_client_id != ci->client_id) this->buttons[line_count].emplace_back(new ClientButton(SPR_CHAT, STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP, COLOUR_ORANGE, ci->client_id, &NetworkClientListWindow::OnClickClientChat)); + if (_network_server && _network_own_client_id != ci->client_id) this->buttons[line_count].emplace_back(new ClientButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_TOOLTIP, COLOUR_RED, ci->client_id, &NetworkClientListWindow::OnClickClientAdmin)); + + this->line_count += 1; } - num *= this->line_height; + chat_button->disabled = !has_players; + } - int diff = (num + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM) - (this->GetWidget(WID_CL_PANEL)->current_y); - /* If height is changed */ - if (diff != 0) { - ResizeWindow(this, 0, diff, false); - return false; + /** + * Rebuild the list, meaning: calculate the lines needed and what buttons go on which line. + */ + void RebuildList() + { + const NetworkClientInfo *own_ci = NetworkClientInfo::GetByClientID(_network_own_client_id); + + this->buttons.clear(); + this->line_count = 0; + + /* Companies */ + for (const Company *c : Company::Iterate()) { + this->RebuildListCompany(c->index, own_ci); } - return true; + + /* Spectators */ + this->RebuildListCompany(COMPANY_SPECTATOR, own_ci); + + this->vscroll->SetCount(this->line_count); + } + + /** + * Get the button at a specific point on the WID_CL_MATRIX. + * @param pt The point to look for a button. + * @return The button or a nullptr if there was none. + */ + ButtonCommon *GetButtonAtPoint(Point pt) + { + uint index = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CL_MATRIX); + NWidgetBase *widget_matrix = this->GetWidget(WID_CL_MATRIX); + + bool rtl = _current_text_dir == TD_RTL; + uint x = rtl ? (uint)widget_matrix->pos_x + WD_FRAMERECT_LEFT : widget_matrix->current_x - WD_FRAMERECT_RIGHT; + + /* Find the buttons for this row. */ + auto button_find = this->buttons.find(index); + if (button_find == this->buttons.end()) return nullptr; + + /* Check if we want to display a tooltip for any of the buttons. */ + for (auto &button : button_find->second) { + uint left = rtl ? x : x - button->width; + uint right = rtl ? x + button->width : x; + + if (IsInsideMM(pt.x, left, right)) { + return button.get(); + } + + int width = button->width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + x += rtl ? width : -width; + } + + return nullptr; + } + +public: + NetworkClientListWindow(WindowDesc *desc, WindowNumber window_number) : + Window(desc) + { + this->CreateNestedTree(); + this->vscroll = this->GetScrollbar(WID_CL_SCROLLBAR); + this->OnInvalidateData(); + this->FinishInitNested(window_number); + } + + void OnInvalidateData(int data = 0, bool gui_scope = true) override + { + this->RebuildList(); + + /* Currently server information is not sync'd to clients, so we cannot show it on clients. */ + this->GetWidget(WID_CL_SERVER_SELECTOR)->SetDisplayedPlane(_network_server ? 0 : SZSP_HORIZONTAL); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { - if (widget != WID_CL_PANEL) return; + switch (widget) { + case WID_CL_SERVER_VISIBILITY: + *size = maxdim(GetStringBoundingBox(_server_visibility_dropdown[0]), GetStringBoundingBox(_server_visibility_dropdown[1])); + size->width += padding.width; + size->height += padding.height; + break; - this->server_client_width = std::max(GetStringBoundingBox(STR_NETWORK_SERVER).width, GetStringBoundingBox(STR_NETWORK_CLIENT).width) + WD_FRAMERECT_RIGHT; - this->icon_size = GetSpriteSize(SPR_COMPANY_ICON); - this->line_height = std::max(this->icon_size.height + 2U, (uint)FONT_HEIGHT_NORMAL); + case WID_CL_MATRIX: { + uint height = std::max({GetSpriteSize(SPR_COMPANY_ICON).height, GetSpriteSize(SPR_JOIN).height, GetSpriteSize(SPR_ADMIN).height, GetSpriteSize(SPR_CHAT).height}); + height += ScaleGUITrad(WD_FRAMERECT_TOP) + ScaleGUITrad(WD_FRAMERECT_BOTTOM); + this->line_height = std::max(height, (uint)FONT_HEIGHT_NORMAL) + ScaleGUITrad(WD_MATRIX_TOP + WD_MATRIX_BOTTOM); - uint width = 100; // Default width - for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - width = std::max(width, GetStringBoundingBox(ci->client_name).width); + resize->width = 1; + resize->height = this->line_height; + fill->height = this->line_height; + size->height = std::max(size->height, 5 * this->line_height); + break; + } } - - size->width = WD_FRAMERECT_LEFT + this->server_client_width + this->icon_size.width + WD_FRAMERECT_LEFT + width + WD_FRAMERECT_RIGHT; } - void OnPaint() override + void OnResize() override { - /* Check if we need to reset the height */ - if (!this->CheckClientListHeight()) return; - - this->DrawWidgets(); + this->vscroll->SetCapacityFromWidget(this, WID_CL_MATRIX); } - void DrawWidget(const Rect &r, int widget) const override + void SetStringParameters(int widget) const override { - if (widget != WID_CL_PANEL) return; + switch (widget) { + case WID_CL_SERVER_NAME: + SetDParamStr(0, _settings_client.network.server_name); + break; - bool rtl = _current_text_dir == TD_RTL; - int icon_offset = (this->line_height - icon_size.height) / 2; - int text_offset = (this->line_height - FONT_HEIGHT_NORMAL) / 2; + case WID_CL_SERVER_VISIBILITY: + SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]); + break; - uint y = r.top + WD_FRAMERECT_TOP; - uint left = r.left + WD_FRAMERECT_LEFT; - uint right = r.right - WD_FRAMERECT_RIGHT; - uint type_icon_width = this->server_client_width + this->icon_size.width + WD_FRAMERECT_LEFT; - - - uint type_left = rtl ? right - this->server_client_width : left; - uint type_right = rtl ? right : left + this->server_client_width - 1; - uint icon_left = rtl ? right - type_icon_width + WD_FRAMERECT_LEFT : left + this->server_client_width; - uint name_left = rtl ? left : left + type_icon_width; - uint name_right = rtl ? right - type_icon_width : right; - - int i = 0; - for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - TextColour colour; - if (this->selected_item == i++) { // Selected item, highlight it - GfxFillRect(r.left + 1, y, r.right - 1, y + this->line_height - 1, PC_BLACK); - colour = TC_WHITE; - } else { - colour = TC_BLACK; - } - - if (ci->client_id == CLIENT_ID_SERVER) { - DrawString(type_left, type_right, y + text_offset, STR_NETWORK_SERVER, colour); - } else { - DrawString(type_left, type_right, y + text_offset, STR_NETWORK_CLIENT, colour); - } - - /* Filter out spectators */ - if (Company::IsValidID(ci->client_playas)) DrawCompanyIcon(ci->client_playas, icon_left, y + icon_offset); - - DrawString(name_left, name_right, y + text_offset, ci->client_name, colour); - - y += line_height; + case WID_CL_CLIENT_NAME: + SetDParamStr(0, _settings_client.network.client_name); + break; } } void OnClick(Point pt, int widget, int click_count) override { - /* Show the popup with option */ - if (this->selected_item != -1) { - int client_no = this->selected_item; - for (NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - if (client_no == 0) { - PopupClientList(ci->client_id, pt.x + this->left, pt.y + this->top); - break; - } - client_no--; + switch (widget) { + case WID_CL_SERVER_NAME_EDIT: + if (!_network_server) break; + + this->query_widget = WID_CL_SERVER_NAME_EDIT; + SetDParamStr(0, _settings_client.network.server_name); + ShowQueryString(STR_JUST_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION, NETWORK_NAME_LENGTH, this, CS_ALPHANUMERAL, QSF_LEN_IN_CHARS); + break; + + case WID_CL_CLIENT_NAME_EDIT: + this->query_widget = WID_CL_CLIENT_NAME_EDIT; + SetDParamStr(0, _settings_client.network.client_name); + ShowQueryString(STR_JUST_RAW_STRING, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION, NETWORK_CLIENT_NAME_LENGTH, this, CS_ALPHANUMERAL, QSF_LEN_IN_CHARS); + break; + + case WID_CL_SERVER_VISIBILITY: + if (!_network_server) break; + + ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_CL_SERVER_VISIBILITY, 0, 0); + break; + + case WID_CL_MATRIX: { + ButtonCommon *button = this->GetButtonAtPoint(pt); + if (button == nullptr) break; + + button->OnClick(this, pt); + break; } } } - void OnMouseOver(Point pt, int widget) override + bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override { - /* -1 means we left the current window */ - if (pt.y == -1) { - this->selected_item = -1; - this->SetDirty(); - return; + switch (widget) { + case WID_CL_MATRIX: { + ButtonCommon *button = this->GetButtonAtPoint(pt); + if (button == nullptr) return false; + + GuiShowTooltips(this, button->tooltip, 0, nullptr, close_cond); + return true; + }; } - /* Find the new selected item (if any) */ - pt.y -= this->GetWidget(WID_CL_PANEL)->pos_y; - int item = -1; - if (IsInsideMM(pt.y, WD_FRAMERECT_TOP, this->GetWidget(WID_CL_PANEL)->current_y - WD_FRAMERECT_BOTTOM)) { - item = (pt.y - WD_FRAMERECT_TOP) / this->line_height; + return false; + } + + void OnDropdownSelect(int widget, int index) override + { + switch (widget) { + case WID_CL_SERVER_VISIBILITY: + if (!_network_server) break; + + _settings_client.network.server_advertise = (index != 0); + break; + + default: + NOT_REACHED(); } - /* It did not change.. no update! */ - if (item == this->selected_item) return; - this->selected_item = item; - - /* Repaint */ this->SetDirty(); } + + void OnQueryTextFinished(char *str) override + { + if (str == nullptr) return; + + switch (this->query_widget) { + default: NOT_REACHED(); + + case WID_CL_SERVER_NAME_EDIT: { + if (!_network_server) break; + + uint index; + GetSettingFromName("network.server_name", &index); + SetSettingValue(index, StrEmpty(str) ? "Unnamed Server" : str); + this->InvalidateData(); + break; + } + + case WID_CL_CLIENT_NAME_EDIT: { + if (!NetworkValidateClientName(str)) break; + + uint index; + GetSettingFromName("network.client_name", &index); + SetSettingValue(index, str); + this->InvalidateData(); + break; + } + + case WID_CL_COMPANY_JOIN: + NetworkClientRequestMove(this->join_company, str); + break; + } + } + + /** + * Draw the buttons for a single line in the matrix. + * + * The x-position in RTL is the most left or otherwise the most right pixel + * we can draw the buttons from. + * + * @param x The x-position to start with the buttons. Updated during this function. + * @param y The y-position to start with the buttons. + * @param buttons The buttons to draw. + */ + void DrawButtons(uint &x, uint y, const std::vector> &buttons) const + { + for (auto &button : buttons) { + bool rtl = _current_text_dir == TD_RTL; + + uint left = rtl ? x : x - button->width; + uint right = rtl ? x + button->width : x; + + int offset = std::max(0, ((int)(this->line_height + 1) - (int)button->height) / 2); + + DrawFrameRect(left, y + offset, right, y + offset + button->height, button->colour, FR_NONE); + DrawSprite(button->sprite, PAL_NONE, left + ScaleGUITrad(WD_FRAMERECT_LEFT), y + offset + ScaleGUITrad(WD_FRAMERECT_TOP)); + if (button->disabled) { + GfxFillRect(left + 1, y + offset + 1, right - 1, y + offset + button->height - 1, _colour_gradient[button->colour & 0xF][2], FILLRECT_CHECKER); + } + + int width = button->width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + x += rtl ? width : -width; + } + } + + /** + * Draw a company and its clients on the matrix. + * @param c The company to draw. + * @param left The most left pixel of the line. + * @param right The most right pixel of the line. + * @param top The top of the first line. + * @param line The Nth line we are drawing. Updated during this function. + */ + void DrawCompany(const Company *c, uint left, uint right, uint top, uint &line) const + { + bool rtl = _current_text_dir == TD_RTL; + int text_y_offset = std::max(0, ((int)(this->line_height + 1) - (int)FONT_HEIGHT_NORMAL) / 2) + WD_MATRIX_BOTTOM; + + Dimension d = GetSpriteSize(SPR_COMPANY_ICON); + int offset = std::max(0, ((int)(this->line_height + 1) - (int)d.height) / 2); + + uint text_left = left + (rtl ? (uint)WD_FRAMERECT_LEFT : d.width + 8); + uint text_right = right - (rtl ? d.width + 8 : (uint)WD_FRAMERECT_RIGHT); + + uint line_start = this->vscroll->GetPosition(); + uint line_end = line_start + this->vscroll->GetCapacity(); + + uint y = top + (this->line_height * (line - line_start)); + + /* Draw the company line (if in range of scrollbar). */ + if (IsInsideMM(line, line_start, line_end)) { + uint x = rtl ? text_left : text_right; + + /* If there are buttons for this company, draw them. */ + auto button_find = this->buttons.find(line); + if (button_find != this->buttons.end()) { + this->DrawButtons(x, y, button_find->second); + } + + if (c == nullptr) { + DrawSprite(SPR_COMPANY_ICON, PALETTE_TO_GREY, rtl ? right - d.width - 4 : left + 4, y + offset); + DrawString(rtl ? x : text_left, rtl ? text_right : x, y + text_y_offset, STR_NETWORK_CLIENT_LIST_SPECTATORS, TC_SILVER); + } else { + DrawCompanyIcon(c->index, rtl ? right - d.width - 4 : left + 4, y + offset); + + SetDParam(0, c->index); + SetDParam(1, c->index); + DrawString(rtl ? x : text_left, rtl ? text_right : x, y + text_y_offset, STR_COMPANY_NAME, TC_SILVER); + } + } + + y += this->line_height; + line++; + + for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { + if (c != nullptr && ci->client_playas != c->index) continue; + if (c == nullptr && ci->client_playas != COMPANY_SPECTATOR) continue; + + /* Draw the player line (if in range of scrollbar). */ + if (IsInsideMM(line, line_start, line_end)) { + uint x = rtl ? text_left : text_right; + + /* If there are buttons for this client, draw them. */ + auto button_find = this->buttons.find(line); + if (button_find != this->buttons.end()) { + this->DrawButtons(x, y, button_find->second); + } + + StringID client_string = STR_JUST_RAW_STRING; + + if (ci->client_id == CLIENT_ID_SERVER) { + client_string = STR_NETWORK_CLIENT_LIST_PLAYER_HOST; + } + if (ci->client_id == _network_own_client_id) { + client_string = STR_NETWORK_CLIENT_LIST_PLAYER_SELF; + } + + SetDParamStr(0, ci->client_name); + DrawString(rtl ? x : text_left + CLIENT_OFFSET_LEFT, rtl ? text_right - CLIENT_OFFSET_LEFT : x, y + text_y_offset, client_string, TC_BLACK); + } + + y += this->line_height; + line++; + } + } + + void DrawWidget(const Rect &r, int widget) const override + { + switch (widget) { + case WID_CL_MATRIX: { + uint line = 0; + + for (const Company *c : Company::Iterate()) { + this->DrawCompany(c, r.left, r.right, r.top, line); + } + /* Specators */ + this->DrawCompany(nullptr, r.left, r.right, r.top, line); + + break; + } + } + } }; void ShowClientList() diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index a0d1a00666..746077abdb 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -288,13 +288,14 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta _network_clients_connected--; DeleteWindowById(WC_CLIENT_LIST_POPUP, this->client_id); - SetWindowDirty(WC_CLIENT_LIST, 0); this->SendPackets(true); delete this->GetInfo(); delete this; + InvalidateWindowData(WC_CLIENT_LIST, 0); + return status; } @@ -1043,6 +1044,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MAP_OK(Packet * this->GetClientName(client_name, lastof(client_name)); NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, client_name, nullptr, this->client_id); + InvalidateWindowData(WC_CLIENT_LIST, 0); /* Mark the client as pre-active, and wait for an ACK * so we know he is done loading and in sync with us */ @@ -2061,6 +2063,9 @@ void NetworkServerDoMove(ClientID client_id, CompanyID company_id) NetworkAction action = (company_id == COMPANY_SPECTATOR) ? NETWORK_ACTION_COMPANY_SPECTATOR : NETWORK_ACTION_COMPANY_JOIN; NetworkServerSendChat(action, DESTTYPE_BROADCAST, 0, "", client_id, company_id + 1); + + InvalidateWindowClassesData(WC_CLIENT_LIST_POPUP); + InvalidateWindowData(WC_CLIENT_LIST, 0); } /** diff --git a/src/table/sprites.h b/src/table/sprites.h index 9071e61dc6..039d50b9de 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -54,7 +54,7 @@ static const SpriteID SPR_LARGE_SMALL_WINDOW = 682; /** Extra graphic spritenumbers */ static const SpriteID SPR_OPENTTD_BASE = 4896; -static const uint16 OPENTTD_SPRITE_COUNT = 186; +static const uint16 OPENTTD_SPRITE_COUNT = 189; /* Halftile-selection sprites */ static const SpriteID SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE; @@ -166,6 +166,10 @@ static const SpriteID SPR_WINDOW_DEFSIZE = SPR_OPENTTD_BASE + 168; static const SpriteID SPR_RENAME = SPR_OPENTTD_BASE + 184; static const SpriteID SPR_GOTO_LOCATION = SPR_OPENTTD_BASE + 185; +static const SpriteID SPR_CHAT = SPR_OPENTTD_BASE + 186; +static const SpriteID SPR_ADMIN = SPR_OPENTTD_BASE + 187; +static const SpriteID SPR_JOIN = SPR_OPENTTD_BASE + 188; + static const SpriteID SPR_IMG_CARGOFLOW = SPR_OPENTTD_BASE + 174; static const SpriteID SPR_SIGNALS_BASE = SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT; diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index ea50661e4c..6a65382b44 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -205,8 +205,7 @@ static void PopupMainToolbMenu(Window *w, int widget, StringID string, int count /** Enum for the Company Toolbar's network related buttons */ static const int CTMN_CLIENT_LIST = -1; ///< Show the client list static const int CTMN_NEW_COMPANY = -2; ///< Create a new company -static const int CTMN_SPECTATE = -3; ///< Become spectator -static const int CTMN_SPECTATOR = -4; ///< Show a company window as spectator +static const int CTMN_SPECTATOR = -3; ///< Show a company window as spectator /** * Pop up a generic company list menu. @@ -227,8 +226,6 @@ static void PopupMainCompanyToolbMenu(Window *w, int widget, int grey = 0) if (_local_company == COMPANY_SPECTATOR) { list.emplace_back(new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_NEW_COMPANY, CTMN_NEW_COMPANY, NetworkMaxCompaniesReached())); - } else { - list.emplace_back(new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_SPECTATE, CTMN_SPECTATE, NetworkMaxSpectatorsReached())); } break; @@ -619,15 +616,6 @@ static CallBackFunction MenuClickCompany(int index) NetworkSendCommand(0, CCA_NEW, 0, CMD_COMPANY_CTRL, nullptr, nullptr, _local_company); } return CBF_NONE; - - case CTMN_SPECTATE: - if (_network_server) { - NetworkServerDoMove(CLIENT_ID_SERVER, COMPANY_SPECTATOR); - MarkWholeScreenDirty(); - } else { - NetworkClientRequestMove(COMPANY_SPECTATOR); - } - return CBF_NONE; } } ShowCompany((CompanyID)index); diff --git a/src/widgets/network_widget.h b/src/widgets/network_widget.h index 79d33fb067..a453b085cf 100644 --- a/src/widgets/network_widget.h +++ b/src/widgets/network_widget.h @@ -96,7 +96,16 @@ enum NetworkLobbyWidgets { /** Widgets of the #NetworkClientListWindow class. */ enum ClientListWidgets { - WID_CL_PANEL, ///< Panel of the window. + WID_CL_PANEL, ///< Panel of the window. + WID_CL_SERVER_SELECTOR, ///< Selector to hide the server frame. + WID_CL_SERVER_NAME, ///< Server name. + WID_CL_SERVER_NAME_EDIT, ///< Edit button for server name. + WID_CL_SERVER_VISIBILITY, ///< Server visibility. + WID_CL_CLIENT_NAME, ///< Client name. + WID_CL_CLIENT_NAME_EDIT, ///< Edit button for client name. + WID_CL_MATRIX, ///< Company/client list. + WID_CL_SCROLLBAR, ///< Scrollbar for company/client list. + WID_CL_COMPANY_JOIN, ///< Used for QueryWindow when a company has a password. }; /** Widgets of the #NetworkClientListPopupWindow class. */