diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 83b2efd443..287c7602f1 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -4175,6 +4175,10 @@ STR_5863 :{SMALLFONT}{BLACK}Only allow players with known keys to join. STR_5864 :This server only allows whitelisted players to connect. STR_5865 :Log chat history STR_5866 :{SMALLFONT}{BLACK}Logs all chat history to files in your user directory. +STR_5867 :{WINDOW_COLOUR_2}Provider Name: {BLACK}{STRING} +STR_5868 :{WINDOW_COLOUR_2}Provider E-mail: {BLACK}{STRING} +STR_5869 :{WINDOW_COLOUR_2}Provider Website: {BLACK}{STRING} +STR_5870 :{SMALLFONT}{BLACK}Show server information ############# # Scenarios # diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index c8bc1a4221..ffccf487f5 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -2639,6 +2639,10 @@ enum { STR_MULTIPLAYER_UNKNOWN_KEY_DISALLOWED = 5864, STR_LOG_CHAT = 5865, STR_LOG_CHAT_TIP = 5866, + STR_PROVIDER_NAME = 5867, + STR_PROVIDER_EMAIL = 5868, + STR_PROVIDER_WEBSITE = 5869, + STR_SHOW_SERVER_INFO_TIP = 5870, // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 diff --git a/src/network/network.cpp b/src/network/network.cpp index ed020487db..eb94b4a959 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -111,6 +111,7 @@ Network::Network() client_command_handlers[NETWORK_COMMAND_SHOWERROR] = &Network::Client_Handle_SHOWERROR; client_command_handlers[NETWORK_COMMAND_GROUPLIST] = &Network::Client_Handle_GROUPLIST; client_command_handlers[NETWORK_COMMAND_EVENT] = &Network::Client_Handle_EVENT; + client_command_handlers[NETWORK_COMMAND_GAMEINFO] = &Network::Client_Handle_GAMEINFO; client_command_handlers[NETWORK_COMMAND_TOKEN] = &Network::Client_Handle_TOKEN; server_command_handlers.resize(NETWORK_COMMAND_MAX, 0); server_command_handlers[NETWORK_COMMAND_AUTH] = &Network::Server_Handle_AUTH; @@ -142,6 +143,12 @@ bool Network::Init() #endif status = NETWORK_STATUS_READY; + + ServerName = std::string(); + ServerDescription = std::string(); + ServerProviderName = std::string(); + ServerProviderEmail = std::string(); + ServerProviderWebsite = std::string(); return true; } @@ -303,6 +310,12 @@ bool Network::BeginServer(unsigned short port, const char* address) return false; } + ServerName = gConfigNetwork.server_name; + ServerDescription = gConfigNetwork.server_description; + ServerProviderName = gConfigNetwork.provider_name; + ServerProviderEmail = gConfigNetwork.provider_email; + ServerProviderWebsite = gConfigNetwork.provider_website; + cheats_reset(); LoadGroups(); BeginChatLog(); @@ -1487,6 +1500,9 @@ void Network::Client_Handle_AUTH(NetworkConnection& connection, NetworkPacket& p { packet >> (uint32&)connection.AuthStatus >> (uint8&)player_id; switch(connection.AuthStatus) { + case NETWORK_AUTH_OK: + Client_Send_GAMEINFO(); + break; case NETWORK_AUTH_BADNAME: connection.SetLastDisconnectReason(STR_MULTIPLAYER_BAD_PLAYER_NAME); shutdown(connection.Socket, SHUT_RDWR); @@ -1518,6 +1534,8 @@ void Network::Client_Handle_AUTH(NetworkConnection& connection, NetworkPacket& p shutdown(connection.Socket, SHUT_RDWR); break; default: + connection.SetLastDisconnectReason(STR_MULTIPLAYER_INCORRECT_SOFTWARE_VERSION); + shutdown(connection.Socket, SHUT_RDWR); break; } } @@ -1913,6 +1931,39 @@ void Network::Client_Handle_EVENT(NetworkConnection& connection, NetworkPacket& } } +void Network::Client_Send_GAMEINFO() +{ + log_verbose("requesting gameinfo"); + std::unique_ptr packet = std::move(NetworkPacket::Allocate()); + *packet << (uint32)NETWORK_COMMAND_GAMEINFO; + server_connection.QueuePacket(std::move(packet)); +} + +std::string json_stdstring_value(const json_t * string) +{ + const char * cstr = json_string_value(string); + return cstr == nullptr ? std::string() : std::string(cstr); +} + +void Network::Client_Handle_GAMEINFO(NetworkConnection& connection, NetworkPacket& packet) +{ + const char * jsonString = packet.ReadString(); + + json_error_t error; + json_t *root = json_loads(jsonString, 0, &error); + + ServerName = json_stdstring_value(json_object_get(root, "name")); + ServerDescription = json_stdstring_value(json_object_get(root, "description")); + + json_t *jsonProvider = json_object_get(root, "provider"); + if (jsonProvider != nullptr) { + ServerProviderName = json_stdstring_value(json_object_get(root, "name")); + ServerProviderEmail = json_stdstring_value(json_object_get(root, "email")); + ServerProviderWebsite = json_stdstring_value(json_object_get(root, "website")); + } + json_decref(root); +} + int network_init() { return gNetwork.Init(); @@ -2394,6 +2445,12 @@ static void network_get_keymap_path(utf8 *buffer, size_t bufferSize) Path::Append(buffer, bufferSize, "keymappings.json"); } +const utf8 * network_get_server_name() { return gNetwork.ServerName.c_str(); } +const utf8 * network_get_server_description() { return gNetwork.ServerDescription.c_str(); } +const utf8 * network_get_server_provider_name() { return gNetwork.ServerProviderName.c_str(); } +const utf8 * network_get_server_provider_email() { return gNetwork.ServerProviderEmail.c_str(); } +const utf8 * network_get_server_provider_website() { return gNetwork.ServerProviderWebsite.c_str(); } + #else int network_get_mode() { return NETWORK_MODE_NONE; } int network_get_status() { return NETWORK_STATUS_NONE; } @@ -2441,4 +2498,9 @@ void network_set_password(const char* password) {} uint8 network_get_current_player_id() { return 0; } int network_get_current_player_group_index() { return 0; } void network_append_chat_log(const utf8 *text) { } +const utf8 * network_get_server_name() { return nullptr; } +const utf8 * network_get_server_description() { return nullptr; } +const utf8 * network_get_server_provider_name() { return nullptr; } +const utf8 * network_get_server_provider_email() { return nullptr; } +const utf8 * network_get_server_provider_website() { return nullptr; } #endif /* DISABLE_NETWORK */ diff --git a/src/network/network.h b/src/network/network.h index 61994aed56..7a81d6f4df 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -144,6 +144,7 @@ public: void Server_Send_GROUPLIST(NetworkConnection& connection); void Server_Send_EVENT_PLAYER_JOINED(const char *playerName); void Server_Send_EVENT_PLAYER_DISCONNECTED(const char *playerName, const char *reason); + void Client_Send_GAMEINFO(); std::vector> player_list; std::vector> group_list; @@ -151,6 +152,12 @@ public: std::vector challenge; NetworkUserManager _userManager; + std::string ServerName; + std::string ServerDescription; + std::string ServerProviderName; + std::string ServerProviderEmail; + std::string ServerProviderWebsite; + private: bool ProcessConnection(NetworkConnection& connection); void ProcessPacket(NetworkConnection& connection, NetworkPacket& packet); @@ -226,6 +233,7 @@ private: void Server_Handle_PING(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_PINGLIST(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_SETDISCONNECTMSG(NetworkConnection& connection, NetworkPacket& packet); + void Client_Handle_GAMEINFO(NetworkConnection& connection, NetworkPacket& packet); void Server_Handle_GAMEINFO(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_SHOWERROR(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_GROUPLIST(NetworkConnection& connection, NetworkPacket& packet); @@ -292,6 +300,11 @@ void network_set_password(const char* password); void network_print_error(); void network_append_chat_log(const utf8 *text); +const utf8 * network_get_server_name(); +const utf8 * network_get_server_description(); +const utf8 * network_get_server_provider_name(); +const utf8 * network_get_server_provider_email(); +const utf8 * network_get_server_provider_website(); #ifdef __cplusplus } diff --git a/src/windows/multiplayer.c b/src/windows/multiplayer.c index 01a19ebfd2..0e4461ceb3 100644 --- a/src/windows/multiplayer.c +++ b/src/windows/multiplayer.c @@ -25,6 +25,7 @@ #include "dropdown.h" enum { + WINDOW_MULTIPLAYER_PAGE_INFORMATION, WINDOW_MULTIPLAYER_PAGE_PLAYERS, WINDOW_MULTIPLAYER_PAGE_GROUPS, WINDOW_MULTIPLAYER_PAGE_OPTIONS @@ -38,10 +39,11 @@ enum WINDOW_MULTIPLAYER_WIDGET_IDX { WIDX_TAB1, WIDX_TAB2, WIDX_TAB3, + WIDX_TAB4, - WIDX_LIST = 7, + WIDX_LIST = 8, - WIDX_DEFAULT_GROUP = 7, + WIDX_DEFAULT_GROUP = 8, WIDX_DEFAULT_GROUP_DROPDOWN, WIDX_ADD_GROUP, WIDX_REMOVE_GROUP, @@ -50,7 +52,7 @@ enum WINDOW_MULTIPLAYER_WIDGET_IDX { WIDX_SELECTED_GROUP_DROPDOWN, WIDX_PERMISSIONS_LIST, - WIDX_LOG_CHAT_CHECKBOX = 7, + WIDX_LOG_CHAT_CHECKBOX = 8, WIDX_KNOWN_KEYS_ONLY_CHECKBOX, }; @@ -59,10 +61,16 @@ enum WINDOW_MULTIPLAYER_WIDGET_IDX { { WWT_CAPTION, 0, 1, 338, 1, 14, STR_MULTIPLAYER, STR_WINDOW_TITLE_TIP }, /* title bar */ \ { WWT_CLOSEBOX, 0, 327, 337, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, /* close x button */ \ { WWT_RESIZE, 1, 0, 339, 43, 239, 0x0FFFFFFFF, STR_NONE }, /* content panel */ \ + { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_SHOW_SERVER_INFO_TIP }, /* tab */ \ { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_PLAYERS_TIP }, /* tab */ \ { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_GROUPS_TIP }, /* tab */ \ { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_OPTIONS_TIP } /* tab */ \ +static rct_widget window_multiplayer_information_widgets[] = { + MAIN_MULTIPLAYER_WIDGETS, + { WIDGETS_END } +}; + static rct_widget window_multiplayer_players_widgets[] = { MAIN_MULTIPLAYER_WIDGETS, { WWT_SCROLL, 1, 3, 336, 60, 236, 2, STR_NONE }, // list @@ -90,19 +98,27 @@ static rct_widget window_multiplayer_options_widgets[] = { }; static rct_widget *window_multiplayer_page_widgets[] = { + window_multiplayer_information_widgets, window_multiplayer_players_widgets, window_multiplayer_groups_widgets, window_multiplayer_options_widgets }; const uint64 window_multiplayer_page_enabled_widgets[] = { - (1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) | (1 << WIDX_TAB3), - (1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) | (1 << WIDX_TAB3) | (1 << WIDX_DEFAULT_GROUP) | (1 << WIDX_DEFAULT_GROUP_DROPDOWN) | (1 << WIDX_ADD_GROUP) | (1 << WIDX_REMOVE_GROUP) | (1 << WIDX_RENAME_GROUP) | (1 << WIDX_SELECTED_GROUP) | (1 << WIDX_SELECTED_GROUP_DROPDOWN), - (1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) | (1 << WIDX_TAB3) | (1 << WIDX_LOG_CHAT_CHECKBOX) | (1 << WIDX_KNOWN_KEYS_ONLY_CHECKBOX), + (1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) | (1 << WIDX_TAB3) | (1 << WIDX_TAB4), + (1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) | (1 << WIDX_TAB3) | (1 << WIDX_TAB4), + (1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) | (1 << WIDX_TAB3) | (1 << WIDX_TAB4) | (1 << WIDX_DEFAULT_GROUP) | (1 << WIDX_DEFAULT_GROUP_DROPDOWN) | (1 << WIDX_ADD_GROUP) | (1 << WIDX_REMOVE_GROUP) | (1 << WIDX_RENAME_GROUP) | (1 << WIDX_SELECTED_GROUP) | (1 << WIDX_SELECTED_GROUP_DROPDOWN), + (1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) | (1 << WIDX_TAB3) | (1 << WIDX_TAB4) | (1 << WIDX_LOG_CHAT_CHECKBOX) | (1 << WIDX_KNOWN_KEYS_ONLY_CHECKBOX), }; static uint8 _selectedGroup = 0; +static void window_multiplayer_information_mouseup(rct_window *w, int widgetIndex); +static void window_multiplayer_information_resize(rct_window *w); +static void window_multiplayer_information_update(rct_window *w); +static void window_multiplayer_information_invalidate(rct_window *w); +static void window_multiplayer_information_paint(rct_window *w, rct_drawpixelinfo *dpi); + static void window_multiplayer_players_mouseup(rct_window *w, int widgetIndex); static void window_multiplayer_players_resize(rct_window *w); static void window_multiplayer_players_update(rct_window *w); @@ -132,6 +148,37 @@ static void window_multiplayer_options_update(rct_window *w); static void window_multiplayer_options_invalidate(rct_window *w); static void window_multiplayer_options_paint(rct_window *w, rct_drawpixelinfo *dpi); +static rct_window_event_list window_multiplayer_information_events = { + NULL, + window_multiplayer_information_mouseup, + window_multiplayer_information_resize, + NULL, + NULL, + NULL, + window_multiplayer_information_update, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + window_multiplayer_information_invalidate, + window_multiplayer_information_paint, + NULL +}; + static rct_window_event_list window_multiplayer_players_events = { NULL, window_multiplayer_players_mouseup, @@ -226,35 +273,35 @@ static rct_window_event_list window_multiplayer_options_events = { }; static rct_window_event_list *window_multiplayer_page_events[] = { + &window_multiplayer_information_events, &window_multiplayer_players_events, &window_multiplayer_groups_events, &window_multiplayer_options_events }; -static const int window_multiplayer_animation_divisor[] = { 4, 2, 2 }; -static const int window_multiplayer_animation_frames[] = { 8, 7, 4 }; +static const int window_multiplayer_animation_divisor[] = { 4, 4, 2, 2 }; +static const int window_multiplayer_animation_frames[] = { 8, 8, 7, 4 }; static void window_multiplayer_draw_tab_images(rct_window *w, rct_drawpixelinfo *dpi); static void window_multiplayer_set_page(rct_window* w, int page); +static bool _windowInformationSizeDirty; +static rct_xy16 _windowInformationSize; + void window_multiplayer_open() { - rct_window* window; - // Check if window is already open - window = window_bring_to_front_by_class(WC_MULTIPLAYER); - if (window != NULL) - return; - - window = window_create_auto_pos(320, 144, &window_multiplayer_players_events, WC_MULTIPLAYER, WF_10 | WF_RESIZABLE); - - window_multiplayer_set_page(window, WINDOW_MULTIPLAYER_PAGE_PLAYERS); - - window->page = WINDOW_MULTIPLAYER_PAGE_PLAYERS; - window->list_information_type = 0; + rct_window *window = window_bring_to_front_by_class(WC_MULTIPLAYER); + if (window == NULL) { + window = window_create_auto_pos(320, 144, &window_multiplayer_players_events, WC_MULTIPLAYER, WF_10 | WF_RESIZABLE); + window_multiplayer_set_page(window, WINDOW_MULTIPLAYER_PAGE_INFORMATION); + } } -static void window_multiplayer_set_page(rct_window* w, int page){ +static void window_multiplayer_set_page(rct_window* w, int page) +{ + _windowInformationSizeDirty = true; + w->page = page; w->frame_no = 0; w->no_list_items = 0; @@ -322,6 +369,115 @@ static void window_multiplayer_groups_show_group_dropdown(rct_window *w, rct_wid } } +#pragma region Information page + +static void window_multiplayer_information_mouseup(rct_window *w, int widgetIndex) +{ + switch (widgetIndex) { + case WIDX_CLOSE: + window_close(w); + break; + case WIDX_TAB1: + case WIDX_TAB2: + case WIDX_TAB3: + case WIDX_TAB4: + if (w->page != widgetIndex - WIDX_TAB1) { + window_multiplayer_set_page(w, widgetIndex - WIDX_TAB1); + } + break; + } +} + +static rct_xy16 window_multiplayer_information_get_size() +{ + if (!_windowInformationSizeDirty) { + return _windowInformationSize; + } + + int width = 300; + int height = 110; + char * buffer = gConfigNetwork.server_description; + + gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM; + + int numLines, fontSpriteBase; + gfx_wrap_string(buffer, width, &numLines, &fontSpriteBase); + int lineHeight = font_get_line_height(fontSpriteBase); + height += (numLines + 1) * lineHeight; + + _windowInformationSizeDirty = false; + _windowInformationSize = (rct_xy16){ width, height }; + return _windowInformationSize; +} + +static void window_multiplayer_information_resize(rct_window *w) +{ + rct_xy16 size = window_multiplayer_information_get_size(); + window_set_resize(w, size.x, size.y, size.x, size.y); +} + +static void window_multiplayer_information_update(rct_window *w) +{ + w->frame_no++; + widget_invalidate(w, WIDX_TAB1 + w->page); +} + +static void window_multiplayer_information_invalidate(rct_window *w) +{ + colour_scheme_update(w); + window_multiplayer_set_pressed_tab(w); + window_multiplayer_anchor_border_widgets(w); + window_align_tabs(w, WIDX_TAB1, WIDX_TAB4); +} + +static void window_multiplayer_information_paint(rct_window *w, rct_drawpixelinfo *dpi) +{ + window_draw_widgets(w, dpi); + window_multiplayer_draw_tab_images(w, dpi); + + rct_drawpixelinfo clippedDPI; + if (clip_drawpixelinfo(&clippedDPI, dpi, w->x, w->y, w->width, w->height)) { + dpi = &clippedDPI; + + int x = 3; + int y = 50; + int width = w->width - 6; + + const utf8 * name = network_get_server_name(); + { + gfx_draw_string_left_wrapped(dpi, (void*)&name, x, y, width, STR_STRING, w->colours[1]); + y += 11; + } + y += 3; + + const utf8 * description = network_get_server_description(); + if (!str_is_null_or_empty(description)) { + gfx_draw_string_left_wrapped(dpi, (void*)&description, x, y, width, STR_STRING, w->colours[1]); + y += 11; + } + y += 8; + + const utf8 * providerName = network_get_server_provider_name(); + if (!str_is_null_or_empty(providerName)) { + gfx_draw_string_left(dpi, STR_PROVIDER_NAME, (void*)&providerName, 0, x, y); + y += 11; + } + + const utf8 * providerEmail = network_get_server_provider_email(); + if (!str_is_null_or_empty(providerEmail)) { + gfx_draw_string_left(dpi, STR_PROVIDER_EMAIL, (void*)&providerEmail, 0, x, y); + y += 11; + } + + const utf8 * providerWebsite = network_get_server_provider_website(); + if (!str_is_null_or_empty(providerWebsite)) { + gfx_draw_string_left(dpi, STR_PROVIDER_WEBSITE, (void*)&providerWebsite, 0, x, y); + } + } +} + +#pragma endregion + #pragma region Players page static void window_multiplayer_players_mouseup(rct_window *w, int widgetIndex) @@ -333,6 +489,7 @@ static void window_multiplayer_players_mouseup(rct_window *w, int widgetIndex) case WIDX_TAB1: case WIDX_TAB2: case WIDX_TAB3: + case WIDX_TAB4: if (w->page != widgetIndex - WIDX_TAB1) { window_multiplayer_set_page(w, widgetIndex - WIDX_TAB1); } @@ -410,7 +567,7 @@ static void window_multiplayer_players_invalidate(rct_window *w) window_multiplayer_anchor_border_widgets(w); window_multiplayer_players_widgets[WIDX_LIST].right = w->width - 4; window_multiplayer_players_widgets[WIDX_LIST].bottom = w->height - 0x0F; - window_align_tabs(w, WIDX_TAB1, WIDX_TAB3); + window_align_tabs(w, WIDX_TAB1, WIDX_TAB4); } static void window_multiplayer_players_paint(rct_window *w, rct_drawpixelinfo *dpi) @@ -514,6 +671,7 @@ static void window_multiplayer_groups_mouseup(rct_window *w, int widgetIndex) case WIDX_TAB1: case WIDX_TAB2: case WIDX_TAB3: + case WIDX_TAB4: if (w->page != widgetIndex - WIDX_TAB1) { window_multiplayer_set_page(w, widgetIndex - WIDX_TAB1); } @@ -639,11 +797,12 @@ static void window_multiplayer_groups_text_input(rct_window *w, int widgetIndex, static void window_multiplayer_groups_invalidate(rct_window *w) { + colour_scheme_update(w); window_multiplayer_set_pressed_tab(w); window_multiplayer_anchor_border_widgets(w); window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].right = w->width - 4; window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].bottom = w->height - 0x0F; - window_align_tabs(w, WIDX_TAB1, WIDX_TAB3); + window_align_tabs(w, WIDX_TAB1, WIDX_TAB4); // select other group if one is removed while (network_get_group_index(_selectedGroup) == -1 && _selectedGroup > 0) { @@ -754,6 +913,7 @@ static void window_multiplayer_options_mouseup(rct_window *w, int widgetIndex) case WIDX_TAB1: case WIDX_TAB2: case WIDX_TAB3: + case WIDX_TAB4: if (w->page != widgetIndex - WIDX_TAB1) { window_multiplayer_set_page(w, widgetIndex - WIDX_TAB1); } @@ -782,9 +942,10 @@ static void window_multiplayer_options_update(rct_window *w) static void window_multiplayer_options_invalidate(rct_window *w) { + colour_scheme_update(w); window_multiplayer_set_pressed_tab(w); window_multiplayer_anchor_border_widgets(w); - window_align_tabs(w, WIDX_TAB1, WIDX_TAB3); + window_align_tabs(w, WIDX_TAB1, WIDX_TAB4); if (network_get_mode() == NETWORK_MODE_CLIENT) { w->widgets[WIDX_KNOWN_KEYS_ONLY_CHECKBOX].type = WWT_EMPTY; @@ -821,6 +982,7 @@ static void window_multiplayer_draw_tab_image(rct_window *w, rct_drawpixelinfo * static void window_multiplayer_draw_tab_images(rct_window *w, rct_drawpixelinfo *dpi) { + window_multiplayer_draw_tab_image(w, dpi, WINDOW_MULTIPLAYER_PAGE_INFORMATION, SPR_TAB_KIOSKS_AND_FACILITIES_0); window_multiplayer_draw_tab_image(w, dpi, WINDOW_MULTIPLAYER_PAGE_PLAYERS, SPR_TAB_GUESTS_0); window_multiplayer_draw_tab_image(w, dpi, WINDOW_MULTIPLAYER_PAGE_GROUPS, SPR_TAB_STAFF_OPTIONS_0); window_multiplayer_draw_tab_image(w, dpi, WINDOW_MULTIPLAYER_PAGE_OPTIONS, SPR_TAB_GEARS_0);