Fix C# build being broken

This commit is contained in:
UnknownShadow200 2018-12-25 00:14:28 +11:00
parent dbdeb0d01b
commit 67558dfe7e
24 changed files with 779 additions and 748 deletions

View File

@ -92,7 +92,7 @@ namespace ClassicalSharp {
float sensitivity = sensiFactor * game.MouseSensitivity;
if (game.SmoothCamera) {
speedX += delta.X * adjust; game.LocalPlayer.ModelScale.XYZ
speedX += delta.X * adjust;
speedX *= slippery;
speedY += delta.Y * adjust;
speedY *= slippery;

View File

@ -1,335 +0,0 @@
#include "AsyncDownloader.h"
#include "Platform.h"
#include "Funcs.h"
#include "Logger.h"
#include "Stream.h"
#include "GameStructs.h"
void ASyncRequest_Free(struct AsyncRequest* request) {
Mem_Free(request->Data);
request->Data = NULL;
request->Size = 0;
}
#define ASYNC_DEF_ELEMS 10
struct AsyncRequestList {
int MaxElems, Count;
struct AsyncRequest* Requests;
struct AsyncRequest DefaultRequests[ASYNC_DEF_ELEMS];
};
static void AsyncRequestList_EnsureSpace(struct AsyncRequestList* list) {
if (list->Count < list->MaxElems) return;
list->Requests = Utils_Resize(list->Requests, &list->MaxElems,
sizeof(struct AsyncRequest), ASYNC_DEF_ELEMS, 10);
}
static void AsyncRequestList_Append(struct AsyncRequestList* list, struct AsyncRequest* item) {
AsyncRequestList_EnsureSpace(list);
list->Requests[list->Count++] = *item;
}
static void AsyncRequestList_Prepend(struct AsyncRequestList* list, struct AsyncRequest* item) {
int i;
AsyncRequestList_EnsureSpace(list);
for (i = list->Count; i > 0; i--) {
list->Requests[i] = list->Requests[i - 1];
}
list->Requests[0] = *item;
list->Count++;
}
static void AsyncRequestList_RemoveAt(struct AsyncRequestList* list, int i) {
if (i < 0 || i >= list->Count) Logger_Abort("Tried to remove element at list end");
for (; i < list->Count - 1; i++) {
list->Requests[i] = list->Requests[i + 1];
}
list->Count--;
}
static void AsyncRequestList_Init(struct AsyncRequestList* list) {
list->MaxElems = ASYNC_DEF_ELEMS;
list->Count = 0;
list->Requests = list->DefaultRequests;
}
static void AsyncRequestList_Free(struct AsyncRequestList* list) {
if (list->Requests != list->DefaultRequests) {
Mem_Free(list->Requests);
}
AsyncRequestList_Init(list);
}
static void* async_waitable;
static void* async_workerThread;
static void* async_pendingMutex;
static void* async_processedMutex;
static void* async_curRequestMutex;
static volatile bool async_terminate;
static struct AsyncRequestList async_pending;
static struct AsyncRequestList async_processed;
const static String async_skinServer = String_FromConst("http://static.classicube.net/skins/");
static struct AsyncRequest async_curRequest;
static volatile int async_curProgress = ASYNC_PROGRESS_NOTHING;
bool AsyncDownloader_Cookies;
static void AsyncDownloader_Add(const String* url, bool priority, const String* id, uint8_t type, TimeMS* lastModified, const String* etag, const String* data) {
struct AsyncRequest req = { 0 };
String reqUrl, reqID, reqEtag;
String_InitArray(reqUrl, req.URL);
String_Copy(&reqUrl, url);
String_InitArray(reqID, req.ID);
String_Copy(&reqID, id);
req.RequestType = type;
Platform_Log2("Adding %s (type %b)", &reqUrl, &type);
String_InitArray(reqEtag, req.Etag);
if (lastModified) { req.LastModified = *lastModified; }
if (etag) { String_Copy(&reqEtag, etag); }
/* request.Data = data; TODO: Implement this. do we need to copy or expect caller to malloc it? */
Mutex_Lock(async_pendingMutex);
{
req.TimeAdded = DateTime_CurrentUTC_MS();
if (priority) {
AsyncRequestList_Prepend(&async_pending, &req);
} else {
AsyncRequestList_Append(&async_pending, &req);
}
}
Mutex_Unlock(async_pendingMutex);
Waitable_Signal(async_waitable);
}
void AsyncDownloader_GetSkin(const String* id, const String* skinName) {
String url; char urlBuffer[STRING_SIZE];
String_InitArray(url, urlBuffer);
if (Utils_IsUrlPrefix(skinName, 0)) {
String_Copy(&url, skinName);
} else {
String_AppendString(&url, &async_skinServer);
String_AppendColorless(&url, skinName);
String_AppendConst(&url, ".png");
}
AsyncDownloader_Add(&url, false, id, REQUEST_TYPE_DATA, NULL, NULL, NULL);
}
void AsyncDownloader_GetData(const String* url, bool priority, const String* id) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_DATA, NULL, NULL, NULL);
}
void AsyncDownloader_GetContentLength(const String* url, bool priority, const String* id) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_CONTENT_LENGTH, NULL, NULL, NULL);
}
void AsyncDownloader_PostString(const String* url, bool priority, const String* id, const String* contents) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_DATA, NULL, NULL, contents);
}
void AsyncDownloader_GetDataEx(const String* url, bool priority, const String* id, TimeMS* lastModified, const String* etag) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_DATA, lastModified, etag, NULL);
}
void AsyncDownloader_PurgeOldEntriesTask(struct ScheduledTask* task) {
struct AsyncRequest* item;
int i;
Mutex_Lock(async_processedMutex);
{
TimeMS now = DateTime_CurrentUTC_MS();
for (i = async_processed.Count - 1; i >= 0; i--) {
item = &async_processed.Requests[i];
if (item->TimeDownloaded + (10 * 1000) >= now) continue;
ASyncRequest_Free(item);
AsyncRequestList_RemoveAt(&async_processed, i);
}
}
Mutex_Unlock(async_processedMutex);
}
static int AsyncRequestList_Find(const String* id, struct AsyncRequest* item) {
String reqID;
int i;
for (i = 0; i < async_processed.Count; i++) {
reqID = String_FromRawArray(async_processed.Requests[i].ID);
if (!String_Equals(id, &reqID)) continue;
*item = async_processed.Requests[i];
return i;
}
return -1;
}
bool AsyncDownloader_Get(const String* id, struct AsyncRequest* item) {
int i;
Mutex_Lock(async_processedMutex);
{
i = AsyncRequestList_Find(id, item);
if (i >= 0) AsyncRequestList_RemoveAt(&async_processed, i);
}
Mutex_Unlock(async_processedMutex);
return i >= 0;
}
bool AsyncDownloader_GetCurrent(struct AsyncRequest* request, int* progress) {
Mutex_Lock(async_curRequestMutex);
{
*request = async_curRequest;
*progress = async_curProgress;
}
Mutex_Unlock(async_curRequestMutex);
return request->ID[0];
}
void AsyncDownloader_Clear(void) {
Mutex_Lock(async_pendingMutex);
{
AsyncRequestList_Free(&async_pending);
}
Mutex_Unlock(async_pendingMutex);
Waitable_Signal(async_waitable);
}
static void AsyncDownloader_ProcessRequest(struct AsyncRequest* request) {
String url = String_FromRawArray(request->URL);
uint64_t beg, end;
uint32_t size, elapsed;
uintptr_t addr;
Platform_Log2("Downloading from %s (type %b)", &url, &request->RequestType);
beg = Stopwatch_Measure();
request->Result = Http_Do(request, &async_curProgress);
end = Stopwatch_Measure();
elapsed = Stopwatch_ElapsedMicroseconds(beg, end) / 1000;
Platform_Log3("HTTP: return code %i (http %i), in %i ms",
&request->Result, &request->StatusCode, &elapsed);
if (request->Data) {
size = request->Size;
addr = (uintptr_t)request->Data;
Platform_Log2("HTTP returned data: %i bytes at %x", &size, &addr);
}
}
static void AsyncDownloader_CompleteResult(struct AsyncRequest* request) {
struct AsyncRequest older;
String id = String_FromRawArray(request->ID);
int index;
request->TimeDownloaded = DateTime_CurrentUTC_MS();
index = AsyncRequestList_Find(&id, &older);
if (index >= 0) {
/* very rare case - priority item was inserted, then inserted again (so put before first item), */
/* and both items got downloaded before an external function removed them from the queue */
if (older.TimeAdded > request->TimeAdded) {
ASyncRequest_Free(request);
} else {
/* normal case, replace older request */
ASyncRequest_Free(&older);
async_processed.Requests[index] = *request;
}
} else {
AsyncRequestList_Append(&async_processed, request);
}
}
static void AsyncDownloader_WorkerFunc(void) {
struct AsyncRequest request;
bool hasRequest, stop;
for (;;) {
hasRequest = false;
Mutex_Lock(async_pendingMutex);
{
stop = async_terminate;
if (!stop && async_pending.Count) {
request = async_pending.Requests[0];
hasRequest = true;
AsyncRequestList_RemoveAt(&async_pending, 0);
}
}
Mutex_Unlock(async_pendingMutex);
if (stop) return;
/* Block until another thread submits a request to do */
if (!hasRequest) {
Platform_LogConst("Going back to sleep...");
Waitable_Wait(async_waitable);
continue;
}
Platform_LogConst("Got something to do!");
Mutex_Lock(async_curRequestMutex);
{
async_curRequest = request;
async_curProgress = ASYNC_PROGRESS_MAKING_REQUEST;
}
Mutex_Unlock(async_curRequestMutex);
Platform_LogConst("Doing it");
/* performing request doesn't need thread safety */
AsyncDownloader_ProcessRequest(&request);
Mutex_Lock(async_processedMutex);
{
AsyncDownloader_CompleteResult(&request);
}
Mutex_Unlock(async_processedMutex);
Mutex_Lock(async_curRequestMutex);
{
async_curRequest.ID[0] = '\0';
async_curProgress = ASYNC_PROGRESS_NOTHING;
}
Mutex_Unlock(async_curRequestMutex);
}
}
/*########################################################################################################################*
*------------------------------------------------AsyncDownloader component------------------------------------------------*
*#########################################################################################################################*/
static void AsyncDownloader_Init(void) {
ScheduledTask_Add(30, AsyncDownloader_PurgeOldEntriesTask);
AsyncRequestList_Init(&async_pending);
AsyncRequestList_Init(&async_processed);
Http_Init();
async_waitable = Waitable_Create();
async_pendingMutex = Mutex_Create();
async_processedMutex = Mutex_Create();
async_curRequestMutex = Mutex_Create();
async_workerThread = Thread_Start(AsyncDownloader_WorkerFunc, false);
}
static void AsyncDownloader_Free(void) {
async_terminate = true;
AsyncDownloader_Clear();
Thread_Join(async_workerThread);
AsyncRequestList_Free(&async_pending);
AsyncRequestList_Free(&async_processed);
Http_Free();
Waitable_Free(async_waitable);
Mutex_Free(async_pendingMutex);
Mutex_Free(async_processedMutex);
Mutex_Free(async_curRequestMutex);
}
struct IGameComponent AsyncDownloader_Component = {
AsyncDownloader_Init, /* Init */
AsyncDownloader_Free, /* Free */
AsyncDownloader_Clear /* Reset */
};

View File

@ -1,63 +0,0 @@
#ifndef CC_ASYNCDOWNLOADER_H
#define CC_ASYNCDOWNLOADER_H
#include "Constants.h"
#include "Utils.h"
/* Downloads images, texture packs, skins, etc in async manner.
Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
*/
struct IGameComponent;
struct ScheduledTask;
#define URL_MAX_SIZE (STRING_SIZE * 2)
extern struct IGameComponent AsyncDownloader_Component;
/* TODO: Implement these */
extern bool AsyncDownloader_Cookies;
/* TODO: Connection pooling */
enum REQUEST_TYPE { REQUEST_TYPE_DATA, REQUEST_TYPE_CONTENT_LENGTH };
enum AsyncProgress {
ASYNC_PROGRESS_NOTHING = -3,
ASYNC_PROGRESS_MAKING_REQUEST = -2,
ASYNC_PROGRESS_FETCHING_DATA = -1
};
struct AsyncRequest {
char URL[URL_MAX_SIZE]; /* URL data is downloaded from/uploaded to. */
char ID[STRING_SIZE]; /* Unique identifier for this request. */
TimeMS TimeAdded; /* Time this request was added to queue of requests. */
TimeMS TimeDownloaded; /* Time response contents were completely downloaded. */
int StatusCode; /* HTTP status code returned in the response. */
uint32_t ContentLength; /* HTTP content length returned in the response. */
ReturnCode Result; /* 0 on success, otherwise platform-specific error. */
void* Data; /* Contents of the response. */
uint32_t Size; /* Size of the contents. */
TimeMS LastModified; /* Time item cached at (if at all) */
char Etag[STRING_SIZE]; /* ETag of cached item (if any) */
uint8_t RequestType; /* Whether to fetch contents or just headers. */
};
void ASyncRequest_Free(struct AsyncRequest* request);
void AsyncDownloader_GetSkin(const String* id, const String* skinName);
/* Asynchronously downloads contents of a webpage. */
void AsyncDownloader_GetData(const String* url, bool priority, const String* id);
/* Asynchronously downloads Content Length header of a webpage. */
void AsyncDownloader_GetContentLength(const String* url, bool priority, const String* id);
/* TODO: Implement post */
void AsyncDownloader_UNSAFE_PostData(const String* url, bool priority, const String* id, const void* data, const uint32_t size);
/* Asynchronously downloads contents of a webpage. */
/* Optionally also sets If-Modified-Since and If-None-Match headers. */
void AsyncDownloader_GetDataEx(const String* url, bool priority, const String* id, TimeMS* lastModified, const String* etag);
/* Attempts to retrieve a fully completed request. */
/* Returns whether a request with a matching id was retrieved. */
bool AsyncDownloader_Get(const String* id, struct AsyncRequest* item);
/* Retrieves information about the request currently being processed. */
/* Returns whether there is actually a request being currently processed. */
bool AsyncDownloader_GetCurrent(struct AsyncRequest* request, int* progress);
/* Clears the list of pending requests. */
void AsyncDownloader_Clear(void);
void AsyncDownloader_PurgeOldEntriesTask(struct ScheduledTask* task);
#endif

View File

@ -187,7 +187,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AsyncDownloader.h" />
<ClInclude Include="Http.h" />
<ClInclude Include="Audio.h" />
<ClInclude Include="AxisLinesRenderer.h" />
<ClInclude Include="BlockID.h" />
@ -253,7 +253,7 @@
<ClInclude Include="World.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="AsyncDownloader.c" />
<ClCompile Include="Http.c" />
<ClCompile Include="Audio.c" />
<ClCompile Include="Camera.c" />
<ClCompile Include="AxisLinesRenderer.c" />

View File

@ -234,9 +234,6 @@
<ClInclude Include="ServerConnection.h">
<Filter>Header Files\Network</Filter>
</ClInclude>
<ClInclude Include="AsyncDownloader.h">
<Filter>Header Files\Network</Filter>
</ClInclude>
<ClInclude Include="Event.h">
<Filter>Header Files\Game</Filter>
</ClInclude>
@ -318,6 +315,9 @@
<ClInclude Include="Logger.h">
<Filter>Header Files\Platform</Filter>
</ClInclude>
<ClInclude Include="Http.h">
<Filter>Header Files\Network</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="String.c">
@ -440,9 +440,6 @@
<ClCompile Include="PacketHandlers.c">
<Filter>Source Files\Network</Filter>
</ClCompile>
<ClCompile Include="AsyncDownloader.c">
<Filter>Source Files\Network</Filter>
</ClCompile>
<ClCompile Include="EnvRenderer.c">
<Filter>Source Files\Rendering</Filter>
</ClCompile>
@ -566,5 +563,8 @@
<ClCompile Include="Logger.c">
<Filter>Source Files\Platform</Filter>
</ClCompile>
<ClCompile Include="Http.c">
<Filter>Source Files\Network</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -11,7 +11,7 @@
#include "Lighting.h"
#include "Drawer2D.h"
#include "Particle.h"
#include "AsyncDownloader.h"
#include "Http.h"
#include "Chat.h"
#include "Model.h"
#include "Input.h"
@ -688,7 +688,7 @@ static void Player_CheckSkin(struct Player* p) {
struct Player* first;
String url, skin = String_FromRawArray(e->SkinNameRaw);
struct AsyncRequest item;
struct HttpRequest item;
struct Stream mem;
Bitmap bmp;
ReturnCode res;
@ -696,14 +696,14 @@ static void Player_CheckSkin(struct Player* p) {
if (!p->FetchedSkin && e->Model->UsesSkin) {
first = Player_FirstOtherWithSameSkinAndFetchedSkin(p);
if (!first) {
AsyncDownloader_GetSkin(&skin, &skin);
Http_AsyncGetSkin(&skin, &skin);
} else {
Player_CopySkin(p, first);
}
p->FetchedSkin = true;
}
if (!AsyncDownloader_Get(&skin, &item)) return;
if (!Http_GetResult(&skin, &item)) return;
if (!item.Data) { Player_SetSkinAll(p, true); return; }
Stream_ReadonlyMemory(&mem, item.Data, item.Size);

View File

@ -18,7 +18,7 @@
#include "Drawer2D.h"
#include "Model.h"
#include "Particle.h"
#include "AsyncDownloader.h"
#include "Http.h"
#include "Inventory.h"
#include "InputHandler.h"
#include "ServerConnection.h"
@ -459,7 +459,7 @@ static void Game_Load(void) {
Game_AddComponent(&Models_Component);
Game_AddComponent(&Entities_Component);
Game_AddComponent(&AsyncDownloader_Component);
Game_AddComponent(&Http_Component);
Game_AddComponent(&Lighting_Component);
Game_AddComponent(&Animations_Component);

637
src/Http.c Normal file
View File

@ -0,0 +1,637 @@
#include "Http.h"
#include "Platform.h"
#include "Funcs.h"
#include "Logger.h"
#include "Stream.h"
#include "GameStructs.h"
#ifdef CC_BUILD_WIN
#define WIN32_LEAN_AND_MEAN
#define NOSERVICE
#define NOMCX
#define NOIME
#define _WIN32_IE 0x0400
#define WINVER 0x0500
#define _WIN32_WINNT 0x0500
#ifndef UNICODE
#define UNICODE
#define _UNICODE
#endif
#include <windows.h>
#include <wininet.h>
#define HTTP_QUERY_ETAG 54 /* Missing from some old MingW32 headers */
#endif
/* POSIX is mainly shared between Linux and OSX */
#ifdef CC_BUILD_POSIX
#include <curl/curl.h>
#include <time.h>
#endif
void HttpRequest_Free(struct HttpRequest* request) {
Mem_Free(request->Data);
request->Data = NULL;
request->Size = 0;
}
/*########################################################################################################################*
*-------------------------------------------------System/Native interface-------------------------------------------------*
*#########################################################################################################################*/
#ifdef CC_BUILD_WIN
static HINTERNET hInternet;
/* TODO: Test last modified and etag even work */
#define FLAG_STATUS HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER
#define FLAG_LENGTH HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER
#define FLAG_LASTMOD HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME
static void Http_SysInit(void) {
/* TODO: Should we use INTERNET_OPEN_TYPE_PRECONFIG instead? */
hInternet = InternetOpenA(GAME_APP_NAME, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (!hInternet) Logger_Abort2(GetLastError(), "Failed to init WinINet");
}
static ReturnCode Http_SysMake(struct HttpRequest* req, HINTERNET* handle) {
String url = String_FromRawArray(req->URL);
char urlStr[URL_MAX_SIZE + 1];
Mem_Copy(urlStr, url.buffer, url.length);
urlStr[url.length] = '\0';
char headersBuffer[STRING_SIZE * 3];
String headers = String_FromArray(headersBuffer);
/* https://stackoverflow.com/questions/25308488/c-wininet-custom-http-headers */
if (req->Etag[0] || req->LastModified) {
if (req->LastModified) {
String_AppendConst(&headers, "If-Modified-Since: ");
DateTime_HttpDate(req->LastModified, &headers);
String_AppendConst(&headers, "\r\n");
}
if (req->Etag[0]) {
String etag = String_FromRawArray(req->Etag);
String_AppendConst(&headers, "If-None-Match: ");
String_AppendString(&headers, &etag);
String_AppendConst(&headers, "\r\n");
}
String_AppendConst(&headers, "\r\n\r\n");
headers.buffer[headers.length] = '\0';
} else { headers.buffer = NULL; }
*handle = InternetOpenUrlA(hInternet, urlStr, headers.buffer, headers.length,
INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD, 0);
return *handle ? 0 : GetLastError();
}
static ReturnCode Http_SysGetHeaders(struct HttpRequest* req, HINTERNET handle) {
DWORD len;
len = sizeof(DWORD);
if (!HttpQueryInfoA(handle, FLAG_STATUS, &req->StatusCode, &len, NULL)) return GetLastError();
len = sizeof(DWORD);
HttpQueryInfoA(handle, FLAG_LENGTH, &req->ContentLength, &len, NULL);
SYSTEMTIME sysTime;
len = sizeof(SYSTEMTIME);
if (HttpQueryInfoA(handle, FLAG_LASTMOD, &sysTime, &len, NULL)) {
struct DateTime time;
time.Year = sysTime.wYear; time.Month = sysTime.wMonth;
time.Day = sysTime.wDay; time.Hour = sysTime.wHour;
time.Minute = sysTime.wMinute; time.Second = sysTime.wSecond;
time.Milli = sysTime.wMilliseconds;
req->LastModified = DateTime_TotalMs(&time);
}
String etag = String_ClearedArray(req->Etag);
len = etag.capacity;
HttpQueryInfoA(handle, HTTP_QUERY_ETAG, etag.buffer, &len, NULL);
return 0;
}
static ReturnCode Http_SysGetData(struct HttpRequest* req, HINTERNET handle, volatile int* progress) {
uint8_t* buffer;
uint32_t size, totalRead;
uint32_t read, avail;
bool success;
*progress = 0;
size = req->ContentLength ? req->ContentLength : 1;
buffer = Mem_Alloc(size, 1, "http get data");
totalRead = 0;
req->Data = buffer;
req->Size = 0;
for (;;) {
if (!InternetQueryDataAvailable(handle, &avail, 0, 0)) break;
if (!avail) break;
/* expand if buffer is too small (some servers don't give content length) */
if (totalRead + avail > size) {
size = totalRead + avail;
buffer = Mem_Realloc(buffer, size, 1, "http inc data");
req->Data = buffer;
}
success = InternetReadFile(handle, &buffer[totalRead], avail, &read);
if (!success) { Mem_Free(buffer); return GetLastError(); }
if (!read) break;
totalRead += read;
if (req->ContentLength) *progress = (int)(100.0f * totalRead / size);
req->Size += read;
}
*progress = 100;
return 0;
}
static ReturnCode Http_SysDo(struct HttpRequest* req, volatile int* progress) {
HINTERNET handle;
ReturnCode res = Http_SysMake(req, &handle);
if (res) return res;
*progress = ASYNC_PROGRESS_FETCHING_DATA;
res = Http_SysGetHeaders(req, handle);
if (res) { InternetCloseHandle(handle); return res; }
if (req->RequestType != REQUEST_TYPE_HEAD && req->StatusCode == 200) {
res = Http_SysGetData(req, handle, progress);
if (res) { InternetCloseHandle(handle); return res; }
}
return InternetCloseHandle(handle) ? 0 : GetLastError();
}
static ReturnCode Http_SysFree(void) {
return InternetCloseHandle(hInternet) ? 0 : GetLastError();
}
#endif
#ifdef CC_BUILD_POSIX
CURL* curl;
static void Http_SysInit(void) {
CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
if (res) Logger_Abort2(res, "Failed to init curl");
curl = curl_easy_init();
if (!curl) Logger_Abort("Failed to init easy curl");
}
static int Http_SysProgress(int* progress, double total, double received, double a, double b) {
if (total == 0) return 0;
*progress = (int)(100 * received / total);
return 0;
}
static struct curl_slist* Http_SysMake(struct HttpRequest* req) {
String tmp; char buffer[STRING_SIZE + 1];
struct curl_slist* list = NULL;
String etag;
if (req->Etag[0]) {
String_InitArray_NT(tmp, buffer);
String_AppendConst(&tmp, "If-None-Match: ");
etag = String_FromRawArray(req->Etag);
String_AppendString(&tmp, &etag);
tmp.buffer[tmp.length] = '\0';
list = curl_slist_append(list, tmp.buffer);
}
if (req->LastModified) {
String_InitArray_NT(tmp, buffer);
String_AppendConst(&tmp, "Last-Modified: ");
DateTime_HttpDate(req->LastModified, &tmp);
tmp.buffer[tmp.length] = '\0';
list = curl_slist_append(list, tmp.buffer);
}
return list;
}
static size_t Http_SysGetHeaders(char *buffer, size_t size, size_t nitems, struct HttpRequest* req) {
String tmp; char tmpBuffer[STRING_SIZE + 1];
String line, name, value;
time_t time;
if (size != 1) return size * nitems; /* non byte header */
line = String_Init(buffer, nitems, nitems);
if (!String_UNSAFE_Separate(&line, ':', &name, &value)) return nitems;
/* value usually has \r\n at end */
if (value.length && value.buffer[value.length - 1] == '\n') value.length--;
if (value.length && value.buffer[value.length - 1] == '\r') value.length--;
if (!value.length) return nitems;
if (String_CaselessEqualsConst(&name, "ETag")) {
tmp = String_ClearedArray(req->Etag);
String_AppendString(&tmp, &value);
} else if (String_CaselessEqualsConst(&name, "Content-Length")) {
Convert_ParseInt(&value, &req->ContentLength);
/* TODO: Fix when ContentLength isn't RequestSize */
req->Size = req->ContentLength;
} else if (String_CaselessEqualsConst(&name, "Last-Modified")) {
String_InitArray_NT(tmp, tmpBuffer);
String_AppendString(&tmp, &value);
tmp.buffer[tmp.length] = '\0';
time = curl_getdate(tmp.buffer, NULL);
if (time == -1) return nitems;
req->LastModified = (uint64_t)time * 1000 + UNIX_EPOCH;
}
return nitems;
}
static size_t Http_SysGetData(char *buffer, size_t size, size_t nitems, struct HttpRequest* req) {
uint32_t total, left;
uint8_t* dst;
total = req->Size;
if (!total || req->RequestType == REQUEST_TYPE_HEAD) return 0;
if (!req->Data) req->Data = Mem_Alloc(total, 1, "http get data");
/* reuse Result as an offset */
left = total - req->Result;
left = min(left, nitems);
dst = (uint8_t*)req->Data + req->Result;
Mem_Copy(dst, buffer, left);
req->Result += left;
return nitems;
}
static ReturnCode Http_SysDo(struct HttpRequest* req, volatile int* progress) {
struct curl_slist* list;
String url = String_FromRawArray(req->URL);
char urlStr[600];
long status = 0;
CURLcode res;
Platform_ConvertString(urlStr, &url);
curl_easy_reset(curl);
list = Http_SysMake(req);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_setopt(curl, CURLOPT_URL, urlStr);
curl_easy_setopt(curl, CURLOPT_USERAGENT, GAME_APP_NAME);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, Http_SysProgress);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, progress);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, Http_SysGetHeaders);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, req);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Http_SysGetData);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, req);
*progress = ASYNC_PROGRESS_FETCHING_DATA;
res = curl_easy_perform(curl);
*progress = 100;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
req->StatusCode = status;
curl_slist_free_all(list);
return res;
}
static ReturnCode Http_SysFree(void) {
curl_easy_cleanup(curl);
curl_global_cleanup();
return 0;
}
#endif
/*########################################################################################################################*
*-------------------------------------------------Asynchronous downloader-------------------------------------------------*
*#########################################################################################################################*/
#define HTTP_DEF_ELEMS 10
struct HttpRequestList {
int MaxElems, Count;
struct HttpRequest* Requests;
struct HttpRequest DefaultRequests[HTTP_DEF_ELEMS];
};
static void HttpRequestList_EnsureSpace(struct HttpRequestList* list) {
if (list->Count < list->MaxElems) return;
list->Requests = Utils_Resize(list->Requests, &list->MaxElems,
sizeof(struct HttpRequest), HTTP_DEF_ELEMS, 10);
}
static void HttpRequestList_Append(struct HttpRequestList* list, struct HttpRequest* item) {
HttpRequestList_EnsureSpace(list);
list->Requests[list->Count++] = *item;
}
static void HttpRequestList_Prepend(struct HttpRequestList* list, struct HttpRequest* item) {
int i;
HttpRequestList_EnsureSpace(list);
for (i = list->Count; i > 0; i--) {
list->Requests[i] = list->Requests[i - 1];
}
list->Requests[0] = *item;
list->Count++;
}
static void HttpRequestList_RemoveAt(struct HttpRequestList* list, int i) {
if (i < 0 || i >= list->Count) Logger_Abort("Tried to remove element at list end");
for (; i < list->Count - 1; i++) {
list->Requests[i] = list->Requests[i + 1];
}
list->Count--;
}
static void HttpRequestList_Init(struct HttpRequestList* list) {
list->MaxElems = HTTP_DEF_ELEMS;
list->Count = 0;
list->Requests = list->DefaultRequests;
}
static void HttpRequestList_Free(struct HttpRequestList* list) {
if (list->Requests != list->DefaultRequests) {
Mem_Free(list->Requests);
}
HttpRequestList_Init(list);
}
static void* http_waitable;
static void* http_workerThread;
static void* http_pendingMutex;
static void* http_processedMutex;
static void* http_curRequestMutex;
static volatile bool http_terminate;
static struct HttpRequestList http_pending;
static struct HttpRequestList http_processed;
const static String http_skinServer = String_FromConst("http://static.classicube.net/skins/");
static struct HttpRequest http_curRequest;
static volatile int http_curProgress = ASYNC_PROGRESS_NOTHING;
bool Http_UseCookies;
/* Adds a request to the list of pending requests, waking up worker thread if needed. */
static void Http_Add(const String* url, bool priority, const String* id, uint8_t type, TimeMS* lastModified, const String* etag, const String* data) {
struct HttpRequest req = { 0 };
String reqUrl, reqID, reqEtag;
String_InitArray(reqUrl, req.URL);
String_Copy(&reqUrl, url);
String_InitArray(reqID, req.ID);
String_Copy(&reqID, id);
req.RequestType = type;
Platform_Log2("Adding %s (type %b)", &reqUrl, &type);
String_InitArray(reqEtag, req.Etag);
if (lastModified) { req.LastModified = *lastModified; }
if (etag) { String_Copy(&reqEtag, etag); }
/* request.Data = data; TODO: Implement this. do we need to copy or expect caller to malloc it? */
Mutex_Lock(http_pendingMutex);
{
req.TimeAdded = DateTime_CurrentUTC_MS();
if (priority) {
HttpRequestList_Prepend(&http_pending, &req);
} else {
HttpRequestList_Append(&http_pending, &req);
}
}
Mutex_Unlock(http_pendingMutex);
Waitable_Signal(http_waitable);
}
void Http_AsyncGetSkin(const String* id, const String* skinName) {
String url; char urlBuffer[STRING_SIZE];
String_InitArray(url, urlBuffer);
if (Utils_IsUrlPrefix(skinName, 0)) {
String_Copy(&url, skinName);
} else {
String_AppendString(&url, &http_skinServer);
String_AppendColorless(&url, skinName);
String_AppendConst(&url, ".png");
}
Http_Add(&url, false, id, REQUEST_TYPE_GET, NULL, NULL, NULL);
}
void Http_AsyncGetData(const String* url, bool priority, const String* id) {
Http_Add(url, priority, id, REQUEST_TYPE_GET, NULL, NULL, NULL);
}
void Http_AsyncGetHeaders(const String* url, bool priority, const String* id) {
Http_Add(url, priority, id, REQUEST_TYPE_HEAD, NULL, NULL, NULL);
}
void AsyncDownloader_PostString(const String* url, bool priority, const String* id, const String* contents) {
Http_Add(url, priority, id, REQUEST_TYPE_GET, NULL, NULL, contents);
}
void Http_AsyncGetDataEx(const String* url, bool priority, const String* id, TimeMS* lastModified, const String* etag) {
Http_Add(url, priority, id, REQUEST_TYPE_GET, lastModified, etag, NULL);
}
void Http_PurgeOldEntriesTask(struct ScheduledTask* task) {
struct HttpRequest* item;
int i;
Mutex_Lock(http_processedMutex);
{
TimeMS now = DateTime_CurrentUTC_MS();
for (i = http_processed.Count - 1; i >= 0; i--) {
item = &http_processed.Requests[i];
if (item->TimeDownloaded + (10 * 1000) >= now) continue;
HttpRequest_Free(item);
HttpRequestList_RemoveAt(&http_processed, i);
}
}
Mutex_Unlock(http_processedMutex);
}
static int HttpRequestList_Find(const String* id, struct HttpRequest* item) {
String reqID;
int i;
for (i = 0; i < http_processed.Count; i++) {
reqID = String_FromRawArray(http_processed.Requests[i].ID);
if (!String_Equals(id, &reqID)) continue;
*item = http_processed.Requests[i];
return i;
}
return -1;
}
bool Http_GetResult(const String* id, struct HttpRequest* item) {
int i;
Mutex_Lock(http_processedMutex);
{
i = HttpRequestList_Find(id, item);
if (i >= 0) HttpRequestList_RemoveAt(&http_processed, i);
}
Mutex_Unlock(http_processedMutex);
return i >= 0;
}
bool Http_GetCurrent(struct HttpRequest* request, int* progress) {
Mutex_Lock(http_curRequestMutex);
{
*request = http_curRequest;
*progress = http_curProgress;
}
Mutex_Unlock(http_curRequestMutex);
return request->ID[0];
}
void Http_ClearPending(void) {
Mutex_Lock(http_pendingMutex);
{
HttpRequestList_Free(&http_pending);
}
Mutex_Unlock(http_pendingMutex);
Waitable_Signal(http_waitable);
}
static void Http_ProcessRequest(struct HttpRequest* request) {
String url = String_FromRawArray(request->URL);
uint64_t beg, end;
uint32_t size, elapsed;
uintptr_t addr;
Platform_Log2("Downloading from %s (type %b)", &url, &request->RequestType);
beg = Stopwatch_Measure();
request->Result = Http_SysDo(request, &http_curProgress);
end = Stopwatch_Measure();
elapsed = Stopwatch_ElapsedMicroseconds(beg, end) / 1000;
Platform_Log3("HTTP: return code %i (http %i), in %i ms",
&request->Result, &request->StatusCode, &elapsed);
if (request->Data) {
size = request->Size;
addr = (uintptr_t)request->Data;
Platform_Log2("HTTP returned data: %i bytes at %x", &size, &addr);
}
}
static void Http_CompleteResult(struct HttpRequest* request) {
struct HttpRequest older;
String id = String_FromRawArray(request->ID);
int index;
request->TimeDownloaded = DateTime_CurrentUTC_MS();
index = HttpRequestList_Find(&id, &older);
if (index >= 0) {
/* very rare case - priority item was inserted, then inserted again (so put before first item), */
/* and both items got downloaded before an external function removed them from the queue */
if (older.TimeAdded > request->TimeAdded) {
HttpRequest_Free(request);
} else {
/* normal case, replace older request */
HttpRequest_Free(&older);
http_processed.Requests[index] = *request;
}
} else {
HttpRequestList_Append(&http_processed, request);
}
}
static void Http_WorkerLoop(void) {
struct HttpRequest request;
bool hasRequest, stop;
for (;;) {
hasRequest = false;
Mutex_Lock(http_pendingMutex);
{
stop = http_terminate;
if (!stop && http_pending.Count) {
request = http_pending.Requests[0];
hasRequest = true;
HttpRequestList_RemoveAt(&http_pending, 0);
}
}
Mutex_Unlock(http_pendingMutex);
if (stop) return;
/* Block until another thread submits a request to do */
if (!hasRequest) {
Platform_LogConst("Going back to sleep...");
Waitable_Wait(http_waitable);
continue;
}
Platform_LogConst("Got something to do!");
Mutex_Lock(http_curRequestMutex);
{
http_curRequest = request;
http_curProgress = ASYNC_PROGRESS_MAKING_REQUEST;
}
Mutex_Unlock(http_curRequestMutex);
Platform_LogConst("Doing it");
/* performing request doesn't need thread safety */
Http_ProcessRequest(&request);
Mutex_Lock(http_processedMutex);
{
Http_CompleteResult(&request);
}
Mutex_Unlock(http_processedMutex);
Mutex_Lock(http_curRequestMutex);
{
http_curRequest.ID[0] = '\0';
http_curProgress = ASYNC_PROGRESS_NOTHING;
}
Mutex_Unlock(http_curRequestMutex);
}
}
/*########################################################################################################################*
*-----------------------------------------------------Http component------------------------------------------------------*
*#########################################################################################################################*/
static void Http_Init(void) {
ScheduledTask_Add(30, Http_PurgeOldEntriesTask);
HttpRequestList_Init(&http_pending);
HttpRequestList_Init(&http_processed);
Http_SysInit();
http_waitable = Waitable_Create();
http_pendingMutex = Mutex_Create();
http_processedMutex = Mutex_Create();
http_curRequestMutex = Mutex_Create();
http_workerThread = Thread_Start(Http_WorkerLoop, false);
}
static void Http_Free(void) {
http_terminate = true;
Http_ClearPending();
Thread_Join(http_workerThread);
HttpRequestList_Free(&http_pending);
HttpRequestList_Free(&http_processed);
Http_SysFree();
Waitable_Free(http_waitable);
Mutex_Free(http_pendingMutex);
Mutex_Free(http_processedMutex);
Mutex_Free(http_curRequestMutex);
}
struct IGameComponent Http_Component = {
Http_Init, /* Init */
Http_Free, /* Free */
Http_ClearPending /* Reset */
};

70
src/Http.h Normal file
View File

@ -0,0 +1,70 @@
#ifndef CC_HTTP_H
#define CC_HTTP_H
#include "Constants.h"
#include "Utils.h"
/* Aysnchronously performs http GET, HEAD, and POST requests.
Typically this is used to download skins, texture packs, etc.
Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
*/
struct IGameComponent;
struct ScheduledTask;
#define URL_MAX_SIZE (STRING_SIZE * 2)
extern struct IGameComponent Http_Component;
/* TODO: Implement these */
extern bool Http_UseCookies;
/* TODO: Connection pooling */
enum HttpRequestType { REQUEST_TYPE_GET, REQUEST_TYPE_HEAD };
enum HttpProgress {
ASYNC_PROGRESS_NOTHING = -3,
ASYNC_PROGRESS_MAKING_REQUEST = -2,
ASYNC_PROGRESS_FETCHING_DATA = -1
};
struct HttpRequest {
char URL[URL_MAX_SIZE]; /* URL data is downloaded from/uploaded to. */
char ID[STRING_SIZE]; /* Unique identifier for this request. */
TimeMS TimeAdded; /* Time this request was added to queue of requests. */
TimeMS TimeDownloaded; /* Time response contents were completely downloaded. */
int StatusCode; /* HTTP status code returned in the response. */
uint32_t ContentLength; /* HTTP content length returned in the response. */
ReturnCode Result; /* 0 on success, otherwise platform-specific error. */
void* Data; /* Contents of the response. (i.e. result data) */
uint32_t Size; /* Size of the contents. */
TimeMS LastModified; /* Time item cached at (if at all) */
char Etag[STRING_SIZE]; /* ETag of cached item (if any) */
uint8_t RequestType; /* Whether to fetch contents or just headers. */
};
/* Frees data from a HTTP request. */
void HttpRequest_Free(struct HttpRequest* request);
/* Aschronously performs a http GET request to download a skin. */
/* If url is a skin, this is the same as Http_AsyncGetData. */
/* If not, instead downloads from http://static.classicube.net/skins/[skinName].png */
void Http_AsyncGetSkin(const String* id, const String* skinName);
/* Asynchronously performs a http GET request. (e.g. to download data) */
void Http_AsyncGetData(const String* url, bool priority, const String* id);
/* Asynchronously performs a http HEAD request. (e.g. to get Content-Length header) */
void Http_AsyncGetHeaders(const String* url, bool priority, const String* id);
/* Asynchronously performs a http POST request. (e.g. to submit data) */
/* NOTE: data and size MUST PERSIST until the request is completed. */
/* TODO: maybe we shouldn't force that.. instead mem alloc data and size */
void Http_AsyncPostData(const String* url, bool priority, const String* id, const void* data, const uint32_t size);
/* Asynchronously performs a http GET request. (e.g. to download data) */
/* Also sets the If-Modified-Since and If-None-Match headers. (if not NULL) */
void Http_AsyncGetDataEx(const String* url, bool priority, const String* id, TimeMS* lastModified, const String* etag);
/* Attempts to retrieve a fully completed request. */
/* NOTE: You MUST also check Result/StatusCode, and check Size is > 0. */
/* (because a completed request may not have completed successfully) */
bool Http_GetResult(const String* id, struct HttpRequest* item);
/* Retrieves information about the request currently being processed. */
bool Http_GetCurrent(struct HttpRequest* request, int* progress);
/* Clears the list of pending requests. */
void Http_ClearPending(void);
void Http_PurgeOldEntriesTask(struct ScheduledTask* task);
#endif

View File

@ -949,7 +949,7 @@ static void ResourcesScreen_Download(void* w, int x, int y) {
static void ResourcesScreen_Next(void* w, int x, int y) {
const static String optionsTxt = String_FromConst("options.txt");
AsyncDownloader_Clear();
Http_ClearPending();
if (File_Exists(&optionsTxt)) {
Launcher_SetScreen(MainScreen_MakeInstance());
@ -1036,7 +1036,7 @@ static void ResourcesScreen_SetStatus(const String* str) {
LWidget_Draw(w);
}
static void ResourcesScreen_UpdateStatus(struct AsyncRequest* req) {
static void ResourcesScreen_UpdateStatus(struct HttpRequest* req) {
String str; char strBuffer[STRING_SIZE];
String id;
BitmapCol boxCol = BITMAPCOL_CONST(120, 85, 151, 255);
@ -1053,10 +1053,10 @@ static void ResourcesScreen_UpdateStatus(struct AsyncRequest* req) {
}
static void ResourcesScreen_UpdateProgress(struct ResourcesScreen* s) {
struct AsyncRequest req;
struct HttpRequest req;
int progress;
if (!AsyncDownloader_GetCurrent(&req, &progress)) return;
if (!Http_GetCurrent(&req, &progress)) return;
ResourcesScreen_UpdateStatus(&req);
/* making request still, haven't started download yet */
if (progress < 0 || progress > 100) return;
@ -1386,9 +1386,9 @@ static void UpdatesScreen_CheckTick(struct UpdatesScreen* s) {
static void UpdatesScreen_UpdateProgress(struct UpdatesScreen* s, struct LWebTask* task) {
String str; char strBuffer[STRING_SIZE];
String identifier;
struct AsyncRequest item;
struct HttpRequest item;
int progress;
if (!AsyncDownloader_GetCurrent(&item, &progress)) return;
if (!Http_GetCurrent(&item, &progress)) return;
identifier = String_FromRawArray(item.ID);
if (!String_Equals(&identifier, &task->Identifier)) return;

View File

@ -1,6 +1,6 @@
#ifndef CC_LSCREENS_H
#define CC_LSCREENS_H
#include "AsyncDownloader.h"
#include "Http.h"
#include "String.h"
#include "Input.h"
/* Implements screens/menus for the launcher.

View File

@ -206,11 +206,11 @@ static void LWebTask_Reset(struct LWebTask* task) {
}
void LWebTask_Tick(struct LWebTask* task) {
struct AsyncRequest req;
struct HttpRequest req;
int delta;
if (task->Completed) return;
if (!AsyncDownloader_Get(&task->Identifier, &req)) return;
if (!Http_GetResult(&task->Identifier, &req)) return;
delta = (int)(DateTime_CurrentUTC_MS() - task->Start);
Platform_Log2("%s took %i", &task->Identifier, &delta);
@ -221,7 +221,7 @@ void LWebTask_Tick(struct LWebTask* task) {
task->Completed = true;
task->Success = !task->Res && req.Data && req.Size;
if (task->Success) task->Handle(req.Data, req.Size);
ASyncRequest_Free(&req);
HttpRequest_Free(&req);
}
@ -249,7 +249,7 @@ void GetTokenTask_Run(void) {
String_InitArray(GetTokenTask.Token, tokenBuffer);
GetTokenTask.Base.Identifier = id;
AsyncDownloader_GetData(&url, false, &id);
Http_AsyncGetData(&url, false, &id);
GetTokenTask.Base.Handle = GetTokenTask_Handle;
}
@ -282,7 +282,7 @@ void SignInTask_Run(const String* user, const String* pass) {
String_Copy(&SignInTask.Username, user);
SignInTask.Base.Identifier = id;
AsyncDownloader_GetData(&url, false, &id);
Http_AsyncGetData(&url, false, &id);
SignInTask.Base.Handle = SignInTask_Handle;
}
// NOTE: Remember to add &c for errors here too
@ -353,7 +353,7 @@ void FetchServerTask_Run(const String* hash) {
String_Format1(&url, "https://www.classicube.net/api/server/%s", hash);
FetchServerTask.Base.Identifier = id;
AsyncDownloader_GetData(&url, false, &id);
Http_AsyncGetData(&url, false, &id);
FetchServerTask.Base.Handle = FetchServerTask_Handle;
}
@ -396,7 +396,7 @@ void FetchServersTask_Run(void) {
FetchServersTask.NumServers = 0;
FetchServersTask.Base.Identifier = id;
AsyncDownloader_GetData(&url, false, &id);
Http_AsyncGetData(&url, false, &id);
FetchServersTask.Base.Handle = FetchServersTask_Handle;
}
@ -444,7 +444,7 @@ void CheckUpdateTask_Run(void) {
String_InitArray(CheckUpdateTask.LatestRelease, relVersionBuffer);
CheckUpdateTask.Base.Identifier = id;
AsyncDownloader_GetData(&url, false, &id);
Http_AsyncGetData(&url, false, &id);
CheckUpdateTask.Base.Handle = CheckUpdateTask_Handle;
}
@ -494,7 +494,7 @@ void FetchUpdateTask_Run(bool release, bool d3d9) {
FetchUpdateTask.Timestamp = release ? CheckUpdateTask.RelTimestamp : CheckUpdateTask.DevTimestamp;
FetchUpdateTask.Base.Identifier = id;
AsyncDownloader_GetData(&url, false, &id);
Http_AsyncGetData(&url, false, &id);
FetchUpdateTask.Base.Handle = FetchUpdateTask_Handle;
}

View File

@ -1,6 +1,6 @@
#ifndef CC_LWEB_H
#define CC_LWEB_H
#include "AsyncDownloader.h"
#include "Http.h"
#include "String.h"
/* Implements asynchronous web tasks for the launcher.
Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3

View File

@ -11,7 +11,7 @@
#include "Window.h"
#include "GameStructs.h"
#include "Event.h"
#include "AsyncDownloader.h"
#include "Http.h"
#include "ExtMath.h"
#include "Funcs.h"
#include "Logger.h"
@ -234,8 +234,8 @@ void Launcher_Run(void) {
Launcher_Framebuffer.Height = Game_Height;
Window_InitRaw(&Launcher_Framebuffer);
AsyncDownloader_Cookies = true;
AsyncDownloader_Component.Init();
Http_UseCookies = true;
Http_Component.Init();
Resources_CheckExistence();
CheckUpdateTask_Run();

View File

@ -14,7 +14,7 @@
#include "ExtMath.h"
#include "Window.h"
#include "Camera.h"
#include "AsyncDownloader.h"
#include "Http.h"
#include "Block.h"
#include "World.h"
#include "Formats.h"
@ -3251,11 +3251,11 @@ static void TexPackOverlay_NoClick(void* screen, void* widget) {
}
static void TexPackOverlay_Render(void* screen, double delta) {
struct AsyncRequest item;
struct HttpRequest item;
struct TexPackOverlay* s = screen;
MenuScreen_Render(s, delta);
if (!AsyncDownloader_Get(&s->Identifier, &item)) return;
if (!Http_GetResult(&s->Identifier, &item)) return;
s->ContentLength = item.ContentLength;
if (!s->ContentLength) return;
@ -3326,7 +3326,7 @@ struct Screen* TexPackOverlay_MakeInstance(const String* url) {
String_Format1(&s->Identifier, "CL_%s", url);
s->ContentLength = 0;
AsyncDownloader_GetContentLength(url, true, &s->Identifier);
Http_AsyncGetHeaders(url, true, &s->Identifier);
s->VTABLE = &TexPackOverlay_VTABLE;
return (struct Screen*)s;
}

View File

@ -17,7 +17,7 @@
#include "Model.h"
#include "Funcs.h"
#include "Lighting.h"
#include "AsyncDownloader.h"
#include "Http.h"
#include "Drawer2D.h"
#include "Logger.h"
#include "TexturePack.h"
@ -243,7 +243,7 @@ static void WoM_CheckMotd(void) {
applied in the new world if the async 'get env request' didn't complete before the old world was unloaded */
wom_counter++;
WoM_UpdateIdentifier();
AsyncDownloader_GetData(&url, true, &wom_identifier);
Http_AsyncGetData(&url, true, &wom_identifier);
wom_sendId = true;
}
@ -268,7 +268,7 @@ static PackedCol WoM_ParseCol(const String* value, PackedCol defaultCol) {
return col;
}
static void WoM_ParseConfig(struct AsyncRequest* item) {
static void WoM_ParseConfig(struct HttpRequest* item) {
String line; char lineBuffer[STRING_SIZE * 2];
struct Stream mem;
String key, value;
@ -308,11 +308,11 @@ static void WoM_Reset(void) {
}
static void WoM_Tick(void) {
struct AsyncRequest item;
if (!AsyncDownloader_Get(&wom_identifier, &item)) return;
struct HttpRequest item;
if (!Http_GetResult(&wom_identifier, &item)) return;
if (item.Data) WoM_ParseConfig(&item);
ASyncRequest_Free(&item);
HttpRequest_Free(&item);
}

View File

@ -4,7 +4,7 @@
#include "ExtMath.h"
#include "Drawer2D.h"
#include "Funcs.h"
#include "AsyncDownloader.h"
#include "Http.h"
#include "Bitmap.h"
#include "Window.h"
@ -1320,276 +1320,6 @@ ReturnCode Socket_Poll(SocketHandle socket, int mode, bool* success) {
#endif
/*########################################################################################################################*
*----------------------------------------------------------Http-----------------------------------------------------------*
*#########################################################################################################################*/
#ifdef CC_BUILD_WIN
static HINTERNET hInternet;
/* TODO: Test last modified and etag even work */
#define FLAG_STATUS HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER
#define FLAG_LENGTH HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER
#define FLAG_LASTMOD HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME
void Http_Init(void) {
/* TODO: Should we use INTERNET_OPEN_TYPE_PRECONFIG instead? */
hInternet = InternetOpenA(GAME_APP_NAME, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (!hInternet) Logger_Abort2(GetLastError(), "Failed to init WinINet");
}
static ReturnCode Http_Make(struct AsyncRequest* req, HINTERNET* handle) {
String url = String_FromRawArray(req->URL);
char urlStr[URL_MAX_SIZE + 1];
Mem_Copy(urlStr, url.buffer, url.length);
urlStr[url.length] = '\0';
char headersBuffer[STRING_SIZE * 3];
String headers = String_FromArray(headersBuffer);
/* https://stackoverflow.com/questions/25308488/c-wininet-custom-http-headers */
if (req->Etag[0] || req->LastModified) {
if (req->LastModified) {
String_AppendConst(&headers, "If-Modified-Since: ");
DateTime_HttpDate(req->LastModified, &headers);
String_AppendConst(&headers, "\r\n");
}
if (req->Etag[0]) {
String etag = String_FromRawArray(req->Etag);
String_AppendConst(&headers, "If-None-Match: ");
String_AppendString(&headers, &etag);
String_AppendConst(&headers, "\r\n");
}
String_AppendConst(&headers, "\r\n\r\n");
headers.buffer[headers.length] = '\0';
} else { headers.buffer = NULL; }
*handle = InternetOpenUrlA(hInternet, urlStr, headers.buffer, headers.length,
INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD, 0);
return *handle ? 0 : GetLastError();
}
static ReturnCode Http_GetHeaders(struct AsyncRequest* req, HINTERNET handle) {
DWORD len;
len = sizeof(DWORD);
if (!HttpQueryInfoA(handle, FLAG_STATUS, &req->StatusCode, &len, NULL)) return GetLastError();
len = sizeof(DWORD);
HttpQueryInfoA(handle, FLAG_LENGTH, &req->ContentLength, &len, NULL);
SYSTEMTIME sysTime;
len = sizeof(SYSTEMTIME);
if (HttpQueryInfoA(handle, FLAG_LASTMOD, &sysTime, &len, NULL)) {
struct DateTime time;
Platform_FromSysTime(&time, &sysTime);
req->LastModified = DateTime_TotalMs(&time);
}
String etag = String_ClearedArray(req->Etag);
len = etag.capacity;
HttpQueryInfoA(handle, HTTP_QUERY_ETAG, etag.buffer, &len, NULL);
return 0;
}
static ReturnCode Http_GetData(struct AsyncRequest* req, HINTERNET handle, volatile int* progress) {
uint8_t* buffer;
uint32_t size, totalRead;
uint32_t read, avail;
bool success;
*progress = 0;
size = req->ContentLength ? req->ContentLength : 1;
buffer = Mem_Alloc(size, 1, "http get data");
totalRead = 0;
req->Data = buffer;
req->Size = 0;
for (;;) {
if (!InternetQueryDataAvailable(handle, &avail, 0, 0)) break;
if (!avail) break;
/* expand if buffer is too small (some servers don't give content length) */
if (totalRead + avail > size) {
size = totalRead + avail;
buffer = Mem_Realloc(buffer, size, 1, "http inc data");
req->Data = buffer;
}
success = InternetReadFile(handle, &buffer[totalRead], avail, &read);
if (!success) { Mem_Free(buffer); return GetLastError(); }
if (!read) break;
totalRead += read;
if (req->ContentLength) *progress = (int)(100.0f * totalRead / size);
req->Size += read;
}
*progress = 100;
return 0;
}
ReturnCode Http_Do(struct AsyncRequest* req, volatile int* progress) {
HINTERNET handle;
ReturnCode res = Http_Make(req, &handle);
if (res) return res;
*progress = ASYNC_PROGRESS_FETCHING_DATA;
res = Http_GetHeaders(req, handle);
if (res) { InternetCloseHandle(handle); return res; }
if (req->RequestType != REQUEST_TYPE_CONTENT_LENGTH && req->StatusCode == 200) {
res = Http_GetData(req, handle, progress);
if (res) { InternetCloseHandle(handle); return res; }
}
return InternetCloseHandle(handle) ? 0 : GetLastError();
}
ReturnCode Http_Free(void) {
return InternetCloseHandle(hInternet) ? 0 : GetLastError();
}
#endif
#ifdef CC_BUILD_POSIX
CURL* curl;
void Http_Init(void) {
CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
if (res) Logger_Abort2(res, "Failed to init curl");
curl = curl_easy_init();
if (!curl) Logger_Abort("Failed to init easy curl");
}
static int Http_Progress(int* progress, double total, double received, double a, double b) {
if (total == 0) return 0;
*progress = (int)(100 * received / total);
return 0;
}
static struct curl_slist* Http_Make(struct AsyncRequest* req) {
String tmp; char buffer[STRING_SIZE + 1];
struct curl_slist* list = NULL;
String etag;
if (req->Etag[0]) {
String_InitArray_NT(tmp, buffer);
String_AppendConst(&tmp, "If-None-Match: ");
etag = String_FromRawArray(req->Etag);
String_AppendString(&tmp, &etag);
tmp.buffer[tmp.length] = '\0';
list = curl_slist_append(list, tmp.buffer);
}
if (req->LastModified) {
String_InitArray_NT(tmp, buffer);
String_AppendConst(&tmp, "Last-Modified: ");
DateTime_HttpDate(req->LastModified, &tmp);
tmp.buffer[tmp.length] = '\0';
list = curl_slist_append(list, tmp.buffer);
}
return list;
}
static size_t Http_GetHeaders(char *buffer, size_t size, size_t nitems, struct AsyncRequest* req) {
String tmp; char tmpBuffer[STRING_SIZE + 1];
String line, name, value;
time_t time;
if (size != 1) return size * nitems; /* non byte header */
line = String_Init(buffer, nitems, nitems);
if (!String_UNSAFE_Separate(&line, ':', &name, &value)) return nitems;
/* value usually has \r\n at end */
if (value.length && value.buffer[value.length - 1] == '\n') value.length--;
if (value.length && value.buffer[value.length - 1] == '\r') value.length--;
if (!value.length) return nitems;
if (String_CaselessEqualsConst(&name, "ETag")) {
tmp = String_ClearedArray(req->Etag);
String_AppendString(&tmp, &value);
} else if (String_CaselessEqualsConst(&name, "Content-Length")) {
Convert_ParseInt(&value, &req->ContentLength);
/* TODO: Fix when ContentLength isn't RequestSize */
req->Size = req->ContentLength;
} else if (String_CaselessEqualsConst(&name, "Last-Modified")) {
String_InitArray_NT(tmp, tmpBuffer);
String_AppendString(&tmp, &value);
tmp.buffer[tmp.length] = '\0';
time = curl_getdate(tmp.buffer, NULL);
if (time == -1) return nitems;
req->LastModified = (uint64_t)time * 1000 + UNIX_EPOCH;
}
return nitems;
}
static size_t Http_GetData(char *buffer, size_t size, size_t nitems, struct AsyncRequest* req) {
uint32_t total, left;
uint8_t* dst;
total = req->Size;
if (!total || req->RequestType == REQUEST_TYPE_CONTENT_LENGTH) return 0;
if (!req->Data) req->Data = Mem_Alloc(total, 1, "http get data");
/* reuse Result as an offset */
left = total - req->Result;
left = min(left, nitems);
dst = (uint8_t*)req->Data + req->Result;
Mem_Copy(dst, buffer, left);
req->Result += left;
return nitems;
}
ReturnCode Http_Do(struct AsyncRequest* req, volatile int* progress) {
struct curl_slist* list;
String url = String_FromRawArray(req->URL);
char urlStr[600];
long status = 0;
CURLcode res;
Platform_ConvertString(urlStr, &url);
curl_easy_reset(curl);
list = Http_Make(req);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_setopt(curl, CURLOPT_URL, urlStr);
curl_easy_setopt(curl, CURLOPT_USERAGENT, GAME_APP_NAME);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, Http_Progress);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, progress);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, Http_GetHeaders);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, req);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Http_GetData);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, req);
*progress = ASYNC_PROGRESS_FETCHING_DATA;
res = curl_easy_perform(curl);
*progress = 100;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
req->StatusCode = status;
curl_slist_free_all(list);
return res;
}
ReturnCode Http_Free(void) {
curl_easy_cleanup(curl);
curl_global_cleanup();
return 0;
}
#endif
/*########################################################################################################################*
*----------------------------------------------------------Audio----------------------------------------------------------*
*#########################################################################################################################*/

View File

@ -6,7 +6,7 @@
Copyright 2017 ClassicalSharp | Licensed under BSD-3
*/
struct DrawTextArgs;
struct AsyncRequest;
struct HttpRequest;
struct DateTime;
enum Socket_PollMode { SOCKET_POLL_READ, SOCKET_POLL_WRITE };
@ -221,14 +221,6 @@ ReturnCode Socket_Close(SocketHandle socket);
/* NOTE: A socket is considered writable once it has finished connecting. */
ReturnCode Socket_Poll(SocketHandle socket, int mode, bool* success);
/* Initalises the platform specific http library state. */
void Http_Init(void);
/* Performs a http request, setting progress as data is received. */
/* NOTE: NOT thread safe - you should ALWAYS use AsyncDownloader for making requests. */
ReturnCode Http_Do(struct AsyncRequest* req, volatile int* progress);
/* Frees the platform specific http library state. */
ReturnCode Http_Free(void);
#define AUDIO_MAX_BUFFERS 4
/* Information about a source of audio. */
struct AudioFormat { uint16_t Channels, BitsPerSample; int SampleRate; };

View File

@ -87,9 +87,9 @@ int main(int argc, char** argv) {
argsCount = Platform_GetCommandLineArgs(argc, argv, args);
/* NOTE: Make sure to comment this out before pushing a commit */
/* String rawArgs = String_FromConst("UnknownShadow200 fffff 127.0.0.1 25565"); */
String rawArgs = String_FromConst("UnknownShadow200 fffff 127.0.0.1 25565");
/* String rawArgs = String_FromConst("UnknownShadow200"); */
/* argsCount = String_UNSAFE_Split(&rawArgs, ' ', args, 4); */
argsCount = String_UNSAFE_Split(&rawArgs, ' ', args, 4);
if (argsCount == 0) {
Launcher_Run();

View File

@ -651,7 +651,7 @@ static void SoundPatcher_DecodeAudio(struct Stream* s, struct VorbisState* ctx)
Mem_Free(samples);
}
static void SoundPatcher_Save(struct ResourceSound* sound, struct AsyncRequest* req) {
static void SoundPatcher_Save(struct ResourceSound* sound, struct HttpRequest* req) {
String path; char pathBuffer[STRING_SIZE];
uint8_t buffer[OGG_BUFFER_SIZE];
struct Stream src, ogg, dst;
@ -675,7 +675,7 @@ static void SoundPatcher_Save(struct ResourceSound* sound, struct AsyncRequest*
if (res) Logger_Warn(res, "closing .wav file");
}
static void MusicPatcher_Save(struct ResourceMusic* music, struct AsyncRequest* req) {
static void MusicPatcher_Save(struct ResourceMusic* music, struct HttpRequest* req) {
String path; char pathBuffer[STRING_SIZE];
ReturnCode res;
@ -701,7 +701,7 @@ CC_NOINLINE static void Fetcher_DownloadAudio(const char* name, const char* hash
String_InitArray(url, urlBuffer);
String_Format3(&url, "http://resources.download.minecraft.net/%r%r/%c",
&hash[0], &hash[1], hash);
AsyncDownloader_GetData(&url, false, &id);
Http_AsyncGetData(&url, false, &id);
}
void Fetcher_Run(void) {
@ -721,7 +721,7 @@ void Fetcher_Run(void) {
id = String_FromReadonly(Resources_Files[i].Name);
url = String_FromReadonly(Resources_Files[i].Url);
AsyncDownloader_GetData(&url, false, &id);
Http_AsyncGetData(&url, false, &id);
}
for (i = 0; i < Array_Elems(Resources_Music); i++) {
@ -739,8 +739,8 @@ static void Fetcher_Finish(void) {
Fetcher_Working = false;
}
CC_NOINLINE static bool Fetcher_Get(const String* id, struct AsyncRequest* req) {
if (!AsyncDownloader_Get(id, req)) return false;
CC_NOINLINE static bool Fetcher_Get(const String* id, struct HttpRequest* req) {
if (!Http_GetResult(id, req)) return false;
if (req->Result) {
Fetcher_Error = req->Result;
@ -762,7 +762,7 @@ CC_NOINLINE static bool Fetcher_Get(const String* id, struct AsyncRequest* req)
static void Fetcher_CheckFile(struct ResourceFile* file) {
String id = String_FromReadonly(file->Name);
struct AsyncRequest req;
struct HttpRequest req;
if (!Fetcher_Get(&id, &req)) return;
file->Downloaded = true;
@ -773,21 +773,21 @@ static void Fetcher_CheckFile(struct ResourceFile* file) {
static void Fetcher_CheckMusic(struct ResourceMusic* music) {
String id = String_FromReadonly(music->Name);
struct AsyncRequest req;
struct HttpRequest req;
if (!Fetcher_Get(&id, &req)) return;
music->Downloaded = true;
MusicPatcher_Save(music, &req);
ASyncRequest_Free(&req);
HttpRequest_Free(&req);
}
static void Fetcher_CheckSound(struct ResourceSound* sound) {
String id = String_FromReadonly(sound->Name);
struct AsyncRequest req;
struct HttpRequest req;
if (!Fetcher_Get(&id, &req)) return;
SoundPatcher_Save(sound, &req);
ASyncRequest_Free(&req);
HttpRequest_Free(&req);
}
/* TODO: Implement this.. */

View File

@ -15,7 +15,7 @@
#include "ExtMath.h"
#include "Window.h"
#include "Camera.h"
#include "AsyncDownloader.h"
#include "Http.h"
#include "Block.h"
#include "Menus.h"
#include "World.h"
@ -816,12 +816,12 @@ static void ChatScreen_SetInitialMessages(struct ChatScreen* s) {
static void ChatScreen_CheckOtherStatuses(struct ChatScreen* s) {
const static String texPack = String_FromConst("texturePack");
String str; char strBuffer[STRING_SIZE];
struct AsyncRequest request;
struct HttpRequest request;
int progress;
bool hasRequest;
String identifier;
hasRequest = AsyncDownloader_GetCurrent(&request, &progress);
hasRequest = Http_GetCurrent(&request, &progress);
identifier = String_FromRawArray(request.ID);
/* Is terrain / texture pack currently being downloaded? */

View File

@ -5,7 +5,7 @@
#include "Chat.h"
#include "Block.h"
#include "Event.h"
#include "AsyncDownloader.h"
#include "Http.h"
#include "Funcs.h"
#include "Entity.h"
#include "Graphics.h"
@ -74,13 +74,13 @@ void ServerConnection_DownloadTexturePack(const String* url) {
}
TexturePack_ExtractCurrent(url);
AsyncDownloader_GetDataEx(url, true, &texPack, &lastModified, &etag);
Http_AsyncGetDataEx(url, true, &texPack, &lastModified, &etag);
}
void ServerConnection_CheckAsyncResources(void) {
const static String texPack = String_FromConst("texturePack");
struct AsyncRequest item;
if (!AsyncDownloader_Get(&texPack, &item)) return;
struct HttpRequest item;
if (!Http_GetResult(&texPack, &item)) return;
if (item.Data) {
TexturePack_Extract_Req(&item);

View File

@ -6,7 +6,7 @@
#include "Graphics.h"
#include "Event.h"
#include "Game.h"
#include "AsyncDownloader.h"
#include "Http.h"
#include "Platform.h"
#include "Deflate.h"
#include "Stream.h"
@ -706,7 +706,7 @@ void TexturePack_ExtractCurrent(const String* url) {
}
}
void TexturePack_Extract_Req(struct AsyncRequest* item) {
void TexturePack_Extract_Req(struct HttpRequest* item) {
String url, etag;
void* data; uint32_t len;
struct Stream mem;
@ -729,5 +729,5 @@ void TexturePack_Extract_Req(struct AsyncRequest* item) {
: TexturePack_ExtractZip(&mem);
if (res) Logger_Warn2(res, png ? "decoding" : "extracting", &url);
ASyncRequest_Free(item);
HttpRequest_Free(item);
}

View File

@ -10,7 +10,7 @@
Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 */
struct Stream;
struct AsyncRequest;
struct HttpRequest;
struct IGameComponent;
extern struct IGameComponent Animations_Component;
@ -97,5 +97,5 @@ void TextureCache_SetLastModified(const String* url, const TimeMS* lastModified)
void TexturePack_ExtractZip_File(const String* filename);
void TexturePack_ExtractDefault(void);
void TexturePack_ExtractCurrent(const String* url);
void TexturePack_Extract_Req(struct AsyncRequest* item);
void TexturePack_Extract_Req(struct HttpRequest* item);
#endif