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); +}