From 2e5549547016f4e290c10274f375dd86c23c0de0 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 8 Nov 2015 22:03:35 +0000 Subject: [PATCH 1/3] improve master server to POST json --- src/network/http.cpp | 65 ++++++++++++++++++++++++++++++++++----- src/network/http.h | 15 +++++++-- src/network/network.cpp | 27 +++++++++++----- src/network/twitch.cpp | 21 +++++++++++-- src/windows/server_list.c | 9 ++++-- 5 files changed, 115 insertions(+), 22 deletions(-) diff --git a/src/network/http.cpp b/src/network/http.cpp index ba6557578c..411d0f0c33 100644 --- a/src/network/http.cpp +++ b/src/network/http.cpp @@ -16,6 +16,14 @@ void http_dispose() { } #define WIN32_LEAN_AND_MEAN #include +#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); diff --git a/src/network/http.h b/src/network/http.h index 1cd567f91d..3df1903fc6 100644 --- a/src/network/http.h +++ b/src/network/http.h @@ -5,13 +5,24 @@ #include #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 diff --git a/src/network/network.cpp b/src/network/network.cpp index 458cb0cfe4..3b916dda30 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -854,10 +854,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 +889,8 @@ void Network::AdvertiseRegister() } http_request_json_dispose(response); }); + + json_decref(body); #endif } @@ -891,13 +899,18 @@ void Network::AdvertiseHeartbeat() #ifndef DISABLE_HTTP // Send the heartbeat request std::string url = GetMasterServerUrl() - + std::string("?command=heartbeat&token=") + advertise_token + + std::string("?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 { + + http_json_request request; + request.url = url.c_str(); + request.method = HTTP_METHOD_PUT; + request.body = NULL; + http_request_json_async(&request, [](http_json_response *response) -> void { if (response == NULL) { log_warning("Unable to connect to master server"); return; @@ -1064,7 +1077,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)); } diff --git a/src/network/twitch.cpp b/src/network/twitch.cpp index 06fb34b629..fbc47434e9 100644 --- a/src/network/twitch.cpp +++ b/src/network/twitch.cpp @@ -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 { diff --git a/src/windows/server_list.c b/src/windows/server_list.c index e8935c65dc..1aef7d1af6 100644 --- a/src/windows/server_list.c +++ b/src/windows/server_list.c @@ -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); } From da6d382c471ccaa2273770c712d70a5d85f98cc4 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 8 Nov 2015 23:29:52 +0000 Subject: [PATCH 2/3] make server heartbeat PUT --- src/network/network.cpp | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 3b916dda30..7aa795a91a 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -898,18 +898,29 @@ void Network::AdvertiseHeartbeat() { #ifndef DISABLE_HTTP // Send the heartbeat request - std::string url = GetMasterServerUrl() - + std::string("?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, "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_json_request request; - request.url = url.c_str(); - request.method = HTTP_METHOD_PUT; - request.body = NULL; http_request_json_async(&request, [](http_json_response *response) -> void { if (response == NULL) { log_warning("Unable to connect to master server"); @@ -928,6 +939,8 @@ void Network::AdvertiseHeartbeat() } http_request_json_dispose(response); }); + + json_decref(body); #endif } From 09cd007f3f4b9acee9cdacb46afcbf9b8ab6122a Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 8 Nov 2015 23:50:38 +0000 Subject: [PATCH 3/3] add dedicated flag to gameInfo --- src/network/network.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/network.cpp b/src/network/network.cpp index 7aa795a91a..eabcfdc06e 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -19,6 +19,7 @@ *****************************************************************************/ extern "C" { +#include "../openrct2.h" #include "../platform/platform.h" } @@ -904,6 +905,7 @@ void Network::AdvertiseHeartbeat() 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();