X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Ffiles.c;h=6d99afccd0af57da1162fedb1ebe9c443f0be85a;hb=b937b0928312ad4a51f45c16a0792a09ba914659;hp=8b50f8e2fca4f9270568c072478e509dd682895c;hpb=5967c94482902b251e034b55e4f85343d94c9610;p=rocksndiamonds.git diff --git a/src/files.c b/src/files.c index 8b50f8e2..6d99afcc 100644 --- a/src/files.c +++ b/src/files.c @@ -3160,7 +3160,7 @@ static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf, value = getMappedElement(value); if (data_type == TYPE_BOOLEAN) - *(boolean *)(conf[i].value) = value; + *(boolean *)(conf[i].value) = (value ? TRUE : FALSE); else *(int *) (conf[i].value) = value; @@ -5895,6 +5895,21 @@ int getMappedElement_SB(int element_ascii, boolean use_ces) return EL_UNDEFINED; } +static void SetLevelSettings_SB(struct LevelInfo *level) +{ + // time settings + level->time = 0; + level->use_step_counter = TRUE; + + // score settings + level->score[SC_TIME_BONUS] = 0; + level->time_score_base = 1; + level->rate_time_over_score = TRUE; + + // game settings + level->auto_exit_sokoban = TRUE; +} + static void LoadLevelFromFileInfo_SB(struct LevelInfo *level, struct LevelFileInfo *level_file_info, boolean level_info_only) @@ -6128,14 +6143,11 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level, } // set special level settings for Sokoban levels - - level->time = 0; - level->use_step_counter = TRUE; + SetLevelSettings_SB(level); if (load_xsb_to_ces) { // special global settings can now be set in level template - level->use_custom_template = TRUE; } } @@ -6474,8 +6486,28 @@ static void LoadLevel_InitVersion(struct LevelInfo *level) level->keep_walkable_ce = TRUE; } +static void LoadLevel_InitSettings_SB(struct LevelInfo *level) +{ + boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found + int x, y; + + // check if this level is (not) a Sokoban level + for (y = 0; y < level->fieldy; y++) + for (x = 0; x < level->fieldx; x++) + if (!IS_SB_ELEMENT(Tile[x][y])) + is_sokoban_level = FALSE; + + if (is_sokoban_level) + { + // set special level settings for Sokoban levels + SetLevelSettings_SB(level); + } +} + static void LoadLevel_InitSettings(struct LevelInfo *level) { + // adjust level settings for (non-native) Sokoban-style levels + LoadLevel_InitSettings_SB(level); } static void LoadLevel_InitStandardElements(struct LevelInfo *level) @@ -7731,6 +7763,7 @@ static void setTapeInfoToDefaults(void) tape.scr_fieldx = SCR_FIELDX_DEFAULT; tape.scr_fieldy = SCR_FIELDY_DEFAULT; + tape.no_info_chunk = TRUE; tape.no_valid_file = FALSE; } @@ -7835,6 +7868,8 @@ static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape) int level_identifier_size; int i; + tape->no_info_chunk = FALSE; + level_identifier_size = getFile16BitBE(file); level_identifier = checked_malloc(level_identifier_size); @@ -8415,11 +8450,34 @@ void DumpTape(struct TapeInfo *tape) } PrintLine("-", 79); + Print("Tape of Level %03d (file version %08d, game version %08d)\n", tape->level_nr, tape->file_version, tape->game_version); Print(" (effective engine version %08d)\n", tape->engine_version); Print("Level series identifier: '%s'\n", tape->level_identifier); + + Print("Special tape properties: "); + if (tape->property_bits == TAPE_PROPERTY_NONE) + Print("[none]"); + if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG) + Print("[em_random_bug]"); + if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED) + Print("[game_speed]"); + if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE) + Print("[pause]"); + if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP) + Print("[single_step]"); + if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT) + Print("[snapshot]"); + if (tape->property_bits & TAPE_PROPERTY_REPLAYED) + Print("[replayed]"); + if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS) + Print("[tas_keys]"); + if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS) + Print("[small_graphics]"); + + Print("\n"); PrintLine("-", 79); tape_frame_counter = 0; @@ -8505,6 +8563,7 @@ static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores) scores->last_added_local = -1; scores->updated = FALSE; + scores->uploaded = FALSE; scores->force_last_added = FALSE; } @@ -8970,14 +9029,14 @@ void SaveScore(int nr) SaveScoreToFilename(filename); } -static void ExecuteAsThread(SDL_ThreadFunction function, char *name, int data, - char *error) +void ExecuteAsThread(SDL_ThreadFunction function, char *name, void *data, + char *error) { - static int data_static; - - data_static = data; - - SDL_Thread *thread = SDL_CreateThread(function, name, &data_static); +#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); @@ -8986,12 +9045,61 @@ static void ExecuteAsThread(SDL_ThreadFunction function, char *name, int data, // nasty kludge to lower probability of intermingled thread error messages Delay(1); +#endif } -static void DownloadServerScoreToCacheExt(struct HttpRequest *request, - struct HttpResponse *response, - int nr) +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; @@ -8999,27 +9107,27 @@ static void DownloadServerScoreToCacheExt(struct HttpRequest *request, snprintf(request->body, MAX_HTTP_BODY_SIZE, "{\n" + "%s" + " \"game_version\": \"%s\",\n" + " \"game_platform\": \"%s\",\n" " \"levelset_identifier\": \"%s\",\n" - " \"level_nr\": \"%d\",\n" - " \"rate_time_over_score\": \"%d\"\n" + " \"level_nr\": \"%d\"\n" "}\n", - levelset.identifier, nr, level.rate_time_over_score); - - if (!DoHttpRequest(request, response)) - { - Error("HTTP request failed: %s", GetHttpError()); + getPasswordJSON(setup.api_server_password), + getProgramRealVersionString(), + getProgramPlatformString(), + levelset.identifier, + level_nr); - return; - } + ConvertHttpRequestBodyToServerEncoding(request); - if (!HTTP_SUCCESS(response->status_code)) - { - Error("server failed to handle request: %d %s", - response->status_code, - response->status_text); + return TRUE; +} - return; - } +static void HandleResponse_ApiGetScore(struct HttpResponse *response, + void *data_raw) +{ + struct ApiGetScoreThreadData *data = data_raw; if (response->body_size == 0) { @@ -9030,7 +9138,7 @@ static void DownloadServerScoreToCacheExt(struct HttpRequest *request, ConvertHttpResponseBodyToClientEncoding(response); - char *filename = getScoreCacheFilename(nr); + char *filename = data->score_cache_filename; FILE *file; int i; @@ -9054,28 +9162,60 @@ static void DownloadServerScoreToCacheExt(struct HttpRequest *request, server_scores.updated = TRUE; } -static void DownloadServerScoreToCache(int nr) +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)) + { + 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); +} + +static int ApiGetScoreThread(void *data_raw) { struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest)); struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); - DownloadServerScoreToCacheExt(request, response, nr); + ApiGetScore_HttpRequest(request, response, data_raw); checked_free(request); checked_free(response); -} - -static int DownloadServerScoreToCacheThread(void *data) -{ - DownloadServerScoreToCache(*(int *)data); return 0; } -static void DownloadServerScoreToCacheAsThread(int nr) +static void ApiGetScoreAsThread(int nr) { - ExecuteAsThread(DownloadServerScoreToCacheThread, - "DownloadServerScoreToCache", nr, + struct ApiGetScoreThreadData *data = CreateThreadData_ApiGetScore(nr); + + ExecuteAsThread(ApiGetScoreThread, + "ApiGetScore", data, "download scores from server"); } @@ -9147,7 +9287,7 @@ static void LoadServerScoreFromCache(int nr) void LoadServerScore(int nr, boolean download_score) { - if (!setup.api_server) + if (!setup.use_api_server) return; // always start with reliable default values @@ -9157,11 +9297,11 @@ void LoadServerScore(int nr, boolean download_score) // (this should prevent reading it while the thread is writing to it) LoadServerScoreFromCache(nr); - if (download_score && runtime.api_server) + if (download_score && runtime.use_api_server) { // 2nd step: download server scores from score server to cache file // (as thread, as it might time out if the server is not reachable) - DownloadServerScoreToCacheAsThread(nr); + ApiGetScoreAsThread(nr); } } @@ -9171,7 +9311,7 @@ static char *get_file_base64(char *filename) if (stat(filename, &file_status) != 0) { - Error("cannot stat file '%s'\n", filename); + Error("cannot stat file '%s'", filename); return NULL; } @@ -9183,7 +9323,7 @@ static char *get_file_base64(char *filename) if (!(file = fopen(filename, MODE_READ))) { - Error("cannot open file '%s'\n", filename); + Error("cannot open file '%s'", filename); checked_free(buffer); @@ -9196,7 +9336,7 @@ static char *get_file_base64(char *filename) if (c == EOF) { - Error("cannot read from input file '%s'\n", filename); + Error("cannot read from input file '%s'", filename); fclose(file); checked_free(buffer); @@ -9219,50 +9359,113 @@ static char *get_file_base64(char *filename) return buffer_encoded; } -static void UploadScoreToServerExt(struct HttpRequest *request, - struct HttpResponse *response, - int nr) +struct ApiAddScoreThreadData +{ + int level_nr; + char *score_tape_filename; + struct ScoreEntry score_entry; +}; + +static void *CreateThreadData_ApiAddScore(int nr, 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->score_entry = *score_entry; + data->score_tape_filename = getStringCopy(score_tape_filename); + + return data; +} + +static void FreeThreadData_ApiAddScore(void *data_raw) +{ + struct ApiAddScoreThreadData *data = data_raw; + + 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; + 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_filename = getScoreTapeFilename(score_entry->tape_basename, nr); - char *tape_base64 = get_file_base64(tape_filename); + char *tape_base64 = get_file_base64(score_tape_filename); if (tape_base64 == NULL) { Error("loading and base64 encoding score tape file failed"); - return; + 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" + " \"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\": \"%s\"\n" "}\n", + getPasswordJSON(setup.api_server_password), getProgramRealVersionString(), - leveldir_current->identifier, - leveldir_current->name, - leveldir_current->author, + getProgramPlatformString(), + (int)global.autoplay_time, + levelset_identifier, + levelset_name, + levelset_author, leveldir_current->levels, leveldir_current->first_level, level_nr, - score_entry->name, + level_name, + level_author, + level.rate_time_over_score, + player_name, + player_uuid, score_entry->score, score_entry->time, score_entry->tape_basename, @@ -9270,8 +9473,32 @@ static void UploadScoreToServerExt(struct HttpRequest *request, 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 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()); @@ -9287,39 +9514,56 @@ static void UploadScoreToServerExt(struct HttpRequest *request, return; } + + HandleResponse_ApiAddScore(response, data_raw); } -static void UploadScoreToServer(int nr) +static void ApiAddScore_HttpRequest(struct HttpRequest *request, + struct HttpResponse *response, + void *data_raw) +{ + ApiAddScore_HttpRequestExt(request, response, data_raw); + + FreeThreadData_ApiAddScore(data_raw); +} + +static int ApiAddScoreThread(void *data_raw) { struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest)); struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); - UploadScoreToServerExt(request, response, nr); + ApiAddScore_HttpRequest(request, response, data_raw); checked_free(request); checked_free(response); -} - -static int UploadScoreToServerThread(void *data) -{ - UploadScoreToServer(*(int *)data); return 0; } -static void UploadScoreToServerAsThread(int nr) +static void ApiAddScoreAsThread(int nr, char *score_tape_filename) { - ExecuteAsThread(UploadScoreToServerThread, - "UploadScoreToServer", nr, + struct ApiAddScoreThreadData *data = + CreateThreadData_ApiAddScore(nr, score_tape_filename); + + ExecuteAsThread(ApiAddScoreThread, + "ApiAddScore", data, "upload score to server"); } void SaveServerScore(int nr) { - if (!runtime.api_server) + if (!runtime.use_api_server) + return; + + ApiAddScoreAsThread(nr, NULL); +} + +void SaveServerScoreFromFile(int nr, char *score_tape_filename) +{ + if (!runtime.use_api_server) return; - UploadScoreToServerAsThread(nr); + ApiAddScoreAsThread(nr, score_tape_filename); } void LoadLocalAndServerScore(int nr, boolean download_score) @@ -9331,7 +9575,7 @@ void LoadLocalAndServerScore(int nr, boolean download_score) // restore last added local score entry (before merging server scores) scores.last_added = scores.last_added_local = last_added_local; - if (setup.api_server && !setup.only_show_local_scores) + if (setup.use_api_server && !setup.only_show_local_scores) { // load server scores from cache file and trigger update from server LoadServerScore(nr, download_score); @@ -9355,6 +9599,10 @@ static struct TokenInfo global_setup_tokens[] = TYPE_STRING, &setup.player_name, "player_name" }, + { + TYPE_STRING, + &setup.player_uuid, "player_uuid" + }, { TYPE_SWITCH, &setup.multiple_users, "multiple_users" @@ -9525,7 +9773,11 @@ static struct TokenInfo global_setup_tokens[] = }, { TYPE_SWITCH, - &setup.show_snapshot_buttons, "show_snapshot_buttons" + &setup.show_load_save_buttons, "show_load_save_buttons" + }, + { + TYPE_SWITCH, + &setup.show_undo_redo_buttons, "show_undo_redo_buttons" }, { TYPE_SWITCH, @@ -9581,11 +9833,23 @@ static struct TokenInfo global_setup_tokens[] = }, { TYPE_SWITCH, - &setup.api_server, "api_server" + &setup.use_api_server, TEST_PREFIX "use_api_server" + }, + { + TYPE_STRING, + &setup.api_server_hostname, TEST_PREFIX "api_server_hostname" }, { TYPE_STRING, - &setup.api_server_hostname, "api_server_hostname" + &setup.api_server_password, TEST_PREFIX "api_server_password" + }, + { + TYPE_SWITCH, + &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes" + }, + { + TYPE_SWITCH, + &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes" }, { TYPE_STRING, @@ -10158,6 +10422,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) int i; si->player_name = getStringCopy(getDefaultUserName(user.nr)); + si->player_uuid = NULL; // (will be set later) si->multiple_users = TRUE; @@ -10202,7 +10467,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) si->game_frame_delay = GAME_FRAME_DELAY; si->sp_show_border_elements = FALSE; si->small_game_graphics = FALSE; - si->show_snapshot_buttons = FALSE; + si->show_load_save_buttons = FALSE; + si->show_undo_redo_buttons = FALSE; si->only_show_local_scores = FALSE; si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR); @@ -10221,8 +10487,11 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) si->network_player_nr = 0; // first player si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT); - si->api_server = TRUE; + si->use_api_server = TRUE; si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME); + si->api_server_password = getStringCopy(UNDEFINED_PASSWORD); + si->ask_for_uploading_tapes = TRUE; + si->provide_uploading_tapes = TRUE; si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT); si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent @@ -10702,6 +10971,14 @@ static void LoadSetup_SpecialPostProcessing(void) // make sure that scroll delay value stays inside valid range setup.scroll_delay_value = MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY); + + if (setup.player_uuid == NULL) + { + // player UUID does not yet exist in setup file + setup.player_uuid = getStringCopy(getUUID()); + + SaveSetup(); + } } void LoadSetup(void) @@ -10839,7 +11116,7 @@ void SaveSetup(void) global_setup_tokens[i].value == &setup.graphics_set || global_setup_tokens[i].value == &setup.volume_simple || global_setup_tokens[i].value == &setup.network_mode || - global_setup_tokens[i].value == &setup.api_server || + global_setup_tokens[i].value == &setup.use_api_server || global_setup_tokens[i].value == &setup.touch.control_type || global_setup_tokens[i].value == &setup.touch.grid_xsize[0] || global_setup_tokens[i].value == &setup.touch.grid_xsize[1]) @@ -13024,6 +13301,11 @@ void ConvertLevels(void) Print("converting level ... "); +#if 0 + // special case: conversion of some EMC levels as requested by ACME + level.game_engine_type = GAME_ENGINE_TYPE_RND; +#endif + level_filename = getDefaultLevelFilename(level_nr); new_level = !fileExists(level_filename);