mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-23 15:52:55 +01:00
Merge branch 'master-server-improvements' into develop
This commit is contained in:
@@ -16,6 +16,14 @@ void http_dispose() { }
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <curl/curl.h>
|
||||
|
||||
#define MIME_TYPE_APPLICATION_JSON "application/json"
|
||||
|
||||
typedef struct {
|
||||
char *ptr;
|
||||
int length;
|
||||
int position;
|
||||
} read_buffer;
|
||||
|
||||
typedef struct {
|
||||
char *ptr;
|
||||
int length;
|
||||
@@ -32,6 +40,22 @@ void http_dispose()
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
static size_t http_request_read_func(void *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
read_buffer *readBuffer = (read_buffer*)userdata;
|
||||
|
||||
size_t remainingBytes = readBuffer->length - readBuffer->position;
|
||||
size_t readBytes = size * nmemb;
|
||||
if (readBytes > remainingBytes) {
|
||||
readBytes = remainingBytes;
|
||||
}
|
||||
|
||||
memcpy(ptr, readBuffer->ptr + readBuffer->position, readBytes);
|
||||
|
||||
readBuffer->position += readBytes;
|
||||
return readBytes;
|
||||
}
|
||||
|
||||
static size_t http_request_write_func(void *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
write_buffer *writeBuffer = (write_buffer*)userdata;
|
||||
@@ -54,33 +78,55 @@ static size_t http_request_write_func(void *ptr, size_t size, size_t nmemb, void
|
||||
return newBytesLength;
|
||||
}
|
||||
|
||||
http_json_response *http_request_json(const char *url)
|
||||
http_json_response *http_request_json(const http_json_request *request)
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode curlResult;
|
||||
http_json_response *response;
|
||||
read_buffer readBuffer;
|
||||
write_buffer writeBuffer;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl == NULL)
|
||||
return NULL;
|
||||
|
||||
if (request->body != NULL) {
|
||||
readBuffer.ptr = json_dumps(request->body, JSON_COMPACT);
|
||||
readBuffer.length = strlen(readBuffer.ptr);
|
||||
readBuffer.position = 0;
|
||||
}
|
||||
|
||||
writeBuffer.ptr = NULL;
|
||||
writeBuffer.length = 0;
|
||||
writeBuffer.capacity = 0;
|
||||
|
||||
curl_slist *headers = NULL;
|
||||
headers = curl_slist_append(headers, "Accept: application/json");
|
||||
headers = curl_slist_append(headers, "Accept: " MIME_TYPE_APPLICATION_JSON);
|
||||
if (request->body != NULL) {
|
||||
headers = curl_slist_append(headers, "Content-Type: " MIME_TYPE_APPLICATION_JSON);
|
||||
|
||||
char contentLengthHeaderValue[64];
|
||||
snprintf(contentLengthHeaderValue, sizeof(contentLengthHeaderValue), "Content-Length: %d", readBuffer.length);
|
||||
headers = curl_slist_append(headers, contentLengthHeaderValue);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, readBuffer.ptr);
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, request->method);
|
||||
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");
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, request->url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &writeBuffer);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_request_write_func);
|
||||
|
||||
curlResult = curl_easy_perform(curl);
|
||||
|
||||
if (request->body != NULL) {
|
||||
free(readBuffer.ptr);
|
||||
}
|
||||
|
||||
if (curlResult != CURLE_OK) {
|
||||
log_error("HTTP request failed: %s.", curl_easy_strerror(curlResult));
|
||||
if (writeBuffer.ptr != NULL)
|
||||
@@ -115,24 +161,27 @@ http_json_response *http_request_json(const char *url)
|
||||
return response;
|
||||
}
|
||||
|
||||
void http_request_json_async(const char *url, void (*callback)(http_json_response*))
|
||||
void http_request_json_async(const http_json_request *request, void (*callback)(http_json_response*))
|
||||
{
|
||||
struct TempThreadArgs {
|
||||
char *url;
|
||||
http_json_request request;
|
||||
void (*callback)(http_json_response*);
|
||||
};
|
||||
|
||||
TempThreadArgs *args = (TempThreadArgs*)malloc(sizeof(TempThreadArgs));
|
||||
args->url = _strdup(url);
|
||||
args->request.url = _strdup(request->url);
|
||||
args->request.method = request->method;
|
||||
args->request.body = json_deep_copy(request->body);
|
||||
args->callback = callback;
|
||||
|
||||
SDL_Thread *thread = SDL_CreateThread([](void *ptr) -> int {
|
||||
TempThreadArgs *args = (TempThreadArgs*)ptr;
|
||||
|
||||
http_json_response *response = http_request_json(args->url);
|
||||
http_json_response *response = http_request_json(&args->request);
|
||||
args->callback(response);
|
||||
|
||||
free(args->url);
|
||||
free((char*)args->request.url);
|
||||
json_decref((json_t*)args->request.body);
|
||||
free(args);
|
||||
return 0;
|
||||
}, NULL, args);
|
||||
|
||||
@@ -5,13 +5,24 @@
|
||||
#include <jansson.h>
|
||||
#include "../common.h"
|
||||
|
||||
typedef struct {
|
||||
const char *method;
|
||||
const char *url;
|
||||
const json_t *body;
|
||||
} http_json_request;
|
||||
|
||||
typedef struct {
|
||||
int status_code;
|
||||
json_t *root;
|
||||
} http_json_response;
|
||||
|
||||
http_json_response *http_request_json(const char *url);
|
||||
void http_request_json_async(const char *url, void (*callback)(http_json_response*));
|
||||
#define HTTP_METHOD_GET "GET"
|
||||
#define HTTP_METHOD_POST "POST"
|
||||
#define HTTP_METHOD_PUT "PUT"
|
||||
#define HTTP_METHOD_DELETE "DELETE"
|
||||
|
||||
http_json_response *http_request_json(const http_json_request *request);
|
||||
void http_request_json_async(const http_json_request *request, void (*callback)(http_json_response*));
|
||||
void http_request_json_dispose(http_json_response *response);
|
||||
#endif // DISABLE_HTTP
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
extern "C" {
|
||||
#include "../openrct2.h"
|
||||
#include "../platform/platform.h"
|
||||
}
|
||||
|
||||
@@ -854,10 +855,16 @@ void Network::AdvertiseRegister()
|
||||
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 {
|
||||
http_json_request request;
|
||||
request.url = GetMasterServerUrl();
|
||||
request.method = HTTP_METHOD_POST;
|
||||
|
||||
json_t *body = json_object();
|
||||
json_object_set(body, "key", json_string(advertise_key.c_str()));
|
||||
json_object_set(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;
|
||||
@@ -883,6 +890,8 @@ void Network::AdvertiseRegister()
|
||||
}
|
||||
http_request_json_dispose(response);
|
||||
});
|
||||
|
||||
json_decref(body);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -890,14 +899,31 @@ 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());
|
||||
http_json_request request;
|
||||
request.url = GetMasterServerUrl();
|
||||
request.method = HTTP_METHOD_PUT;
|
||||
|
||||
// TODO send status data (e.g. players) via JSON body
|
||||
json_t *body = json_object();
|
||||
json_object_set(body, "token", json_string(advertise_token.c_str()));
|
||||
json_object_set(body, "dedicated", json_boolean(gOpenRCT2Headless));
|
||||
json_object_set(body, "players", json_integer(network_get_num_players()));
|
||||
|
||||
json_t *gameInfo = json_object();
|
||||
json_object_set(gameInfo, "mapSize", json_integer(RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE, uint8) - 2));
|
||||
json_object_set(gameInfo, "day", json_integer(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_TICKS, uint16)));
|
||||
json_object_set(gameInfo, "month", json_integer(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16)));
|
||||
json_object_set(gameInfo, "guests", json_integer(RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16)));
|
||||
json_object_set(gameInfo, "parkValue", json_integer(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PARK_VALUE, money32)));
|
||||
if (!(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY)) {
|
||||
money32 cash = DECRYPT_MONEY(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONEY_ENCRYPTED, money32));
|
||||
json_object_set(gameInfo, "cash", json_integer(cash));
|
||||
}
|
||||
|
||||
json_object_set(body, "gameInfo", gameInfo);
|
||||
request.body = body;
|
||||
|
||||
gNetwork.last_heartbeat_time = SDL_GetTicks();
|
||||
http_request_json_async(url.c_str(), [](http_json_response *response) -> void {
|
||||
http_request_json_async(&request, [](http_json_response *response) -> void {
|
||||
if (response == NULL) {
|
||||
log_warning("Unable to connect to master server");
|
||||
return;
|
||||
@@ -915,6 +941,8 @@ void Network::AdvertiseHeartbeat()
|
||||
}
|
||||
http_request_json_dispose(response);
|
||||
});
|
||||
|
||||
json_decref(body);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1064,7 +1092,7 @@ void Network::Server_Send_GAMEINFO(NetworkConnection& connection)
|
||||
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);
|
||||
json_decref(obj);
|
||||
#endif
|
||||
connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
@@ -130,7 +130,12 @@ static void twitch_join()
|
||||
|
||||
_twitchState = TWITCH_STATE_JOINING;
|
||||
_twitchIdle = false;
|
||||
http_request_json_async(url, [](http_json_response *jsonResponse) -> void {
|
||||
|
||||
http_json_request request;
|
||||
request.url = url;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = NULL;
|
||||
http_request_json_async(&request, [](http_json_response *jsonResponse) -> void {
|
||||
if (jsonResponse == NULL) {
|
||||
_twitchState = TWITCH_STATE_LEFT;
|
||||
console_writeline("Unable to connect to twitch channel.");
|
||||
@@ -191,7 +196,12 @@ static void twitch_get_followers()
|
||||
|
||||
_twitchState = TWITCH_STATE_WAITING;
|
||||
_twitchIdle = false;
|
||||
http_request_json_async(url, [](http_json_response *jsonResponse) -> void {
|
||||
|
||||
http_json_request request;
|
||||
request.url = url;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = NULL;
|
||||
http_request_json_async(&request, [](http_json_response *jsonResponse) -> void {
|
||||
if (jsonResponse == NULL) {
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
} else {
|
||||
@@ -212,7 +222,12 @@ static void twitch_get_messages()
|
||||
|
||||
_twitchState = TWITCH_STATE_WAITING;
|
||||
_twitchIdle = false;
|
||||
http_request_json_async(url, [](http_json_response *jsonResponse) -> void {
|
||||
|
||||
http_json_request request;
|
||||
request.url = url;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = NULL;
|
||||
http_request_json_async(&request, [](http_json_response *jsonResponse) -> void {
|
||||
if (jsonResponse == NULL) {
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
} else {
|
||||
|
||||
@@ -651,7 +651,12 @@ static void fetch_servers()
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(_mutex);
|
||||
http_request_json_async(masterServerUrl, fetch_servers_callback);
|
||||
|
||||
http_json_request request;
|
||||
request.url = masterServerUrl;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = NULL;
|
||||
http_request_json_async(&request, fetch_servers_callback);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -721,7 +726,7 @@ static void fetch_servers_callback(http_json_response* response)
|
||||
}
|
||||
http_request_json_dispose(response);
|
||||
|
||||
rct_window* window = window_bring_to_front_by_class(WC_SERVER_LIST);
|
||||
rct_window *window = window_find_by_class(WC_SERVER_LIST);
|
||||
if (window != NULL) {
|
||||
window_invalidate(window);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user