diff options
author | Patric Stout <truebrain@openttd.org> | 2021-04-23 01:52:57 +0200 |
---|---|---|
committer | Patric Stout <github@truebrain.nl> | 2021-04-24 21:43:58 +0200 |
commit | ff708c2c659477da04fab108ca8e46a60c0d60b0 (patch) | |
tree | 0a60165ca72b8cc20664bf04032ae0e172f4a13b | |
parent | 526635942451479bee66e9eb61c50f91ae48a7dd (diff) | |
download | openttd-ff708c2c659477da04fab108ca8e46a60c0d60b0.tar.xz |
Add: admin menu for companies in multiplayer games
You can now easily do:
- a password reset (unlock)
- remove an empty company (reset company)
-rw-r--r-- | src/company_cmd.cpp | 3 | ||||
-rw-r--r-- | src/lang/english.txt | 12 | ||||
-rw-r--r-- | src/network/network_gui.cpp | 234 | ||||
-rw-r--r-- | src/network/network_server.cpp | 3 | ||||
-rw-r--r-- | src/widgets/network_widget.h | 5 | ||||
-rw-r--r-- | src/window_type.h | 6 |
6 files changed, 97 insertions, 166 deletions
diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index c3483a9bf..063d32d3f 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -571,7 +571,6 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY) GeneratePresidentName(c); SetWindowDirty(WC_GRAPH_LEGEND, 0); - InvalidateWindowClassesData(WC_CLIENT_LIST_POPUP); InvalidateWindowData(WC_CLIENT_LIST, 0); InvalidateWindowData(WC_LINKGRAPH_LEGEND, 0); BuildOwnerLegend(); @@ -909,6 +908,8 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 CompanyAdminRemove(c_index, (CompanyRemoveReason)reason); if (StoryPage::GetNumItems() == 0 || Goal::GetNumItems() == 0) InvalidateWindowData(WC_MAIN_TOOLBAR, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); + break; } diff --git a/src/lang/english.txt b/src/lang/english.txt index 13cde55d9..a6d70e26d 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2124,10 +2124,6 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Company STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Online players STR_NETWORK_COMPANY_LIST_NEW_COMPANY :New company -# Network client list popup for clients -STR_NETWORK_CLIENTLIST_KICK :Kick -STR_NETWORK_CLIENTLIST_BAN :Ban - # Network client list STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multiplayer STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server @@ -2144,13 +2140,19 @@ STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Edit you 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_ADMIN_CLIENT_TOOLTIP :{BLACK}Administrative actions to perform for this client +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Administrative actions to perform for this company 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_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Delete +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Password unlock + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client STR_NETWORK_SPECTATORS :Spectators diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index fcfeded5a..ceeca633e 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -17,6 +17,7 @@ #include "network.h" #include "network_base.h" #include "network_content.h" +#include "network_server.h" #include "../gui.h" #include "network_udp.h" #include "../window_func.h" @@ -26,6 +27,7 @@ #include "../querystring_gui.h" #include "../sortlist_type.h" #include "../company_func.h" +#include "../command_func.h" #include "../core/geometry_func.hpp" #include "../genworld.h" #include "../map_type.h" @@ -1570,146 +1572,6 @@ NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company) extern void DrawCompanyIcon(CompanyID cid, int x, int y); -/** - * Prototype for ClientList actions. - * @param ci The information about the current client. - */ -typedef void ClientList_Action_Proc(const NetworkClientInfo *ci); - -static const NWidgetPart _nested_client_list_popup_widgets[] = { - NWidget(WWT_PANEL, COLOUR_GREY, WID_CLP_PANEL), EndContainer(), -}; - -static WindowDesc _client_list_popup_desc( - WDP_AUTO, nullptr, 0, 0, - WC_CLIENT_LIST_POPUP, WC_CLIENT_LIST, - 0, - _nested_client_list_popup_widgets, lengthof(_nested_client_list_popup_widgets) -); - -/* Here we start to define the options out of the menu */ -static void ClientList_Kick(const NetworkClientInfo *ci) -{ - NetworkServerKickClient(ci->client_id, nullptr); -} - -static void ClientList_Ban(const NetworkClientInfo *ci) -{ - NetworkServerKickOrBanIP(ci->client_id, true, nullptr); -} - -/** Popup selection window to chose an action to perform */ -struct NetworkClientListPopupWindow : Window { - /** Container for actions that can be executed. */ - struct ClientListAction { - StringID name; ///< Name of the action to execute - ClientList_Action_Proc *proc; ///< Action to execute - }; - - uint sel_index; - ClientID client_id; - Point desired_location; - std::vector<ClientListAction> actions; ///< Actions to execute - - /** - * Add an action to the list of actions to execute. - * @param name the name of the action - * @param proc the procedure to execute for the action - */ - inline void AddAction(StringID name, ClientList_Action_Proc *proc) - { - this->actions.push_back({name, proc}); - } - - NetworkClientListPopupWindow(WindowDesc *desc, int x, int y, ClientID client_id) : - Window(desc), - sel_index(0), client_id(client_id) - { - this->desired_location.x = x; - this->desired_location.y = y; - - const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id); - - /* 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); - this->AddAction(STR_NETWORK_CLIENTLIST_BAN, &ClientList_Ban); - } - - this->InitNested(client_id); - CLRBITS(this->flags, WF_WHITE_BORDER); - } - - Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) override - { - return this->desired_location; - } - - void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override - { - Dimension d = *size; - for (const ClientListAction &action : this->actions) { - d = maxdim(GetStringBoundingBox(action.name), d); - } - - d.height *= (uint)this->actions.size(); - 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; - } - - void DrawWidget(const Rect &r, int widget) const override - { - /* Draw the actions */ - int sel = this->sel_index; - int y = r.top + WD_FRAMERECT_TOP; - for (const ClientListAction &action : this->actions) { - TextColour colour; - if (sel-- == 0) { // Selected item, highlight it - GfxFillRect(r.left + 1, y, r.right - 1, y + FONT_HEIGHT_NORMAL - 1, PC_BLACK); - colour = TC_WHITE; - } else { - colour = TC_BLACK; - } - - DrawString(r.left + WD_FRAMERECT_LEFT + 4, r.right - WD_FRAMERECT_RIGHT - 4, y, action.name, colour); - y += FONT_HEIGHT_NORMAL; - } - } - - void OnMouseLoop() override - { - /* We selected an action */ - uint index = (_cursor.pos.y - this->top - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL; - - if (_left_button_down) { - if (index == this->sel_index || index >= this->actions.size()) return; - - this->sel_index = index; - this->SetDirty(); - } else { - if (index < this->actions.size() && _cursor.pos.y >= this->top) { - const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(this->client_id); - if (ci != nullptr) this->actions[index].proc(ci); - } - - DeleteWindowByClass(WC_CLIENT_LIST_POPUP); - } - } -}; - -/** - * Show the popup (action list) - */ -static void PopupClientList(ClientID client_id, int x, int y) -{ - DeleteWindowByClass(WC_CLIENT_LIST_POPUP); - - if (NetworkClientInfo::GetByClientID(client_id) == nullptr) return; - - new NetworkClientListPopupWindow(&_client_list_popup_desc, x, y, client_id); -} - static const NWidgetPart _nested_client_list_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), @@ -1759,6 +1621,17 @@ static WindowDesc _client_list_desc( ); /** + * The possibly entries in a DropDown for an admin. + * Client and companies are mixed; they just have to be unique. + */ +enum DropDownAdmin { + DD_CLIENT_ADMIN_KICK, + DD_CLIENT_ADMIN_BAN, + DD_COMPANY_ADMIN_RESET, + DD_COMPANY_ADMIN_UNLOCK, +}; + +/** * Button shown for either a company or client in the client-list. * * These buttons are dynamic and strongly depends on which company/client @@ -1774,11 +1647,11 @@ public: uint height; ///< Calculated height of the button. uint width; ///< Calculated width of the button. - ButtonCommon(SpriteID sprite, StringID tooltip, Colours colour) : + ButtonCommon(SpriteID sprite, StringID tooltip, Colours colour, bool disabled = false) : sprite(sprite), tooltip(tooltip), colour(colour), - disabled(false) + disabled(disabled) { Dimension d = GetSpriteSize(sprite); this->height = d.height + ScaleGUITrad(WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM); @@ -1803,8 +1676,8 @@ private: 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), + Button(SpriteID sprite, StringID tooltip, Colours colour, T id, ButtonCallback proc, bool disabled = false) : + ButtonCommon(sprite, tooltip, colour, disabled), id(id), proc(proc) { @@ -1830,6 +1703,9 @@ 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. + ClientID dd_client_id; ///< During admin dropdown, track which client this was for. + CompanyID dd_company_id; ///< During admin dropdown, track which company this was for. + 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. @@ -1877,10 +1753,42 @@ private: */ static void OnClickClientAdmin(NetworkClientListWindow *w, Point pt, ClientID client_id) { - PopupClientList(client_id, pt.x + w->left, pt.y + w->top); + DropDownList list; + list.emplace_back(new DropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK, DD_CLIENT_ADMIN_KICK, false)); + list.emplace_back(new DropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN, DD_CLIENT_ADMIN_BAN, false)); + + Rect wi_rect; + wi_rect.left = pt.x; + wi_rect.right = pt.x; + wi_rect.top = pt.y; + wi_rect.bottom = pt.y; + + w->dd_client_id = client_id; + ShowDropDownListAt(w, std::move(list), -1, WID_CL_MATRIX, wi_rect, COLOUR_GREY, true, true); } /** + * Admin 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 OnClickCompanyAdmin(NetworkClientListWindow *w, Point pt, CompanyID company_id) + { + DropDownList list; + list.emplace_back(new DropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET, DD_COMPANY_ADMIN_RESET, NetworkCompanyHasClients(company_id))); + list.emplace_back(new DropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK, DD_COMPANY_ADMIN_UNLOCK, !NetworkCompanyIsPassworded(company_id))); + + Rect wi_rect; + wi_rect.left = pt.x; + wi_rect.right = pt.x; + wi_rect.top = pt.y; + wi_rect.bottom = pt.y; + + w->dd_company_id = company_id; + ShowDropDownListAt(w, std::move(list), -1, WID_CL_MATRIX, wi_rect, COLOUR_GREY, true, true); + } + /** * Chat button on a Client is clicked. * @param w The instance of this window. * @param pt The point where this button was clicked. @@ -1900,6 +1808,7 @@ private: { 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); + if (_network_server) this->buttons[line_count].emplace_back(new CompanyButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP, COLOUR_RED, company_id, &NetworkClientListWindow::OnClickCompanyAdmin)); 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)); @@ -1910,12 +1819,13 @@ private: if (ci->client_playas != company_id) continue; has_players = true; + if (_network_server) this->buttons[line_count].emplace_back(new ClientButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP, COLOUR_RED, ci->client_id, &NetworkClientListWindow::OnClickClientAdmin, _network_own_client_id == ci->client_id)); 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; } + /* Disable the chat button when there are players in this company. */ chat_button->disabled = !has_players; } @@ -2084,6 +1994,14 @@ public: return false; } + void OnDropdownClose(Point pt, int widget, int index, bool instant_close) override + { + /* If you close the dropdown outside the list, don't take any action. */ + if (widget == WID_CL_MATRIX) return; + + Window::OnDropdownClose(pt, widget, index, instant_close); + } + void OnDropdownSelect(int widget, int index) override { switch (widget) { @@ -2093,6 +2011,30 @@ public: _settings_client.network.server_advertise = (index != 0); break; + case WID_CL_MATRIX: + switch (index) { + case DD_CLIENT_ADMIN_KICK: + NetworkServerKickClient(this->dd_client_id, nullptr); + break; + + case DD_CLIENT_ADMIN_BAN: + NetworkServerKickOrBanIP(this->dd_client_id, true, nullptr); + break; + + case DD_COMPANY_ADMIN_RESET: + if (NetworkCompanyHasClients(this->dd_company_id)) break; + DoCommandP(0, CCA_DELETE | this->dd_company_id << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL); + break; + + case DD_COMPANY_ADMIN_UNLOCK: + NetworkServerSetCompanyPassword(this->dd_company_id, "", false); + break; + + default: + NOT_REACHED(); + } + break; + default: NOT_REACHED(); } diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 746077abd..f6b3e1192 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -287,8 +287,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta extern byte _network_clients_connected; _network_clients_connected--; - DeleteWindowById(WC_CLIENT_LIST_POPUP, this->client_id); - this->SendPackets(true); delete this->GetInfo(); @@ -2064,7 +2062,6 @@ 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/widgets/network_widget.h b/src/widgets/network_widget.h index a453b085c..2ed94d284 100644 --- a/src/widgets/network_widget.h +++ b/src/widgets/network_widget.h @@ -108,11 +108,6 @@ enum ClientListWidgets { WID_CL_COMPANY_JOIN, ///< Used for QueryWindow when a company has a password. }; -/** Widgets of the #NetworkClientListPopupWindow class. */ -enum ClientListPopupWidgets { - WID_CLP_PANEL, ///< Panel of the window. -}; - /** Widgets of the #NetworkJoinStatusWindow class. */ enum NetworkJoinStatusWidgets { WID_NJS_BACKGROUND, ///< Background of the window. diff --git a/src/window_type.h b/src/window_type.h index e4b08e6e5..2b486fbdf 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -472,12 +472,6 @@ enum WindowClass { WC_CLIENT_LIST, /** - * Popup for the client list; %Window numbers: - * - #ClientID = #ClientListPopupWidgets - */ - WC_CLIENT_LIST_POPUP, - - /** * Network status window; %Window numbers: * - #WN_NETWORK_STATUS_WINDOW_JOIN = #NetworkJoinStatusWidgets * - #WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD = #NetworkContentDownloadStatusWidgets |