summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatric Stout <truebrain@openttd.org>2021-04-23 01:52:57 +0200
committerPatric Stout <github@truebrain.nl>2021-04-24 21:43:58 +0200
commitff708c2c659477da04fab108ca8e46a60c0d60b0 (patch)
tree0a60165ca72b8cc20664bf04032ae0e172f4a13b
parent526635942451479bee66e9eb61c50f91ae48a7dd (diff)
downloadopenttd-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.cpp3
-rw-r--r--src/lang/english.txt12
-rw-r--r--src/network/network_gui.cpp234
-rw-r--r--src/network/network_server.cpp3
-rw-r--r--src/widgets/network_widget.h5
-rw-r--r--src/window_type.h6
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