mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-04 13:42:55 +01:00
Merge branch 'refactor/network-advertise' into develop
This commit is contained in:
@@ -119,6 +119,7 @@
|
||||
<ClCompile Include="src\network\NetworkKey.cpp" />
|
||||
<ClCompile Include="src\network\NetworkPacket.cpp" />
|
||||
<ClCompile Include="src\network\NetworkPlayer.cpp" />
|
||||
<ClCompile Include="src\network\NetworkServerAdvertiser.cpp" />
|
||||
<ClCompile Include="src\network\NetworkUser.cpp" />
|
||||
<ClCompile Include="src\network\TcpSocket.cpp" />
|
||||
<ClCompile Include="src\network\twitch.cpp" />
|
||||
@@ -438,6 +439,7 @@
|
||||
<ClInclude Include="src\network\NetworkGroup.h" />
|
||||
<ClInclude Include="src\network\NetworkPacket.h" />
|
||||
<ClInclude Include="src\network\NetworkPlayer.h" />
|
||||
<ClInclude Include="src\network\NetworkServerAdvertiser.h" />
|
||||
<ClInclude Include="src\network\NetworkTypes.h" />
|
||||
<ClInclude Include="src\network\NetworkUser.h" />
|
||||
<ClInclude Include="src\network\TcpSocket.h" />
|
||||
|
||||
258
src/network/NetworkServerAdvertiser.cpp
Normal file
258
src/network/NetworkServerAdvertiser.cpp
Normal file
@@ -0,0 +1,258 @@
|
||||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/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.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
|
||||
#include <string>
|
||||
#include "../core/Console.hpp"
|
||||
#include "../core/String.hpp"
|
||||
#include "../core/Util.hpp"
|
||||
#include "network.h"
|
||||
#include "NetworkServerAdvertiser.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../addresses.h"
|
||||
#include "../config.h"
|
||||
#include "../localisation/date.h"
|
||||
#include "../management/finance.h"
|
||||
#include "../peep/peep.h"
|
||||
#include "../world/map.h"
|
||||
#include "../world/park.h"
|
||||
#include "http.h"
|
||||
}
|
||||
|
||||
enum MASTER_SERVER_STATUS
|
||||
{
|
||||
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
|
||||
|
||||
class NetworkServerAdvertiser : public INetworkServerAdvertiser
|
||||
{
|
||||
private:
|
||||
uint16 _port;
|
||||
|
||||
ADVERTISE_STATUS _status = ADVERTISE_STATUS_UNREGISTERED;
|
||||
uint32 _lastAdvertiseTime = 0;
|
||||
uint32 _lastHeartbeatTime = 0;
|
||||
|
||||
// Our unique token for this server
|
||||
std::string _token;
|
||||
|
||||
// Key received from the master server
|
||||
std::string _key;
|
||||
|
||||
public:
|
||||
NetworkServerAdvertiser(uint16 port)
|
||||
{
|
||||
_port = port;
|
||||
_key = GenerateAdvertiseKey();
|
||||
}
|
||||
|
||||
ADVERTISE_STATUS GetStatus() override
|
||||
{
|
||||
return _status;
|
||||
}
|
||||
|
||||
void Update() override
|
||||
{
|
||||
switch (_status) {
|
||||
case ADVERTISE_STATUS_UNREGISTERED:
|
||||
if (_lastAdvertiseTime == 0 || SDL_TICKS_PASSED(SDL_GetTicks(), _lastAdvertiseTime + MASTER_SERVER_REGISTER_TIME))
|
||||
{
|
||||
SendRegistration();
|
||||
}
|
||||
break;
|
||||
case ADVERTISE_STATUS_REGISTERED:
|
||||
if (SDL_TICKS_PASSED(SDL_GetTicks(), _lastHeartbeatTime + MASTER_SERVER_HEARTBEAT_TIME))
|
||||
{
|
||||
SendHeartbeat();
|
||||
}
|
||||
break;
|
||||
// exhaust enum values to satisfy clang
|
||||
case ADVERTISE_STATUS_DISABLED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void SendRegistration()
|
||||
{
|
||||
_lastAdvertiseTime = SDL_GetTicks();
|
||||
|
||||
// Send the registration request
|
||||
http_json_request request;
|
||||
request.tag = this;
|
||||
request.url = GetMasterServerUrl();
|
||||
request.method = HTTP_METHOD_POST;
|
||||
|
||||
json_t *body = json_object();
|
||||
json_object_set_new(body, "key", json_string(_key.c_str()));
|
||||
json_object_set_new(body, "port", json_integer(_port));
|
||||
request.body = body;
|
||||
|
||||
http_request_json_async(&request, [](http_json_response * response) -> void
|
||||
{
|
||||
if (response == nullptr)
|
||||
{
|
||||
Console::WriteLine("Unable to connect to master server");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto advertiser = (NetworkServerAdvertiser *)response->tag;
|
||||
advertiser->OnRegistrationResponse(response->root);
|
||||
http_request_json_dispose(response);
|
||||
}
|
||||
});
|
||||
|
||||
json_decref(body);
|
||||
}
|
||||
|
||||
void SendHeartbeat()
|
||||
{
|
||||
http_json_request request;
|
||||
request.tag = this;
|
||||
request.url = GetMasterServerUrl();
|
||||
request.method = HTTP_METHOD_PUT;
|
||||
|
||||
json_t * jsonBody = GetHeartbeatJson();
|
||||
request.body = jsonBody;
|
||||
|
||||
_lastHeartbeatTime = SDL_GetTicks();
|
||||
http_request_json_async(&request, [](http_json_response *response) -> void
|
||||
{
|
||||
if (response == nullptr)
|
||||
{
|
||||
log_warning("Unable to connect to master server");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto advertiser = (NetworkServerAdvertiser *)response->tag;
|
||||
advertiser->OnHeartbeatResponse(response->root);
|
||||
http_request_json_dispose(response);
|
||||
}
|
||||
});
|
||||
|
||||
json_decref(jsonBody);
|
||||
}
|
||||
|
||||
void OnRegistrationResponse(json_t * jsonRoot)
|
||||
{
|
||||
json_t *jsonStatus = json_object_get(jsonRoot, "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(jsonRoot, "token");
|
||||
if (json_is_string(jsonToken))
|
||||
{
|
||||
_token = std::string(json_string_value(jsonToken));
|
||||
_status = ADVERTISE_STATUS_REGISTERED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const char * message = "Invalid response from server";
|
||||
json_t * jsonMessage = json_object_get(jsonRoot, "message");
|
||||
if (json_is_string(jsonMessage))
|
||||
{
|
||||
message = json_string_value(jsonMessage);
|
||||
}
|
||||
Console::Error::WriteLine("Unable to advertise: %s", message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnHeartbeatResponse(json_t * jsonRoot)
|
||||
{
|
||||
json_t *jsonStatus = json_object_get(jsonRoot, "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)
|
||||
{
|
||||
_status = ADVERTISE_STATUS_UNREGISTERED;
|
||||
Console::WriteLine("Master server heartbeat failed: Invalid Token");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_t * GetHeartbeatJson()
|
||||
{
|
||||
uint32 numPlayers = network_get_num_players();
|
||||
|
||||
json_t * root = json_object();
|
||||
json_object_set_new(root, "token", json_string(_token.c_str()));
|
||||
json_object_set_new(root, "players", json_integer(numPlayers));
|
||||
|
||||
json_t * gameInfo = json_object();
|
||||
json_object_set_new(gameInfo, "mapSize", json_integer(gMapSize - 2));
|
||||
json_object_set_new(gameInfo, "day", json_integer(gDateMonthTicks));
|
||||
json_object_set_new(gameInfo, "month", json_integer(gDateMonthsElapsed));
|
||||
json_object_set_new(gameInfo, "guests", json_integer(gNumGuestsInPark));
|
||||
json_object_set_new(gameInfo, "parkValue", json_integer(gParkValue));
|
||||
if (!(gParkFlags & PARK_FLAGS_NO_MONEY))
|
||||
{
|
||||
money32 cash = DECRYPT_MONEY(gCashEncrypted);
|
||||
json_object_set_new(gameInfo, "cash", json_integer(cash));
|
||||
}
|
||||
json_object_set_new(root, "gameInfo", gameInfo);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static std::string GenerateAdvertiseKey()
|
||||
{
|
||||
// Generate a string of 16 random hex characters (64-integer key as a hex formatted string)
|
||||
static const 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() % Util::CountOf(hexChars);
|
||||
key[i] = hexChars[hexCharIndex];
|
||||
}
|
||||
key[Util::CountOf(key) - 1] = 0;
|
||||
return key;
|
||||
}
|
||||
|
||||
static const char * GetMasterServerUrl()
|
||||
{
|
||||
const char * result = OPENRCT2_MASTER_SERVER_URL;
|
||||
if (!String::IsNullOrEmpty(gConfigNetwork.master_server_url))
|
||||
{
|
||||
result = gConfigNetwork.master_server_url;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
INetworkServerAdvertiser * CreateServerAdvertiser(uint16 port)
|
||||
{
|
||||
return new NetworkServerAdvertiser(port);
|
||||
}
|
||||
|
||||
#endif // DISABLE_NETWORK
|
||||
36
src/network/NetworkServerAdvertiser.h
Normal file
36
src/network/NetworkServerAdvertiser.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/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.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
enum ADVERTISE_STATUS
|
||||
{
|
||||
ADVERTISE_STATUS_DISABLED,
|
||||
ADVERTISE_STATUS_UNREGISTERED,
|
||||
ADVERTISE_STATUS_REGISTERED,
|
||||
};
|
||||
|
||||
interface INetworkServerAdvertiser
|
||||
{
|
||||
virtual ~INetworkServerAdvertiser() { }
|
||||
|
||||
virtual ADVERTISE_STATUS GetStatus() abstract;
|
||||
virtual void Update() abstract;
|
||||
};
|
||||
|
||||
INetworkServerAdvertiser * CreateServerAdvertiser(uint16 port);
|
||||
@@ -191,6 +191,7 @@ http_json_response *http_request_json(const http_json_request *request)
|
||||
root = json_loads(writeBuffer.ptr, 0, &error);
|
||||
if (root != NULL) {
|
||||
response = (http_json_response*)malloc(sizeof(http_json_response));
|
||||
response->tag = request->tag;
|
||||
response->status_code = (int)httpStatusCode;
|
||||
response->root = root;
|
||||
}
|
||||
@@ -209,6 +210,7 @@ void http_request_json_async(const http_json_request *request, void (*callback)(
|
||||
args->request.url = _strdup(request->url);
|
||||
args->request.method = request->method;
|
||||
args->request.body = json_deep_copy(request->body);
|
||||
args->request.tag = request->tag;
|
||||
args->callback = callback;
|
||||
|
||||
SDL_Thread *thread = SDL_CreateThread([](void *ptr) -> int {
|
||||
|
||||
@@ -22,12 +22,14 @@
|
||||
#include "../common.h"
|
||||
|
||||
typedef struct http_json_request {
|
||||
void *tag;
|
||||
const char *method;
|
||||
const char *url;
|
||||
const json_t *body;
|
||||
} http_json_request;
|
||||
|
||||
typedef struct http_json_response {
|
||||
void *tag;
|
||||
int status_code;
|
||||
json_t *root;
|
||||
} http_json_response;
|
||||
|
||||
@@ -73,12 +73,6 @@ extern "C" {
|
||||
|
||||
Network gNetwork;
|
||||
|
||||
enum {
|
||||
ADVERTISE_STATUS_DISABLED,
|
||||
ADVERTISE_STATUS_UNREGISTERED,
|
||||
ADVERTISE_STATUS_REGISTERED
|
||||
};
|
||||
|
||||
enum {
|
||||
MASTER_SERVER_STATUS_OK = 200,
|
||||
MASTER_SERVER_STATUS_INVALID_TOKEN = 401,
|
||||
@@ -108,7 +102,6 @@ Network::Network()
|
||||
status = NETWORK_STATUS_NONE;
|
||||
last_tick_sent_time = 0;
|
||||
last_ping_sent_time = 0;
|
||||
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;
|
||||
@@ -178,6 +171,7 @@ void Network::Close()
|
||||
} else if (mode == NETWORK_MODE_SERVER) {
|
||||
delete listening_socket;
|
||||
listening_socket = nullptr;
|
||||
delete _advertiser;
|
||||
}
|
||||
|
||||
mode = NETWORK_MODE_NONE;
|
||||
@@ -323,17 +317,9 @@ bool Network::BeginServer(unsigned short port, const char* address)
|
||||
|
||||
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;
|
||||
_advertiser = CreateServerAdvertiser(listening_port);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -400,17 +386,8 @@ void Network::UpdateServer()
|
||||
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;
|
||||
if (_advertiser != nullptr) {
|
||||
_advertiser->Update();
|
||||
}
|
||||
|
||||
ITcpSocket * tcpSocket = listening_socket->Accept();
|
||||
@@ -652,102 +629,6 @@ const char *Network::GetMasterServerUrl()
|
||||
}
|
||||
}
|
||||
|
||||
void Network::AdvertiseRegister()
|
||||
{
|
||||
#ifndef DISABLE_HTTP
|
||||
last_advertise_time = SDL_GetTicks();
|
||||
|
||||
// Send the registration request
|
||||
http_json_request request;
|
||||
request.url = GetMasterServerUrl();
|
||||
request.method = HTTP_METHOD_POST;
|
||||
|
||||
json_t *body = json_object();
|
||||
json_object_set_new(body, "key", json_string(advertise_key.c_str()));
|
||||
json_object_set_new(body, "port", json_integer(listening_port));
|
||||
request.body = body;
|
||||
|
||||
http_request_json_async(&request, [](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);
|
||||
});
|
||||
|
||||
json_decref(body);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Network::AdvertiseHeartbeat()
|
||||
{
|
||||
#ifndef DISABLE_HTTP
|
||||
// Send the heartbeat request
|
||||
http_json_request request;
|
||||
request.url = GetMasterServerUrl();
|
||||
request.method = HTTP_METHOD_PUT;
|
||||
|
||||
json_t *body = json_object();
|
||||
json_object_set_new(body, "token", json_string(advertise_token.c_str()));
|
||||
json_object_set_new(body, "players", json_integer(network_get_num_players()));
|
||||
|
||||
json_t *gameInfo = json_object();
|
||||
json_object_set_new(gameInfo, "mapSize", json_integer(gMapSize - 2));
|
||||
json_object_set_new(gameInfo, "day", json_integer(gDateMonthTicks));
|
||||
json_object_set_new(gameInfo, "month", json_integer(gDateMonthsElapsed));
|
||||
json_object_set_new(gameInfo, "guests", json_integer(gNumGuestsInPark));
|
||||
json_object_set_new(gameInfo, "parkValue", json_integer(gParkValue));
|
||||
if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) {
|
||||
money32 cash = DECRYPT_MONEY(gCashEncrypted);
|
||||
json_object_set_new(gameInfo, "cash", json_integer(cash));
|
||||
}
|
||||
|
||||
json_object_set_new(body, "gameInfo", gameInfo);
|
||||
request.body = body;
|
||||
|
||||
gNetwork.last_heartbeat_time = SDL_GetTicks();
|
||||
http_request_json_async(&request, [](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);
|
||||
});
|
||||
|
||||
json_decref(body);
|
||||
#endif
|
||||
}
|
||||
|
||||
NetworkGroup* Network::AddGroup()
|
||||
{
|
||||
NetworkGroup* addedgroup = nullptr;
|
||||
|
||||
@@ -75,6 +75,7 @@ extern "C" {
|
||||
#include "NetworkKey.h"
|
||||
#include "NetworkPacket.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "NetworkServerAdvertiser.h"
|
||||
#include "NetworkUser.h"
|
||||
#include "TcpSocket.h"
|
||||
|
||||
@@ -107,8 +108,6 @@ public:
|
||||
void KickPlayer(int playerId);
|
||||
void SetPassword(const char* password);
|
||||
void ShutdownClient();
|
||||
void AdvertiseRegister();
|
||||
void AdvertiseHeartbeat();
|
||||
NetworkGroup* AddGroup();
|
||||
void RemoveGroup(uint8 id);
|
||||
uint8 GetDefaultGroup();
|
||||
@@ -204,12 +203,8 @@ private:
|
||||
std::vector<uint8> chunk_buffer;
|
||||
std::string password;
|
||||
bool _desynchronised = false;
|
||||
INetworkServerAdvertiser * _advertiser = nullptr;
|
||||
uint32 server_connect_time = 0;
|
||||
uint32 last_advertise_time = 0;
|
||||
std::string advertise_token;
|
||||
std::string advertise_key;
|
||||
int advertise_status = 0;
|
||||
uint32 last_heartbeat_time = 0;
|
||||
uint8 default_group = 0;
|
||||
SDL_RWops *_chatLogStream;
|
||||
std::string _chatLogPath;
|
||||
|
||||
Reference in New Issue
Block a user