X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Ffiles.c;fp=src%2Ffiles.c;h=2216c6713db44a1fa8e201f755f5bfd94810cd68;hp=307b78895dd8e13c290095e3402f1355a1d7df23;hb=e1451ba8236ab4c3eb4db057139c374b26e8b56c;hpb=730e92ef422a3be2315067584eaeb7dc6585b6f4 diff --git a/src/files.c b/src/files.c index 307b7889..2216c671 100644 --- a/src/files.c +++ b/src/files.c @@ -22,6 +22,7 @@ #include "tools.h" #include "tape.h" #include "config.h" +#include "api.h" #define ENABLE_UNUSED_CODE 0 // currently unused functions #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference @@ -9168,273 +9169,6 @@ void SaveScore(int nr) SaveScoreToFilename(filename); } -void ExecuteAsThread(SDL_ThreadFunction function, char *name, void *data, - char *error) -{ -#if defined(PLATFORM_EMSCRIPTEN) - // threads currently not fully supported by Emscripten/SDL and some browsers - function(data); -#else - SDL_Thread *thread = SDL_CreateThread(function, name, data); - - if (thread != NULL) - SDL_DetachThread(thread); - else - Error("Cannot create thread to %s!", error); - - // nasty kludge to lower probability of intermingled thread error messages - Delay(1); -#endif -} - -char *getPasswordJSON(char *password) -{ - static char password_json[MAX_FILENAME_LEN] = ""; - static boolean initialized = FALSE; - - if (!initialized) - { - if (password != NULL && - !strEqual(password, "") && - !strEqual(password, UNDEFINED_PASSWORD)) - snprintf(password_json, MAX_FILENAME_LEN, - " \"password\": \"%s\",\n", - setup.api_server_password); - - initialized = TRUE; - } - - return password_json; -} - -struct ApiGetScoreThreadData -{ - int level_nr; - char *score_cache_filename; -}; - -static void *CreateThreadData_ApiGetScore(int nr) -{ - struct ApiGetScoreThreadData *data = - checked_malloc(sizeof(struct ApiGetScoreThreadData)); - char *score_cache_filename = getScoreCacheFilename(nr); - - data->level_nr = nr; - data->score_cache_filename = getStringCopy(score_cache_filename); - - return data; -} - -static void FreeThreadData_ApiGetScore(void *data_raw) -{ - struct ApiGetScoreThreadData *data = data_raw; - - checked_free(data->score_cache_filename); - checked_free(data); -} - -static boolean SetRequest_ApiGetScore(struct HttpRequest *request, - void *data_raw) -{ - struct ApiGetScoreThreadData *data = data_raw; - int level_nr = data->level_nr; - - request->hostname = setup.api_server_hostname; - request->port = API_SERVER_PORT; - request->method = API_SERVER_METHOD; - request->uri = API_SERVER_URI_GET; - - char *levelset_identifier = getEscapedJSON(leveldir_current->identifier); - char *levelset_name = getEscapedJSON(leveldir_current->name); - - snprintf(request->body, MAX_HTTP_BODY_SIZE, - "{\n" - "%s" - " \"game_version\": \"%s\",\n" - " \"game_platform\": \"%s\",\n" - " \"levelset_identifier\": \"%s\",\n" - " \"levelset_name\": \"%s\",\n" - " \"level_nr\": \"%d\"\n" - "}\n", - getPasswordJSON(setup.api_server_password), - getProgramRealVersionString(), - getProgramPlatformString(), - levelset_identifier, - levelset_name, - level_nr); - - checked_free(levelset_identifier); - checked_free(levelset_name); - - ConvertHttpRequestBodyToServerEncoding(request); - - return TRUE; -} - -static void HandleResponse_ApiGetScore(struct HttpResponse *response, - void *data_raw) -{ - struct ApiGetScoreThreadData *data = data_raw; - - if (response->body_size == 0) - { - // no scores available for this level - - return; - } - - ConvertHttpResponseBodyToClientEncoding(response); - - char *filename = data->score_cache_filename; - FILE *file; - int i; - - // used instead of "leveldir_current->subdir" (for network games) - InitScoreCacheDirectory(levelset.identifier); - - if (!(file = fopen(filename, MODE_WRITE))) - { - Warn("cannot save score cache file '%s'", filename); - - return; - } - - for (i = 0; i < response->body_size; i++) - fputc(response->body[i], file); - - fclose(file); - - SetFilePermissions(filename, PERMS_PRIVATE); - - server_scores.updated = TRUE; -} - -#if defined(PLATFORM_EMSCRIPTEN) -static void Emscripten_ApiGetScore_Loaded(unsigned handle, void *data_raw, - void *buffer, unsigned int size) -{ - struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size); - - if (response != NULL) - { - HandleResponse_ApiGetScore(response, data_raw); - - checked_free(response); - } - else - { - Error("server response too large to handle (%d bytes)", size); - } - - FreeThreadData_ApiGetScore(data_raw); -} - -static void Emscripten_ApiGetScore_Failed(unsigned handle, void *data_raw, - int code, const char *status) -{ - Error("server failed to handle request: %d %s", code, status); - - FreeThreadData_ApiGetScore(data_raw); -} - -static void Emscripten_ApiGetScore_Progress(unsigned handle, void *data_raw, - int bytes, int size) -{ - // nothing to do here -} - -static void Emscripten_ApiGetScore_HttpRequest(struct HttpRequest *request, - void *data_raw) -{ - if (!SetRequest_ApiGetScore(request, data_raw)) - { - FreeThreadData_ApiGetScore(data_raw); - - return; - } - - emscripten_async_wget2_data(request->uri, - request->method, - request->body, - data_raw, - TRUE, - Emscripten_ApiGetScore_Loaded, - Emscripten_ApiGetScore_Failed, - Emscripten_ApiGetScore_Progress); -} - -#else - -static void ApiGetScore_HttpRequestExt(struct HttpRequest *request, - struct HttpResponse *response, - void *data_raw) -{ - if (!SetRequest_ApiGetScore(request, data_raw)) - return; - - if (!DoHttpRequest(request, response)) - { - Error("HTTP request failed: %s", GetHttpError()); - - return; - } - - if (!HTTP_SUCCESS(response->status_code)) - { - // do not show error message if no scores found for this level set - if (response->status_code == 404) - return; - - Error("server failed to handle request: %d %s", - response->status_code, - response->status_text); - - return; - } - - HandleResponse_ApiGetScore(response, data_raw); -} - -static void ApiGetScore_HttpRequest(struct HttpRequest *request, - struct HttpResponse *response, - void *data_raw) -{ - ApiGetScore_HttpRequestExt(request, response, data_raw); - - FreeThreadData_ApiGetScore(data_raw); -} -#endif - -static int ApiGetScoreThread(void *data_raw) -{ - struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest)); - struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); - - program.api_thread_count++; - -#if defined(PLATFORM_EMSCRIPTEN) - Emscripten_ApiGetScore_HttpRequest(request, data_raw); -#else - ApiGetScore_HttpRequest(request, response, data_raw); -#endif - - program.api_thread_count--; - - checked_free(request); - checked_free(response); - - return 0; -} - -static void ApiGetScoreAsThread(int nr) -{ - struct ApiGetScoreThreadData *data = CreateThreadData_ApiGetScore(nr); - - ExecuteAsThread(ApiGetScoreThread, - "ApiGetScore", data, - "download scores from server"); -} - static void LoadServerScoreFromCache(int nr) { struct ScoreEntry score_entry; @@ -9527,61 +9261,7 @@ void LoadServerScore(int nr, boolean download_score) } } -static char *get_file_base64(char *filename) -{ - struct stat file_status; - - if (stat(filename, &file_status) != 0) - { - Error("cannot stat file '%s'", filename); - - return NULL; - } - - int buffer_size = file_status.st_size; - byte *buffer = checked_malloc(buffer_size); - FILE *file; - int i; - - if (!(file = fopen(filename, MODE_READ))) - { - Error("cannot open file '%s'", filename); - - checked_free(buffer); - - return NULL; - } - - for (i = 0; i < buffer_size; i++) - { - int c = fgetc(file); - - if (c == EOF) - { - Error("cannot read from input file '%s'", filename); - - fclose(file); - checked_free(buffer); - - return NULL; - } - - buffer[i] = (byte)c; - } - - fclose(file); - - int buffer_encoded_size = base64_encoded_size(buffer_size); - char *buffer_encoded = checked_malloc(buffer_encoded_size); - - base64_encode(buffer_encoded, buffer, buffer_size); - - checked_free(buffer); - - return buffer_encoded; -} - -static void PrepareScoreTapesForUpload(char *leveldir_subdir) +void PrepareScoreTapesForUpload(char *leveldir_subdir) { MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir); @@ -9595,289 +9275,6 @@ static void PrepareScoreTapesForUpload(char *leveldir_subdir) SaveSetup_ServerSetup(); } -struct ApiAddScoreThreadData -{ - int level_nr; - boolean tape_saved; - char *leveldir_subdir; - char *score_tape_filename; - struct ScoreEntry score_entry; -}; - -static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved, - char *score_tape_filename) -{ - struct ApiAddScoreThreadData *data = - checked_malloc(sizeof(struct ApiAddScoreThreadData)); - struct ScoreEntry *score_entry = &scores.entry[scores.last_added]; - - if (score_tape_filename == NULL) - score_tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr); - - data->level_nr = nr; - data->tape_saved = tape_saved; - data->leveldir_subdir = getStringCopy(leveldir_current->subdir); - data->score_tape_filename = getStringCopy(score_tape_filename); - data->score_entry = *score_entry; - - return data; -} - -static void FreeThreadData_ApiAddScore(void *data_raw) -{ - struct ApiAddScoreThreadData *data = data_raw; - - checked_free(data->leveldir_subdir); - checked_free(data->score_tape_filename); - checked_free(data); -} - -static boolean SetRequest_ApiAddScore(struct HttpRequest *request, - void *data_raw) -{ - struct ApiAddScoreThreadData *data = data_raw; - struct ScoreEntry *score_entry = &data->score_entry; - char *score_tape_filename = data->score_tape_filename; - boolean tape_saved = data->tape_saved; - int level_nr = data->level_nr; - - request->hostname = setup.api_server_hostname; - request->port = API_SERVER_PORT; - request->method = API_SERVER_METHOD; - request->uri = API_SERVER_URI_ADD; - - char *tape_base64 = get_file_base64(score_tape_filename); - - if (tape_base64 == NULL) - { - Error("loading and base64 encoding score tape file failed"); - - return FALSE; - } - - char *player_name_raw = score_entry->name; - char *player_uuid_raw = setup.player_uuid; - - if (options.player_name != NULL && global.autoplay_leveldir != NULL) - { - player_name_raw = options.player_name; - player_uuid_raw = ""; - } - - char *levelset_identifier = getEscapedJSON(leveldir_current->identifier); - char *levelset_name = getEscapedJSON(leveldir_current->name); - char *levelset_author = getEscapedJSON(leveldir_current->author); - char *level_name = getEscapedJSON(level.name); - char *level_author = getEscapedJSON(level.author); - char *player_name = getEscapedJSON(player_name_raw); - char *player_uuid = getEscapedJSON(player_uuid_raw); - - snprintf(request->body, MAX_HTTP_BODY_SIZE, - "{\n" - "%s" - " \"game_version\": \"%s\",\n" - " \"game_platform\": \"%s\",\n" - " \"batch_time\": \"%d\",\n" - " \"levelset_identifier\": \"%s\",\n" - " \"levelset_name\": \"%s\",\n" - " \"levelset_author\": \"%s\",\n" - " \"levelset_num_levels\": \"%d\",\n" - " \"levelset_first_level\": \"%d\",\n" - " \"level_nr\": \"%d\",\n" - " \"level_name\": \"%s\",\n" - " \"level_author\": \"%s\",\n" - " \"use_step_counter\": \"%d\",\n" - " \"rate_time_over_score\": \"%d\",\n" - " \"player_name\": \"%s\",\n" - " \"player_uuid\": \"%s\",\n" - " \"score\": \"%d\",\n" - " \"time\": \"%d\",\n" - " \"tape_basename\": \"%s\",\n" - " \"tape_saved\": \"%d\",\n" - " \"tape\": \"%s\"\n" - "}\n", - getPasswordJSON(setup.api_server_password), - getProgramRealVersionString(), - getProgramPlatformString(), - (int)global.autoplay_time, - levelset_identifier, - levelset_name, - levelset_author, - leveldir_current->levels, - leveldir_current->first_level, - level_nr, - level_name, - level_author, - level.use_step_counter, - level.rate_time_over_score, - player_name, - player_uuid, - score_entry->score, - score_entry->time, - score_entry->tape_basename, - tape_saved, - tape_base64); - - checked_free(tape_base64); - - checked_free(levelset_identifier); - checked_free(levelset_name); - checked_free(levelset_author); - checked_free(level_name); - checked_free(level_author); - checked_free(player_name); - checked_free(player_uuid); - - ConvertHttpRequestBodyToServerEncoding(request); - - return TRUE; -} - -static void HandleResponse_ApiAddScore(struct HttpResponse *response, - void *data_raw) -{ - server_scores.uploaded = TRUE; -} - -static void HandleFailure_ApiAddScore(void *data_raw) -{ - struct ApiAddScoreThreadData *data = data_raw; - - PrepareScoreTapesForUpload(data->leveldir_subdir); -} - -#if defined(PLATFORM_EMSCRIPTEN) -static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw, - void *buffer, unsigned int size) -{ - struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size); - - if (response != NULL) - { - HandleResponse_ApiAddScore(response, data_raw); - - checked_free(response); - } - else - { - Error("server response too large to handle (%d bytes)", size); - - HandleFailure_ApiAddScore(data_raw); - } - - FreeThreadData_ApiAddScore(data_raw); -} - -static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw, - int code, const char *status) -{ - Error("server failed to handle request: %d %s", code, status); - - HandleFailure_ApiAddScore(data_raw); - - FreeThreadData_ApiAddScore(data_raw); -} - -static void Emscripten_ApiAddScore_Progress(unsigned handle, void *data_raw, - int bytes, int size) -{ - // nothing to do here -} - -static void Emscripten_ApiAddScore_HttpRequest(struct HttpRequest *request, - void *data_raw) -{ - if (!SetRequest_ApiAddScore(request, data_raw)) - { - FreeThreadData_ApiAddScore(data_raw); - - return; - } - - emscripten_async_wget2_data(request->uri, - request->method, - request->body, - data_raw, - TRUE, - Emscripten_ApiAddScore_Loaded, - Emscripten_ApiAddScore_Failed, - Emscripten_ApiAddScore_Progress); -} - -#else - -static void ApiAddScore_HttpRequestExt(struct HttpRequest *request, - struct HttpResponse *response, - void *data_raw) -{ - if (!SetRequest_ApiAddScore(request, data_raw)) - return; - - if (!DoHttpRequest(request, response)) - { - Error("HTTP request failed: %s", GetHttpError()); - - HandleFailure_ApiAddScore(data_raw); - - return; - } - - if (!HTTP_SUCCESS(response->status_code)) - { - Error("server failed to handle request: %d %s", - response->status_code, - response->status_text); - - HandleFailure_ApiAddScore(data_raw); - - return; - } - - HandleResponse_ApiAddScore(response, data_raw); -} - -static void ApiAddScore_HttpRequest(struct HttpRequest *request, - struct HttpResponse *response, - void *data_raw) -{ - ApiAddScore_HttpRequestExt(request, response, data_raw); - - FreeThreadData_ApiAddScore(data_raw); -} -#endif - -static int ApiAddScoreThread(void *data_raw) -{ - struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest)); - struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); - - program.api_thread_count++; - -#if defined(PLATFORM_EMSCRIPTEN) - Emscripten_ApiAddScore_HttpRequest(request, data_raw); -#else - ApiAddScore_HttpRequest(request, response, data_raw); -#endif - - program.api_thread_count--; - - checked_free(request); - checked_free(response); - - return 0; -} - -static void ApiAddScoreAsThread(int nr, boolean tape_saved, - char *score_tape_filename) -{ - struct ApiAddScoreThreadData *data = - CreateThreadData_ApiAddScore(nr, tape_saved, score_tape_filename); - - ExecuteAsThread(ApiAddScoreThread, - "ApiAddScore", data, - "upload score to server"); -} - void SaveServerScore(int nr, boolean tape_saved) { if (!runtime.use_api_server)