From d66eb70f56bc7db5155921a2394ce3e4f77a3130 Mon Sep 17 00:00:00 2001 From: zsilencer Date: Sun, 1 Nov 2015 14:28:01 -0700 Subject: [PATCH 1/9] fix potential overflow --- src/network/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index faf75bc010..6896ad320e 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -1182,7 +1182,7 @@ int Network::Client_Handle_PINGLIST(NetworkConnection& connection, NetworkPacket int Network::Client_Handle_SETDISCONNECTMSG(NetworkConnection& connection, NetworkPacket& packet) { - static char msg[256] = {0}; + static char msg[100] = {0}; const char* disconnectmsg = packet.ReadString(); if (disconnectmsg) { safe_strncpy(msg, disconnectmsg, sizeof(msg)); From 3b639ced4765be7a954e64c4bfb9ae560c95ad0f Mon Sep 17 00:00:00 2001 From: zsilencer Date: Sun, 1 Nov 2015 17:28:53 -0700 Subject: [PATCH 2/9] allow host to specify password #2072 --- data/language/english_uk.txt | 2 + projects/openrct2.vcxproj | 1 + projects/openrct2.vcxproj.filters | 3 + src/config.c | 3 +- src/config.h | 1 + src/interface/window.h | 2 + src/localisation/string_ids.h | 3 + src/network/network.cpp | 98 +++++++++++---- src/network/network.h | 7 ++ src/windows/server_list.c | 2 +- src/windows/server_start.c | 190 ++++++++++++++++++++++++++++++ 11 files changed, 285 insertions(+), 27 deletions(-) create mode 100644 src/windows/server_start.c diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index 59af7466cd..27d2ee1cbe 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -3904,6 +3904,8 @@ STR_5562 :WARNING! STR_5563 :This feature is currently unstable, take extra caution. STR_5564 :Insert Corrupt Element STR_5565 :{SMALLFONT}{BLACK}Inserts a corrupt map element at top of tile. This will hide any element above the corrupt element. +STR_5566 :Password: +STR_5567 :Advertise ##################### # Rides/attractions # diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj index 05dae3caf6..74c3166567 100644 --- a/projects/openrct2.vcxproj +++ b/projects/openrct2.vcxproj @@ -103,6 +103,7 @@ + diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters index c08b462c63..734c2e6363 100644 --- a/projects/openrct2.vcxproj.filters +++ b/projects/openrct2.vcxproj.filters @@ -546,6 +546,9 @@ Source\Platform + + Source\Windows + diff --git a/src/config.c b/src/config.c index a2d5d74d1a..5dc77248c8 100644 --- a/src/config.c +++ b/src/config.c @@ -239,7 +239,8 @@ 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, NETWORK_DEFAULT_PORT, NULL }, - { offsetof(network_configuration, stay_connected), "stay_connected", CONFIG_VALUE_TYPE_BOOLEAN, false, 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 }, }; config_section_definition _sectionDefinitions[] = { diff --git a/src/config.h b/src/config.h index b41965c4d7..e91bbc28a5 100644 --- a/src/config.h +++ b/src/config.h @@ -214,6 +214,7 @@ typedef struct { utf8string player_name; uint32 default_port; uint8 stay_connected; + uint8 advertise; } network_configuration; typedef struct theme_window { diff --git a/src/interface/window.h b/src/interface/window.h index e37b9244c6..7714153a70 100644 --- a/src/interface/window.h +++ b/src/interface/window.h @@ -450,6 +450,7 @@ enum { WC_PLAYER_LIST = 125, WC_NETWORK_STATUS = 126, WC_SERVER_LIST = 127, + WC_SERVER_START = 128, // Only used for colour schemes WC_STAFF = 220, @@ -629,6 +630,7 @@ void window_player_list_open(); void window_network_status_open(const char* text); void window_network_status_close(); void window_server_list_open(); +void window_server_start_open(); void window_research_open(); void window_research_development_page_paint(rct_window *w, rct_drawpixelinfo *dpi, int baseWidgetIndex); diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index 42619e6b09..45483c0bc3 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -2154,6 +2154,9 @@ enum { STR_INSERT_CORRUPT = 5564, STR_INSERT_CORRUPT_TIP = 5565, + STR_PASSWORD = 5566, + STR_ADVERTISE = 5567, + // 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 6896ad320e..74f5994690 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -67,7 +67,8 @@ enum { NETWORK_COMMAND_PING, NETWORK_COMMAND_PINGLIST, NETWORK_COMMAND_SETDISCONNECTMSG, - NETWORK_COMMAND_MAX + NETWORK_COMMAND_MAX, + NETWORK_COMMAND_INVALID = -1 }; const char *NetworkCommandNames[] = { @@ -104,6 +105,15 @@ uint8* NetworkPacket::GetData() return &(*data)[0]; } +uint32 NetworkPacket::GetCommand() +{ + if (data->size() >= sizeof(uint32)) { + return ByteSwapBE(*(uint32*)(&(*data)[0])); + } else { + return NETWORK_COMMAND_INVALID; + } +} + void NetworkPacket::Write(uint8* bytes, unsigned int size) { data->insert(data->end(), bytes, bytes + size); @@ -147,6 +157,18 @@ void NetworkPacket::Clear() data->clear(); } +bool NetworkPacket::CommandRequiresAuth() +{ + switch (GetCommand()) { + case NETWORK_COMMAND_PING: + return false; + case NETWORK_COMMAND_AUTH: + return false; + default: + return true; + } +} + NetworkPlayer::NetworkPlayer(const char* name) { safe_strncpy((char*)NetworkPlayer::name, name, sizeof(NetworkPlayer::name)); @@ -234,7 +256,7 @@ bool NetworkConnection::SendPacket(NetworkPacket& packet) void NetworkConnection::QueuePacket(std::unique_ptr packet) { - if (authstatus == NETWORK_AUTH_OK || authstatus == NETWORK_AUTH_REQUESTED) { + if (authstatus == NETWORK_AUTH_OK || !packet->CommandRequiresAuth()) { packet->size = (uint16)packet->data->size(); outboundpackets.push_back(std::move(packet)); } @@ -573,7 +595,7 @@ void Network::UpdateClient() bool connectfailed = false; switch(status){ case NETWORK_STATUS_RESOLVING:{ - if(server_address.GetResolveStatus() == NetworkAddress::RESOLVE_OK){ + if (server_address.GetResolveStatus() == NetworkAddress::RESOLVE_OK) { server_connection.socket = socket(server_address.ss->ss_family, SOCK_STREAM, IPPROTO_TCP); if (server_connection.socket == INVALID_SOCKET) { log_error("Unable to create socket."); @@ -606,8 +628,7 @@ void Network::UpdateClient() int error = 0; socklen_t len = sizeof(error); int result = getsockopt(server_connection.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len); - if (result != 0) - { + if (result != 0) { log_error("getsockopt failed with error %d", LAST_SOCKET_ERROR()); break; } @@ -631,8 +652,7 @@ void Network::UpdateClient() error = 0; socklen_t len = sizeof(error); result = getsockopt(server_connection.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len); - if (result != 0) - { + if (result != 0) { log_error("getsockopt failed with error %d", LAST_SOCKET_ERROR()); break; } @@ -728,6 +748,26 @@ bool Network::CheckSRAND(uint32 tick, uint32 srand0) return true; } +void Network::KickPlayer(int playerId) +{ + NetworkPlayer *player = GetPlayerByID(playerId); + for(auto it = client_connection_list.begin(); it != client_connection_list.end(); it++) { + if ((*it)->player->id == playerId) { + // Disconnect the client gracefully + (*it)->last_disconnect_reason = "Kicked"; + Server_Send_SETDISCONNECTMSG(*(*it), "Get out of the server!"); + shutdown((*it)->socket, SHUT_RD); + (*it)->SendQueuedPackets(); + break; + } + } +} + +void Network::SetPassword(const char* password) +{ + safe_strncpy(Network::password, password, sizeof(Network::password)); +} + void Network::Client_Send_AUTH(const char* gameversion, const char* name, const char* password) { std::unique_ptr packet = std::move(NetworkPacket::Allocate()); @@ -846,7 +886,6 @@ void Network::Server_Send_SETDISCONNECTMSG(NetworkConnection& connection, const bool Network::ProcessConnection(NetworkConnection& connection) { - connection.SendQueuedPackets(); int packetStatus; do { packetStatus = connection.ReadPacket(); @@ -871,12 +910,11 @@ bool Network::ProcessConnection(NetworkConnection& connection) break; } } while (packetStatus == NETWORK_READPACKET_MORE_DATA || packetStatus == NETWORK_READPACKET_SUCCESS); -#if !DEBUG + connection.SendQueuedPackets(); if (!connection.ReceivedPacketRecently()) { connection.last_disconnect_reason = "No Data"; return false; } -#endif return true; } @@ -991,6 +1029,20 @@ void Network::PrintError() int Network::Client_Handle_AUTH(NetworkConnection& connection, NetworkPacket& packet) { packet >> (uint32&)connection.authstatus >> (uint8&)player_id; + switch(connection.authstatus) { + case NETWORK_AUTH_BADNAME: + connection.last_disconnect_reason = "Bad Player Name"; + shutdown(connection.socket, SHUT_RDWR); + break; + case NETWORK_AUTH_BADVERSION: + connection.last_disconnect_reason = "Incorrect Software Version"; + shutdown(connection.socket, SHUT_RDWR); + break; + case NETWORK_AUTH_BADPASSWORD: + connection.last_disconnect_reason = "Bad Password"; + shutdown(connection.socket, SHUT_RDWR); + break; + } return 1; } @@ -1028,6 +1080,10 @@ int Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& pa std::unique_ptr responsepacket = std::move(NetworkPacket::Allocate()); *responsepacket << (uint32)NETWORK_COMMAND_AUTH << (uint32)connection.authstatus << (uint8)playerid; connection.QueuePacket(std::move(responsepacket)); + if (connection.authstatus != NETWORK_AUTH_OK) { + shutdown(connection.socket, SHUT_RD); + connection.SendQueuedPackets(); + } } return 1; } @@ -1300,25 +1356,16 @@ void network_send_gamecmd(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 } } -void Network::KickPlayer(int playerId) -{ - NetworkPlayer *player = GetPlayerByID(playerId); - for(auto it = client_connection_list.begin(); it != client_connection_list.end(); it++) { - if ((*it)->player->id == playerId) { - // Disconnect the client gracefully - (*it)->last_disconnect_reason = "Kicked"; - Server_Send_SETDISCONNECTMSG(*(*it), "Get out of the server!"); - shutdown((*it)->socket, SHUT_RD); - break; - } - } -} - void network_kick_player(int playerId) { gNetwork.KickPlayer(playerId); } +void network_set_password(const char* password) +{ + gNetwork.SetPassword(password); +} + #else int network_get_mode() { return NETWORK_MODE_NONE; } int network_get_status() { return NETWORK_STATUS_NONE; } @@ -1335,6 +1382,7 @@ 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_close() {} -void network_kick_player(int playerId) { } +void network_kick_player(int playerId) {} +void network_set_password(const char* password) {} uint8 network_get_current_player_id() { return 0; } #endif /* DISABLE_NETWORK */ diff --git a/src/network/network.h b/src/network/network.h index 2356937cdb..349f80a7a9 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -69,6 +69,9 @@ extern "C" { #ifndef SHUT_RD #define SHUT_RD SD_RECEIVE #endif + #ifndef SHUT_RDWR + #define SHUT_RDWR SD_BOTH + #endif #else #include #include @@ -109,6 +112,7 @@ public: static std::unique_ptr Allocate(); static std::unique_ptr Duplicate(NetworkPacket& packet); uint8* GetData(); + uint32 GetCommand(); template NetworkPacket& operator<<(T value) { T swapped = ByteSwapBE(value); uint8* bytes = (uint8*)&swapped; data->insert(data->end(), bytes, bytes + sizeof(value)); return *this; } void Write(uint8* bytes, unsigned int size); @@ -118,6 +122,7 @@ public: const uint8* Read(unsigned int size); const char* ReadString(); void Clear(); + bool CommandRequiresAuth(); uint16 size; std::shared_ptr> data; @@ -209,6 +214,7 @@ public: void SendPacketToClients(NetworkPacket& packet); bool CheckSRAND(uint32 tick, uint32 srand0); void KickPlayer(int playerId); + void SetPassword(const char* password); void Client_Send_AUTH(const char* gameversion, const char* name, const char* password); void Server_Send_MAP(NetworkConnection* connection = nullptr); @@ -314,6 +320,7 @@ void network_send_chat(const char* text); void network_send_gamecmd(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp, uint8 callback); void network_kick_player(int playerId); +void network_set_password(const char* password); void network_print_error(); diff --git a/src/windows/server_list.c b/src/windows/server_list.c index fb10c4442d..0db84db62a 100644 --- a/src/windows/server_list.c +++ b/src/windows/server_list.c @@ -185,7 +185,7 @@ static void window_server_list_mouseup(rct_window *w, int widgetIndex) break; case WIDX_START_SERVER: server_list_update_player_name(); - window_loadsave_open(LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME | LOADSAVETYPE_NETWORK, NULL); + window_server_start_open(); break; } } diff --git a/src/windows/server_start.c b/src/windows/server_start.c new file mode 100644 index 0000000000..f53909b2e8 --- /dev/null +++ b/src/windows/server_start.c @@ -0,0 +1,190 @@ +/***************************************************************************** +* Copyright (c) 2014 Ted John +* OpenRCT2, an open source clone of Roller Coaster Tycoon 2. +* +* This file is part of OpenRCT2. +* +* OpenRCT2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. + +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "../config.h" +#include "../interface/themes.h" +#include "../interface/widget.h" +#include "../interface/window.h" +#include "../localisation/localisation.h" +#include "../network/network.h" +#include "../sprites.h" +#include "../util/util.h" +#include "error.h" + +char _password[33]; + +enum { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PASSWORD_INPUT, + WIDX_ADVERTISE_CHECKBOX, + WIDX_START_SERVER +}; + +#define WW 300 +#define WH 100 + +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_DROPDOWN_BUTTON, 1, 6, 106, WH-6-11, WH-6, STR_START_SERVER, STR_NONE }, // start server button + { WIDGETS_END }, +}; + +static void window_server_start_close(rct_window *w); +static void window_server_start_mouseup(rct_window *w, int widgetIndex); +static void window_server_start_update(rct_window *w); +static void window_server_start_textinput(rct_window *w, int widgetIndex, char *text); +static void window_server_start_invalidate(rct_window *w); +static void window_server_start_paint(rct_window *w, rct_drawpixelinfo *dpi); + +static rct_window_event_list window_server_start_events = { + window_server_start_close, + window_server_start_mouseup, + NULL, + NULL, + NULL, + NULL, + window_server_start_update, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + window_server_start_textinput, + NULL, + NULL, + NULL, + NULL, + NULL, + window_server_start_invalidate, + window_server_start_paint, + NULL +}; + +void window_server_start_open() +{ + rct_window* window; + + // Check if window is already open + window = window_bring_to_front_by_class(WC_SERVER_START); + if (window != NULL) + return; + + window = window_create_centred(WW, WH, &window_server_start_events, WC_SERVER_START, WF_10); + + window->widgets = window_server_start_widgets; + window->enabled_widgets = ( + (1 << WIDX_CLOSE) | + (1 << WIDX_PASSWORD_INPUT) | + (1 << WIDX_ADVERTISE_CHECKBOX) | + (1 << WIDX_START_SERVER) + ); + window_init_scroll_widgets(window); + window->no_list_items = 0; + window->selected_list_item = -1; + window->frame_no = 0; + window->min_width = window->width; + window->min_height = window->height; + window->max_width = window->min_width; + window->max_height = window->min_height; + + window->page = 0; + window->list_information_type = 0; + window->colours[0] = 1; + window->colours[1] = 26; + window->colours[2] = 26; +} + +static void window_server_start_close(rct_window *w) +{ + +} + +static void window_server_start_mouseup(rct_window *w, int widgetIndex) +{ + switch (widgetIndex) { + case WIDX_CLOSE: + window_close(w); + break; + case WIDX_PASSWORD_INPUT: + window_start_textbox(w, widgetIndex, 1170, (uint32)_password, 32); + break; + case WIDX_ADVERTISE_CHECKBOX: + gConfigNetwork.advertise = !gConfigNetwork.advertise; + config_save_default(); + window_invalidate(w); + break; + case WIDX_START_SERVER: + network_set_password(_password); + window_loadsave_open(LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME | LOADSAVETYPE_NETWORK, NULL); + break; + } +} + +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_PASSWORD_INPUT); + } +} + +static void window_server_start_textinput(rct_window *w, int widgetIndex, char *text) +{ + if (text == NULL) return; + + switch (widgetIndex) { + case WIDX_PASSWORD_INPUT: + if (strcmp(_password, text) == 0) + return; + + memset(_password, 0, sizeof(_password)); + if (strlen(text) > 0) { + safe_strncpy(_password, text, sizeof(_password)); + } + + widget_invalidate(w, WIDX_PASSWORD_INPUT); + break; + } +} + +static void window_server_start_invalidate(rct_window *w) +{ + widget_set_checkbox_value(w, WIDX_ADVERTISE_CHECKBOX, gConfigNetwork.advertise); +} + +static void window_server_start_paint(rct_window *w, rct_drawpixelinfo *dpi) +{ + window_draw_widgets(w, dpi); + + gfx_draw_string_left(dpi, STR_PASSWORD, NULL, w->colours[1], w->x + 6, w->y + w->widgets[WIDX_PASSWORD_INPUT].top); +} From 9acfd27735b603a696ecdcd5077a01ea3650564c Mon Sep 17 00:00:00 2001 From: zsilencer Date: Sun, 1 Nov 2015 20:12:14 -0700 Subject: [PATCH 3/9] password prompt for client --- data/language/english_uk.txt | 2 ++ src/interface/window.h | 1 + src/localisation/string_ids.h | 2 ++ src/network/network.cpp | 38 ++++++++++++++++++++++++++--------- src/network/network.h | 5 ++++- src/windows/network_status.c | 28 +++++++++++++++++++++++++- 6 files changed, 64 insertions(+), 12 deletions(-) diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index 27d2ee1cbe..a177274400 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -3906,6 +3906,8 @@ STR_5564 :Insert Corrupt Element STR_5565 :{SMALLFONT}{BLACK}Inserts a corrupt map element at top of tile. This will hide any element above the corrupt element. STR_5566 :Password: STR_5567 :Advertise +STR_5568 :Password Required +STR_5569 :This server requires a password ##################### # Rides/attractions # diff --git a/src/interface/window.h b/src/interface/window.h index 7714153a70..429a6ce07f 100644 --- a/src/interface/window.h +++ b/src/interface/window.h @@ -629,6 +629,7 @@ void window_cheats_open(); void window_player_list_open(); void window_network_status_open(const char* text); void window_network_status_close(); +void window_network_status_open_password(); void window_server_list_open(); void window_server_start_open(); diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index 45483c0bc3..a78cf7c0c3 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -2156,6 +2156,8 @@ enum { STR_PASSWORD = 5566, STR_ADVERTISE = 5567, + STR_PASSWORD_REQUIRED = 5568, + STR_PASSWORD_REQUIRED_DESC = 5569, // 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 74f5994690..d887652989 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -779,6 +779,21 @@ void Network::Client_Send_AUTH(const char* gameversion, const char* name, const server_connection.QueuePacket(std::move(packet)); } +void Network::Server_Send_AUTH(NetworkConnection& connection) +{ + uint8 new_playerid = 0; + if (connection.player) { + new_playerid = connection.player->id; + } + std::unique_ptr packet = std::move(NetworkPacket::Allocate()); + *packet << (uint32)NETWORK_COMMAND_AUTH << (uint32)connection.authstatus << (uint8)new_playerid; + connection.QueuePacket(std::move(packet)); + if (connection.authstatus != NETWORK_AUTH_OK && connection.authstatus != NETWORK_AUTH_REQUIREPASSWORD) { + shutdown(connection.socket, SHUT_RD); + connection.SendQueuedPackets(); + } +} + void Network::Server_Send_MAP(NetworkConnection* connection) { int buffersize = 0x600000; @@ -1042,6 +1057,9 @@ int Network::Client_Handle_AUTH(NetworkConnection& connection, NetworkPacket& pa connection.last_disconnect_reason = "Bad Password"; shutdown(connection.socket, SHUT_RDWR); break; + case NETWORK_AUTH_REQUIREPASSWORD: + window_network_status_open_password(); + break; } return 1; } @@ -1052,21 +1070,22 @@ int Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& pa const char* gameversion = packet.ReadString(); const char* name = packet.ReadString(); const char* password = packet.ReadString(); - uint8 playerid = 0; if (!gameversion || strcmp(gameversion, OPENRCT2_VERSION) != 0) { connection.authstatus = NETWORK_AUTH_BADVERSION; } else if (!name) { connection.authstatus = NETWORK_AUTH_BADNAME; } else - if (!password || strcmp(password, Network::password) != 0) { + if (!password || strlen(password) == 0) { + connection.authstatus = NETWORK_AUTH_REQUIREPASSWORD; + } else + if (strcmp(password, Network::password) != 0) { connection.authstatus = NETWORK_AUTH_BADPASSWORD; } else { connection.authstatus = NETWORK_AUTH_OK; NetworkPlayer* player = AddPlayer(name); connection.player = player; if (player) { - playerid = player->id; char text[256]; char* lineCh = text; lineCh = utf8_write_codepoint(lineCh, FORMAT_OUTLINE); @@ -1077,13 +1096,7 @@ int Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& pa Server_Send_MAP(&connection); } } - std::unique_ptr responsepacket = std::move(NetworkPacket::Allocate()); - *responsepacket << (uint32)NETWORK_COMMAND_AUTH << (uint32)connection.authstatus << (uint8)playerid; - connection.QueuePacket(std::move(responsepacket)); - if (connection.authstatus != NETWORK_AUTH_OK) { - shutdown(connection.socket, SHUT_RD); - connection.SendQueuedPackets(); - } + Server_Send_AUTH(connection); } return 1; } @@ -1356,6 +1369,11 @@ 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); +} + void network_kick_player(int playerId) { gNetwork.KickPlayer(playerId); diff --git a/src/network/network.h b/src/network/network.h index 349f80a7a9..0b20f8fd17 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -37,7 +37,8 @@ enum { NETWORK_AUTH_OK, NETWORK_AUTH_BADVERSION, NETWORK_AUTH_BADNAME, - NETWORK_AUTH_BADPASSWORD + NETWORK_AUTH_BADPASSWORD, + NETWORK_AUTH_REQUIREPASSWORD }; enum { @@ -217,6 +218,7 @@ public: void SetPassword(const char* password); void Client_Send_AUTH(const char* gameversion, 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); void Server_Send_CHAT(const char* text); @@ -318,6 +320,7 @@ int network_get_player_id(unsigned int index); void network_send_map(); void network_send_chat(const char* text); void network_send_gamecmd(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp, uint8 callback); +void network_send_password(const char* password); void network_kick_player(int playerId); void network_set_password(const char* password); diff --git a/src/windows/network_status.c b/src/windows/network_status.c index cadcbe99b9..86652b435d 100644 --- a/src/windows/network_status.c +++ b/src/windows/network_status.c @@ -25,10 +25,13 @@ #include "../util/util.h" #include "../network/network.h" +char _password[33]; + enum WINDOW_NETWORK_STATUS_WIDGET_IDX { WIDX_BACKGROUND, WIDX_TITLE, WIDX_CLOSE, + WIDX_PASSWORD }; static rct_widget window_network_status_widgets[] = { @@ -42,6 +45,7 @@ static char window_network_status_text[1024]; static void window_network_status_mouseup(rct_window *w, int widgetIndex); static void window_network_status_update(rct_window *w); +static void window_network_status_textinput(rct_window *w, int widgetIndex, char *text); static void window_network_status_invalidate(rct_window *w); static void window_network_status_paint(rct_window *w, rct_drawpixelinfo *dpi); @@ -65,7 +69,7 @@ static rct_window_event_list window_network_status_events = { NULL, NULL, NULL, - NULL, + window_network_status_textinput, NULL, NULL, NULL, @@ -112,6 +116,16 @@ void window_network_status_close() window_close_by_class(WC_NETWORK_STATUS); } +void window_network_status_open_password() +{ + rct_window* window; + window = window_bring_to_front_by_class(WC_NETWORK_STATUS); + if (window == NULL) + return; + + window_text_input_raw_open(window, WIDX_PASSWORD, STR_PASSWORD_REQUIRED, STR_PASSWORD_REQUIRED_DESC, _password, 32); +} + static void window_network_status_mouseup(rct_window *w, int widgetIndex) { switch (widgetIndex) { @@ -126,6 +140,18 @@ static void window_network_status_update(rct_window *w) widget_invalidate(w, WIDX_BACKGROUND); } +static void window_network_status_textinput(rct_window *w, int widgetIndex, char *text) +{ + strcpy(_password, ""); + switch (widgetIndex) { + case WIDX_PASSWORD: + if (text != NULL) + safe_strncpy(_password, text, sizeof(_password)); + break; + } + network_send_password(_password); +} + static void window_network_status_invalidate(rct_window *w) { window_network_status_widgets[WIDX_BACKGROUND].right = w->width - 1; From 1e9381ef9716deb4dd6000968220e16086b0c20f Mon Sep 17 00:00:00 2001 From: zsilencer Date: Tue, 3 Nov 2015 16:31:09 -0700 Subject: [PATCH 4/9] master server list --- data/language/english_uk.txt | 8 + src/config.c | 3 + src/config.h | 3 + src/game.c | 4 +- src/interface/chat.c | 5 +- src/localisation/string_ids.h | 7 + src/network/network.cpp | 139 ++++++++++++----- src/network/network.h | 16 +- src/rct2.h | 1 + src/windows/network_status.c | 6 +- src/windows/server_list.c | 276 +++++++++++++++++++++++++--------- src/windows/server_start.c | 82 +++++++++- 12 files changed, 436 insertions(+), 114 deletions(-) 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); } From 74fe482a621aaf3be6fa496390decf638fd6d449 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sat, 7 Nov 2015 23:47:50 +0000 Subject: [PATCH 5/9] improve game info json --- src/config.c | 1 + src/config.h | 1 + src/network/network.cpp | 6 +++--- src/windows/server_list.c | 14 +++++++------- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/config.c b/src/config.c index 7244269e58..f3040804a9 100644 --- a/src/config.c +++ b/src/config.c @@ -243,6 +243,7 @@ config_property_definition _networkDefinitions[] = { { 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, server_description), "server_description", CONFIG_VALUE_TYPE_STRING, {.value_string = "" }, NULL }, { offsetof(network_configuration, master_url), "master_url", CONFIG_VALUE_TYPE_STRING, {.value_string = OPENRCT2_MASTER_URL }, NULL } }; diff --git a/src/config.h b/src/config.h index 120ee9dfb0..0e3d7d560d 100644 --- a/src/config.h +++ b/src/config.h @@ -217,6 +217,7 @@ typedef struct { uint8 advertise; uint8 maxplayers; utf8string server_name; + utf8string server_description; utf8string master_url; } network_configuration; diff --git a/src/network/network.cpp b/src/network/network.cpp index dae6b7cb7b..e97892e946 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -940,11 +940,11 @@ void Network::Server_Send_GAMEINFO(NetworkConnection& connection) #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, "requiresPassword", json_boolean(password.size() > 0)); 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)); + json_object_set(obj, "maxPlayers", json_integer(gConfigNetwork.maxplayers)); + json_object_set(obj, "description", json_string(gConfigNetwork.server_description)); packet->WriteString(json_dumps(obj, 0)); json_object_clear(obj); #endif diff --git a/src/windows/server_list.c b/src/windows/server_list.c index aa9126b314..a941c24f6b 100644 --- a/src/windows/server_list.c +++ b/src/windows/server_list.c @@ -39,7 +39,7 @@ typedef struct { char *address; utf8 *name; - bool haspassword; + bool requiresPassword; utf8 *description; char *version; bool favorite; @@ -479,7 +479,7 @@ static void server_list_load_saved_servers() serverInfo->address = freadstralloc(file); serverInfo->name = freadstralloc(file); - serverInfo->haspassword = false; + serverInfo->requiresPassword = false; serverInfo->description = freadstralloc(file); serverInfo->version = _strdup(""); serverInfo->favorite = true; @@ -573,7 +573,7 @@ static saved_server* add_saved_server(char *address) saved_server* newserver = &_savedServers[index]; newserver->address = _strdup(address); newserver->name = _strdup(address); - newserver->haspassword = false; + newserver->requiresPassword = false; newserver->description = _strdup(""); newserver->version = _strdup(""); newserver->favorite = false; @@ -664,17 +664,17 @@ static void fetch_servers_callback(http_json_response* response) 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 *requiresPassword = json_object_get(server, "requiresPassword"); json_t *version = json_object_get(server, "version"); json_t *players = json_object_get(server, "players"); - json_t *maxplayers = json_object_get(server, "maxplayers"); + json_t *maxplayers = json_object_get(server, "maxPlayers"); + json_t *description = json_object_get(server, "description"); 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); + newserver->requiresPassword = json_boolean_value(requiresPassword); SafeFree(newserver->description); newserver->description = _strdup(json_string_value(description)); SafeFree(newserver->version); From 9bad4183a7584306ec876fb512e8327a27b6ffdf Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sat, 7 Nov 2015 23:56:16 +0000 Subject: [PATCH 6/9] add default master server url and refactor --- src/config.c | 6 +++--- src/config.h | 2 +- src/network/network.cpp | 12 +++++++++--- src/rct2.h | 2 +- src/windows/server_list.c | 23 +++++++++++++---------- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/config.c b/src/config.c index f3040804a9..40e83036fa 100644 --- a/src/config.c +++ b/src/config.c @@ -157,7 +157,7 @@ config_enum_definition _dateFormatEnum[] = { config_property_definition _generalDefinitions[] = { { offsetof(general_configuration, always_show_gridlines), "always_show_gridlines", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL }, - { offsetof(general_configuration, autosave_frequency), "autosave", CONFIG_VALUE_TYPE_UINT8, AUTOSAVE_EVERY_5MINUTES, NULL }, + { offsetof(general_configuration, autosave_frequency), "autosave", CONFIG_VALUE_TYPE_UINT8, AUTOSAVE_EVERY_5MINUTES, NULL }, { offsetof(general_configuration, confirmation_prompt), "confirmation_prompt", CONFIG_VALUE_TYPE_UINT8, 0, NULL }, { offsetof(general_configuration, construction_marker_colour), "construction_marker_colour", CONFIG_VALUE_TYPE_UINT8, false, NULL }, { offsetof(general_configuration, currency_format), "currency_format", CONFIG_VALUE_TYPE_UINT8, CURRENCY_POUNDS, _currencyEnum }, @@ -243,8 +243,8 @@ config_property_definition _networkDefinitions[] = { { 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, server_description), "server_description", CONFIG_VALUE_TYPE_STRING, {.value_string = "" }, NULL }, - { offsetof(network_configuration, master_url), "master_url", CONFIG_VALUE_TYPE_STRING, {.value_string = OPENRCT2_MASTER_URL }, NULL } + { offsetof(network_configuration, server_description), "server_description", CONFIG_VALUE_TYPE_STRING, {.value_string = NULL }, NULL }, + { offsetof(network_configuration, master_server_url), "master_server_url", CONFIG_VALUE_TYPE_STRING, {.value_string = NULL }, NULL } }; config_section_definition _sectionDefinitions[] = { diff --git a/src/config.h b/src/config.h index 0e3d7d560d..fc070133f4 100644 --- a/src/config.h +++ b/src/config.h @@ -218,7 +218,7 @@ typedef struct { uint8 maxplayers; utf8string server_name; utf8string server_description; - utf8string master_url; + utf8string master_server_url; } network_configuration; typedef struct theme_window { diff --git a/src/network/network.cpp b/src/network/network.cpp index e97892e946..71d52ef82c 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -791,11 +791,17 @@ void Network::ShutdownClient() void Network::Advertise() { - if (gConfigNetwork.advertise && strlen(gConfigNetwork.master_url) > 0) { + if (gConfigNetwork.advertise) { 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{}); + const char *masterServerUrl = OPENRCT2_MASTER_SERVER_URL; + if (!str_is_null_or_empty(gConfigNetwork.master_server_url)) { + masterServerUrl = gConfigNetwork.master_server_url; + } + + std::string url = masterServerUrl + std::string("?port=") + std::to_string(listening_port); + http_request_json_async(url.c_str(), [](http_json_response *response)->void { }); #endif } } diff --git a/src/rct2.h b/src/rct2.h index 77937a0b2b..841aa697d8 100644 --- a/src/rct2.h +++ b/src/rct2.h @@ -105,7 +105,7 @@ typedef utf16* utf16string; #define OPENRCT2_BRANCH "develop" #define OPENRCT2_COMMIT_SHA1 "" #define OPENRCT2_COMMIT_SHA1_SHORT "" -#define OPENRCT2_MASTER_URL "" +#define OPENRCT2_MASTER_SERVER_URL "https://servers.openrct2.website" // Represent fixed point numbers. dp = decimal point typedef uint8 fixed8_1dp; diff --git a/src/windows/server_list.c b/src/windows/server_list.c index a941c24f6b..cd9cd9c46b 100644 --- a/src/windows/server_list.c +++ b/src/windows/server_list.c @@ -638,17 +638,20 @@ static void join_server(char *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); + const char *masterServerUrl = OPENRCT2_MASTER_SERVER_URL; + if (!str_is_null_or_empty(gConfigNetwork.master_server_url)) { + masterServerUrl = gConfigNetwork.master_server_url; } + + 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(masterServerUrl, fetch_servers_callback); #endif } From 782d3e63b275b92579442b08db8423b1163cdd69 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 8 Nov 2015 02:12:12 +0000 Subject: [PATCH 7/9] add new master server functionality --- src/network/network.cpp | 137 ++++++++++++++++++++++++++++++++++---- src/network/network.h | 9 ++- src/windows/server_list.c | 84 +++++++++++++++-------- 3 files changed, 188 insertions(+), 42 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 71d52ef82c..47179d2986 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -84,6 +84,22 @@ const char *NetworkCommandNames[] = { "NETWORK_COMMAND_PINGLIST", }; +enum { + ADVERTISE_STATUS_DISABLED, + ADVERTISE_STATUS_UNREGISTERED, + ADVERTISE_STATUS_REGISTERED +}; + +enum { + MASTER_SERVER_STATUS_OK = 200, + MASTER_SERVER_STATUS_INVALID_TOKEN = 401, + MASTER_SERVER_STATUS_SERVER_NOT_FOUND = 404, + MASTER_SERVER_STATUS_INTERNAL_ERROR = 500 +}; + +constexpr int MASTER_SERVER_REGISTER_TIME = 2 * 1000; // 2 seconds +constexpr int MASTER_SERVER_HEARTBEAT_TIME = 1 * 1000; // 1 second + NetworkPacket::NetworkPacket() { transferred = 0; @@ -521,8 +537,17 @@ bool Network::BeginServer(unsigned short port, const char* address) mode = NETWORK_MODE_SERVER; status = NETWORK_STATUS_CONNECTED; listening_port = port; + advertise_status = ADVERTISE_STATUS_DISABLED; + last_advertise_time = 0; + last_heartbeat_time = 0; + advertise_token = ""; + advertise_key = GenerateAdvertiseKey(); - Advertise(); +#ifndef DISABLE_HTTP + if (gConfigNetwork.advertise) { + advertise_status = ADVERTISE_STATUS_UNREGISTERED; + } +#endif return true; } @@ -587,9 +612,20 @@ void Network::UpdateServer() Server_Send_PING(); Server_Send_PINGLIST(); } - if (SDL_TICKS_PASSED(SDL_GetTicks(), last_advertise_time + 60000)) { - Advertise(); + + switch (advertise_status) { + case ADVERTISE_STATUS_UNREGISTERED: + if (SDL_TICKS_PASSED(SDL_GetTicks(), last_advertise_time + MASTER_SERVER_REGISTER_TIME)) { + AdvertiseRegister(); + } + break; + case ADVERTISE_STATUS_REGISTERED: + if (SDL_TICKS_PASSED(SDL_GetTicks(), last_heartbeat_time + MASTER_SERVER_HEARTBEAT_TIME)) { + AdvertiseHeartbeat(); + } + break; } + SOCKET socket = accept(listening_socket, NULL, NULL); if (socket == INVALID_SOCKET) { if (LAST_SOCKET_ERROR() != EWOULDBLOCK) { @@ -789,21 +825,98 @@ void Network::ShutdownClient() } } -void Network::Advertise() +std::string Network::GenerateAdvertiseKey() { - if (gConfigNetwork.advertise) { - last_advertise_time = SDL_GetTicks(); + // Generate a string of 16 random hex characters (64-integer key as a hex formatted string) + static char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + char key[17]; + for (int i = 0; i < 16; i++) { + int hexCharIndex = rand() % countof(hexChars); + key[i] = hexChars[hexCharIndex]; + } + key[countof(key) - 1] = 0; + return key; +} + +const char *Network::GetMasterServerUrl() +{ + if (str_is_null_or_empty(gConfigNetwork.master_server_url)) { + return OPENRCT2_MASTER_SERVER_URL; + } else { + return gConfigNetwork.master_server_url; + } +} + +void Network::AdvertiseRegister() +{ #ifndef DISABLE_HTTP - const char *masterServerUrl = OPENRCT2_MASTER_SERVER_URL; - if (!str_is_null_or_empty(gConfigNetwork.master_server_url)) { - masterServerUrl = gConfigNetwork.master_server_url; + last_advertise_time = SDL_GetTicks(); + + // Send the registration request + std::string url = GetMasterServerUrl() + + std::string("?command=register&port=") + std::to_string(listening_port) + + std::string("key=") + advertise_key; + http_request_json_async(url.c_str(), [](http_json_response *response) -> void { + if (response == NULL) { + log_warning("Unable to connect to master server"); + return; } - std::string url = masterServerUrl + std::string("?port=") + std::to_string(listening_port); - http_request_json_async(url.c_str(), [](http_json_response *response)->void { }); + json_t *jsonStatus = json_object_get(response->root, "status"); + if (json_is_integer(jsonStatus)) { + int status = (int)json_integer_value(jsonStatus); + if (status == MASTER_SERVER_STATUS_OK) { + json_t *jsonToken = json_object_get(response->root, "token"); + if (json_is_string(jsonToken)) { + gNetwork.advertise_token = json_string_value(jsonToken); + gNetwork.advertise_status = ADVERTISE_STATUS_REGISTERED; + } + } else { + const char *message = "Invalid response from server"; + json_t *jsonMessage = json_object_get(response->root, "message"); + if (json_is_string(jsonMessage)) { + message = json_string_value(jsonMessage); + } + log_warning("Unable to advertise: %s", message); + } + } + http_request_json_dispose(response); + }); +#endif +} + +void Network::AdvertiseHeartbeat() +{ +#ifndef DISABLE_HTTP + // Send the heartbeat request + std::string url = GetMasterServerUrl() + + std::string("?command=heartbeat&token=") + advertise_token + + std::string("key=") + advertise_key + + std::string("players=") + std::to_string(network_get_num_players()); + + // TODO send status data (e.g. players) via JSON body + + gNetwork.last_heartbeat_time = SDL_GetTicks(); + http_request_json_async(url.c_str(), [](http_json_response *response) -> void { + if (response == NULL) { + log_warning("Unable to connect to master server"); + return; + } + + json_t *jsonStatus = json_object_get(response->root, "status"); + if (json_is_integer(jsonStatus)) { + int status = (int)json_integer_value(jsonStatus); + if (status == MASTER_SERVER_STATUS_OK) { + // Master server has successfully updated our server status + } else if (status == MASTER_SERVER_STATUS_INVALID_TOKEN) { + gNetwork.advertise_status = ADVERTISE_STATUS_UNREGISTERED; + log_warning("Master server heartbeat failed: Invalid Token"); + } + } + http_request_json_dispose(response); + }); #endif - } } void Network::Client_Send_AUTH(const char* name, const char* password) diff --git a/src/network/network.h b/src/network/network.h index 9fd433c4c3..0c092fa1f7 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -218,7 +218,8 @@ public: void KickPlayer(int playerId); void SetPassword(const char* password); void ShutdownClient(); - void Advertise(); + void AdvertiseRegister(); + void AdvertiseHeartbeat(); void Client_Send_AUTH(const char* name, const char* password); void Server_Send_AUTH(NetworkConnection& connection); @@ -245,6 +246,8 @@ private: void RemoveClient(std::unique_ptr& connection); NetworkPlayer* AddPlayer(const char* name); void PrintError(); + const char *GetMasterServerUrl(); + std::string GenerateAdvertiseKey(); struct GameCommand { @@ -278,6 +281,10 @@ private: bool _desynchronised; uint32 server_connect_time; uint32 last_advertise_time; + std::string advertise_token; + std::string advertise_key; + int advertise_status; + uint32 last_heartbeat_time; void UpdateServer(); void UpdateClient(); diff --git a/src/windows/server_list.c b/src/windows/server_list.c index cd9cd9c46b..4201f2eef8 100644 --- a/src/windows/server_list.c +++ b/src/windows/server_list.c @@ -658,36 +658,62 @@ static void fetch_servers() #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 *requiresPassword = json_object_get(server, "requiresPassword"); - json_t *version = json_object_get(server, "version"); - json_t *players = json_object_get(server, "players"); - json_t *maxplayers = json_object_get(server, "maxPlayers"); - json_t *description = json_object_get(server, "description"); - - 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->requiresPassword = json_boolean_value(requiresPassword); - 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); + if (response == NULL) { + log_warning("Unable to connect to master server"); + return; } + + json_t *jsonStatus = json_object_get(response->root, "status"); + if (!json_is_number(jsonStatus)) { + http_request_json_dispose(response); + log_warning("Invalid response from master server"); + return; + } + + int status = (int)json_integer_value(jsonStatus); + if (status != 200) { + http_request_json_dispose(response); + log_warning("Master server failed to return servers"); + return; + } + + json_t *jsonServers = json_object_get(response->root, "servers"); + if (!json_is_array(jsonServers)) { + http_request_json_dispose(response); + log_warning("Invalid response from master server"); + return; + } + + 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 *requiresPassword = json_object_get(server, "requiresPassword"); + json_t *version = json_object_get(server, "version"); + json_t *players = json_object_get(server, "players"); + json_t *maxplayers = json_object_get(server, "maxPlayers"); + json_t *description = json_object_get(server, "description"); + + 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->requiresPassword = json_boolean_value(requiresPassword); + 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); From 0b9882b96fc49b29366edda5da972eae1dbddf32 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 8 Nov 2015 02:22:49 +0000 Subject: [PATCH 8/9] fix master server poll times --- src/network/network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 47179d2986..4084075d1c 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -97,8 +97,8 @@ enum { MASTER_SERVER_STATUS_INTERNAL_ERROR = 500 }; -constexpr int MASTER_SERVER_REGISTER_TIME = 2 * 1000; // 2 seconds -constexpr int MASTER_SERVER_HEARTBEAT_TIME = 1 * 1000; // 1 second +constexpr int MASTER_SERVER_REGISTER_TIME = 120 * 1000; // 2 minutes +constexpr int MASTER_SERVER_HEARTBEAT_TIME = 60 * 1000; // 1 minute NetworkPacket::NetworkPacket() { From d2aa7364928da59f8f00c294354079d14d8c33cb Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 8 Nov 2015 03:55:30 +0000 Subject: [PATCH 9/9] fix master server communication --- src/network/http.cpp | 4 ++++ src/network/network.cpp | 7 +++---- src/windows/server_list.c | 25 ++++++++++++++++--------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/network/http.cpp b/src/network/http.cpp index 3711694acb..ba6557578c 100644 --- a/src/network/http.cpp +++ b/src/network/http.cpp @@ -69,6 +69,10 @@ http_json_response *http_request_json(const char *url) writeBuffer.length = 0; writeBuffer.capacity = 0; + curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Accept: application/json"); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true); curl_easy_setopt(curl, CURLOPT_CAINFO, "curl-ca-bundle.crt"); diff --git a/src/network/network.cpp b/src/network/network.cpp index 4084075d1c..eef553901a 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -615,7 +615,7 @@ void Network::UpdateServer() switch (advertise_status) { case ADVERTISE_STATUS_UNREGISTERED: - if (SDL_TICKS_PASSED(SDL_GetTicks(), last_advertise_time + MASTER_SERVER_REGISTER_TIME)) { + if (last_advertise_time == 0 || SDL_TICKS_PASSED(SDL_GetTicks(), last_advertise_time + MASTER_SERVER_REGISTER_TIME)) { AdvertiseRegister(); } break; @@ -856,7 +856,7 @@ void Network::AdvertiseRegister() // Send the registration request std::string url = GetMasterServerUrl() + std::string("?command=register&port=") + std::to_string(listening_port) - + std::string("key=") + advertise_key; + + std::string("&key=") + advertise_key; http_request_json_async(url.c_str(), [](http_json_response *response) -> void { if (response == NULL) { log_warning("Unable to connect to master server"); @@ -892,8 +892,7 @@ void Network::AdvertiseHeartbeat() // Send the heartbeat request std::string url = GetMasterServerUrl() + std::string("?command=heartbeat&token=") + advertise_token - + std::string("key=") + advertise_key - + std::string("players=") + std::to_string(network_get_num_players()); + + std::string("&players=") + std::to_string(network_get_num_players()); // TODO send status data (e.g. players) via JSON body diff --git a/src/windows/server_list.c b/src/windows/server_list.c index 4201f2eef8..5bbb4bfbff 100644 --- a/src/windows/server_list.c +++ b/src/windows/server_list.c @@ -684,32 +684,39 @@ static void fetch_servers_callback(http_json_response* response) return; } - int count = json_array_size(response->root); + int count = json_array_size(jsonServers); for (int i = 0; i < count; i++) { - json_t *server = json_array_get(response->root, i); + json_t *server = json_array_get(jsonServers, i); if (!json_is_object(server)) { continue; } - json_t *address = json_object_get(server, "address"); + json_t *port = json_object_get(server, "port"); json_t *name = json_object_get(server, "name"); + json_t *description = json_object_get(server, "description"); json_t *requiresPassword = json_object_get(server, "requiresPassword"); json_t *version = json_object_get(server, "version"); json_t *players = json_object_get(server, "players"); - json_t *maxplayers = json_object_get(server, "maxPlayers"); - json_t *description = json_object_get(server, "description"); + json_t *maxPlayers = json_object_get(server, "maxPlayers"); + json_t *ip = json_object_get(server, "ip"); + json_t *ip4 = json_object_get(ip, "v4"); + json_t *ip6 = json_object_get(ip, "v6"); + json_t *addressIp = json_array_get(ip4, 0); + + char address[256]; + snprintf(address, sizeof(address), "%s:%d", json_string_value(addressIp), (int)json_integer_value(port)); SDL_LockMutex(_mutex); - saved_server* newserver = add_saved_server((char*)json_string_value(address)); + saved_server* newserver = add_saved_server(address); SafeFree(newserver->name); + SafeFree(newserver->description); + SafeFree(newserver->version); newserver->name = _strdup(json_string_value(name)); newserver->requiresPassword = json_boolean_value(requiresPassword); - 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); + newserver->maxplayers = (uint8)json_integer_value(maxPlayers); SDL_UnlockMutex(_mutex); } http_request_json_dispose(response);