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