mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-23 06:44:38 +01:00
Merge branch 'zsilencer-network' into develop
This commit is contained in:
@@ -3899,11 +3899,23 @@ 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.
|
||||
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
|
||||
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 #
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
<ClCompile Include="..\src\windows\network_status.c" />
|
||||
<ClCompile Include="..\src\windows\player_list.c" />
|
||||
<ClCompile Include="..\src\windows\server_list.c" />
|
||||
<ClCompile Include="..\src\windows\server_start.c" />
|
||||
<ClCompile Include="..\src\windows\title_command_editor.c" />
|
||||
<ClCompile Include="..\src\windows\title_editor.c" />
|
||||
<ClCompile Include="..\src\windows\maze_construction.c" />
|
||||
|
||||
@@ -546,6 +546,9 @@
|
||||
<ClCompile Include="..\src\platform\linux.c">
|
||||
<Filter>Source\Platform</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\windows\server_start.c">
|
||||
<Filter>Source\Windows</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\src\management\award.h">
|
||||
|
||||
@@ -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 },
|
||||
@@ -239,7 +239,12 @@ 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 },
|
||||
{ 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 }, NULL },
|
||||
{ offsetof(network_configuration, master_server_url), "master_server_url", CONFIG_VALUE_TYPE_STRING, {.value_string = NULL }, NULL }
|
||||
};
|
||||
|
||||
config_section_definition _sectionDefinitions[] = {
|
||||
|
||||
@@ -214,6 +214,11 @@ typedef struct {
|
||||
utf8string player_name;
|
||||
uint32 default_port;
|
||||
uint8 stay_connected;
|
||||
uint8 advertise;
|
||||
uint8 maxplayers;
|
||||
utf8string server_name;
|
||||
utf8string server_description;
|
||||
utf8string master_server_url;
|
||||
} network_configuration;
|
||||
|
||||
typedef struct theme_window {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
@@ -628,7 +629,9 @@ 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();
|
||||
|
||||
void window_research_open();
|
||||
void window_research_development_page_paint(rct_window *w, rct_drawpixelinfo *dpi, int baseWidgetIndex);
|
||||
|
||||
@@ -2154,6 +2154,18 @@ enum {
|
||||
STR_INSERT_CORRUPT = 5564,
|
||||
STR_INSERT_CORRUPT_TIP = 5565,
|
||||
|
||||
STR_PASSWORD = 5566,
|
||||
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
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,7 +68,9 @@ enum {
|
||||
NETWORK_COMMAND_PING,
|
||||
NETWORK_COMMAND_PINGLIST,
|
||||
NETWORK_COMMAND_SETDISCONNECTMSG,
|
||||
NETWORK_COMMAND_MAX
|
||||
NETWORK_COMMAND_GAMEINFO,
|
||||
NETWORK_COMMAND_MAX,
|
||||
NETWORK_COMMAND_INVALID = -1
|
||||
};
|
||||
|
||||
const char *NetworkCommandNames[] = {
|
||||
@@ -81,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 = 120 * 1000; // 2 minutes
|
||||
constexpr int MASTER_SERVER_HEARTBEAT_TIME = 60 * 1000; // 1 minute
|
||||
|
||||
NetworkPacket::NetworkPacket()
|
||||
{
|
||||
transferred = 0;
|
||||
@@ -104,6 +123,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 +175,18 @@ void NetworkPacket::Clear()
|
||||
data->clear();
|
||||
}
|
||||
|
||||
bool NetworkPacket::CommandRequiresAuth()
|
||||
{
|
||||
switch (GetCommand()) {
|
||||
case NETWORK_COMMAND_PING:
|
||||
case NETWORK_COMMAND_AUTH:
|
||||
case NETWORK_COMMAND_GAMEINFO:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
NetworkPlayer::NetworkPlayer(const char* name)
|
||||
{
|
||||
safe_strncpy((char*)NetworkPlayer::name, name, sizeof(NetworkPlayer::name));
|
||||
@@ -232,11 +272,15 @@ bool NetworkConnection::SendPacket(NetworkPacket& packet)
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetworkConnection::QueuePacket(std::unique_ptr<NetworkPacket> packet)
|
||||
void NetworkConnection::QueuePacket(std::unique_ptr<NetworkPacket> packet, bool front)
|
||||
{
|
||||
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));
|
||||
if (front) {
|
||||
outboundpackets.push_front(std::move(packet));
|
||||
} else {
|
||||
outboundpackets.push_back(std::move(packet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +400,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;
|
||||
@@ -372,6 +416,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()
|
||||
@@ -425,6 +470,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;
|
||||
@@ -486,6 +535,19 @@ 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_status = ADVERTISE_STATUS_DISABLED;
|
||||
last_advertise_time = 0;
|
||||
last_heartbeat_time = 0;
|
||||
advertise_token = "";
|
||||
advertise_key = GenerateAdvertiseKey();
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
if (gConfigNetwork.advertise) {
|
||||
advertise_status = ADVERTISE_STATUS_UNREGISTERED;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -544,14 +606,26 @@ 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();
|
||||
}
|
||||
|
||||
switch (advertise_status) {
|
||||
case ADVERTISE_STATUS_UNREGISTERED:
|
||||
if (last_advertise_time == 0 || 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) {
|
||||
@@ -573,7 +647,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 +680,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,29 +704,26 @@ 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;
|
||||
}
|
||||
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();
|
||||
@@ -702,10 +772,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,17 +798,152 @@ bool Network::CheckSRAND(uint32 tick, uint32 srand0)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Network::Client_Send_AUTH(const char* gameversion, const char* name, const char* password)
|
||||
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)
|
||||
{
|
||||
Network::password = password;
|
||||
}
|
||||
|
||||
void Network::ShutdownClient()
|
||||
{
|
||||
if (GetMode() == NETWORK_MODE_CLIENT) {
|
||||
shutdown(server_connection.socket, SHUT_RDWR);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Network::GenerateAdvertiseKey()
|
||||
{
|
||||
// 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
|
||||
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;
|
||||
}
|
||||
|
||||
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("&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)
|
||||
{
|
||||
std::unique_ptr<NetworkPacket> 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;
|
||||
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<NetworkPacket> 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;
|
||||
@@ -765,7 +970,7 @@ void Network::Client_Send_CHAT(const char* text)
|
||||
{
|
||||
std::unique_ptr<NetworkPacket> 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));
|
||||
}
|
||||
|
||||
@@ -773,7 +978,7 @@ void Network::Server_Send_CHAT(const char* text)
|
||||
{
|
||||
std::unique_ptr<NetworkPacket> packet = std::move(NetworkPacket::Allocate());
|
||||
*packet << (uint32)NETWORK_COMMAND_CHAT;
|
||||
packet->Write((uint8*)text, strlen(text) + 1);
|
||||
packet->WriteString(text);
|
||||
SendPacketToClients(*packet);
|
||||
}
|
||||
|
||||
@@ -793,6 +998,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<NetworkPacket> 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);
|
||||
@@ -818,12 +1024,13 @@ void Network::Client_Send_PING()
|
||||
|
||||
void Network::Server_Send_PING()
|
||||
{
|
||||
last_ping_sent_time = SDL_GetTicks();
|
||||
std::unique_ptr<NetworkPacket> 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()
|
||||
@@ -844,9 +1051,26 @@ void Network::Server_Send_SETDISCONNECTMSG(NetworkConnection& connection, const
|
||||
connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
void Network::Server_Send_GAMEINFO(NetworkConnection& connection)
|
||||
{
|
||||
std::unique_ptr<NetworkPacket> 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, "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, "description", json_string(gConfigNetwork.server_description));
|
||||
packet->WriteString(json_dumps(obj, 0));
|
||||
json_object_clear(obj);
|
||||
#endif
|
||||
connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
bool Network::ProcessConnection(NetworkConnection& connection)
|
||||
{
|
||||
connection.SendQueuedPackets();
|
||||
int packetStatus;
|
||||
do {
|
||||
packetStatus = connection.ReadPacket();
|
||||
@@ -871,12 +1095,13 @@ 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";
|
||||
if (!connection.last_disconnect_reason) {
|
||||
connection.last_disconnect_reason = "No Data";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -889,7 +1114,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);
|
||||
}
|
||||
}
|
||||
@@ -935,7 +1160,7 @@ void Network::RemoveClient(std::unique_ptr<NetworkConnection>& 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);
|
||||
@@ -991,6 +1216,27 @@ 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;
|
||||
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;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1000,21 +1246,25 @@ 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) && Network::password.size() > 0) {
|
||||
connection.authstatus = NETWORK_AUTH_REQUIREPASSWORD;
|
||||
} else
|
||||
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);
|
||||
connection.player = player;
|
||||
if (player) {
|
||||
playerid = player->id;
|
||||
char text[256];
|
||||
char* lineCh = text;
|
||||
lineCh = utf8_write_codepoint(lineCh, FORMAT_OUTLINE);
|
||||
@@ -1025,9 +1275,7 @@ int Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& pa
|
||||
Server_Send_MAP(&connection);
|
||||
}
|
||||
}
|
||||
std::unique_ptr<NetworkPacket> responsepacket = std::move(NetworkPacket::Allocate());
|
||||
*responsepacket << (uint32)NETWORK_COMMAND_AUTH << (uint32)connection.authstatus << (uint8)playerid;
|
||||
connection.QueuePacket(std::move(responsepacket));
|
||||
Server_Send_AUTH(connection);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -1182,15 +1430,21 @@ int Network::Client_Handle_PINGLIST(NetworkConnection& connection, NetworkPacket
|
||||
|
||||
int Network::Client_Handle_SETDISCONNECTMSG(NetworkConnection& connection, NetworkPacket& packet)
|
||||
{
|
||||
static char msg[256] = {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();
|
||||
@@ -1201,13 +1455,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)
|
||||
@@ -1300,18 +1555,9 @@ void network_send_gamecmd(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32
|
||||
}
|
||||
}
|
||||
|
||||
void Network::KickPlayer(int playerId)
|
||||
void network_send_password(const char* password)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
gNetwork.Client_Send_AUTH(gConfigNetwork.player_name, password);
|
||||
}
|
||||
|
||||
void network_kick_player(int playerId)
|
||||
@@ -1319,9 +1565,15 @@ 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; }
|
||||
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() {}
|
||||
@@ -1334,7 +1586,10 @@ 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_kick_player(int playerId) { }
|
||||
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; }
|
||||
#endif /* DISABLE_NETWORK */
|
||||
|
||||
@@ -37,7 +37,9 @@ enum {
|
||||
NETWORK_AUTH_OK,
|
||||
NETWORK_AUTH_BADVERSION,
|
||||
NETWORK_AUTH_BADNAME,
|
||||
NETWORK_AUTH_BADPASSWORD
|
||||
NETWORK_AUTH_BADPASSWORD,
|
||||
NETWORK_AUTH_FULL,
|
||||
NETWORK_AUTH_REQUIREPASSWORD
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -69,6 +71,9 @@ extern "C" {
|
||||
#ifndef SHUT_RD
|
||||
#define SHUT_RD SD_RECEIVE
|
||||
#endif
|
||||
#ifndef SHUT_RDWR
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#endif
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
@@ -109,6 +114,7 @@ public:
|
||||
static std::unique_ptr<NetworkPacket> Allocate();
|
||||
static std::unique_ptr<NetworkPacket> Duplicate(NetworkPacket& packet);
|
||||
uint8* GetData();
|
||||
uint32 GetCommand();
|
||||
template <typename T>
|
||||
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 +124,7 @@ public:
|
||||
const uint8* Read(unsigned int size);
|
||||
const char* ReadString();
|
||||
void Clear();
|
||||
bool CommandRequiresAuth();
|
||||
|
||||
uint16 size;
|
||||
std::shared_ptr<std::vector<uint8>> data;
|
||||
@@ -141,7 +148,7 @@ public:
|
||||
NetworkConnection();
|
||||
~NetworkConnection();
|
||||
int ReadPacket();
|
||||
void QueuePacket(std::unique_ptr<NetworkPacket> packet);
|
||||
void QueuePacket(std::unique_ptr<NetworkPacket> packet, bool front = false);
|
||||
void SendQueuedPackets();
|
||||
bool SetTCPNoDelay(bool on);
|
||||
bool SetNonBlocking(bool on);
|
||||
@@ -206,11 +213,16 @@ 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 AdvertiseRegister();
|
||||
void AdvertiseHeartbeat();
|
||||
|
||||
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);
|
||||
void Server_Send_CHAT(const char* text);
|
||||
@@ -222,6 +234,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<std::unique_ptr<NetworkPlayer>> player_list;
|
||||
|
||||
@@ -233,6 +246,8 @@ private:
|
||||
void RemoveClient(std::unique_ptr<NetworkConnection>& connection);
|
||||
NetworkPlayer* AddPlayer(const char* name);
|
||||
void PrintError();
|
||||
const char *GetMasterServerUrl();
|
||||
std::string GenerateAdvertiseKey();
|
||||
|
||||
struct GameCommand
|
||||
{
|
||||
@@ -251,6 +266,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;
|
||||
@@ -261,9 +277,14 @@ private:
|
||||
std::list<std::unique_ptr<NetworkConnection>> client_connection_list;
|
||||
std::multiset<GameCommand> game_command_queue;
|
||||
std::vector<uint8> chunk_buffer;
|
||||
char password[33];
|
||||
std::string password;
|
||||
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();
|
||||
@@ -284,6 +305,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
|
||||
@@ -294,6 +316,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);
|
||||
|
||||
@@ -312,8 +335,10 @@ 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);
|
||||
|
||||
void network_print_error();
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ typedef utf16* utf16string;
|
||||
#define OPENRCT2_BRANCH "develop"
|
||||
#define OPENRCT2_COMMIT_SHA1 ""
|
||||
#define OPENRCT2_COMMIT_SHA1_SHORT ""
|
||||
#define OPENRCT2_MASTER_SERVER_URL "https://servers.openrct2.website"
|
||||
|
||||
// Represent fixed point numbers. dp = decimal point
|
||||
typedef uint8 fixed8_1dp;
|
||||
|
||||
@@ -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,22 @@ 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;
|
||||
}
|
||||
if (text == NULL) {
|
||||
network_shutdown_client();
|
||||
} else {
|
||||
network_send_password(_password);
|
||||
}
|
||||
}
|
||||
|
||||
static void window_network_status_invalidate(rct_window *w)
|
||||
{
|
||||
window_network_status_widgets[WIDX_BACKGROUND].right = w->width - 1;
|
||||
|
||||
@@ -18,12 +18,15 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#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 requiresPassword;
|
||||
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,12 +211,14 @@ 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_loadsave_open(LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME | LOADSAVETYPE_NETWORK, NULL);
|
||||
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->requiresPassword = 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->requiresPassword = 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,96 @@ static void join_server(char *address, bool spectate)
|
||||
free(address);
|
||||
}
|
||||
}
|
||||
|
||||
static void fetch_servers()
|
||||
{
|
||||
#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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
static void fetch_servers_callback(http_json_response* 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(jsonServers);
|
||||
for (int i = 0; i < count; i++) {
|
||||
json_t *server = json_array_get(jsonServers, i);
|
||||
if (!json_is_object(server)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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 *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(address);
|
||||
SafeFree(newserver->name);
|
||||
SafeFree(newserver->description);
|
||||
SafeFree(newserver->version);
|
||||
newserver->name = _strdup(json_string_value(name));
|
||||
newserver->requiresPassword = json_boolean_value(requiresPassword);
|
||||
newserver->description = _strdup(json_string_value(description));
|
||||
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
|
||||
|
||||
266
src/windows/server_start.c
Normal file
266
src/windows/server_start.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/*****************************************************************************
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#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 _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 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, 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 },
|
||||
};
|
||||
|
||||
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_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)
|
||||
);
|
||||
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;
|
||||
|
||||
sprintf(_port, "%lu", gConfigNetwork.default_port);
|
||||
safe_strncpy(_name, gConfigNetwork.server_name, sizeof(_name));
|
||||
}
|
||||
|
||||
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_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();
|
||||
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_NAME_INPUT);
|
||||
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_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;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user