mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-15 19:13:07 +01:00
Merge pull request #6978 from tobiaskohlbau/refactor/network
HTTP: Refactor into modern C++
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/localisation/Localisation.h>
|
||||
#include <openrct2/network/http.h>
|
||||
#include <openrct2/network/Http.h>
|
||||
#include <openrct2/network/network.h>
|
||||
#include <openrct2/network/ServerList.h>
|
||||
#include <openrct2/sprites.h>
|
||||
@@ -33,6 +33,10 @@
|
||||
#include <openrct2/interface/Colour.h>
|
||||
#include <openrct2/drawing/Drawing.h>
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
using namespace OpenRCT2::Network;
|
||||
#endif
|
||||
|
||||
#define WWIDTH_MIN 500
|
||||
#define WHEIGHT_MIN 300
|
||||
#define WWIDTH_MAX 1200
|
||||
@@ -134,9 +138,9 @@ static void dispose_server_entry_list();
|
||||
static server_entry & add_server_entry(const std::string &address);
|
||||
static void sort_servers();
|
||||
static void join_server(std::string address);
|
||||
static void fetch_servers();
|
||||
#ifndef DISABLE_HTTP
|
||||
static void fetch_servers_callback(http_response_t* response);
|
||||
static void fetch_servers();
|
||||
static void fetch_servers_callback(Http::Response & response);
|
||||
#endif
|
||||
static bool is_version_valid(const std::string &version);
|
||||
|
||||
@@ -179,7 +183,9 @@ rct_window * window_server_list_open()
|
||||
server_list_load_server_entries();
|
||||
window->no_list_items = (uint16)_serverEntries.size();
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
fetch_servers();
|
||||
#endif
|
||||
|
||||
return window;
|
||||
}
|
||||
@@ -219,7 +225,9 @@ static void window_server_list_mouseup(rct_window *w, rct_widgetindex widgetInde
|
||||
break;
|
||||
}
|
||||
case WIDX_FETCH_SERVERS:
|
||||
#ifndef DISABLE_HTTP
|
||||
fetch_servers();
|
||||
#endif
|
||||
break;
|
||||
case WIDX_ADD_SERVER:
|
||||
window_text_input_open(w, widgetIndex, STR_ADD_SERVER, STR_ENTER_HOSTNAME_OR_IP_ADDRESS, STR_NONE, 0, 128);
|
||||
@@ -611,9 +619,9 @@ static void join_server(std::string address)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
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;
|
||||
@@ -633,17 +641,14 @@ static void fetch_servers()
|
||||
sort_servers();
|
||||
}
|
||||
|
||||
http_request_t request = {};
|
||||
Http::Request request;
|
||||
request.url = masterServerUrl;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = nullptr;
|
||||
request.type = HTTP_DATA_JSON;
|
||||
request.method = Http::Method::GET;
|
||||
request.header["Accept"] = "application/json";
|
||||
status_text = STR_SERVER_LIST_CONNECTING;
|
||||
http_request_async(&request, fetch_servers_callback);
|
||||
#endif
|
||||
Http::DoAsync(request, fetch_servers_callback);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
static uint32 get_total_player_count()
|
||||
{
|
||||
return std::accumulate(
|
||||
@@ -656,18 +661,19 @@ static uint32 get_total_player_count()
|
||||
});
|
||||
}
|
||||
|
||||
static void fetch_servers_callback(http_response_t* response)
|
||||
static void fetch_servers_callback(Http::Response & response)
|
||||
{
|
||||
if (response == nullptr) {
|
||||
if (response.status != Http::Status::OK)
|
||||
{
|
||||
status_text = STR_SERVER_LIST_NO_CONNECTION;
|
||||
window_invalidate_by_class(WC_SERVER_LIST);
|
||||
log_warning("Unable to connect to master server");
|
||||
return;
|
||||
}
|
||||
|
||||
json_t *jsonStatus = json_object_get(response->root, "status");
|
||||
json_t * root = Json::FromString(response.body);
|
||||
json_t * jsonStatus = json_object_get(root, "status");
|
||||
if (!json_is_number(jsonStatus)) {
|
||||
http_request_dispose(response);
|
||||
status_text = STR_SERVER_LIST_INVALID_RESPONSE_JSON_NUMBER;
|
||||
window_invalidate_by_class(WC_SERVER_LIST);
|
||||
log_warning("Invalid response from master server");
|
||||
@@ -676,16 +682,14 @@ static void fetch_servers_callback(http_response_t* response)
|
||||
|
||||
sint32 status = (sint32)json_integer_value(jsonStatus);
|
||||
if (status != 200) {
|
||||
http_request_dispose(response);
|
||||
status_text = STR_SERVER_LIST_MASTER_SERVER_FAILED;
|
||||
window_invalidate_by_class(WC_SERVER_LIST);
|
||||
log_warning("Master server failed to return servers");
|
||||
return;
|
||||
}
|
||||
|
||||
json_t *jsonServers = json_object_get(response->root, "servers");
|
||||
json_t * jsonServers = json_object_get(root, "servers");
|
||||
if (!json_is_array(jsonServers)) {
|
||||
http_request_dispose(response);
|
||||
status_text = STR_SERVER_LIST_INVALID_RESPONSE_JSON_ARRAY;
|
||||
window_invalidate_by_class(WC_SERVER_LIST);
|
||||
log_warning("Invalid response from master server");
|
||||
@@ -728,7 +732,6 @@ static void fetch_servers_callback(http_response_t* response)
|
||||
newserver.maxplayers = (uint8)json_integer_value(maxPlayers);
|
||||
}
|
||||
}
|
||||
http_request_dispose(response);
|
||||
|
||||
sort_servers();
|
||||
_numPlayersOnline = get_total_player_count();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
@@ -56,12 +56,13 @@
|
||||
#include "world/Park.h"
|
||||
#include "Version.h"
|
||||
|
||||
#include "audio/audio.h"
|
||||
#include "config/Config.h"
|
||||
#include "drawing/LightFX.h"
|
||||
#include "Editor.h"
|
||||
#include "Game.h"
|
||||
#include "Input.h"
|
||||
#include "Intro.h"
|
||||
#include "audio/audio.h"
|
||||
#include "config/Config.h"
|
||||
#include "drawing/LightFX.h"
|
||||
#include "interface/Chat.h"
|
||||
#include "interface/InteractiveConsole.h"
|
||||
#include "interface/Viewport.h"
|
||||
@@ -69,7 +70,7 @@
|
||||
#include "localisation/Date.h"
|
||||
#include "localisation/LocalisationService.h"
|
||||
#include "network/DiscordService.h"
|
||||
#include "network/http.h"
|
||||
#include "network/Http.h"
|
||||
#include "network/network.h"
|
||||
#include "network/twitch.h"
|
||||
#include "platform/platform.h"
|
||||
@@ -102,6 +103,9 @@ namespace OpenRCT2
|
||||
std::unique_ptr<DiscordService> _discordService;
|
||||
#endif
|
||||
StdInOutConsole _stdInOutConsole;
|
||||
#ifndef DISABLE_HTTP
|
||||
Network::Http::Http _http;
|
||||
#endif
|
||||
|
||||
// Game states
|
||||
std::unique_ptr<TitleScreen> _titleScreen;
|
||||
@@ -143,7 +147,6 @@ namespace OpenRCT2
|
||||
~Context() override
|
||||
{
|
||||
window_close_all();
|
||||
http_dispose();
|
||||
object_manager_unload_all_objects();
|
||||
gfx_object_check_all_images_freed();
|
||||
gfx_unload_g2();
|
||||
@@ -420,7 +423,6 @@ namespace OpenRCT2
|
||||
audio_init_ride_sounds_and_info();
|
||||
}
|
||||
|
||||
http_init();
|
||||
network_set_env(_env);
|
||||
chat_init();
|
||||
CopyOriginalUserFilesOver();
|
||||
@@ -718,7 +720,7 @@ namespace OpenRCT2
|
||||
#ifndef DISABLE_HTTP
|
||||
// Download park and open it using its temporary filename
|
||||
void * data;
|
||||
size_t dataSize = http_download_park(gOpenRCT2StartupActionPath, &data);
|
||||
size_t dataSize = Network::Http::DownloadPark(gOpenRCT2StartupActionPath, &data);
|
||||
if (dataSize == 0)
|
||||
{
|
||||
title_load();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
@@ -58,4 +58,16 @@ namespace Json
|
||||
size_t jsonOutputSize = String::SizeOf(jsonOutput);
|
||||
fs.Write(jsonOutput, jsonOutputSize);
|
||||
}
|
||||
|
||||
json_t * FromString(const std::string & raw)
|
||||
{
|
||||
json_t * root;
|
||||
json_error_t error;
|
||||
root = json_loads(raw.c_str(), 0, &error);
|
||||
if (root == nullptr)
|
||||
{
|
||||
throw JsonException(&error);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
} // namespace Json
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
@@ -29,6 +29,8 @@ namespace Json
|
||||
|
||||
json_t * ReadFromFile(const utf8 * path, size_t maxSize = MAX_JSON_SIZE);
|
||||
void WriteToFile(const utf8 * path, const json_t * json, size_t flags = 0);
|
||||
|
||||
json_t * FromString(const std::string & raw);
|
||||
}
|
||||
|
||||
class JsonException final : public std::runtime_error
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
@@ -14,322 +14,217 @@
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include "http.h"
|
||||
|
||||
#ifdef DISABLE_HTTP
|
||||
|
||||
void http_init() { }
|
||||
void http_dispose() { }
|
||||
|
||||
#else
|
||||
|
||||
#include "Http.h"
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "../core/Console.hpp"
|
||||
#include "../core/Math.hpp"
|
||||
#ifndef DISABLE_HTTP
|
||||
|
||||
#include "../Version.h"
|
||||
#include "../core/Console.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
// cURL includes windows.h, but we don't need all of it.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
// cURL includes windows.h, but we don't need all of it.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <curl/curl.h>
|
||||
|
||||
#define MIME_TYPE_APPLICATION_JSON "application/json"
|
||||
#define OPENRCT2_USER_AGENT "OpenRCT2/" OPENRCT2_VERSION
|
||||
|
||||
struct HttpRequest2
|
||||
namespace OpenRCT2::Network::Http
|
||||
{
|
||||
void * Tag = nullptr;
|
||||
std::string Method;
|
||||
std::string Url;
|
||||
HTTP_DATA_TYPE Type;
|
||||
bool ForceIPv4 = false;
|
||||
size_t Size = 0;
|
||||
union
|
||||
{
|
||||
char * Buffer = nullptr;
|
||||
json_t * Json;
|
||||
} Body;
|
||||
|
||||
HttpRequest2() { }
|
||||
|
||||
HttpRequest2(const HttpRequest2 &request)
|
||||
{
|
||||
Tag = request.Tag;
|
||||
Method = request.Method;
|
||||
Url = request.Url;
|
||||
Type = request.Type;
|
||||
ForceIPv4 = request.ForceIPv4;
|
||||
Size = request.Size;
|
||||
if (request.Type == HTTP_DATA_JSON)
|
||||
{
|
||||
Body.Json = json_deep_copy(request.Body.Json);
|
||||
}
|
||||
else
|
||||
{
|
||||
Body.Buffer = new char[request.Size];
|
||||
memcpy(Body.Buffer, request.Body.Buffer, request.Size);
|
||||
}
|
||||
}
|
||||
|
||||
explicit HttpRequest2(const http_request_t * request)
|
||||
{
|
||||
Tag = request->tag;
|
||||
Method = std::string(request->method);
|
||||
Url = std::string(request->url);
|
||||
Type = request->type;
|
||||
ForceIPv4 = request->forceIPv4;
|
||||
Size = request->size;
|
||||
if (request->type == HTTP_DATA_JSON)
|
||||
{
|
||||
Body.Json = json_deep_copy(request->root);
|
||||
}
|
||||
else
|
||||
{
|
||||
Body.Buffer = new char[request->size];
|
||||
memcpy(Body.Buffer, request->body, request->size);
|
||||
}
|
||||
}
|
||||
|
||||
~HttpRequest2()
|
||||
{
|
||||
if (Type == HTTP_DATA_JSON)
|
||||
{
|
||||
json_decref(Body.Json);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete Body.Buffer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct read_buffer {
|
||||
char *ptr;
|
||||
size_t length;
|
||||
size_t position;
|
||||
};
|
||||
|
||||
struct write_buffer {
|
||||
char *ptr;
|
||||
size_t length;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
void http_init()
|
||||
Http::Http()
|
||||
{
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
}
|
||||
|
||||
void http_dispose()
|
||||
Http::~Http()
|
||||
{
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
static size_t http_request_write_func(void *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
static size_t writeData(const char * src, size_t size, size_t nmemb, void * userdata)
|
||||
{
|
||||
write_buffer *writeBuffer = (write_buffer*)userdata;
|
||||
size_t realsize = size * nmemb;
|
||||
Response * res = static_cast<Response *>(userdata);
|
||||
res->body += std::string(src, src + realsize);
|
||||
|
||||
size_t newBytesLength = size * nmemb;
|
||||
if (newBytesLength > 0) {
|
||||
size_t newCapacity = writeBuffer->capacity;
|
||||
size_t newLength = writeBuffer->length + newBytesLength;
|
||||
while (newLength > newCapacity) {
|
||||
newCapacity = Math::Max<size_t>(4096, newCapacity * 2);
|
||||
}
|
||||
if (newCapacity != writeBuffer->capacity) {
|
||||
writeBuffer->ptr = (char*)realloc(writeBuffer->ptr, newCapacity);
|
||||
writeBuffer->capacity = newCapacity;
|
||||
}
|
||||
|
||||
memcpy(writeBuffer->ptr + writeBuffer->length, ptr, newBytesLength);
|
||||
writeBuffer->length = newLength;
|
||||
}
|
||||
return newBytesLength;
|
||||
return realsize;
|
||||
}
|
||||
|
||||
static http_response_t *http_request(const HttpRequest2 &request)
|
||||
static size_t header_callback(const char * src, size_t size, size_t nitems, void * userdata)
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode curlResult;
|
||||
http_response_t *response;
|
||||
read_buffer readBuffer = {};
|
||||
write_buffer writeBuffer;
|
||||
size_t realsize = nitems * size;
|
||||
Response * res = static_cast<Response *>(userdata);
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (request.Type == HTTP_DATA_JSON && request.Body.Json != nullptr) {
|
||||
readBuffer.ptr = json_dumps(request.Body.Json, JSON_COMPACT);
|
||||
readBuffer.length = strlen(readBuffer.ptr);
|
||||
readBuffer.position = 0;
|
||||
} else if (request.Type == HTTP_DATA_RAW && request.Body.Buffer != nullptr) {
|
||||
readBuffer.ptr = request.Body.Buffer;
|
||||
readBuffer.length = request.Size;
|
||||
readBuffer.position = 0;
|
||||
auto line = std::string(src, src + realsize);
|
||||
auto pos = line.find(':');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string key = line.substr(0, pos);
|
||||
// substract 4 chars for ": " and "\r\n"
|
||||
std::string value = line.substr(pos + 2, line.size() - pos - 4);
|
||||
res->header[key] = value;
|
||||
}
|
||||
|
||||
writeBuffer.ptr = nullptr;
|
||||
writeBuffer.length = 0;
|
||||
writeBuffer.capacity = 0;
|
||||
return realsize;
|
||||
}
|
||||
|
||||
curl_slist *headers = nullptr;
|
||||
struct WriteThis
|
||||
{
|
||||
const char * readptr;
|
||||
size_t sizeleft;
|
||||
};
|
||||
|
||||
if (request.Type == HTTP_DATA_JSON) {
|
||||
headers = curl_slist_append(headers, "Accept: " MIME_TYPE_APPLICATION_JSON);
|
||||
static size_t read_callback(void * dst, size_t size, size_t nmemb, void * userp)
|
||||
{
|
||||
WriteThis * wt = static_cast<WriteThis *>(userp);
|
||||
size_t buffer_size = size * nmemb;
|
||||
|
||||
if (request.Body.Json != nullptr) {
|
||||
headers = curl_slist_append(headers, "Content-Type: " MIME_TYPE_APPLICATION_JSON);
|
||||
}
|
||||
if (wt->sizeleft)
|
||||
{
|
||||
size_t copy_this_much = wt->sizeleft;
|
||||
if (copy_this_much > buffer_size)
|
||||
copy_this_much = buffer_size;
|
||||
memcpy(dst, wt->readptr, copy_this_much);
|
||||
|
||||
wt->readptr += copy_this_much;
|
||||
wt->sizeleft -= copy_this_much;
|
||||
return copy_this_much;
|
||||
}
|
||||
|
||||
if (readBuffer.ptr != nullptr) {
|
||||
char contentLengthHeaderValue[64];
|
||||
snprintf(contentLengthHeaderValue, sizeof(contentLengthHeaderValue), "Content-Length: %zu", readBuffer.length);
|
||||
headers = curl_slist_append(headers, contentLengthHeaderValue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, readBuffer.ptr);
|
||||
Response Do(const Request & req)
|
||||
{
|
||||
CURL * curl = curl_easy_init();
|
||||
std::shared_ptr<void> _(nullptr, [curl](...) { curl_easy_cleanup(curl); });
|
||||
|
||||
if (!curl)
|
||||
std::runtime_error("Failed to initialize curl");
|
||||
|
||||
Response res;
|
||||
|
||||
if (req.method == Method::POST || req.method == Method::PUT)
|
||||
{
|
||||
WriteThis wt;
|
||||
wt.readptr = req.body.c_str();
|
||||
wt.sizeleft = req.body.size();
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_READDATA, &wt);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)wt.sizeleft);
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, request.Method.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
if (req.forceIPv4)
|
||||
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
|
||||
if (req.method == Method::POST)
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
|
||||
if (req.method == Method::PUT)
|
||||
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, req.url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&res);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&res);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, OPENRCT2_USER_AGENT);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, request.Url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &writeBuffer);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_request_write_func);
|
||||
if (request.ForceIPv4)
|
||||
|
||||
curl_slist * chunk = nullptr;
|
||||
std::shared_ptr<void> __(nullptr, [chunk](...) { curl_slist_free_all(chunk); });
|
||||
for (auto header : req.header)
|
||||
{
|
||||
// Force resolving to IPv4 to fix issues where advertising over IPv6 does not work
|
||||
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
std::string hs = header.first + ": " + header.second;
|
||||
chunk = curl_slist_append(chunk, hs.c_str());
|
||||
}
|
||||
|
||||
curlResult = curl_easy_perform(curl);
|
||||
|
||||
if (request.Type == HTTP_DATA_JSON && request.Body.Json != nullptr) {
|
||||
free(readBuffer.ptr);
|
||||
}
|
||||
|
||||
if (curlResult != CURLE_OK) {
|
||||
log_error("HTTP request failed: %s.", curl_easy_strerror(curlResult));
|
||||
if (writeBuffer.ptr != nullptr)
|
||||
free(writeBuffer.ptr);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
long httpStatusCode;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpStatusCode);
|
||||
|
||||
char* contentType;
|
||||
curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &contentType);
|
||||
|
||||
// Null terminate the response buffer
|
||||
writeBuffer.length++;
|
||||
writeBuffer.ptr = (char*)realloc(writeBuffer.ptr, writeBuffer.length);
|
||||
writeBuffer.capacity = writeBuffer.length;
|
||||
writeBuffer.ptr[writeBuffer.length - 1] = 0;
|
||||
|
||||
response = nullptr;
|
||||
|
||||
// Parse as JSON if response is JSON
|
||||
if (contentType != nullptr && strstr(contentType, "json") != nullptr) {
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
root = json_loads(writeBuffer.ptr, 0, &error);
|
||||
if (root != nullptr) {
|
||||
response = (http_response_t*) malloc(sizeof(http_response_t));
|
||||
response->tag = request.Tag;
|
||||
response->status_code = (sint32) httpStatusCode;
|
||||
response->root = root;
|
||||
response->type = HTTP_DATA_JSON;
|
||||
response->size = writeBuffer.length;
|
||||
if (req.header.size() != 0)
|
||||
{
|
||||
if (chunk == nullptr)
|
||||
{
|
||||
throw std::runtime_error("Failed to set headers");
|
||||
}
|
||||
free(writeBuffer.ptr);
|
||||
} else {
|
||||
response = (http_response_t*) malloc(sizeof(http_response_t));
|
||||
response->tag = request.Tag;
|
||||
response->status_code = (sint32) httpStatusCode;
|
||||
response->body = writeBuffer.ptr;
|
||||
response->type = HTTP_DATA_RAW;
|
||||
response->size = writeBuffer.length;
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
CURLcode curl_code = curl_easy_perform(curl);
|
||||
if (curl_code != CURLE_OK)
|
||||
{
|
||||
throw std::runtime_error("Failed to perform request");
|
||||
}
|
||||
|
||||
return response;
|
||||
// gets freed by curl_easy_cleanup
|
||||
char * content_type;
|
||||
long code;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
|
||||
curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type);
|
||||
res.status = static_cast<Status>(code);
|
||||
res.content_type = std::string(content_type);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void http_request_async(const http_request_t * request, void (*callback)(http_response_t*))
|
||||
void DoAsync(const Request & req, std::function<void(Response & res)> fn)
|
||||
{
|
||||
auto request2 = HttpRequest2(request);
|
||||
auto thread = std::thread([](const HttpRequest2 &req, void(*callback2)(http_response_t*)) -> void
|
||||
{
|
||||
http_response_t * response = http_request(req);
|
||||
callback2(response);
|
||||
}, std::move(request2), callback);
|
||||
auto thread = std::thread([=]() {
|
||||
Response res;
|
||||
try
|
||||
{
|
||||
res = Do(req);
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
res.error = e.what();
|
||||
return;
|
||||
}
|
||||
fn(res);
|
||||
});
|
||||
thread.detach();
|
||||
}
|
||||
|
||||
void http_request_dispose(http_response_t *response)
|
||||
{
|
||||
if (response->type == HTTP_DATA_JSON && response->root != nullptr)
|
||||
json_decref(response->root);
|
||||
else if (response->type == HTTP_DATA_RAW && response->body != nullptr)
|
||||
free(response->body);
|
||||
|
||||
free(response);
|
||||
}
|
||||
|
||||
const char *http_get_extension_from_url(const char *url, const char *fallback)
|
||||
{
|
||||
const char *extension = strrchr(url, '.');
|
||||
|
||||
// Assume a save file by default if no valid extension can be determined
|
||||
if (extension == nullptr || strchr(extension, '/') != nullptr) {
|
||||
return fallback;
|
||||
} else {
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
|
||||
size_t http_download_park(const char * url, void * * outData)
|
||||
size_t DownloadPark(const char * url, void ** outData)
|
||||
{
|
||||
// Download park to buffer in memory
|
||||
HttpRequest2 request;
|
||||
request.Url = url;
|
||||
request.Method = "GET";
|
||||
request.Type = HTTP_DATA_NONE;
|
||||
|
||||
http_response_t *response = http_request(request);
|
||||
|
||||
if (response == nullptr || response->status_code != 200) {
|
||||
Console::Error::WriteLine("Failed to download '%s'", request.Url.c_str());
|
||||
if (response != nullptr) {
|
||||
http_request_dispose(response);
|
||||
}
|
||||
Request request;
|
||||
request.url = url;
|
||||
request.method = Method::GET;
|
||||
|
||||
Response res;
|
||||
try
|
||||
{
|
||||
res = Do(request);
|
||||
if (res.status != Status::OK)
|
||||
std::runtime_error("bad http status");
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
Console::Error::WriteLine("Failed to download '%s', cause %s", request.url.c_str(), e.what());
|
||||
*outData = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t dataSize = response->size - 1;
|
||||
void * data = malloc(dataSize);
|
||||
if (data == nullptr) {
|
||||
dataSize = 0;
|
||||
size_t dataSize = res.body.size() - 1;
|
||||
void * data = malloc(dataSize);
|
||||
if (data == nullptr)
|
||||
{
|
||||
Console::Error::WriteLine("Failed to allocate memory for downloaded park.");
|
||||
} else {
|
||||
memcpy(data, response->body, dataSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
http_request_dispose(response);
|
||||
|
||||
memcpy(data, res.body.c_str(), dataSize);
|
||||
*outData = data;
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Network
|
||||
|
||||
#endif
|
||||
|
||||
80
src/openrct2/network/Http.h
Normal file
80
src/openrct2/network/Http.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma region Copyright (c) 2014-2018 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
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
namespace OpenRCT2::Network::Http
|
||||
{
|
||||
|
||||
enum class Status
|
||||
{
|
||||
OK = 200
|
||||
};
|
||||
|
||||
enum class Method
|
||||
{
|
||||
GET,
|
||||
POST,
|
||||
PUT
|
||||
};
|
||||
|
||||
struct Response
|
||||
{
|
||||
Status status;
|
||||
std::string content_type;
|
||||
std::string body = "";
|
||||
std::map<std::string, std::string> header = {};
|
||||
std::string error = "";
|
||||
};
|
||||
|
||||
struct Request
|
||||
{
|
||||
std::string url;
|
||||
std::map<std::string, std::string> header = {};
|
||||
Method method = Method::GET;
|
||||
std::string body = "";
|
||||
bool forceIPv4 = false;
|
||||
};
|
||||
|
||||
struct Http
|
||||
{
|
||||
Http();
|
||||
~Http();
|
||||
};
|
||||
|
||||
Response Do(const Request & req);
|
||||
void DoAsync(const Request & req, std::function<void(Response & res)> fn);
|
||||
|
||||
/**
|
||||
* Download a park via HTTP/S from the given URL into a memory buffer. This is
|
||||
* a blocking operation.
|
||||
* @param url The URL to download the park from.
|
||||
* @param outData The data returned.
|
||||
* @returns The size of the data or 0 if the download failed.
|
||||
*/
|
||||
size_t DownloadPark(const char * url, void ** outData);
|
||||
|
||||
} /* namespace http */
|
||||
|
||||
#endif // DISABLE_HTTP
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
@@ -31,7 +31,9 @@
|
||||
#include "../util/Util.h"
|
||||
#include "../world/Map.h"
|
||||
#include "../world/Park.h"
|
||||
#include "http.h"
|
||||
#include "Http.h"
|
||||
|
||||
using namespace OpenRCT2::Network;
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
|
||||
@@ -103,30 +105,26 @@ private:
|
||||
_lastAdvertiseTime = platform_get_ticks();
|
||||
|
||||
// Send the registration request
|
||||
http_request_t request = {};
|
||||
request.tag = this;
|
||||
Http::Request request;
|
||||
request.url = GetMasterServerUrl();
|
||||
request.method = HTTP_METHOD_POST;
|
||||
request.method = Http::Method::POST;
|
||||
request.forceIPv4 = forceIPv4;
|
||||
|
||||
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.root = body;
|
||||
request.type = HTTP_DATA_JSON;
|
||||
request.body = json_dumps(body, JSON_COMPACT);
|
||||
request.header["Content-Type"] = "application/json";
|
||||
|
||||
http_request_async(&request, [](http_response_t * response) -> void
|
||||
{
|
||||
if (response == nullptr)
|
||||
Http::DoAsync(request, [&](Http::Response response) -> void {
|
||||
if (response.status != Http::Status::OK)
|
||||
{
|
||||
Console::WriteLine("Unable to connect to master server");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto advertiser = static_cast<NetworkServerAdvertiser*>(response->tag);
|
||||
advertiser->OnRegistrationResponse(response->root);
|
||||
http_request_dispose(response);
|
||||
}
|
||||
|
||||
json_t * root = Json::FromString(response.body);
|
||||
this->OnRegistrationResponse(root);
|
||||
});
|
||||
|
||||
json_decref(body);
|
||||
@@ -134,31 +132,27 @@ private:
|
||||
|
||||
void SendHeartbeat()
|
||||
{
|
||||
http_request_t request = {};
|
||||
request.tag = this;
|
||||
Http::Request request;
|
||||
request.url = GetMasterServerUrl();
|
||||
request.method = HTTP_METHOD_PUT;
|
||||
request.method = Http::Method::POST;
|
||||
|
||||
json_t * jsonBody = GetHeartbeatJson();
|
||||
request.root = jsonBody;
|
||||
request.type = HTTP_DATA_JSON;
|
||||
json_t * body = GetHeartbeatJson();
|
||||
request.body = json_dumps(body, JSON_COMPACT);
|
||||
request.header["Content-Type"] = "application/json";
|
||||
|
||||
_lastHeartbeatTime = platform_get_ticks();
|
||||
http_request_async(&request, [](http_response_t *response) -> void
|
||||
{
|
||||
if (response == nullptr)
|
||||
Http::DoAsync(request, [&](Http::Response response) -> void {
|
||||
if (response.status != Http::Status::OK)
|
||||
{
|
||||
log_warning("Unable to connect to master server");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto advertiser = static_cast<NetworkServerAdvertiser*>(response->tag);
|
||||
advertiser->OnHeartbeatResponse(response->root);
|
||||
http_request_dispose(response);
|
||||
Console::WriteLine("Unable to connect to master server");
|
||||
return;
|
||||
}
|
||||
|
||||
json_t * root = Json::FromString(response.body);
|
||||
this->OnHeartbeatResponse(root);
|
||||
});
|
||||
|
||||
json_decref(jsonBody);
|
||||
json_decref(body);
|
||||
}
|
||||
|
||||
void OnRegistrationResponse(json_t * jsonRoot)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
@@ -26,13 +26,17 @@
|
||||
#error HTTP must be enabled to use the TWITCH functionality.
|
||||
#endif
|
||||
|
||||
#include <jansson.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "../Context.h"
|
||||
|
||||
#include "../core/Math.hpp"
|
||||
#include "../core/String.hpp"
|
||||
#include "../OpenRCT2.h"
|
||||
|
||||
#include "../config/Config.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../drawing/Drawing.h"
|
||||
#include "../Game.h"
|
||||
#include "../interface/InteractiveConsole.h"
|
||||
@@ -42,10 +46,12 @@
|
||||
#include "../platform/platform.h"
|
||||
#include "../util/Util.h"
|
||||
#include "../world/Sprite.h"
|
||||
#include "http.h"
|
||||
#include "Http.h"
|
||||
#include "twitch.h"
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Network;
|
||||
|
||||
|
||||
bool gTwitchEnable = false;
|
||||
|
||||
@@ -107,7 +113,7 @@ namespace Twitch
|
||||
static bool _twitchIdle = true;
|
||||
static uint32 _twitchLastPulseTick = 0;
|
||||
static sint32 _twitchLastPulseOperation = 1;
|
||||
static http_response_t * _twitchJsonResponse;
|
||||
static Http::Response _twitchJsonResponse;
|
||||
|
||||
static void Join();
|
||||
static void Leave();
|
||||
@@ -203,37 +209,33 @@ namespace Twitch
|
||||
_twitchState = TWITCH_STATE_JOINING;
|
||||
_twitchIdle = false;
|
||||
|
||||
http_request_t request = {};
|
||||
Http::Request request;
|
||||
request.url = url;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = nullptr;
|
||||
request.type = HTTP_DATA_JSON;
|
||||
http_request_async(&request, [](http_response_t *jsonResponse) -> void
|
||||
{
|
||||
auto context = GetContext();
|
||||
if (jsonResponse == nullptr)
|
||||
request.method = Http::Method::GET;
|
||||
|
||||
Http::DoAsync(request, [](Http::Response res) {
|
||||
std::shared_ptr<void> _(nullptr, [&](...) { _twitchIdle = true; });
|
||||
|
||||
if (res.status != Http::Status::OK)
|
||||
{
|
||||
_twitchState = TWITCH_STATE_LEFT;
|
||||
context->WriteLine("Unable to connect to twitch channel.");
|
||||
GetContext()->WriteLine("Unable to connect to twitch channel.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto root = Json::FromString(res.body);
|
||||
json_t * jsonStatus = json_object_get(root, "status");
|
||||
if (json_is_number(jsonStatus) && json_integer_value(jsonStatus) == TWITCH_STATUS_OK)
|
||||
{
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
json_t * jsonStatus = json_object_get(jsonResponse->root, "status");
|
||||
if (json_is_number(jsonStatus) && json_integer_value(jsonStatus) == TWITCH_STATUS_OK)
|
||||
{
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
_twitchState = TWITCH_STATE_LEFT;
|
||||
}
|
||||
|
||||
http_request_dispose(jsonResponse);
|
||||
|
||||
_twitchLastPulseTick = 0;
|
||||
context->WriteLine("Connected to twitch channel.");
|
||||
_twitchState = TWITCH_STATE_LEFT;
|
||||
}
|
||||
_twitchIdle = true;
|
||||
|
||||
_twitchLastPulseTick = 0;
|
||||
GetContext()->WriteLine("Connected to twitch channel.");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -242,13 +244,8 @@ namespace Twitch
|
||||
*/
|
||||
static void Leave()
|
||||
{
|
||||
if (_twitchJsonResponse != nullptr)
|
||||
{
|
||||
http_request_dispose(_twitchJsonResponse);
|
||||
_twitchJsonResponse = nullptr;
|
||||
}
|
||||
|
||||
GetContext()->WriteLine("Left twitch channel.");
|
||||
_twitchJsonResponse = {};
|
||||
_twitchState = TWITCH_STATE_LEFT;
|
||||
_twitchLastPulseTick = 0;
|
||||
gTwitchEnable = false;
|
||||
@@ -288,23 +285,17 @@ namespace Twitch
|
||||
_twitchState = TWITCH_STATE_WAITING;
|
||||
_twitchIdle = false;
|
||||
|
||||
http_request_t request = {};
|
||||
request.url = url;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = nullptr;
|
||||
request.type = HTTP_DATA_JSON;
|
||||
http_request_async(&request, [](http_response_t * jsonResponse) -> void
|
||||
{
|
||||
if (jsonResponse == nullptr)
|
||||
Http::DoAsync({ url }, [](Http::Response res) {
|
||||
std::shared_ptr<void> _(nullptr, [&](...) { _twitchIdle = true; });
|
||||
|
||||
if (res.status != Http::Status::OK)
|
||||
{
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_twitchJsonResponse = jsonResponse;
|
||||
_twitchState = TWITCH_STATE_GET_FOLLOWERS;
|
||||
}
|
||||
_twitchIdle = true;
|
||||
|
||||
_twitchJsonResponse = res;
|
||||
_twitchState = TWITCH_STATE_GET_FOLLOWERS;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -326,37 +317,31 @@ namespace Twitch
|
||||
_twitchState = TWITCH_STATE_WAITING;
|
||||
_twitchIdle = false;
|
||||
|
||||
http_request_t request = {};
|
||||
request.url = url;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = nullptr;
|
||||
request.type = HTTP_DATA_JSON;
|
||||
http_request_async(&request, [](http_response_t * jsonResponse) -> void
|
||||
{
|
||||
if (jsonResponse == nullptr)
|
||||
Http::DoAsync({ url }, [](Http::Response res) {
|
||||
std::shared_ptr<void> _(nullptr, [&](...) { _twitchIdle = true; });
|
||||
|
||||
if (res.status != Http::Status::OK)
|
||||
{
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_twitchJsonResponse = jsonResponse;
|
||||
_twitchState = TWITCH_STATE_GET_MESSAGES;
|
||||
}
|
||||
_twitchIdle = true;
|
||||
|
||||
_twitchJsonResponse = res;
|
||||
_twitchState = TWITCH_STATE_GET_MESSAGES;
|
||||
});
|
||||
}
|
||||
|
||||
static void ParseFollowers()
|
||||
{
|
||||
http_response_t *jsonResponse = _twitchJsonResponse;
|
||||
if (json_is_array(jsonResponse->root))
|
||||
json_t * root = Json::FromString(_twitchJsonResponse.body);
|
||||
if (json_is_array(root))
|
||||
{
|
||||
std::vector<AudienceMember> members;
|
||||
|
||||
size_t audienceCount = json_array_size(jsonResponse->root);
|
||||
size_t audienceCount = json_array_size(root);
|
||||
for (size_t i = 0; i < audienceCount; i++)
|
||||
{
|
||||
json_t * jsonAudienceMember = json_array_get(jsonResponse->root, i);
|
||||
json_t * jsonAudienceMember = json_array_get(root, i);
|
||||
auto member = AudienceMember::FromJson(jsonAudienceMember);
|
||||
if (!String::IsNullOrEmpty(member.Name))
|
||||
{
|
||||
@@ -371,8 +356,7 @@ namespace Twitch
|
||||
ManageGuestNames(members);
|
||||
}
|
||||
|
||||
http_request_dispose(_twitchJsonResponse);
|
||||
_twitchJsonResponse = nullptr;
|
||||
_twitchJsonResponse = {};
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
|
||||
gfx_invalidate_screen();
|
||||
@@ -380,12 +364,12 @@ namespace Twitch
|
||||
|
||||
static void ParseMessages()
|
||||
{
|
||||
http_response_t * jsonResponse = _twitchJsonResponse;
|
||||
if (json_is_array(jsonResponse->root))
|
||||
json_t * root = Json::FromString(_twitchJsonResponse.body);
|
||||
if (json_is_array(root))
|
||||
{
|
||||
size_t messageCount = json_array_size(jsonResponse->root);
|
||||
size_t messageCount = json_array_size(root);
|
||||
for (size_t i = 0; i < messageCount; i++) {
|
||||
json_t * jsonMessage = json_array_get(jsonResponse->root, i);
|
||||
json_t * jsonMessage = json_array_get(root, i);
|
||||
if (!json_is_object(jsonMessage))
|
||||
{
|
||||
continue;
|
||||
@@ -397,8 +381,7 @@ namespace Twitch
|
||||
}
|
||||
}
|
||||
|
||||
http_request_dispose(_twitchJsonResponse);
|
||||
_twitchJsonResponse = nullptr;
|
||||
_twitchJsonResponse = {};
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
}
|
||||
|
||||
@@ -569,7 +552,7 @@ namespace Twitch
|
||||
safe_strcpy(buffer + 1, message, sizeof(buffer) - 1);
|
||||
|
||||
utf8_remove_formatting(buffer, false);
|
||||
|
||||
|
||||
// TODO Create a new news item type for twitch which has twitch icon
|
||||
news_item_add_to_queue_raw(NEWS_ITEM_BLANK, buffer, 0);
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#pragma region Copyright (c) 2014-2017 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 _HTTP_H_
|
||||
#define _HTTP_H_
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
#include <string>
|
||||
#include <jansson.h>
|
||||
#include "../common.h"
|
||||
|
||||
enum HTTP_DATA_TYPE {
|
||||
HTTP_DATA_NONE,
|
||||
HTTP_DATA_RAW,
|
||||
HTTP_DATA_JSON
|
||||
};
|
||||
|
||||
struct http_request_t {
|
||||
void *tag;
|
||||
std::string method;
|
||||
std::string url;
|
||||
HTTP_DATA_TYPE type = HTTP_DATA_NONE;
|
||||
bool forceIPv4;
|
||||
size_t size;
|
||||
union {
|
||||
const json_t *root;
|
||||
char* body;
|
||||
};
|
||||
};
|
||||
|
||||
struct http_response_t {
|
||||
void *tag;
|
||||
sint32 status_code;
|
||||
HTTP_DATA_TYPE type;
|
||||
size_t size;
|
||||
union {
|
||||
json_t *root;
|
||||
char* body;
|
||||
};
|
||||
};
|
||||
|
||||
#define HTTP_METHOD_GET "GET"
|
||||
#define HTTP_METHOD_POST "POST"
|
||||
#define HTTP_METHOD_PUT "PUT"
|
||||
#define HTTP_METHOD_DELETE "DELETE"
|
||||
|
||||
void http_request_async(const http_request_t *request, void (*callback)(http_response_t*));
|
||||
void http_request_dispose(http_response_t *response);
|
||||
|
||||
const char *http_get_extension_from_url(const char *url, const char *fallback);
|
||||
|
||||
/**
|
||||
* Download a park via HTTP/S from the given URL into a memory buffer. This is
|
||||
* a blocking operation.
|
||||
* @param url The URL to download the park from.
|
||||
* @param outData The data returned.
|
||||
* @returns The size of the data or 0 if the download failed.
|
||||
*/
|
||||
size_t http_download_park(const char * url, void * * outData);
|
||||
#endif // DISABLE_HTTP
|
||||
|
||||
// These callbacks are defined anyway, but are dummy if HTTP is disabled
|
||||
void http_init();
|
||||
void http_dispose();
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user