summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/network.cpp5
-rw-r--r--src/network/network_client.cpp10
-rw-r--r--src/network/network_gui.cpp625
-rw-r--r--src/network/network_server.cpp7
4 files changed, 511 insertions, 136 deletions
diff --git a/src/network/network.cpp b/src/network/network.cpp
index 97eadb976..1e0838684 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 6156dc486..d5fe64b11 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 dd25ad5bc..fcfeded5a 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 <emscripten.h>
#endif
+#include <map>
+
+#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,166 +1713,553 @@ 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<typename T>
+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<CompanyID>;
+using ClientButton = Button<ClientID>;
+
+/**
* 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<uint, std::vector<std::unique_ptr<ButtonCommon>>> 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);
+ }
+
+ /**
+ * 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.
+ */
+ static void OnClickCompanyJoin(NetworkClientListWindow *w, Point pt, CompanyID company_id)
+ {
+ 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);
+ }
}
/**
- * Finds the amount of clients and set the height correct
+ * 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.
*/
- bool CheckClientListHeight()
+ static void OnClickClientAdmin(NetworkClientListWindow *w, Point pt, ClientID client_id)
{
- int num = 0;
+ PopupClientList(client_id, pt.x + w->left, pt.y + w->top);
+ }
- /* Should be replaced with a loop through all clients */
+ /**
+ * 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;
+ }
+
+ chat_button->disabled = !has_players;
+ }
+
+ /**
+ * 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);
}
- num *= this->line_height;
+ /* 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<NWidgetBase>(WID_CL_MATRIX);
- int diff = (num + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM) - (this->GetWidget<NWidgetBase>(WID_CL_PANEL)->current_y);
- /* If height is changed */
- if (diff != 0) {
- ResizeWindow(this, 0, diff, false);
- return false;
+ 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 true;
+
+ 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<NWidgetStacked>(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 OnResize() override
+ {
+ this->vscroll->SetCapacityFromWidget(this, WID_CL_MATRIX);
}
- void OnPaint() override
+ void SetStringParameters(int widget) const override
{
- /* Check if we need to reset the height */
- if (!this->CheckClientListHeight()) return;
+ switch (widget) {
+ case WID_CL_SERVER_NAME:
+ SetDParamStr(0, _settings_client.network.server_name);
+ break;
- this->DrawWidgets();
+ case WID_CL_SERVER_VISIBILITY:
+ SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]);
+ break;
+
+ case WID_CL_CLIENT_NAME:
+ SetDParamStr(0, _settings_client.network.client_name);
+ break;
+ }
}
- void DrawWidget(const Rect &r, int widget) const override
+ void OnClick(Point pt, int widget, int click_count) override
{
- if (widget != WID_CL_PANEL) return;
+ switch (widget) {
+ case WID_CL_SERVER_NAME_EDIT:
+ if (!_network_server) 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;
+ 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;
- 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;
+ 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;
- 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;
+ ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_CL_SERVER_VISIBILITY, 0, 0);
+ break;
- 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;
+ case WID_CL_MATRIX: {
+ ButtonCommon *button = this->GetButtonAtPoint(pt);
+ if (button == nullptr) break;
+
+ button->OnClick(this, pt);
+ break;
}
+ }
+ }
- 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);
+ bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override
+ {
+ 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;
+ };
+ }
+
+ 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();
+ }
+
+ 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;
}
- /* Filter out spectators */
- if (Company::IsValidID(ci->client_playas)) DrawCompanyIcon(ci->client_playas, icon_left, y + icon_offset);
+ case WID_CL_CLIENT_NAME_EDIT: {
+ if (!NetworkValidateClientName(str)) break;
- DrawString(name_left, name_right, y + text_offset, ci->client_name, colour);
+ uint index;
+ GetSettingFromName("network.client_name", &index);
+ SetSettingValue(index, str);
+ this->InvalidateData();
+ break;
+ }
- y += line_height;
+ case WID_CL_COMPANY_JOIN:
+ NetworkClientRequestMove(this->join_company, str);
+ break;
}
}
- void OnClick(Point pt, int widget, int click_count) override
+ /**
+ * 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<std::unique_ptr<ButtonCommon>> &buttons) const
{
- /* 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--;
+ 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;
}
}
- void OnMouseOver(Point pt, int widget) override
+ /**
+ * 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
{
- /* -1 means we left the current window */
- if (pt.y == -1) {
- this->selected_item = -1;
- this->SetDirty();
- return;
+ 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);
+ }
}
- /* Find the new selected item (if any) */
- pt.y -= this->GetWidget<NWidgetBase>(WID_CL_PANEL)->pos_y;
- int item = -1;
- if (IsInsideMM(pt.y, WD_FRAMERECT_TOP, this->GetWidget<NWidgetBase>(WID_CL_PANEL)->current_y - WD_FRAMERECT_BOTTOM)) {
- item = (pt.y - WD_FRAMERECT_TOP) / this->line_height;
+ 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;
- /* It did not change.. no update! */
- if (item == this->selected_item) return;
- this->selected_item = item;
+ 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);
- /* Repaint */
- this->SetDirty();
+ break;
+ }
+ }
}
};
diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp
index a0d1a0066..746077abd 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);
}
/**