mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-16 11:06:06 -04:00
Show explanation message for http related errors (like server connecting timeout, SSL error), make showing http error messages more consistent in launcher
This commit is contained in:
parent
d78b31a941
commit
3819e93dad
@ -401,9 +401,6 @@
|
||||
<ClCompile Include="Widgets.c">
|
||||
<Filter>Source Files\2D</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Input.c">
|
||||
<Filter>Source Files\Platform</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils.c">
|
||||
<Filter>Source Files\Utils</Filter>
|
||||
</ClCompile>
|
||||
@ -548,5 +545,8 @@
|
||||
<ClCompile Include="Protocol.c">
|
||||
<Filter>Source Files\Network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Input.c">
|
||||
<Filter>Source Files\Game</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
25
src/Http.c
25
src/Http.c
@ -15,6 +15,12 @@
|
||||
#define _UNICODE
|
||||
#endif
|
||||
|
||||
#ifdef UNICODE
|
||||
#define Platform_DecodeString(dst, src, len) String_AppendUtf16(dst, (Codepoint*)(src), (len) * 2)
|
||||
#else
|
||||
#define Platform_DecodeString(dst, src, len) String_DecodeCP1252(dst, (uint8_t*)(src), len)
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#elif defined CC_BUILD_WEB
|
||||
@ -252,6 +258,7 @@ static void Http_ParseHeader(struct HttpRequest* req, const String* line) {
|
||||
static void Http_SysInit(void) { }
|
||||
static void Http_SysFree(void) { }
|
||||
static void Http_DownloadAsync(struct HttpRequest* req);
|
||||
bool Http_DescribeError(ReturnCode res, String* dst) { return false; }
|
||||
|
||||
static void Http_DownloadNextAsync(void) {
|
||||
struct HttpRequest req;
|
||||
@ -400,6 +407,16 @@ static ReturnCode HttpCache_Lookup(struct HttpCacheEntry* e) {
|
||||
return HttpCache_Insert(i, e);
|
||||
}
|
||||
|
||||
bool Http_DescribeError(ReturnCode res, String* dst) {
|
||||
TCHAR chars[600];
|
||||
res = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
GetModuleHandle(TEXT("wininet.dll")), res, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), chars, 600, NULL);
|
||||
if (!res) return false;
|
||||
|
||||
Platform_DecodeString(dst, chars, res);
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
@ -548,6 +565,14 @@ static void Http_SysFree(void) {
|
||||
#elif defined CC_BUILD_CURL
|
||||
static CURL* curl;
|
||||
|
||||
bool Http_DescribeError(ReturnCode res, String* dst) {
|
||||
const char* err = curl_easy_strerror(res);
|
||||
if (!err) return false;
|
||||
|
||||
String_AppendConst(dst, err);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Http_SysInit(void) {
|
||||
CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
if (res) Logger_Abort2(res, "Failed to init curl");
|
||||
|
@ -62,6 +62,8 @@ void Http_UrlEncodeUtf8(String* dst, const String* src);
|
||||
/* Formats a date for inclusion in HTTP headers. */
|
||||
/* NOTE: Time is always assumed as being UTC. */
|
||||
void Http_FormatDate(TimeMS ms, String* str);
|
||||
/* Outputs more detailed information about errors with http requests. */
|
||||
bool Http_DescribeError(ReturnCode res, String* dst);
|
||||
|
||||
/* Attempts to retrieve a fully completed request. */
|
||||
/* NOTE: You MUST also check Result/StatusCode, and check Size is > 0. */
|
||||
|
@ -692,17 +692,12 @@ CC_NOINLINE static void MainScreen_GetResume(struct ResumeInfo* info, bool full)
|
||||
CC_NOINLINE static void MainScreen_Error(struct LWebTask* task, const char* action) {
|
||||
String str; char strBuffer[STRING_SIZE];
|
||||
struct MainScreen* s = &MainScreen_Instance;
|
||||
|
||||
String_InitArray(str, strBuffer);
|
||||
s->SigningIn = false;
|
||||
|
||||
if (task->Status) {
|
||||
String_Format1(&str, "&cclassicube.net returned: %i error", &task->Status);
|
||||
} else {
|
||||
String_Format1(&str, "&cError %i when connecting to classicube.net", &task->Res);
|
||||
}
|
||||
LWebTask_DisplayError(task, action, &str);
|
||||
LLabel_SetText(&s->LblStatus, &str);
|
||||
LWidget_Redraw(&s->LblStatus);
|
||||
s->SigningIn = false;
|
||||
}
|
||||
|
||||
static void MainScreen_Login(void* w, int x, int y) {
|
||||
@ -886,7 +881,7 @@ static void MainScreen_TickGetToken(struct MainScreen* s) {
|
||||
if (GetTokenTask.Base.Success) {
|
||||
SignInTask_Run(&s->IptUsername.Text, &s->IptPassword.Text);
|
||||
} else {
|
||||
MainScreen_Error(&GetTokenTask.Base, "sign in");
|
||||
MainScreen_Error(&GetTokenTask.Base, "signing in");
|
||||
}
|
||||
}
|
||||
|
||||
@ -910,7 +905,7 @@ static void MainScreen_TickSignIn(struct MainScreen* s) {
|
||||
LLabel_SetText(&s->LblStatus, &fetchMsg);
|
||||
LWidget_Redraw(&s->LblStatus);
|
||||
} else {
|
||||
MainScreen_Error(&SignInTask.Base, "sign in");
|
||||
MainScreen_Error(&SignInTask.Base, "signing in");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1164,7 +1159,7 @@ static void ServersScreen_Connect(void* w, int x, int y) {
|
||||
String* hash = &ServersScreen_Instance.IptHash.Text;
|
||||
|
||||
if (!hash->length && table->RowsCount) {
|
||||
hash = <able_Get(table->TopRow)->Hash;
|
||||
hash = <able_Get(table->TopRow)->hash;
|
||||
}
|
||||
Launcher_ConnectToServer(hash);
|
||||
}
|
||||
@ -1217,7 +1212,7 @@ static void ServersScreen_ReloadServers(struct ServersScreen* s) {
|
||||
LTable_Sort(&s->Table);
|
||||
|
||||
for (i = 0; i < FetchServersTask.NumServers; i++) {
|
||||
FetchFlagsTask_Add(&FetchServersTask.Servers[i].Country);
|
||||
FetchFlagsTask_Add(&FetchServersTask.Servers[i].country);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1521,14 +1516,17 @@ static void UpdatesScreen_UpdateProgress(struct UpdatesScreen* s, struct LWebTas
|
||||
}
|
||||
|
||||
static void UpdatesScreen_FetchTick(struct UpdatesScreen* s) {
|
||||
static const String failedMsg = String_FromConst("&cFailed to fetch update");
|
||||
String str; char strBuffer[STRING_SIZE];
|
||||
if (!FetchUpdateTask.Base.Working) return;
|
||||
|
||||
LWebTask_Tick(&FetchUpdateTask.Base);
|
||||
UpdatesScreen_UpdateProgress(s, &FetchUpdateTask.Base);
|
||||
if (!FetchUpdateTask.Base.Completed) return;
|
||||
|
||||
if (!FetchUpdateTask.Base.Success) {
|
||||
LLabel_SetText(&s->LblStatus, &failedMsg);
|
||||
String_InitArray(str, strBuffer);
|
||||
LWebTask_DisplayError(&FetchUpdateTask.Base, "fetching update", &str);
|
||||
LLabel_SetText(&s->LblStatus, &str);
|
||||
LWidget_Redraw(&s->LblStatus);
|
||||
} else {
|
||||
/* WebTask handles saving of ClassiCube.update for us */
|
||||
|
148
src/LWeb.c
148
src/LWeb.c
@ -14,7 +14,7 @@
|
||||
#define TOKEN_FALSE 3
|
||||
#define TOKEN_NULL 4
|
||||
/* Consumes n characters from the JSON stream */
|
||||
#define JsonContext_Consume(ctx, n) ctx->Cur += n; ctx->Left -= n;
|
||||
#define JsonContext_Consume(ctx, n) ctx->cur += n; ctx->left -= n;
|
||||
|
||||
static const String strTrue = String_FromConst("true");
|
||||
static const String strFalse = String_FromConst("false");
|
||||
@ -30,10 +30,10 @@ static bool Json_IsNumber(char c) {
|
||||
|
||||
static bool Json_ConsumeConstant(struct JsonContext* ctx, const String* value) {
|
||||
int i;
|
||||
if (value->length > ctx->Left) return false;
|
||||
if (value->length > ctx->left) return false;
|
||||
|
||||
for (i = 0; i < value->length; i++) {
|
||||
if (ctx->Cur[i] != value->buffer[i]) return false;
|
||||
if (ctx->cur[i] != value->buffer[i]) return false;
|
||||
}
|
||||
|
||||
JsonContext_Consume(ctx, value->length);
|
||||
@ -42,10 +42,10 @@ static bool Json_ConsumeConstant(struct JsonContext* ctx, const String* value) {
|
||||
|
||||
static int Json_ConsumeToken(struct JsonContext* ctx) {
|
||||
char c;
|
||||
for (; ctx->Left && Json_IsWhitespace(*ctx->Cur); ) { JsonContext_Consume(ctx, 1); }
|
||||
if (!ctx->Left) return TOKEN_NONE;
|
||||
for (; ctx->left && Json_IsWhitespace(*ctx->cur); ) { JsonContext_Consume(ctx, 1); }
|
||||
if (!ctx->left) return TOKEN_NONE;
|
||||
|
||||
c = *ctx->Cur;
|
||||
c = *ctx->cur;
|
||||
if (c == '{' || c == '}' || c == '[' || c == ']' || c == ',' || c == '"' || c == ':') {
|
||||
JsonContext_Consume(ctx, 1); return c;
|
||||
}
|
||||
@ -64,8 +64,8 @@ static int Json_ConsumeToken(struct JsonContext* ctx) {
|
||||
|
||||
static String Json_ConsumeNumber(struct JsonContext* ctx) {
|
||||
int len = 0;
|
||||
for (; ctx->Left && Json_IsNumber(*ctx->Cur); len++) { JsonContext_Consume(ctx, 1); }
|
||||
return String_Init(ctx->Cur - len, len, len);
|
||||
for (; ctx->left && Json_IsNumber(*ctx->cur); len++) { JsonContext_Consume(ctx, 1); }
|
||||
return String_Init(ctx->cur - len, len, len);
|
||||
}
|
||||
|
||||
static void Json_ConsumeString(struct JsonContext* ctx, String* str) {
|
||||
@ -73,23 +73,23 @@ static void Json_ConsumeString(struct JsonContext* ctx, String* str) {
|
||||
char c;
|
||||
str->length = 0;
|
||||
|
||||
for (; ctx->Left;) {
|
||||
c = *ctx->Cur; JsonContext_Consume(ctx, 1);
|
||||
for (; ctx->left;) {
|
||||
c = *ctx->cur; JsonContext_Consume(ctx, 1);
|
||||
if (c == '"') return;
|
||||
if (c != '\\') { String_Append(str, c); continue; }
|
||||
|
||||
/* form of \X */
|
||||
if (!ctx->Left) break;
|
||||
c = *ctx->Cur; JsonContext_Consume(ctx, 1);
|
||||
if (!ctx->left) break;
|
||||
c = *ctx->cur; JsonContext_Consume(ctx, 1);
|
||||
if (c == '/' || c == '\\' || c == '"') { String_Append(str, c); continue; }
|
||||
|
||||
/* form of \uYYYY */
|
||||
if (c != 'u' || ctx->Left < 4) break;
|
||||
if (c != 'u' || ctx->left < 4) break;
|
||||
|
||||
if (!PackedCol_Unhex(ctx->Cur[0], &h[0])) break;
|
||||
if (!PackedCol_Unhex(ctx->Cur[1], &h[1])) break;
|
||||
if (!PackedCol_Unhex(ctx->Cur[2], &h[2])) break;
|
||||
if (!PackedCol_Unhex(ctx->Cur[3], &h[3])) break;
|
||||
if (!PackedCol_Unhex(ctx->cur[0], &h[0])) break;
|
||||
if (!PackedCol_Unhex(ctx->cur[1], &h[1])) break;
|
||||
if (!PackedCol_Unhex(ctx->cur[2], &h[2])) break;
|
||||
if (!PackedCol_Unhex(ctx->cur[3], &h[3])) break;
|
||||
|
||||
codepoint = (h[0] << 12) | (h[1] << 8) | (h[2] << 4) | h[3];
|
||||
/* don't want control characters in names/software */
|
||||
@ -98,13 +98,13 @@ static void Json_ConsumeString(struct JsonContext* ctx, String* str) {
|
||||
JsonContext_Consume(ctx, 4);
|
||||
}
|
||||
|
||||
ctx->Failed = true; str->length = 0;
|
||||
ctx->failed = true; str->length = 0;
|
||||
}
|
||||
static String Json_ConsumeValue(int token, struct JsonContext* ctx);
|
||||
|
||||
static void Json_ConsumeObject(struct JsonContext* ctx) {
|
||||
char keyBuffer[STRING_SIZE];
|
||||
String value, oldKey = ctx->CurKey;
|
||||
String value, oldKey = ctx->curKey;
|
||||
int token;
|
||||
ctx->OnNewObject(ctx);
|
||||
|
||||
@ -113,19 +113,19 @@ static void Json_ConsumeObject(struct JsonContext* ctx) {
|
||||
if (token == ',') continue;
|
||||
if (token == '}') return;
|
||||
|
||||
if (token != '"') { ctx->Failed = true; return; }
|
||||
String_InitArray(ctx->CurKey, keyBuffer);
|
||||
Json_ConsumeString(ctx, &ctx->CurKey);
|
||||
if (token != '"') { ctx->failed = true; return; }
|
||||
String_InitArray(ctx->curKey, keyBuffer);
|
||||
Json_ConsumeString(ctx, &ctx->curKey);
|
||||
|
||||
token = Json_ConsumeToken(ctx);
|
||||
if (token != ':') { ctx->Failed = true; return; }
|
||||
if (token != ':') { ctx->failed = true; return; }
|
||||
|
||||
token = Json_ConsumeToken(ctx);
|
||||
if (token == TOKEN_NONE) { ctx->Failed = true; return; }
|
||||
if (token == TOKEN_NONE) { ctx->failed = true; return; }
|
||||
|
||||
value = Json_ConsumeValue(token, ctx);
|
||||
ctx->OnValue(ctx, &value);
|
||||
ctx->CurKey = oldKey;
|
||||
ctx->curKey = oldKey;
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ static void Json_ConsumeArray(struct JsonContext* ctx) {
|
||||
if (token == ',') continue;
|
||||
if (token == ']') return;
|
||||
|
||||
if (token == TOKEN_NONE) { ctx->Failed = true; return; }
|
||||
if (token == TOKEN_NONE) { ctx->failed = true; return; }
|
||||
value = Json_ConsumeValue(token, ctx);
|
||||
ctx->OnValue(ctx, &value);
|
||||
}
|
||||
@ -162,10 +162,10 @@ static String Json_ConsumeValue(int token, struct JsonContext* ctx) {
|
||||
static void Json_NullOnNew(struct JsonContext* ctx) { }
|
||||
static void Json_NullOnValue(struct JsonContext* ctx, const String* v) { }
|
||||
void Json_Init(struct JsonContext* ctx, String* str) {
|
||||
ctx->Cur = str->buffer;
|
||||
ctx->Left = str->length;
|
||||
ctx->Failed = false;
|
||||
ctx->CurKey = String_Empty;
|
||||
ctx->cur = str->buffer;
|
||||
ctx->left = str->length;
|
||||
ctx->failed = false;
|
||||
ctx->curKey = String_Empty;
|
||||
|
||||
ctx->OnNewArray = Json_NullOnNew;
|
||||
ctx->OnNewObject = Json_NullOnNew;
|
||||
@ -226,6 +226,18 @@ void LWebTask_Tick(struct LWebTask* task) {
|
||||
HttpRequest_Free(&req);
|
||||
}
|
||||
|
||||
void LWebTask_DisplayError(struct LWebTask* task, const char* action, String* dst) {
|
||||
if (task->Res) {
|
||||
/* Non HTTP error - this is not good */
|
||||
Logger_SysWarn(task->Res, action, Http_DescribeError);
|
||||
String_Format2(dst, "&cError %i when %c", &task->Res, action);
|
||||
} else if (task->Status != 200) {
|
||||
String_Format2(dst, "&c%i error when %c", &task->Status, action);
|
||||
} else {
|
||||
String_Format1(dst, "&cEmpty response when %c", action);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*-------------------------------------------------------GetTokenTask------------------------------------------------------*
|
||||
@ -234,7 +246,7 @@ struct GetTokenTaskData GetTokenTask;
|
||||
char tokenBuffer[STRING_SIZE];
|
||||
|
||||
static void GetTokenTask_OnValue(struct JsonContext* ctx, const String* str) {
|
||||
if (!String_CaselessEqualsConst(&ctx->CurKey, "token")) return;
|
||||
if (!String_CaselessEqualsConst(&ctx->curKey, "token")) return;
|
||||
String_Copy(&GetTokenTask.Token, str);
|
||||
}
|
||||
|
||||
@ -277,9 +289,9 @@ static void SignInTask_LogError(const String* str) {
|
||||
}
|
||||
|
||||
static void SignInTask_OnValue(struct JsonContext* ctx, const String* str) {
|
||||
if (String_CaselessEqualsConst(&ctx->CurKey, "username")) {
|
||||
if (String_CaselessEqualsConst(&ctx->curKey, "username")) {
|
||||
String_Copy(&SignInTask.Username, str);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "errors")) {
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "errors")) {
|
||||
SignInTask_LogError(str);
|
||||
}
|
||||
}
|
||||
@ -321,46 +333,46 @@ struct FetchServerData FetchServerTask;
|
||||
static struct ServerInfo* curServer;
|
||||
|
||||
static void ServerInfo_Init(struct ServerInfo* info) {
|
||||
String_InitArray(info->Hash, info->_hashBuffer);
|
||||
String_InitArray(info->Name, info->_nameBuffer);
|
||||
String_InitArray(info->IP, info->_ipBuffer);
|
||||
String_InitArray(info->hash, info->_hashBuffer);
|
||||
String_InitArray(info->name, info->_nameBuffer);
|
||||
String_InitArray(info->ip, info->_ipBuffer);
|
||||
|
||||
String_InitArray(info->Mppass, info->_mppassBuffer);
|
||||
String_InitArray(info->Software, info->_softBuffer);
|
||||
String_InitArray(info->Country, info->_countryBuffer);
|
||||
String_InitArray(info->mppass, info->_mppassBuffer);
|
||||
String_InitArray(info->software, info->_softBuffer);
|
||||
String_InitArray(info->country, info->_countryBuffer);
|
||||
|
||||
info->Players = 0;
|
||||
info->MaxPlayers = 0;
|
||||
info->Uptime = 0;
|
||||
info->Featured = false;
|
||||
info->players = 0;
|
||||
info->maxPlayers = 0;
|
||||
info->uptime = 0;
|
||||
info->featured = false;
|
||||
info->_order = -100000;
|
||||
}
|
||||
|
||||
static void ServerInfo_Parse(struct JsonContext* ctx, const String* val) {
|
||||
struct ServerInfo* info = curServer;
|
||||
if (String_CaselessEqualsConst(&ctx->CurKey, "hash")) {
|
||||
String_Copy(&info->Hash, val);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "name")) {
|
||||
String_Copy(&info->Name, val);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "players")) {
|
||||
Convert_ParseInt(val, &info->Players);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "maxplayers")) {
|
||||
Convert_ParseInt(val, &info->MaxPlayers);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "uptime")) {
|
||||
Convert_ParseInt(val, &info->Uptime);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "mppass")) {
|
||||
String_Copy(&info->Mppass, val);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "ip")) {
|
||||
String_Copy(&info->IP, val);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "port")) {
|
||||
Convert_ParseInt(val, &info->Port);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "software")) {
|
||||
String_Copy(&info->Software, val);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "featured")) {
|
||||
Convert_ParseBool(val, &info->Featured);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "country_abbr")) {
|
||||
if (String_CaselessEqualsConst(&ctx->curKey, "hash")) {
|
||||
String_Copy(&info->hash, val);
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "name")) {
|
||||
String_Copy(&info->name, val);
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "players")) {
|
||||
Convert_ParseInt(val, &info->players);
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "maxplayers")) {
|
||||
Convert_ParseInt(val, &info->maxPlayers);
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "uptime")) {
|
||||
Convert_ParseInt(val, &info->uptime);
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "mppass")) {
|
||||
String_Copy(&info->mppass, val);
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "ip")) {
|
||||
String_Copy(&info->ip, val);
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "port")) {
|
||||
Convert_ParseInt(val, &info->port);
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "software")) {
|
||||
String_Copy(&info->software, val);
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "featured")) {
|
||||
Convert_ParseBool(val, &info->featured);
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "country_abbr")) {
|
||||
/* Two letter country codes, see ISO 3166-1 alpha-2 */
|
||||
String_Copy(&info->Country, val);
|
||||
String_Copy(&info->country, val);
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,11 +472,11 @@ CC_NOINLINE static TimeMS CheckUpdateTask_ParseTime(const String* str) {
|
||||
}
|
||||
|
||||
static void CheckUpdateTask_OnValue(struct JsonContext* ctx, const String* str) {
|
||||
if (String_CaselessEqualsConst(&ctx->CurKey, "release_version")) {
|
||||
if (String_CaselessEqualsConst(&ctx->curKey, "release_version")) {
|
||||
String_Copy(&CheckUpdateTask.LatestRelease, str);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "latest_ts")) {
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "latest_ts")) {
|
||||
CheckUpdateTask.DevTimestamp = CheckUpdateTask_ParseTime(str);
|
||||
} else if (String_CaselessEqualsConst(&ctx->CurKey, "release_ts")) {
|
||||
} else if (String_CaselessEqualsConst(&ctx->curKey, "release_ts")) {
|
||||
CheckUpdateTask.RelTimestamp = CheckUpdateTask_ParseTime(str);
|
||||
}
|
||||
}
|
||||
|
15
src/LWeb.h
15
src/LWeb.h
@ -12,10 +12,10 @@ typedef void (*JsonOnNew)(struct JsonContext* ctx);
|
||||
|
||||
/* State for parsing JSON text */
|
||||
struct JsonContext {
|
||||
char* Cur; /* Pointer to current character in JSON stream being inspected. */
|
||||
int Left; /* Number of characters left to be inspected. */
|
||||
bool Failed; /* Whether there was an error parsing the JSON. */
|
||||
String CurKey; /* Key/Name of current member */
|
||||
char* cur; /* Pointer to current character in JSON stream being inspected. */
|
||||
int left; /* Number of characters left to be inspected. */
|
||||
bool failed; /* Whether there was an error parsing the JSON. */
|
||||
String curKey; /* Key/Name of current member */
|
||||
|
||||
JsonOnNew OnNewArray; /* Invoked when start of an array is read. */
|
||||
JsonOnNew OnNewObject; /* Invoked when start of an object is read. */
|
||||
@ -31,9 +31,9 @@ void Json_Parse(struct JsonContext* ctx);
|
||||
|
||||
/* Represents all known details about a server. */
|
||||
struct ServerInfo {
|
||||
String Hash, Name, IP, Mppass, Software, Country;
|
||||
int Players, MaxPlayers, Port, Uptime;
|
||||
bool Featured;
|
||||
String hash, name, ip, mppass, software, country;
|
||||
int players, maxPlayers, port, uptime;
|
||||
bool featured;
|
||||
int _order; /* (internal) order in servers table after filtering */
|
||||
char _hashBuffer[32], _nameBuffer[STRING_SIZE];
|
||||
char _ipBuffer[16], _mppassBuffer[STRING_SIZE];
|
||||
@ -55,6 +55,7 @@ struct LWebTask {
|
||||
void (*Handle)(uint8_t* data, uint32_t len);
|
||||
};
|
||||
void LWebTask_Tick(struct LWebTask* task);
|
||||
void LWebTask_DisplayError(struct LWebTask* task, const char* action, String* dst);
|
||||
|
||||
|
||||
extern struct GetTokenTaskData {
|
||||
|
@ -634,26 +634,26 @@ void LSlider_Init(struct LSlider* w, int width, int height) {
|
||||
*------------------------------------------------------TableWidget--------------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
static void FlagColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
|
||||
Bitmap* bmp = Flags_Get(&row->Country);
|
||||
Bitmap* bmp = Flags_Get(&row->country);
|
||||
if (bmp) Drawer2D_BmpCopy(&Launcher_Framebuffer, x + 2, y + 6, bmp);
|
||||
}
|
||||
|
||||
static void NameColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
|
||||
args->text = row->Name;
|
||||
args->text = row->name;
|
||||
}
|
||||
static int NameColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
|
||||
return String_Compare(&b->Name, &a->Name);
|
||||
return String_Compare(&b->name, &a->name);
|
||||
}
|
||||
|
||||
static void PlayersColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
|
||||
String_Format2(&args->text, "%i/%i", &row->Players, &row->MaxPlayers);
|
||||
String_Format2(&args->text, "%i/%i", &row->players, &row->maxPlayers);
|
||||
}
|
||||
static int PlayersColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
|
||||
return b->Players - a->Players;
|
||||
return b->players - a->players;
|
||||
}
|
||||
|
||||
static void UptimeColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
|
||||
int uptime = row->Uptime;
|
||||
int uptime = row->uptime;
|
||||
char unit = 's';
|
||||
|
||||
if (uptime >= SECS_PER_DAY * 7) {
|
||||
@ -666,14 +666,14 @@ static void UptimeColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args,
|
||||
String_Format2(&args->text, "%i%r", &uptime, &unit);
|
||||
}
|
||||
static int UptimeColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
|
||||
return b->Uptime - a->Uptime;
|
||||
return b->uptime - a->uptime;
|
||||
}
|
||||
|
||||
static void SoftwareColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
|
||||
args->text = row->Software;
|
||||
args->text = row->software;
|
||||
}
|
||||
static int SoftwareColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
|
||||
return String_Compare(&b->Software, &a->Software);
|
||||
return String_Compare(&b->software, &a->software);
|
||||
}
|
||||
|
||||
static struct LTableColumn tableColumns[5] = {
|
||||
@ -712,7 +712,7 @@ static int LTable_GetSelectedIndex(struct LTable* w) {
|
||||
|
||||
for (row = 0; row < w->RowsCount; row++) {
|
||||
entry = LTable_Get(row);
|
||||
if (String_CaselessEquals(w->SelectedHash, &entry->Hash)) return row;
|
||||
if (String_CaselessEquals(w->SelectedHash, &entry->hash)) return row;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -723,7 +723,7 @@ static void LTable_SetSelectedTo(struct LTable* w, int index) {
|
||||
if (index >= w->RowsCount) index = w->RowsCount - 1;
|
||||
if (index < 0) index = 0;
|
||||
|
||||
String_Copy(w->SelectedHash, <able_Get(index)->Hash);
|
||||
String_Copy(w->SelectedHash, <able_Get(index)->hash);
|
||||
LTable_ShowSelected(w);
|
||||
w->OnSelectedChanged();
|
||||
}
|
||||
@ -759,8 +759,8 @@ static BitmapCol LTable_RowCol(struct LTable* w, struct ServerInfo* row) {
|
||||
bool selected;
|
||||
|
||||
if (row) {
|
||||
selected = String_Equals(&row->Hash, w->SelectedHash);
|
||||
if (row->Featured) {
|
||||
selected = String_Equals(&row->hash, w->SelectedHash);
|
||||
if (row->featured) {
|
||||
return selected ? featSelCol : featuredCol;
|
||||
} else if (selected) {
|
||||
return selectedCol;
|
||||
@ -804,8 +804,8 @@ static void LTable_DrawGridlines(struct LTable* w) {
|
||||
x, w->Y + w->HdrHeight, w->Width, GRIDLINE_SIZE);
|
||||
|
||||
for (i = 0; i < w->NumColumns; i++) {
|
||||
x += w->Columns[i].Width;
|
||||
if (!w->Columns[i].ColumnGridline) continue;
|
||||
x += w->Columns[i].width;
|
||||
if (!w->Columns[i].columnGridline) continue;
|
||||
|
||||
Drawer2D_Clear(&Launcher_Framebuffer, Launcher_BackgroundCol,
|
||||
x, w->Y, GRIDLINE_SIZE, w->Height);
|
||||
@ -829,13 +829,13 @@ static void LTable_DrawHeaders(struct LTable* w) {
|
||||
x = w->X; y = w->Y;
|
||||
|
||||
for (i = 0; i < w->NumColumns; i++) {
|
||||
args.text = String_FromReadonly(w->Columns[i].Name);
|
||||
args.text = String_FromReadonly(w->Columns[i].name);
|
||||
Drawer2D_DrawClippedText(&Launcher_Framebuffer, &args,
|
||||
x + CELL_XOFFSET, y + HDR_YOFFSET,
|
||||
w->Columns[i].Width - CELL_XPADDING);
|
||||
w->Columns[i].width - CELL_XPADDING);
|
||||
|
||||
x += w->Columns[i].Width;
|
||||
if (w->Columns[i].ColumnGridline) x += GRIDLINE_SIZE;
|
||||
x += w->Columns[i].width;
|
||||
if (w->Columns[i].columnGridline) x += GRIDLINE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -865,11 +865,11 @@ static void LTable_DrawRows(struct LTable* w) {
|
||||
if (args.text.length) {
|
||||
Drawer2D_DrawClippedText(&Launcher_Framebuffer, &args,
|
||||
x + CELL_XOFFSET, y + ROW_YOFFSET,
|
||||
w->Columns[i].Width - CELL_XPADDING);
|
||||
w->Columns[i].width - CELL_XPADDING);
|
||||
}
|
||||
|
||||
x += w->Columns[i].Width;
|
||||
if (w->Columns[i].ColumnGridline) x += GRIDLINE_SIZE;
|
||||
x += w->Columns[i].width;
|
||||
if (w->Columns[i].columnGridline) x += GRIDLINE_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -930,8 +930,8 @@ static void LTable_MouseMove(void* widget, int deltaX, int deltaY, bool wasOver)
|
||||
if (!deltaX || x >= w->X + w->Width - 20) return;
|
||||
col = w->DraggingColumn;
|
||||
|
||||
w->Columns[col].Width += deltaX;
|
||||
Math_Clamp(w->Columns[col].Width, 20, w->Width - 20);
|
||||
w->Columns[col].width += deltaX;
|
||||
Math_Clamp(w->Columns[col].width, 20, w->Width - 20);
|
||||
LWidget_Redraw(w);
|
||||
}
|
||||
}
|
||||
@ -946,7 +946,7 @@ static void LTable_RowsClick(struct LTable* w) {
|
||||
|
||||
/* double click on row to join */
|
||||
if (w->_lastClick + 1000 >= now && row == w->_lastRow) {
|
||||
Launcher_ConnectToServer(<able_Get(row)->Hash);
|
||||
Launcher_ConnectToServer(<able_Get(row)->hash);
|
||||
}
|
||||
|
||||
w->_lastRow = LTable_GetSelectedIndex(w);
|
||||
@ -959,25 +959,25 @@ static void LTable_HeadersClick(struct LTable* w) {
|
||||
|
||||
for (i = 0, x = w->X; i < w->NumColumns; i++) {
|
||||
/* clicked on gridline, begin dragging */
|
||||
if (mouseX >= (x - 8) && mouseX < (x + 8) && w->Columns[i].Interactable) {
|
||||
if (mouseX >= (x - 8) && mouseX < (x + 8) && w->Columns[i].interactable) {
|
||||
w->DraggingColumn = i - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
x += w->Columns[i].Width;
|
||||
if (w->Columns[i].ColumnGridline) x += GRIDLINE_SIZE;
|
||||
x += w->Columns[i].width;
|
||||
if (w->Columns[i].columnGridline) x += GRIDLINE_SIZE;
|
||||
}
|
||||
|
||||
for (i = 0, x = w->X; i < w->NumColumns; i++) {
|
||||
if (mouseX >= x && mouseX < (x + w->Columns[i].Width) && w->Columns[i].Interactable) {
|
||||
if (mouseX >= x && mouseX < (x + w->Columns[i].width) && w->Columns[i].interactable) {
|
||||
sortingCol = i;
|
||||
w->Columns[i].InvertSort = !w->Columns[i].InvertSort;
|
||||
w->Columns[i].invertSort = !w->Columns[i].invertSort;
|
||||
LTable_Sort(w);
|
||||
return;
|
||||
}
|
||||
|
||||
x += w->Columns[i].Width;
|
||||
if (w->Columns[i].ColumnGridline) x += GRIDLINE_SIZE;
|
||||
x += w->Columns[i].width;
|
||||
if (w->Columns[i].columnGridline) x += GRIDLINE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1085,7 +1085,7 @@ void LTable_ApplyFilter(struct LTable* w) {
|
||||
|
||||
count = FetchServersTask.NumServers;
|
||||
for (i = 0, j = 0; i < count; i++) {
|
||||
if (String_CaselessContains(&Servers_Get(i)->Name, w->Filter)) {
|
||||
if (String_CaselessContains(&Servers_Get(i)->name, w->Filter)) {
|
||||
FetchServersTask.Servers[j++]._order = FetchServersTask.Orders[i];
|
||||
}
|
||||
}
|
||||
@ -1103,12 +1103,12 @@ static int LTable_SortOrder(const struct ServerInfo* a, const struct ServerInfo*
|
||||
int order;
|
||||
if (sortingCol >= 0) {
|
||||
order = tableColumns[sortingCol].SortOrder(a, b);
|
||||
return tableColumns[sortingCol].InvertSort ? -order : order;
|
||||
return tableColumns[sortingCol].invertSort ? -order : order;
|
||||
}
|
||||
|
||||
/* Default sort order. (most active server, then by highest uptime) */
|
||||
if (a->Players != b->Players) return a->Players - b->Players;
|
||||
return a->Uptime - b->Uptime;
|
||||
if (a->players != b->players) return a->players - b->players;
|
||||
return a->uptime - b->uptime;
|
||||
}
|
||||
|
||||
static void LTable_QuickSort(int left, int right) {
|
||||
|
@ -116,9 +116,9 @@ struct DrawTextArgs;
|
||||
|
||||
struct LTableColumn {
|
||||
/* Name of this column. */
|
||||
const char* Name;
|
||||
const char* name;
|
||||
/* Width of this column in pixels. */
|
||||
int Width;
|
||||
int width;
|
||||
/* Draws the value of this column for the given row. */
|
||||
/* If args.Text is changed to something, that text gets drawn afterwards. */
|
||||
/* Most of the time that's all you need to do. */
|
||||
@ -126,12 +126,12 @@ struct LTableColumn {
|
||||
/* Returns sort order of two rows, based on value of this column in both rows. */
|
||||
int (*SortOrder)(const struct ServerInfo* a, const struct ServerInfo* b);
|
||||
/* Whether a vertical gridline (and padding) appears after this. */
|
||||
bool ColumnGridline;
|
||||
bool columnGridline;
|
||||
/* Whether user can interact with this column. */
|
||||
/* Non-interactable columns can't be sorted/resized. */
|
||||
bool Interactable;
|
||||
bool interactable;
|
||||
/* Whether to invert the order of row sorting. */
|
||||
bool InvertSort;
|
||||
bool invertSort;
|
||||
};
|
||||
|
||||
/* Represents a table of server entries. */
|
||||
|
@ -45,18 +45,19 @@ CC_NOINLINE static void Launcher_StartFromInfo(struct ServerInfo* info) {
|
||||
String port; char portBuffer[STRING_INT_CHARS];
|
||||
String_InitArray(port, portBuffer);
|
||||
|
||||
String_AppendInt(&port, info->Port);
|
||||
Launcher_StartGame(&SignInTask.Username, &info->Mppass, &info->IP, &port, &info->Name);
|
||||
String_AppendInt(&port, info->port);
|
||||
Launcher_StartGame(&SignInTask.Username, &info->mppass, &info->ip, &port, &info->name);
|
||||
}
|
||||
|
||||
bool Launcher_ConnectToServer(const String* hash) {
|
||||
int i;
|
||||
struct ServerInfo* info;
|
||||
String logMsg;
|
||||
int i;
|
||||
if (!hash->length) return false;
|
||||
|
||||
for (i = 0; i < FetchServersTask.NumServers; i++) {
|
||||
info = &FetchServersTask.Servers[i];
|
||||
if (!String_Equals(hash, &info->Hash)) continue;
|
||||
if (!String_Equals(hash, &info->hash)) continue;
|
||||
|
||||
Launcher_StartFromInfo(info);
|
||||
return true;
|
||||
@ -71,18 +72,16 @@ bool Launcher_ConnectToServer(const String* hash) {
|
||||
Thread_Sleep(10);
|
||||
}
|
||||
|
||||
if (FetchServerTask.Server.Hash.length) {
|
||||
if (FetchServerTask.Server.hash.length) {
|
||||
Launcher_StartFromInfo(&FetchServerTask.Server);
|
||||
return true;
|
||||
} else if (FetchServerTask.Base.Res) {
|
||||
Logger_SimpleWarn(FetchServerTask.Base.Res, "fetching server info");
|
||||
} else if (FetchServerTask.Base.Status != 200) {
|
||||
/* TODO: Use a better dialog message.. */
|
||||
Logger_SimpleWarn(FetchServerTask.Base.Status, "fetching server info");
|
||||
} else {
|
||||
} else if (FetchServerTask.Base.Success) {
|
||||
Window_ShowDialog("Failed to connect", "No server has that hash");
|
||||
} else {
|
||||
logMsg = String_Init(NULL, 0, 0);
|
||||
LWebTask_DisplayError(&FetchServerTask.Base, "fetching server info", &logMsg);
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -60,7 +60,7 @@ static void Server_CheckAsyncResources(void) {
|
||||
TexturePack_Extract_Req(&item);
|
||||
HttpRequest_Free(&item);
|
||||
} else if (item.Result) {
|
||||
Chat_Add1("&cError %i when trying to download texture pack", &item.Result);
|
||||
Logger_SysWarn(item.Result, "trying to download texture pack", Http_DescribeError);
|
||||
} else {
|
||||
int status = item.StatusCode;
|
||||
if (status == 200 || status == 304) return;
|
||||
|
Loading…
x
Reference in New Issue
Block a user