diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index 54cb8589d4..975d04a36f 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -3843,3 +3843,4 @@ STR_5501 :Start Server STR_5502 :Multiplayer STR_5503 :Enter hostname or IP address: STR_5504 :{SMALLFONT}{BLACK}Show multiplayer status +STR_5505 :Unable to connect to server. diff --git a/src/config.c b/src/config.c index 9e52f68dea..26f9f401a6 100644 --- a/src/config.c +++ b/src/config.c @@ -20,12 +20,13 @@ #include "addresses.h" #include "config.h" +#include "interface/themes.h" +#include "interface/title_sequences.h" #include "localisation/language.h" #include "localisation/localisation.h" -#include "util/util.h" -#include "interface/themes.h" +#include "network/network.h" #include "openrct2.h" -#include "interface/title_sequences.h" +#include "util/util.h" // Magic number for original game cfg file static const int MagicNumber = 0x0003113A; @@ -232,7 +233,7 @@ config_property_definition _twitchDefinitions[] = { config_property_definition _networkDefinitions[] = { { offsetof(network_configuration, player_name), "player_name", CONFIG_VALUE_TYPE_STRING, {.value_string = "Player" }, NULL }, - { offsetof(network_configuration, default_port), "default_port", CONFIG_VALUE_TYPE_UINT32, true, NULL }, + { offsetof(network_configuration, default_port), "default_port", CONFIG_VALUE_TYPE_UINT32, NETWORK_DEFAULT_PORT, NULL }, }; config_section_definition _sectionDefinitions[] = { diff --git a/src/drawing/string.c b/src/drawing/string.c index d42b7a6a00..e495959634 100644 --- a/src/drawing/string.c +++ b/src/drawing/string.c @@ -1268,6 +1268,8 @@ static void ttf_process_initial_colour(int colour, text_draw_info *info) static void ttf_draw_string(rct_drawpixelinfo *dpi, char *text, int colour, int x, int y) { + if (text == NULL) return; + text_draw_info info; info.font_sprite_base = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16); info.flags = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_FLAGS, uint16); diff --git a/src/interface/window.h b/src/interface/window.h index a60cace19f..b856ae8611 100644 --- a/src/interface/window.h +++ b/src/interface/window.h @@ -472,6 +472,7 @@ typedef enum { BTM_TB_DIRTY_FLAG_PARK_RATING = (1 << 4) } BTM_TOOLBAR_DIRTY_FLAGS; +// 000N_TTTL enum { LOADSAVETYPE_LOAD = 0 << 0, LOADSAVETYPE_SAVE = 1 << 0, @@ -479,7 +480,9 @@ enum { LOADSAVETYPE_GAME = 0 << 1, LOADSAVETYPE_LANDSCAPE = 1 << 1, LOADSAVETYPE_SCENARIO = 2 << 1, - LOADSAVETYPE_TRACK = 3 << 1 + LOADSAVETYPE_TRACK = 3 << 1, + + LOADSAVETYPE_NETWORK = 1 << 4, }; extern bool gLoadSaveTitleSequenceSave; diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index 352b9ef238..c2d0b7e7fb 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -2044,6 +2044,7 @@ enum { STR_ENTER_HOSTNAME_OR_IP_ADDRESS = 5503, STR_SHOW_MULTIPLAYER_STATUS_TIP = 5504, + STR_UNABLE_TO_CONNECT_TO_SERVER = 5505, // 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 ab3696eca8..b1cc1e0606 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -26,6 +26,7 @@ #include "network.h" extern "C" { #include "../addresses.h" +#include "../config.h" #include "../game.h" #include "../interface/chat.h" #include "../interface/window.h" @@ -301,7 +302,7 @@ bool Network::BeginClient(const char* host, unsigned short port) server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (server_socket == INVALID_SOCKET) { log_error("Unable to create socket."); - return 0; + return false; } SOCKADDR_IN server_address; @@ -326,7 +327,7 @@ bool Network::BeginClient(const char* host, unsigned short port) mode = NETWORK_MODE_CLIENT; - Client_Send_AUTH(OPENRCT2_VERSION, "Player", ""); + Client_Send_AUTH(OPENRCT2_VERSION, gConfigNetwork.player_name, ""); return true; } diff --git a/src/windows/loadsave.c b/src/windows/loadsave.c index ca0ccc0315..504306ff34 100644 --- a/src/windows/loadsave.c +++ b/src/windows/loadsave.c @@ -162,7 +162,7 @@ rct_window *window_loadsave_open(int type, char *defaultName) } _loadsaveType = type; - switch (type) { + switch (type & 0x0F) { case (LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME): w->widgets[WIDX_TITLE].image = STR_LOAD_GAME; break; @@ -186,8 +186,8 @@ rct_window *window_loadsave_open(int type, char *defaultName) w->no_list_items = 0; w->selected_list_item = -1; - includeNewItem = (type & 1) == LOADSAVETYPE_SAVE; - switch (type & 6) { + includeNewItem = (type & 0x01) == LOADSAVETYPE_SAVE; + switch (type & 0x0E) { case LOADSAVETYPE_GAME: platform_get_user_directory(path, "save"); if (!platform_ensure_directory_exists(path)) { @@ -383,7 +383,7 @@ static void window_loadsave_scrollmousedown(rct_window *w, int scrollIndex, int } else { // TYPE_FILE // Load or overwrite - if ((_loadsaveType & 1) == LOADSAVETYPE_SAVE) + if ((_loadsaveType & 0x01) == LOADSAVETYPE_SAVE) window_overwrite_prompt_open(_listItems[selectedItem].name, _listItems[selectedItem].path); else window_loadsave_select(w, _listItems[selectedItem].path); @@ -730,103 +730,103 @@ static void window_loadsave_populate_list(int includeNewItem, bool browsable, co static void window_loadsave_select(rct_window *w, const char *path) { SDL_RWops* rw; - switch (_loadsaveType) { + switch (_loadsaveType & 0x0E) { case (LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME) : - if (gLoadSaveTitleSequenceSave) { - utf8 newName[MAX_PATH]; - char *extension = (char*)path_get_extension(path_get_filename(path)); - strcpy(newName, path_get_filename(path)); - if (_stricmp(extension, ".sv6") != 0 && _stricmp(extension, ".sc6") != 0) - strcat(newName, ".sv6"); - if (title_sequence_save_exists(gCurrentTitleSequence, newName)) { - RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint32) = (uint32)&_listItems[w->selected_list_item].name; - window_text_input_open(w, WIDX_SCROLL, 5435, 5404, 1170, (uint32)_listItems[w->selected_list_item].name, TITLE_SEQUENCE_MAX_SAVE_LENGTH - 1); - } - else { - title_sequence_add_save(gCurrentTitleSequence, path, newName); - window_close(w); - } - } - else if (game_load_save(path)) { - window_close(w); - gfx_invalidate_screen(); - rct2_endupdate(); + if (gLoadSaveTitleSequenceSave) { + utf8 newName[MAX_PATH]; + char *extension = (char*)path_get_extension(path_get_filename(path)); + strcpy(newName, path_get_filename(path)); + if (_stricmp(extension, ".sv6") != 0 && _stricmp(extension, ".sc6") != 0) + strcat(newName, ".sv6"); + if (title_sequence_save_exists(gCurrentTitleSequence, newName)) { + RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint32) = (uint32)&_listItems[w->selected_list_item].name; + window_text_input_open(w, WIDX_SCROLL, 5435, 5404, 1170, (uint32)_listItems[w->selected_list_item].name, TITLE_SEQUENCE_MAX_SAVE_LENGTH - 1); } else { - // 1050, not the best message... - window_error_open(STR_LOAD_GAME, 1050); + title_sequence_add_save(gCurrentTitleSequence, path, newName); + window_close(w); } - break; - case (LOADSAVETYPE_SAVE | LOADSAVETYPE_GAME) : - rw = platform_sdl_rwfromfile(path, "wb+"); - if (rw != NULL) { - int success = scenario_save(rw, gConfigGeneral.save_plugin_data ? 1 : 0); - SDL_RWclose(rw); - if (success) { - window_close_by_class(WC_LOADSAVE); - game_do_command(0, 1047, 0, -1, GAME_COMMAND_SET_RIDE_APPEARANCE, 0, 0); - gfx_invalidate_screen(); - } else { - window_error_open(STR_SAVE_GAME, 1047); - } + } + else if (game_load_save(path)) { + window_close(w); + gfx_invalidate_screen(); + rct2_endupdate(); + } + else { + // 1050, not the best message... + window_error_open(STR_LOAD_GAME, 1050); + } + break; + case (LOADSAVETYPE_SAVE | LOADSAVETYPE_GAME) : + rw = platform_sdl_rwfromfile(path, "wb+"); + if (rw != NULL) { + int success = scenario_save(rw, gConfigGeneral.save_plugin_data ? 1 : 0); + SDL_RWclose(rw); + if (success) { + window_close_by_class(WC_LOADSAVE); + game_do_command(0, 1047, 0, -1, GAME_COMMAND_SET_RIDE_APPEARANCE, 0, 0); + gfx_invalidate_screen(); } else { window_error_open(STR_SAVE_GAME, 1047); } - break; - case (LOADSAVETYPE_LOAD | LOADSAVETYPE_LANDSCAPE) : - editor_load_landscape(path); - if (1) { + } else { + window_error_open(STR_SAVE_GAME, 1047); + } + break; + case (LOADSAVETYPE_LOAD | LOADSAVETYPE_LANDSCAPE) : + editor_load_landscape(path); + if (1) { + gfx_invalidate_screen(); + rct2_endupdate(); + } + else { + // 1050, not the best message... + window_error_open(STR_LOAD_LANDSCAPE, 1050); + } + break; + case (LOADSAVETYPE_SAVE | LOADSAVETYPE_LANDSCAPE) : + rw = platform_sdl_rwfromfile(path, "wb+"); + if (rw != NULL) { + int success = scenario_save(rw, gConfigGeneral.save_plugin_data ? 3 : 2); + SDL_RWclose(rw); + if (success) { + window_close_by_class(WC_LOADSAVE); gfx_invalidate_screen(); - rct2_endupdate(); - } - else { - // 1050, not the best message... - window_error_open(STR_LOAD_LANDSCAPE, 1050); - } - break; - case (LOADSAVETYPE_SAVE | LOADSAVETYPE_LANDSCAPE) : - rw = platform_sdl_rwfromfile(path, "wb+"); - if (rw != NULL) { - int success = scenario_save(rw, gConfigGeneral.save_plugin_data ? 3 : 2); - SDL_RWclose(rw); - if (success) { - window_close_by_class(WC_LOADSAVE); - gfx_invalidate_screen(); - } else { - window_error_open(STR_SAVE_LANDSCAPE, 1049); - } } else { window_error_open(STR_SAVE_LANDSCAPE, 1049); } - break; - case (LOADSAVETYPE_SAVE | LOADSAVETYPE_SCENARIO) : - { - rct_s6_info *s6Info = (rct_s6_info*)0x0141F570; - int parkFlagsBackup = RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32); - RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) &= ~PARK_FLAGS_18; - s6Info->var_000 = 255; - rw = platform_sdl_rwfromfile(path, "wb+"); - int success = 0; - if (rw != NULL) { - success = scenario_save(rw, gConfigGeneral.save_plugin_data ? 3 : 2); - SDL_RWclose(rw); - } - RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) = parkFlagsBackup; - - if (success) { - window_close_by_class(WC_LOADSAVE); - title_load(); - } else { - window_error_open(STR_SAVE_SCENARIO, STR_SCENARIO_SAVE_FAILED); - s6Info->var_000 = 4; - } + } else { + window_error_open(STR_SAVE_LANDSCAPE, 1049); } break; - case (LOADSAVETYPE_LOAD | LOADSAVETYPE_TRACK) : - window_install_track_open(path); - window_close_by_class(WC_LOADSAVE); - break; + case (LOADSAVETYPE_SAVE | LOADSAVETYPE_SCENARIO) : + { + rct_s6_info *s6Info = (rct_s6_info*)0x0141F570; + int parkFlagsBackup = RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32); + RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) &= ~PARK_FLAGS_18; + s6Info->var_000 = 255; + rw = platform_sdl_rwfromfile(path, "wb+"); + int success = 0; + if (rw != NULL) { + success = scenario_save(rw, gConfigGeneral.save_plugin_data ? 3 : 2); + SDL_RWclose(rw); } + RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) = parkFlagsBackup; + + if (success) { + window_close_by_class(WC_LOADSAVE); + title_load(); + } else { + window_error_open(STR_SAVE_SCENARIO, STR_SCENARIO_SAVE_FAILED); + s6Info->var_000 = 4; + } + break; + } + case (LOADSAVETYPE_LOAD | LOADSAVETYPE_TRACK) : + window_install_track_open(path); + window_close_by_class(WC_LOADSAVE); + break; + } } #pragma region Overwrite prompt diff --git a/src/windows/server_list.c b/src/windows/server_list.c index e3215acac0..bfb5b3465c 100644 --- a/src/windows/server_list.c +++ b/src/windows/server_list.c @@ -24,6 +24,7 @@ #include "../localisation/localisation.h" #include "../network/network.h" #include "../sprites.h" +#include "error.h" #define WWIDTH_MIN 500 #define WHEIGHT_MIN 300 @@ -79,8 +80,6 @@ static void window_server_list_invalidate(rct_window *w); static void window_server_list_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_server_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex); -static void server_list_get_item_button(int buttonIndex, int x, int y, int width, int *outX, int *outY); - static rct_window_event_list window_server_list_events = { window_server_list_close, window_server_list_mouseup, @@ -114,10 +113,15 @@ static rct_window_event_list window_server_list_events = { static int _hoverButtonIndex = -1; +static void server_list_get_item_button(int buttonIndex, int x, int y, int width, int *outX, int *outY); +static void server_list_update_player_name(rct_window *w); static void server_list_load_saved_servers(); static void server_list_save_saved_servers(); static void dispose_saved_server_list(); static void dispose_saved_server(saved_server *serverInfo); +static void add_saved_server(char *address); +static void remove_saved_server(int index); +static void join_server(char *address, bool spectate); void window_server_list_open() { @@ -162,11 +166,7 @@ void window_server_list_open() static void window_server_list_close(rct_window *w) { - if (strlen(_playerName) > 0) { - SafeFree(gConfigNetwork.player_name); - gConfigNetwork.player_name = _strdup(_playerName); - config_save_default(); - } + server_list_update_player_name(w); dispose_saved_server_list(); } @@ -182,6 +182,9 @@ static void window_server_list_mouseup(rct_window *w, int widgetIndex) case WIDX_ADD_SERVER: window_text_input_open(w, widgetIndex, STR_ADD_SERVER, STR_ENTER_HOSTNAME_OR_IP_ADDRESS, STR_NONE, 0, 128); break; + case WIDX_START_SERVER: + window_loadsave_open(LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME | LOADSAVETYPE_NETWORK, NULL); + break; } } @@ -206,15 +209,23 @@ static void window_server_list_scroll_getsize(rct_window *w, int scrollIndex, in static void window_server_list_scroll_mousedown(rct_window *w, int scrollIndex, int x, int y) { - if (w->selected_list_item == -1) return; + int serverIndex = w->selected_list_item; + if (serverIndex < 0) return; + if (serverIndex >= _numSavedServers) return; + + char *serverAddress = _savedServers[serverIndex].address; switch (_hoverButtonIndex) { case WIDX_LIST_REMOVE: + remove_saved_server(serverIndex); + w->no_list_items = _numSavedServers; + window_invalidate(w); break; case WIDX_LIST_SPECTATE: + join_server(serverAddress, true); break; default: - // Join + join_server(serverAddress, false); break; } } @@ -251,20 +262,29 @@ static void window_server_list_scroll_mouseover(rct_window *w, int scrollIndex, static void window_server_list_textinput(rct_window *w, int widgetIndex, char *text) { - if (widgetIndex != WIDX_PLAYER_NAME_INPUT || text == NULL) - return; + if (text == NULL || text[0] == 0) return; - if (strcmp(_playerName, text) == 0) - return; + switch (widgetIndex) { + case WIDX_PLAYER_NAME_INPUT: + if (strcmp(_playerName, text) == 0) + return; - if (strlen(text) == 0) { - memset(_playerName, 0, sizeof(_playerName)); - } else { - memset(_playerName, 0, sizeof(_playerName)); - strcpy(_playerName, text); + if (strlen(text) == 0) { + memset(_playerName, 0, sizeof(_playerName)); + } else { + memset(_playerName, 0, sizeof(_playerName)); + strcpy(_playerName, text); + } + + widget_invalidate(w, WIDX_PLAYER_NAME_INPUT); + break; + + case WIDX_ADD_SERVER: + add_saved_server(text); + w->no_list_items = _numSavedServers; + window_invalidate(w); + break; } - - widget_invalidate(w, WIDX_PLAYER_NAME_INPUT); } static void window_server_list_invalidate(rct_window *w) @@ -348,6 +368,15 @@ static void server_list_get_item_button(int buttonIndex, int x, int y, int width *outY = y + 2; } +static void server_list_update_player_name(rct_window *w) +{ + if (strlen(_playerName) > 0) { + SafeFree(gConfigNetwork.player_name); + gConfigNetwork.player_name = _strdup(_playerName); + config_save_default(); + } +} + static char *freadstralloc(FILE *file) { int capacity = 64; @@ -451,3 +480,56 @@ static void dispose_saved_server(saved_server *serverInfo) SafeFree(serverInfo->name); SafeFree(serverInfo->description); } + +static void add_saved_server(char *address) +{ + _numSavedServers++; + if (_savedServers == NULL) { + _savedServers = malloc(_numSavedServers * sizeof(saved_server)); + } else { + _savedServers = realloc(_savedServers, _numSavedServers * sizeof(saved_server)); + } + + int index = _numSavedServers - 1; + _savedServers[index].address = _strdup(address); + _savedServers[index].name = _strdup(address); + _savedServers[index].description = NULL; +} + +static void remove_saved_server(int index) +{ + if (_numSavedServers <= index) return; + + int serversToMove = _numSavedServers - index - 1; + memmove(&_savedServers[index], &_savedServers[index + 1], serversToMove * sizeof(saved_server)); + + _numSavedServers--; + _savedServers = realloc(_savedServers, _numSavedServers * sizeof(saved_server)); +} + +static char *substr(char *start, int length) +{ + char *result = malloc(length + 1); + memcpy(result, start, length); + result[length] = 0; + return result; +} + +static void join_server(char *address, bool spectate) +{ + int port = gConfigNetwork.default_port; + + char *colon = strchr(address, ':'); + if (colon != NULL) { + address = substr(address, colon - address); + sscanf(colon + 1, "%d", &port); + } + + if (!network_begin_client(address, port)) { + window_error_open(STR_UNABLE_TO_CONNECT_TO_SERVER, STR_NONE); + } + + if (colon != NULL) { + free(address); + } +}