diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index a177274400..1032ba2d42 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -3899,6 +3899,7 @@ STR_5557 :Stay connected after desynchronisation (Multiplayer) STR_5558 :A restart is required for this setting to take effect STR_5559 :10 min. inspections STR_5560 :{SMALLFONT}{BLACK}Sets the inspection time to 'Every 10 minutes' on all rides +<<<<<<< HEAD STR_5561 :Failed to load language file STR_5562 :WARNING! STR_5563 :This feature is currently unstable, take extra caution. @@ -3908,6 +3909,13 @@ STR_5566 :Password: STR_5567 :Advertise STR_5568 :Password Required STR_5569 :This server requires a password +STR_5570 :Fetch Servers +STR_5571 :Join Game +STR_5572 :Add To Favorites +STR_5573 :Remove From Favorites +STR_5574 :Server Name: +STR_5575 :Max Players: +STR_5576 :Port: ##################### # Rides/attractions # diff --git a/src/config.c b/src/config.c index 5dc77248c8..7244269e58 100644 --- a/src/config.c +++ b/src/config.c @@ -241,6 +241,9 @@ config_property_definition _networkDefinitions[] = { { offsetof(network_configuration, default_port), "default_port", CONFIG_VALUE_TYPE_UINT32, NETWORK_DEFAULT_PORT, NULL }, { offsetof(network_configuration, stay_connected), "stay_connected", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, { offsetof(network_configuration, advertise), "advertise", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL }, + { offsetof(network_configuration, maxplayers), "maxplayers", CONFIG_VALUE_TYPE_UINT8, 16, NULL }, + { offsetof(network_configuration, server_name), "server_name", CONFIG_VALUE_TYPE_STRING, {.value_string = "Server" }, NULL }, + { offsetof(network_configuration, master_url), "master_url", CONFIG_VALUE_TYPE_STRING, {.value_string = OPENRCT2_MASTER_URL }, NULL } }; config_section_definition _sectionDefinitions[] = { diff --git a/src/config.h b/src/config.h index e91bbc28a5..120ee9dfb0 100644 --- a/src/config.h +++ b/src/config.h @@ -215,6 +215,9 @@ typedef struct { uint32 default_port; uint8 stay_connected; uint8 advertise; + uint8 maxplayers; + utf8string server_name; + utf8string master_url; } network_configuration; typedef struct theme_window { diff --git a/src/game.c b/src/game.c index c15739fe7e..bfeb6a639c 100644 --- a/src/game.c +++ b/src/game.c @@ -261,7 +261,7 @@ void game_update() numUpdates = clamp(1, numUpdates, 4); } - if (network_get_mode() == NETWORK_MODE_CLIENT && network_get_status() == NETWORK_STATUS_CONNECTED) { + if (network_get_mode() == NETWORK_MODE_CLIENT && network_get_status() == NETWORK_STATUS_CONNECTED && network_get_authstatus() == NETWORK_AUTH_OK) { if (network_get_server_tick() - RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) >= 10) { // make sure client doesn't fall behind the server too much numUpdates += 10; @@ -340,7 +340,7 @@ void game_logic_update() gInUpdateCode = true; /////////////////////////// network_update(); - if (network_get_mode() == NETWORK_MODE_CLIENT && network_get_status() == NETWORK_STATUS_CONNECTED) { + if (network_get_mode() == NETWORK_MODE_CLIENT && network_get_status() == NETWORK_STATUS_CONNECTED && network_get_authstatus() == NETWORK_AUTH_OK) { if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) >= network_get_server_tick()) { // dont run past the server return; diff --git a/src/interface/chat.c b/src/interface/chat.c index ec783c1b4d..eb656b22eb 100644 --- a/src/interface/chat.c +++ b/src/interface/chat.c @@ -60,7 +60,8 @@ void chat_update() void chat_draw() { - if (network_get_mode() == NETWORK_MODE_NONE) { + if (network_get_mode() == NETWORK_MODE_NONE || network_get_status() != NETWORK_STATUS_CONNECTED || network_get_authstatus() != NETWORK_AUTH_OK) { + gChatOpen = false; return; } rct_drawpixelinfo *dpi = (rct_drawpixelinfo*)RCT2_ADDRESS_SCREEN_DPI; @@ -102,7 +103,7 @@ void chat_history_add(const char *src) { int index = _chatHistoryIndex % CHAT_HISTORY_SIZE; memset(_chatHistory[index], 0, CHAT_INPUT_SIZE); - memcpy(_chatHistory[index], src, min(strlen(src), CHAT_INPUT_SIZE)); + memcpy(_chatHistory[index], src, min(strlen(src), CHAT_INPUT_SIZE - 1)); _chatHistoryTime[index] = SDL_GetTicks(); _chatHistoryIndex++; Mixer_Play_Effect(SOUND_NEWS_ITEM, 0, SDL_MIX_MAXVOLUME, 0, 1.5f, true); diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index a78cf7c0c3..c6192a913a 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -2158,6 +2158,13 @@ enum { STR_ADVERTISE = 5567, STR_PASSWORD_REQUIRED = 5568, STR_PASSWORD_REQUIRED_DESC = 5569, + STR_FETCH_SERVERS = 5570, + STR_JOIN_GAME = 5571, + STR_ADD_TO_FAVORITES = 5572, + STR_REMOVE_FROM_FAVORITES = 5573, + STR_SERVER_NAME = 5574, + STR_MAX_PLAYERS = 5575, + STR_PORT = 5576, // 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 d887652989..dae6b7cb7b 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -41,6 +41,7 @@ extern "C" { #include "../interface/window.h" #include "../localisation/date.h" #include "../localisation/localisation.h" +#include "../network/http.h" #include "../scenario.h" #include "../windows/error.h" #include "../util/util.h" @@ -67,6 +68,7 @@ enum { NETWORK_COMMAND_PING, NETWORK_COMMAND_PINGLIST, NETWORK_COMMAND_SETDISCONNECTMSG, + NETWORK_COMMAND_GAMEINFO, NETWORK_COMMAND_MAX, NETWORK_COMMAND_INVALID = -1 }; @@ -161,8 +163,8 @@ bool NetworkPacket::CommandRequiresAuth() { switch (GetCommand()) { case NETWORK_COMMAND_PING: - return false; case NETWORK_COMMAND_AUTH: + case NETWORK_COMMAND_GAMEINFO: return false; default: return true; @@ -254,11 +256,15 @@ bool NetworkConnection::SendPacket(NetworkPacket& packet) return false; } -void NetworkConnection::QueuePacket(std::unique_ptr packet) +void NetworkConnection::QueuePacket(std::unique_ptr packet, bool front) { if (authstatus == NETWORK_AUTH_OK || !packet->CommandRequiresAuth()) { packet->size = (uint16)packet->data->size(); - outboundpackets.push_back(std::move(packet)); + if (front) { + outboundpackets.push_front(std::move(packet)); + } else { + outboundpackets.push_back(std::move(packet)); + } } } @@ -378,7 +384,7 @@ Network::Network() status = NETWORK_STATUS_NONE; last_tick_sent_time = 0; last_ping_sent_time = 0; - strcpy(password, ""); + last_advertise_time = 0; client_command_handlers.resize(NETWORK_COMMAND_MAX, 0); client_command_handlers[NETWORK_COMMAND_AUTH] = &Network::Client_Handle_AUTH; client_command_handlers[NETWORK_COMMAND_MAP] = &Network::Client_Handle_MAP; @@ -394,6 +400,7 @@ Network::Network() server_command_handlers[NETWORK_COMMAND_CHAT] = &Network::Server_Handle_CHAT; server_command_handlers[NETWORK_COMMAND_GAMECMD] = &Network::Server_Handle_GAMECMD; server_command_handlers[NETWORK_COMMAND_PING] = &Network::Server_Handle_PING; + server_command_handlers[NETWORK_COMMAND_GAMEINFO] = &Network::Server_Handle_GAMEINFO; } Network::~Network() @@ -447,6 +454,10 @@ void Network::Close() bool Network::BeginClient(const char* host, unsigned short port) { + if (GetMode() != NETWORK_MODE_NONE) { + return false; + } + Close(); if (!Init()) return false; @@ -508,6 +519,10 @@ bool Network::BeginServer(unsigned short port, const char* address) printf("Ready for clients...\n"); mode = NETWORK_MODE_SERVER; + status = NETWORK_STATUS_CONNECTED; + listening_port = port; + + Advertise(); return true; } @@ -566,14 +581,15 @@ void Network::UpdateServer() } } if (SDL_TICKS_PASSED(SDL_GetTicks(), last_tick_sent_time + 25)) { - last_tick_sent_time = SDL_GetTicks(); Server_Send_TICK(); } if (SDL_TICKS_PASSED(SDL_GetTicks(), last_ping_sent_time + 3000)) { - last_ping_sent_time = SDL_GetTicks(); Server_Send_PING(); Server_Send_PINGLIST(); } + if (SDL_TICKS_PASSED(SDL_GetTicks(), last_advertise_time + 60000)) { + Advertise(); + } SOCKET socket = accept(listening_socket, NULL, NULL); if (socket == INVALID_SOCKET) { if (LAST_SOCKET_ERROR() != EWOULDBLOCK) { @@ -659,21 +675,19 @@ void Network::UpdateClient() if (error == 0) { status = NETWORK_STATUS_CONNECTED; server_connection.ResetLastPacketTime(); - Client_Send_AUTH(OPENRCT2_VERSION, gConfigNetwork.player_name, ""); + Client_Send_AUTH(gConfigNetwork.player_name, ""); window_network_status_open("Authenticating..."); } } }break; case NETWORK_STATUS_CONNECTED: if (!ProcessConnection(server_connection)) { - char errormsg[256]; - char reason[100]; - reason[0] = 0; + std::string errormsg = "Disconnected"; if (server_connection.last_disconnect_reason) { - sprintf(reason, ": %s", server_connection.last_disconnect_reason); + errormsg += ": "; + errormsg += server_connection.last_disconnect_reason; } - sprintf(errormsg, "Disconnected%s", reason); - window_network_status_open(errormsg); + window_network_status_open(errormsg.c_str()); Close(); } ProcessGameCommandQueue(); @@ -722,10 +736,10 @@ const char* Network::FormatChat(NetworkPlayer* fromplayer, const char* text) return formatted; } -void Network::SendPacketToClients(NetworkPacket& packet) +void Network::SendPacketToClients(NetworkPacket& packet, bool front) { for (auto it = client_connection_list.begin(); it != client_connection_list.end(); it++) { - (*it)->QueuePacket(std::move(NetworkPacket::Duplicate(packet))); + (*it)->QueuePacket(std::move(NetworkPacket::Duplicate(packet)), front); } } @@ -765,14 +779,32 @@ void Network::KickPlayer(int playerId) void Network::SetPassword(const char* password) { - safe_strncpy(Network::password, password, sizeof(Network::password)); + Network::password = password; } -void Network::Client_Send_AUTH(const char* gameversion, const char* name, const char* password) +void Network::ShutdownClient() +{ + if (GetMode() == NETWORK_MODE_CLIENT) { + shutdown(server_connection.socket, SHUT_RDWR); + } +} + +void Network::Advertise() +{ + if (gConfigNetwork.advertise && strlen(gConfigNetwork.master_url) > 0) { + last_advertise_time = SDL_GetTicks(); +#ifndef DISABLE_HTTP + std::string url = gConfigNetwork.master_url + std::string("?port=") + std::to_string(listening_port); + http_request_json_async(url.c_str(), [](http_json_response *response)->void{}); +#endif + } +} + +void Network::Client_Send_AUTH(const char* name, const char* password) { std::unique_ptr packet = std::move(NetworkPacket::Allocate()); *packet << (uint32)NETWORK_COMMAND_AUTH; - packet->WriteString(gameversion); + packet->WriteString(OPENRCT2_VERSION); packet->WriteString(name); packet->WriteString(password); server_connection.authstatus = NETWORK_AUTH_REQUESTED; @@ -820,7 +852,7 @@ void Network::Client_Send_CHAT(const char* text) { std::unique_ptr packet = std::move(NetworkPacket::Allocate()); *packet << (uint32)NETWORK_COMMAND_CHAT; - packet->Write((uint8*)text, strlen(text) + 1); + packet->WriteString(text); server_connection.QueuePacket(std::move(packet)); } @@ -828,7 +860,7 @@ void Network::Server_Send_CHAT(const char* text) { std::unique_ptr packet = std::move(NetworkPacket::Allocate()); *packet << (uint32)NETWORK_COMMAND_CHAT; - packet->Write((uint8*)text, strlen(text) + 1); + packet->WriteString(text); SendPacketToClients(*packet); } @@ -848,6 +880,7 @@ void Network::Server_Send_GAMECMD(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx void Network::Server_Send_TICK() { + last_tick_sent_time = SDL_GetTicks(); std::unique_ptr packet = std::move(NetworkPacket::Allocate()); *packet << (uint32)NETWORK_COMMAND_TICK << (uint32)RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) << (uint32)RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_SRAND_0, uint32); SendPacketToClients(*packet); @@ -873,12 +906,13 @@ void Network::Client_Send_PING() void Network::Server_Send_PING() { + last_ping_sent_time = SDL_GetTicks(); std::unique_ptr packet = std::move(NetworkPacket::Allocate()); *packet << (uint32)NETWORK_COMMAND_PING; for (auto it = client_connection_list.begin(); it != client_connection_list.end(); it++) { (*it)->ping_time = SDL_GetTicks(); } - SendPacketToClients(*packet); + SendPacketToClients(*packet, true); } void Network::Server_Send_PINGLIST() @@ -899,6 +933,24 @@ void Network::Server_Send_SETDISCONNECTMSG(NetworkConnection& connection, const connection.QueuePacket(std::move(packet)); } +void Network::Server_Send_GAMEINFO(NetworkConnection& connection) +{ + std::unique_ptr packet = std::move(NetworkPacket::Allocate()); + *packet << (uint32)NETWORK_COMMAND_GAMEINFO; +#ifndef DISABLE_HTTP + json_t* obj = json_object(); + json_object_set(obj, "name", json_string(gConfigNetwork.server_name)); + json_object_set(obj, "haspassword", json_integer(password.size() > 0 ? 1 : 0)); + json_object_set(obj, "description", json_string("")); + json_object_set(obj, "version", json_string(OPENRCT2_VERSION)); + json_object_set(obj, "players", json_integer(player_list.size())); + json_object_set(obj, "maxplayers", json_integer(gConfigNetwork.maxplayers)); + packet->WriteString(json_dumps(obj, 0)); + json_object_clear(obj); +#endif + connection.QueuePacket(std::move(packet)); +} + bool Network::ProcessConnection(NetworkConnection& connection) { int packetStatus; @@ -927,7 +979,9 @@ bool Network::ProcessConnection(NetworkConnection& connection) } while (packetStatus == NETWORK_READPACKET_MORE_DATA || packetStatus == NETWORK_READPACKET_SUCCESS); connection.SendQueuedPackets(); if (!connection.ReceivedPacketRecently()) { - connection.last_disconnect_reason = "No Data"; + if (!connection.last_disconnect_reason) { + connection.last_disconnect_reason = "No Data"; + } return false; } return true; @@ -942,7 +996,7 @@ void Network::ProcessPacket(NetworkConnection& connection, NetworkPacket& packet switch (gNetwork.GetMode()) { case NETWORK_MODE_SERVER: if (server_command_handlers[command]) { - if (connection.authstatus == NETWORK_AUTH_OK || command == NETWORK_COMMAND_AUTH) { + if (connection.authstatus == NETWORK_AUTH_OK || !packet.CommandRequiresAuth()) { (this->*server_command_handlers[command])(connection, packet); } } @@ -988,7 +1042,7 @@ void Network::RemoveClient(std::unique_ptr& connection) lineCh = utf8_write_codepoint(lineCh, FORMAT_RED); char reasonstr[100]; reasonstr[0] = 0; - if (connection->last_disconnect_reason) { + if (connection->last_disconnect_reason && strlen(connection->last_disconnect_reason) < sizeof(reasonstr)) { sprintf(reasonstr, " (%s)", connection->last_disconnect_reason); } sprintf(lineCh, "%s has disconnected%s", connection_player->name, reasonstr); @@ -1057,6 +1111,10 @@ int Network::Client_Handle_AUTH(NetworkConnection& connection, NetworkPacket& pa connection.last_disconnect_reason = "Bad Password"; shutdown(connection.socket, SHUT_RDWR); break; + case NETWORK_AUTH_FULL: + connection.last_disconnect_reason = "Server Full"; + shutdown(connection.socket, SHUT_RDWR); + break; case NETWORK_AUTH_REQUIREPASSWORD: window_network_status_open_password(); break; @@ -1076,11 +1134,14 @@ int Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& pa if (!name) { connection.authstatus = NETWORK_AUTH_BADNAME; } else - if (!password || strlen(password) == 0) { + if ((!password || strlen(password) == 0) && Network::password.size() > 0) { connection.authstatus = NETWORK_AUTH_REQUIREPASSWORD; } else - if (strcmp(password, Network::password) != 0) { + if (password && Network::password != password) { connection.authstatus = NETWORK_AUTH_BADPASSWORD; + } else + if (gConfigNetwork.maxplayers <= player_list.size()) { + connection.authstatus = NETWORK_AUTH_FULL; } else { connection.authstatus = NETWORK_AUTH_OK; NetworkPlayer* player = AddPlayer(name); @@ -1251,15 +1312,21 @@ int Network::Client_Handle_PINGLIST(NetworkConnection& connection, NetworkPacket int Network::Client_Handle_SETDISCONNECTMSG(NetworkConnection& connection, NetworkPacket& packet) { - static char msg[100] = {0}; + static std::string msg; const char* disconnectmsg = packet.ReadString(); if (disconnectmsg) { - safe_strncpy(msg, disconnectmsg, sizeof(msg)); - connection.last_disconnect_reason = msg; + msg = disconnectmsg; + connection.last_disconnect_reason = msg.c_str(); } return 1; } +int Network::Server_Handle_GAMEINFO(NetworkConnection& connection, NetworkPacket& packet) +{ + Server_Send_GAMEINFO(connection); + return 1; +} + int network_init() { return gNetwork.Init(); @@ -1270,13 +1337,14 @@ void network_close() gNetwork.Close(); } +void network_shutdown_client() +{ + gNetwork.ShutdownClient(); +} + int network_begin_client(const char *host, int port) { - if (gNetwork.GetMode() == NETWORK_MODE_NONE) { return gNetwork.BeginClient(host, port); - } else { - return false; - } } int network_begin_server(int port) @@ -1371,7 +1439,7 @@ void network_send_gamecmd(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 void network_send_password(const char* password) { - gNetwork.Client_Send_AUTH(OPENRCT2_VERSION, gConfigNetwork.player_name, password); + gNetwork.Client_Send_AUTH(gConfigNetwork.player_name, password); } void network_kick_player(int playerId) @@ -1387,6 +1455,7 @@ void network_set_password(const char* password) #else int network_get_mode() { return NETWORK_MODE_NONE; } int network_get_status() { return NETWORK_STATUS_NONE; } +int network_get_authstatus() { return NETWORK_AUTH_NONE; } uint32 network_get_server_tick() { return RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32); } void network_send_gamecmd(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp, uint8 callback) {} void network_send_map() {} @@ -1399,7 +1468,9 @@ uint32 network_get_player_flags(unsigned int index) { return 0; } int network_get_player_ping(unsigned int index) { return 0; } int network_get_player_id(unsigned int index) { return 0; } void network_send_chat(const char* text) {} +void network_send_password(const char* password) {} void network_close() {} +void network_shutdown_client() {} void network_kick_player(int playerId) {} void network_set_password(const char* password) {} uint8 network_get_current_player_id() { return 0; } diff --git a/src/network/network.h b/src/network/network.h index 0b20f8fd17..9fd433c4c3 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -38,6 +38,7 @@ enum { NETWORK_AUTH_BADVERSION, NETWORK_AUTH_BADNAME, NETWORK_AUTH_BADPASSWORD, + NETWORK_AUTH_FULL, NETWORK_AUTH_REQUIREPASSWORD }; @@ -147,7 +148,7 @@ public: NetworkConnection(); ~NetworkConnection(); int ReadPacket(); - void QueuePacket(std::unique_ptr packet); + void QueuePacket(std::unique_ptr packet, bool front = false); void SendQueuedPackets(); bool SetTCPNoDelay(bool on); bool SetNonBlocking(bool on); @@ -212,12 +213,14 @@ public: void Update(); NetworkPlayer* GetPlayerByID(int id); const char* FormatChat(NetworkPlayer* fromplayer, const char* text); - void SendPacketToClients(NetworkPacket& packet); + void SendPacketToClients(NetworkPacket& packet, bool front = false); bool CheckSRAND(uint32 tick, uint32 srand0); void KickPlayer(int playerId); void SetPassword(const char* password); + void ShutdownClient(); + void Advertise(); - void Client_Send_AUTH(const char* gameversion, const char* name, const char* password); + void Client_Send_AUTH(const char* name, const char* password); void Server_Send_AUTH(NetworkConnection& connection); void Server_Send_MAP(NetworkConnection* connection = nullptr); void Client_Send_CHAT(const char* text); @@ -230,6 +233,7 @@ public: void Server_Send_PING(); void Server_Send_PINGLIST(); void Server_Send_SETDISCONNECTMSG(NetworkConnection& connection, const char* msg); + void Server_Send_GAMEINFO(NetworkConnection& connection); std::vector> player_list; @@ -259,6 +263,7 @@ private: NetworkAddress server_address; bool wsa_initialized; SOCKET listening_socket; + unsigned short listening_port; NetworkConnection server_connection; uint32 last_tick_sent_time; uint32 last_ping_sent_time; @@ -269,9 +274,10 @@ private: std::list> client_connection_list; std::multiset game_command_queue; std::vector chunk_buffer; - char password[33]; + std::string password; bool _desynchronised; uint32 server_connect_time; + uint32 last_advertise_time; void UpdateServer(); void UpdateClient(); @@ -292,6 +298,7 @@ private: int Server_Handle_PING(NetworkConnection& connection, NetworkPacket& packet); int Client_Handle_PINGLIST(NetworkConnection& connection, NetworkPacket& packet); int Client_Handle_SETDISCONNECTMSG(NetworkConnection& connection, NetworkPacket& packet); + int Server_Handle_GAMEINFO(NetworkConnection& connection, NetworkPacket& packet); }; #endif // __cplusplus @@ -302,6 +309,7 @@ extern "C" { #endif // __cplusplus int network_init(); void network_close(); +void network_shutdown_client(); int network_begin_client(const char *host, int port); int network_begin_server(int port); diff --git a/src/rct2.h b/src/rct2.h index 9a98d84e81..77937a0b2b 100644 --- a/src/rct2.h +++ b/src/rct2.h @@ -105,6 +105,7 @@ typedef utf16* utf16string; #define OPENRCT2_BRANCH "develop" #define OPENRCT2_COMMIT_SHA1 "" #define OPENRCT2_COMMIT_SHA1_SHORT "" +#define OPENRCT2_MASTER_URL "" // Represent fixed point numbers. dp = decimal point typedef uint8 fixed8_1dp; diff --git a/src/windows/network_status.c b/src/windows/network_status.c index 86652b435d..1aadcf2d51 100644 --- a/src/windows/network_status.c +++ b/src/windows/network_status.c @@ -149,7 +149,11 @@ static void window_network_status_textinput(rct_window *w, int widgetIndex, char safe_strncpy(_password, text, sizeof(_password)); break; } - network_send_password(_password); + if (text == NULL) { + network_shutdown_client(); + } else { + network_send_password(_password); + } } static void window_network_status_invalidate(rct_window *w) diff --git a/src/windows/server_list.c b/src/windows/server_list.c index 0db84db62a..aa9126b314 100644 --- a/src/windows/server_list.c +++ b/src/windows/server_list.c @@ -18,12 +18,15 @@ * along with this program. If not, see . *****************************************************************************/ +#include "../interface/colour.h" #include "../interface/themes.h" #include "../interface/widget.h" #include "../interface/window.h" #include "../localisation/localisation.h" +#include "../network/http.h" #include "../network/network.h" #include "../sprites.h" +#include "../windows/dropdown.h" #include "../util/util.h" #include "error.h" @@ -31,17 +34,23 @@ #define WHEIGHT_MIN 300 #define WWIDTH_MAX 1200 #define WHEIGHT_MAX 800 -#define ITEM_HEIGHT (3 + 11 + 1 + 11 + 3) +#define ITEM_HEIGHT (3 + 9 + 3) typedef struct { char *address; utf8 *name; + bool haspassword; utf8 *description; + char *version; + bool favorite; + uint8 players; + uint8 maxplayers; } saved_server; -char _playerName[64]; +char _playerName[32 + 1]; saved_server *_savedServers = NULL; int _numSavedServers = 0; +SDL_mutex *_mutex = 0; enum { WIDX_BACKGROUND, @@ -49,6 +58,7 @@ enum { WIDX_CLOSE, WIDX_PLAYER_NAME_INPUT, WIDX_LIST, + WIDX_FETCH_SERVERS, WIDX_ADD_SERVER, WIDX_START_SERVER }; @@ -64,14 +74,16 @@ static rct_widget window_server_list_widgets[] = { { WWT_CLOSEBOX, 0, 327, 337, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button { WWT_TEXT_BOX, 1, 100, 344, 20, 31, (uint32)_playerName, STR_NONE }, // player name text box { WWT_SCROLL, 1, 6, 337, 37, 50, STR_NONE, STR_NONE }, // server list - { WWT_DROPDOWN_BUTTON, 1, 6, 106, 53, 64, STR_ADD_SERVER, STR_NONE }, // add server button - { WWT_DROPDOWN_BUTTON, 1, 112, 212, 53, 64, STR_START_SERVER, STR_NONE }, // start server button + { WWT_DROPDOWN_BUTTON, 1, 6, 106, 53, 64, STR_FETCH_SERVERS, STR_NONE }, // fetch servers button + { WWT_DROPDOWN_BUTTON, 1, 112, 212, 53, 64, STR_ADD_SERVER, STR_NONE }, // add server button + { WWT_DROPDOWN_BUTTON, 1, 218, 318, 53, 64, STR_START_SERVER, STR_NONE }, // start server button { WIDGETS_END }, }; static void window_server_list_close(rct_window *w); static void window_server_list_mouseup(rct_window *w, int widgetIndex); static void window_server_list_resize(rct_window *w); +static void window_server_list_dropdown(rct_window *w, int widgetIndex, int dropdownIndex); static void window_server_list_update(rct_window *w); static void window_server_list_scroll_getsize(rct_window *w, int scrollIndex, int *width, int *height); static void window_server_list_scroll_mousedown(rct_window *w, int scrollIndex, int x, int y); @@ -86,7 +98,7 @@ static rct_window_event_list window_server_list_events = { window_server_list_mouseup, window_server_list_resize, NULL, - NULL, + window_server_list_dropdown, NULL, window_server_list_update, NULL, @@ -112,17 +124,25 @@ static rct_window_event_list window_server_list_events = { window_server_list_scrollpaint }; +enum { + DDIDX_JOIN, + DDIDX_FAVORITE +}; + 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(); 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 saved_server* add_saved_server(char *address); static void remove_saved_server(int index); -static void join_server(char *address, bool spectate); +static void join_server(char *address); +static void fetch_servers(); +#ifndef DISABLE_HTTP +static void fetch_servers_callback(http_json_response* response); +#endif void window_server_list_open() { @@ -133,12 +153,17 @@ void window_server_list_open() if (window != NULL) return; + if (_mutex == 0) { + _mutex = SDL_CreateMutex(); + } + window = window_create_centred(WWIDTH_MIN, WHEIGHT_MIN, &window_server_list_events, WC_SERVER_LIST, WF_10 | WF_RESIZABLE); window->widgets = window_server_list_widgets; window->enabled_widgets = ( (1 << WIDX_CLOSE) | (1 << WIDX_PLAYER_NAME_INPUT) | + (1 << WIDX_FETCH_SERVERS) | (1 << WIDX_ADD_SERVER) | (1 << WIDX_START_SERVER) ); @@ -163,12 +188,18 @@ void window_server_list_open() server_list_load_saved_servers(); window->no_list_items = _numSavedServers; + + fetch_servers(); } static void window_server_list_close(rct_window *w) { - server_list_update_player_name(); dispose_saved_server_list(); + if (_mutex) { + SDL_LockMutex(_mutex); + SDL_DestroyMutex(_mutex); + _mutex = 0; + } } static void window_server_list_mouseup(rct_window *w, int widgetIndex) @@ -180,11 +211,13 @@ static void window_server_list_mouseup(rct_window *w, int widgetIndex) case WIDX_PLAYER_NAME_INPUT: window_start_textbox(w, widgetIndex, 1170, (uint32)_playerName, 63); break; + case WIDX_FETCH_SERVERS: + fetch_servers(); + break; 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: - server_list_update_player_name(); window_server_start_open(); break; } @@ -195,6 +228,25 @@ static void window_server_list_resize(rct_window *w) window_set_resize(w, WWIDTH_MIN, WHEIGHT_MIN, WWIDTH_MAX, WHEIGHT_MAX); } +static void window_server_list_dropdown(rct_window *w, int widgetIndex, int dropdownIndex) +{ + int serverIndex = w->selected_list_item; + if (serverIndex < 0) return; + if (serverIndex >= _numSavedServers) return; + + char *serverAddress = _savedServers[serverIndex].address; + + switch (dropdownIndex) { + case DDIDX_JOIN: + join_server(serverAddress); + break; + case DDIDX_FAVORITE: + _savedServers[serverIndex].favorite = !_savedServers[serverIndex].favorite; + server_list_save_saved_servers(); + break; + } +} + static void window_server_list_update(rct_window *w) { if (gCurrentTextBox.window.classification == w->classification && gCurrentTextBox.window.number == w->number) { @@ -217,22 +269,17 @@ static void window_server_list_scroll_mousedown(rct_window *w, int scrollIndex, char *serverAddress = _savedServers[serverIndex].address; - switch (_hoverButtonIndex) { - case WIDX_LIST_REMOVE: - remove_saved_server(serverIndex); - server_list_save_saved_servers(); - w->no_list_items = _numSavedServers; - window_invalidate(w); - break; - case WIDX_LIST_SPECTATE: - server_list_update_player_name(); - join_server(serverAddress, true); - break; - default: - server_list_update_player_name(); - join_server(serverAddress, false); - break; + rct_widget *listWidget = &w->widgets[WIDX_LIST]; + int ddx = w->x + listWidget->left + x; + int ddy = w->y + listWidget->top + y; + + gDropdownItemsFormat[0] = STR_JOIN_GAME; + if (_savedServers[serverIndex].favorite) { + gDropdownItemsFormat[1] = STR_REMOVE_FROM_FAVORITES; + } else { + gDropdownItemsFormat[1] = STR_ADD_TO_FAVORITES; } + window_dropdown_show_text(ddx, ddy, 0, COLOUR_GREY, 0, 2); } static void window_server_list_scroll_mouseover(rct_window *w, int scrollIndex, int x, int y) @@ -281,13 +328,18 @@ static void window_server_list_textinput(rct_window *w, int widgetIndex, char *t safe_strncpy(_playerName, text, sizeof(_playerName)); } + if (strlen(_playerName) > 0) { + SafeFree(gConfigNetwork.player_name); + gConfigNetwork.player_name = _strdup(_playerName); + config_save_default(); + } + widget_invalidate(w, WIDX_PLAYER_NAME_INPUT); break; case WIDX_ADD_SERVER: add_saved_server(text); server_list_save_saved_servers(); - w->no_list_items = _numSavedServers; window_invalidate(w); break; } @@ -305,23 +357,26 @@ static void window_server_list_invalidate(rct_window *w) window_server_list_widgets[WIDX_LIST].left = 6; window_server_list_widgets[WIDX_LIST].right = w->width - 6; window_server_list_widgets[WIDX_LIST].bottom = w->height - 6 - 11 - 6; + window_server_list_widgets[WIDX_FETCH_SERVERS].top = w->height - 6 - 11; + window_server_list_widgets[WIDX_FETCH_SERVERS].bottom = w->height - 6; window_server_list_widgets[WIDX_ADD_SERVER].top = w->height - 6 - 11; window_server_list_widgets[WIDX_ADD_SERVER].bottom = w->height - 6; window_server_list_widgets[WIDX_START_SERVER].top = w->height - 6 - 11; window_server_list_widgets[WIDX_START_SERVER].bottom = w->height - 6; + + w->no_list_items = _numSavedServers; } static void window_server_list_paint(rct_window *w, rct_drawpixelinfo *dpi) { window_draw_widgets(w, dpi); - gfx_draw_string_left(dpi, STR_PLAYER_NAME, NULL, w->colours[1], w->x + 6, w->y + w->widgets[WIDX_PLAYER_NAME_INPUT].top); + gfx_draw_string_left(dpi, STR_PLAYER_NAME, NULL, COLOUR_WHITE, w->x + 6, w->y + w->widgets[WIDX_PLAYER_NAME_INPUT].top); } static void window_server_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex) { uint32 colour; - int bx, by; colour = ((char*)0x0141FC48)[w->colours[1] * 8]; colour = (colour << 24) | (colour << 16) | (colour << 8) | colour; @@ -342,27 +397,26 @@ static void window_server_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi gfx_fill_rect(dpi, 0, y, width, y + ITEM_HEIGHT, 0x02000031); } + int colour = w->colours[1]; + if (serverDetails->favorite) { + colour = COLOUR_YELLOW; + } + // Draw server information if (highlighted) { - gfx_draw_string(dpi, serverDetails->address, w->colours[1], 3, y + 3); + gfx_draw_string(dpi, serverDetails->address, colour, 3, y + 3); } else { - gfx_draw_string(dpi, serverDetails->name, w->colours[1], 3, y + 3); + gfx_draw_string(dpi, serverDetails->name, colour, 3, y + 3); } - gfx_draw_string(dpi, serverDetails->description, w->colours[1], 3, y + 14); + //gfx_draw_string(dpi, serverDetails->description, w->colours[1], 3, y + 14); - // Draw delete server button - server_list_get_item_button(0, 0, y, width, &bx, &by); - if (highlighted && _hoverButtonIndex == WIDX_LIST_REMOVE) { - gfx_fill_rect_inset(dpi, bx, by, bx + 24, by + 24, w->colours[1], 0); + // Draw number of players + char players[32]; + players[0] = 0; + if (serverDetails->maxplayers > 0) { + sprintf(players, "%d/%d", serverDetails->players, serverDetails->maxplayers); } - gfx_draw_sprite(dpi, SPR_DEMOLISH, bx, by, 0); - - // Draw spectate server button - server_list_get_item_button(1, 0, y, width, &bx, &by); - if (highlighted && _hoverButtonIndex == WIDX_LIST_SPECTATE) { - gfx_fill_rect_inset(dpi, bx, by, bx + 24, by + 24, w->colours[1], 0); - } - gfx_draw_sprite(dpi, SPR_LOCATE, bx, by, 0); + gfx_draw_string(dpi, players, w->colours[1], width - 3 - 14 - gfx_get_string_width(players), y + 3); y += ITEM_HEIGHT; } @@ -374,15 +428,6 @@ 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() -{ - if (strlen(_playerName) > 0) { - SafeFree(gConfigNetwork.player_name); - gConfigNetwork.player_name = _strdup(_playerName); - config_save_default(); - } -} - static char *freadstralloc(SDL_RWops *file) { int capacity = 64; @@ -421,6 +466,7 @@ static void server_list_load_saved_servers() return; } + SDL_LockMutex(_mutex); dispose_saved_server_list(); // Read number of saved servers @@ -433,10 +479,16 @@ static void server_list_load_saved_servers() serverInfo->address = freadstralloc(file); serverInfo->name = freadstralloc(file); + serverInfo->haspassword = false; serverInfo->description = freadstralloc(file); + serverInfo->version = _strdup(""); + serverInfo->favorite = true; + serverInfo->players = 0; + serverInfo->maxplayers = 0; } SDL_RWclose(file); + SDL_UnlockMutex(_mutex); } static void server_list_save_saved_servers() @@ -453,23 +505,34 @@ static void server_list_save_saved_servers() return; } + SDL_LockMutex(_mutex); + int count = 0; + for (int i = 0; i < _numSavedServers; i++) { + saved_server *serverInfo = &_savedServers[i]; + if (serverInfo->favorite) { + count++; + } + } // Write number of saved servers - SDL_RWwrite(file, &_numSavedServers, sizeof(uint32), 1); + SDL_RWwrite(file, &count, sizeof(uint32), 1); // Write each saved server for (int i = 0; i < _numSavedServers; i++) { saved_server *serverInfo = &_savedServers[i]; - - SDL_RWwrite(file, serverInfo->address, strlen(serverInfo->address) + 1, 1); - SDL_RWwrite(file, serverInfo->name, strlen(serverInfo->name) + 1, 1); - SDL_RWwrite(file, serverInfo->description, strlen(serverInfo->description) + 1, 1); + if (serverInfo->favorite) { + SDL_RWwrite(file, serverInfo->address, strlen(serverInfo->address) + 1, 1); + SDL_RWwrite(file, serverInfo->name, strlen(serverInfo->name) + 1, 1); + SDL_RWwrite(file, serverInfo->description, strlen(serverInfo->description) + 1, 1); + } } SDL_RWclose(file); + SDL_UnlockMutex(_mutex); } static void dispose_saved_server_list() { + SDL_LockMutex(_mutex); if (_savedServers != NULL) { for (int i = 0; i < _numSavedServers; i++) { dispose_saved_server(&_savedServers[i]); @@ -478,6 +541,7 @@ static void dispose_saved_server_list() _savedServers = NULL; } _numSavedServers = 0; + SDL_UnlockMutex(_mutex); } static void dispose_saved_server(saved_server *serverInfo) @@ -485,10 +549,19 @@ static void dispose_saved_server(saved_server *serverInfo) SafeFree(serverInfo->address); SafeFree(serverInfo->name); SafeFree(serverInfo->description); + SafeFree(serverInfo->version); } -static void add_saved_server(char *address) +static saved_server* add_saved_server(char *address) { + SDL_LockMutex(_mutex); + for (int i = 0; i < _numSavedServers; i++) { + if (strcmp(_savedServers[i].address, address) == 0) { + SDL_UnlockMutex(_mutex); + return &_savedServers[i]; + } + } + _numSavedServers++; if (_savedServers == NULL) { _savedServers = malloc(_numSavedServers * sizeof(saved_server)); @@ -497,20 +570,30 @@ static void add_saved_server(char *address) } int index = _numSavedServers - 1; - _savedServers[index].address = _strdup(address); - _savedServers[index].name = _strdup(address); - _savedServers[index].description = _strdup(""); + saved_server* newserver = &_savedServers[index]; + newserver->address = _strdup(address); + newserver->name = _strdup(address); + newserver->haspassword = false; + newserver->description = _strdup(""); + newserver->version = _strdup(""); + newserver->favorite = false; + newserver->players = 0; + newserver->maxplayers = 0; + SDL_UnlockMutex(_mutex); + return newserver; } static void remove_saved_server(int index) { - if (_numSavedServers <= index) return; + SDL_LockMutex(_mutex); + if (_numSavedServers > index) { + int serversToMove = _numSavedServers - index - 1; + memmove(&_savedServers[index], &_savedServers[index + 1], serversToMove * sizeof(saved_server)); - int serversToMove = _numSavedServers - index - 1; - memmove(&_savedServers[index], &_savedServers[index + 1], serversToMove * sizeof(saved_server)); - - _numSavedServers--; - _savedServers = realloc(_savedServers, _numSavedServers * sizeof(saved_server)); + _numSavedServers--; + _savedServers = realloc(_savedServers, _numSavedServers * sizeof(saved_server)); + } + SDL_UnlockMutex(_mutex); } static char *substr(char *start, int length) @@ -521,7 +604,7 @@ static char *substr(char *start, int length) return result; } -static void join_server(char *address, bool spectate) +static void join_server(char *address) { int port = gConfigNetwork.default_port; @@ -551,3 +634,60 @@ static void join_server(char *address, bool spectate) free(address); } } + +static void fetch_servers() +{ +#ifndef DISABLE_HTTP + if(strlen(gConfigNetwork.master_url) > 0){ + SDL_LockMutex(_mutex); + for (int i = 0; i < _numSavedServers; i++) { + if (!_savedServers[i].favorite) { + remove_saved_server(i); + i = 0; + } + } + SDL_UnlockMutex(_mutex); + http_request_json_async(gConfigNetwork.master_url, fetch_servers_callback); + } +#endif +} + +#ifndef DISABLE_HTTP +static void fetch_servers_callback(http_json_response* response) +{ + if (response && json_is_array(response->root)) { + int count = json_array_size(response->root); + for (int i = 0; i < count; i++) { + json_t *server = json_array_get(response->root, i); + if (!json_is_object(server)) + continue; + + json_t *address = json_object_get(server, "address"); + json_t *name = json_object_get(server, "name"); + json_t *haspassword = json_object_get(server, "haspassword"); + json_t *description = json_object_get(server, "description"); + json_t *version = json_object_get(server, "version"); + json_t *players = json_object_get(server, "players"); + json_t *maxplayers = json_object_get(server, "maxplayers"); + + SDL_LockMutex(_mutex); + saved_server* newserver = add_saved_server((char*)json_string_value(address)); + SafeFree(newserver->name); + newserver->name = _strdup(json_string_value(name)); + newserver->haspassword = (uint8)json_integer_value(haspassword); + SafeFree(newserver->description); + newserver->description = _strdup(json_string_value(description)); + SafeFree(newserver->version); + newserver->version = _strdup(json_string_value(version)); + newserver->players = (uint8)json_integer_value(players); + newserver->maxplayers = (uint8)json_integer_value(maxplayers); + SDL_UnlockMutex(_mutex); + } + http_request_json_dispose(response); + } + rct_window* window = window_bring_to_front_by_class(WC_SERVER_LIST); + if (window != NULL) { + window_invalidate(window); + } +} +#endif diff --git a/src/windows/server_start.c b/src/windows/server_start.c index f53909b2e8..752c7efd4b 100644 --- a/src/windows/server_start.c +++ b/src/windows/server_start.c @@ -28,26 +28,38 @@ #include "../util/util.h" #include "error.h" +char _port[7]; +char _name[65]; char _password[33]; enum { WIDX_BACKGROUND, WIDX_TITLE, WIDX_CLOSE, + WIDX_PORT_INPUT, + WIDX_NAME_INPUT, WIDX_PASSWORD_INPUT, + WIDX_MAXPLAYERS, + WIDX_MAXPLAYERS_INCREASE, + WIDX_MAXPLAYERS_DECREASE, WIDX_ADVERTISE_CHECKBOX, WIDX_START_SERVER }; #define WW 300 -#define WH 100 +#define WH 120 static rct_widget window_server_start_widgets[] = { { WWT_FRAME, 0, 0, WW-1, 0, WH-1, 0xFFFFFFFF, STR_NONE }, // panel / background { WWT_CAPTION, 0, 1, WW-2, 1, 14, STR_START_SERVER, STR_WINDOW_TITLE_TIP }, // title bar { WWT_CLOSEBOX, 0, WW-13, WW-3, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button - { WWT_TEXT_BOX, 1, 150, WW-8, 20, 32, (uint32)_password, STR_NONE }, // password text box - { WWT_CHECKBOX, 1, 6, WW-8, 36, 45, STR_ADVERTISE, STR_NONE }, // advertise checkbox + { WWT_TEXT_BOX, 1, 120, WW-8, 20, 32, (uint32)_port, STR_NONE }, // port text box + { WWT_TEXT_BOX, 1, 120, WW-8, 36, 48, (uint32)_name, STR_NONE }, // name text box + { WWT_TEXT_BOX, 1, 120, WW-8, 52, 64, (uint32)_password, STR_NONE }, // password text box + { WWT_SPINNER, 1, 120, WW-8, 68, 77, 1871, STR_NONE }, // max players + { WWT_DROPDOWN_BUTTON, 1, WW-18, WW-8, 68, 72, STR_NUMERIC_UP, STR_NONE }, + { WWT_DROPDOWN_BUTTON, 1, WW-18, WW-8, 72, 76, STR_NUMERIC_DOWN, STR_NONE }, + { WWT_CHECKBOX, 1, 6, WW-8, 85, 91, STR_ADVERTISE, STR_NONE }, // advertise checkbox { WWT_DROPDOWN_BUTTON, 1, 6, 106, WH-6-11, WH-6, STR_START_SERVER, STR_NONE }, // start server button { WIDGETS_END }, }; @@ -104,7 +116,12 @@ void window_server_start_open() window->widgets = window_server_start_widgets; window->enabled_widgets = ( (1 << WIDX_CLOSE) | + (1 << WIDX_PORT_INPUT) | + (1 << WIDX_NAME_INPUT) | (1 << WIDX_PASSWORD_INPUT) | + (1 << WIDX_MAXPLAYERS) | + (1 << WIDX_MAXPLAYERS_INCREASE) | + (1 << WIDX_MAXPLAYERS_DECREASE) | (1 << WIDX_ADVERTISE_CHECKBOX) | (1 << WIDX_START_SERVER) ); @@ -122,6 +139,9 @@ void window_server_start_open() window->colours[0] = 1; window->colours[1] = 26; window->colours[2] = 26; + + sprintf(_port, "%lu", gConfigNetwork.default_port); + safe_strncpy(_name, gConfigNetwork.server_name, sizeof(_name)); } static void window_server_start_close(rct_window *w) @@ -135,9 +155,29 @@ static void window_server_start_mouseup(rct_window *w, int widgetIndex) case WIDX_CLOSE: window_close(w); break; + case WIDX_PORT_INPUT: + window_start_textbox(w, widgetIndex, 1170, (uint32)_port, 6); + break; + case WIDX_NAME_INPUT: + window_start_textbox(w, widgetIndex, 1170, (uint32)_name, 64); + break; case WIDX_PASSWORD_INPUT: window_start_textbox(w, widgetIndex, 1170, (uint32)_password, 32); break; + case WIDX_MAXPLAYERS_INCREASE: + if (gConfigNetwork.maxplayers < 255) { + gConfigNetwork.maxplayers++; + } + config_save_default(); + window_invalidate(w); + break; + case WIDX_MAXPLAYERS_DECREASE: + if (gConfigNetwork.maxplayers > 1) { + gConfigNetwork.maxplayers--; + } + config_save_default(); + window_invalidate(w); + break; case WIDX_ADVERTISE_CHECKBOX: gConfigNetwork.advertise = !gConfigNetwork.advertise; config_save_default(); @@ -154,6 +194,7 @@ static void window_server_start_update(rct_window *w) { if (gCurrentTextBox.window.classification == w->classification && gCurrentTextBox.window.number == w->number) { window_update_textbox_caret(); + widget_invalidate(w, WIDX_NAME_INPUT); widget_invalidate(w, WIDX_PASSWORD_INPUT); } } @@ -163,6 +204,37 @@ static void window_server_start_textinput(rct_window *w, int widgetIndex, char * if (text == NULL) return; switch (widgetIndex) { + case WIDX_PORT_INPUT: + if (strcmp(_port, text) == 0) + return; + + memset(_port, 0, sizeof(_port)); + if (strlen(text) > 0) { + safe_strncpy(_port, text, sizeof(_port)); + } + + gConfigNetwork.default_port = atoi(_port); + config_save_default(); + + widget_invalidate(w, WIDX_NAME_INPUT); + break; + case WIDX_NAME_INPUT: + if (strcmp(_name, text) == 0) + return; + + memset(_name, 0, sizeof(_name)); + if (strlen(text) > 0) { + safe_strncpy(_name, text, sizeof(_name)); + } + + if (strlen(_name) > 0) { + SafeFree(gConfigNetwork.server_name); + gConfigNetwork.server_name = _strdup(_name); + config_save_default(); + } + + widget_invalidate(w, WIDX_NAME_INPUT); + break; case WIDX_PASSWORD_INPUT: if (strcmp(_password, text) == 0) return; @@ -180,11 +252,15 @@ static void window_server_start_textinput(rct_window *w, int widgetIndex, char * static void window_server_start_invalidate(rct_window *w) { widget_set_checkbox_value(w, WIDX_ADVERTISE_CHECKBOX, gConfigNetwork.advertise); + RCT2_GLOBAL(0x013CE964, uint16) = gConfigNetwork.maxplayers; } static void window_server_start_paint(rct_window *w, rct_drawpixelinfo *dpi) { window_draw_widgets(w, dpi); + gfx_draw_string_left(dpi, STR_PORT, NULL, w->colours[1], w->x + 6, w->y + w->widgets[WIDX_PORT_INPUT].top); + gfx_draw_string_left(dpi, STR_SERVER_NAME, NULL, w->colours[1], w->x + 6, w->y + w->widgets[WIDX_NAME_INPUT].top); gfx_draw_string_left(dpi, STR_PASSWORD, NULL, w->colours[1], w->x + 6, w->y + w->widgets[WIDX_PASSWORD_INPUT].top); + gfx_draw_string_left(dpi, STR_MAX_PLAYERS, NULL, w->colours[1], w->x + 6, w->y + w->widgets[WIDX_MAXPLAYERS].top); }