improved handling of failed tape uploads to score server
[rocksndiamonds.git] / src / files.c
index fab3df37653d139a61b68d0b52aa39e09c306e5a..fa5b9e45de312059aed05884faf00b2da6e12c91 100644 (file)
@@ -9114,20 +9114,28 @@ static boolean SetRequest_ApiGetScore(struct HttpRequest *request,
   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_identifier,
+          levelset_name,
           level_nr);
 
+  checked_free(levelset_identifier);
+  checked_free(levelset_name);
+
   ConvertHttpRequestBodyToServerEncoding(request);
 
   return TRUE;
@@ -9243,6 +9251,10 @@ static void ApiGetScore_HttpRequestExt(struct HttpRequest *request,
 
   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);
@@ -9268,12 +9280,16 @@ 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);
 
@@ -9429,14 +9445,31 @@ static char *get_file_base64(char *filename)
   return buffer_encoded;
 }
 
+static void PrepareScoreTapesForUpload(char *leveldir_subdir)
+{
+  MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
+
+  // if score tape not uploaded, ask for uploading missing tapes later
+  if (!setup.has_remaining_tapes)
+    setup.ask_for_remaining_tapes = TRUE;
+
+  setup.provide_uploading_tapes = TRUE;
+  setup.has_remaining_tapes = TRUE;
+
+  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, char *score_tape_filename)
+static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved,
+                                         char *score_tape_filename)
 {
   struct ApiAddScoreThreadData *data =
     checked_malloc(sizeof(struct ApiAddScoreThreadData));
@@ -9446,8 +9479,10 @@ static void *CreateThreadData_ApiAddScore(int nr, char *score_tape_filename)
     score_tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr);
 
   data->level_nr = nr;
-  data->score_entry = *score_entry;
+  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;
 }
@@ -9456,6 +9491,7 @@ 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);
 }
@@ -9466,6 +9502,7 @@ static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
   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;
@@ -9513,12 +9550,14 @@ static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
           "  \"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),
@@ -9533,12 +9572,14 @@ static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
           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);
@@ -9562,6 +9603,13 @@ static void HandleResponse_ApiAddScore(struct HttpResponse *response,
   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)
@@ -9577,6 +9625,8 @@ static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw,
   else
   {
     Error("server response too large to handle (%d bytes)", size);
+
+    HandleFailure_ApiAddScore(data_raw);
   }
 
   FreeThreadData_ApiAddScore(data_raw);
@@ -9587,6 +9637,8 @@ static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw,
 {
   Error("server failed to handle request: %d %s", code, status);
 
+  HandleFailure_ApiAddScore(data_raw);
+
   FreeThreadData_ApiAddScore(data_raw);
 }
 
@@ -9629,6 +9681,8 @@ static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
   {
     Error("HTTP request failed: %s", GetHttpError());
 
+    HandleFailure_ApiAddScore(data_raw);
+
     return;
   }
 
@@ -9638,6 +9692,8 @@ static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
          response->status_code,
          response->status_text);
 
+    HandleFailure_ApiAddScore(data_raw);
+
     return;
   }
 
@@ -9659,54 +9715,69 @@ 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, char *score_tape_filename)
+static void ApiAddScoreAsThread(int nr, boolean tape_saved,
+                               char *score_tape_filename)
 {
   struct ApiAddScoreThreadData *data =
-    CreateThreadData_ApiAddScore(nr, score_tape_filename);
+    CreateThreadData_ApiAddScore(nr, tape_saved, score_tape_filename);
 
   ExecuteAsThread(ApiAddScoreThread,
                  "ApiAddScore", data,
                  "upload score to server");
 }
 
-void SaveServerScore(int nr)
+void SaveServerScore(int nr, boolean tape_saved)
 {
   if (!runtime.use_api_server)
+  {
+    PrepareScoreTapesForUpload(leveldir_current->subdir);
+
     return;
+  }
 
-  ApiAddScoreAsThread(nr, NULL);
+  ApiAddScoreAsThread(nr, tape_saved, NULL);
 }
 
-void SaveServerScoreFromFile(int nr, char *score_tape_filename)
+void SaveServerScoreFromFile(int nr, boolean tape_saved,
+                            char *score_tape_filename)
 {
   if (!runtime.use_api_server)
     return;
 
-  ApiAddScoreAsThread(nr, score_tape_filename);
+  ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
 }
 
 void LoadLocalAndServerScore(int nr, boolean download_score)
 {
   int last_added_local = scores.last_added_local;
 
-  LoadScore(nr);
+  // needed if only showing server scores
+  setScoreInfoToDefaults();
+
+  if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
+    LoadScore(nr);
 
   // restore last added local score entry (before merging server scores)
   scores.last_added = scores.last_added_local = last_added_local;
 
-  if (setup.use_api_server && !setup.only_show_local_scores)
+  if (setup.use_api_server &&
+      !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
   {
     // load server scores from cache file and trigger update from server
     LoadServerScore(nr, download_score);
@@ -9907,8 +9978,8 @@ static struct TokenInfo global_setup_tokens[] =
     &setup.show_undo_redo_buttons,             "show_undo_redo_buttons"
   },
   {
-    TYPE_SWITCH,
-    &setup.only_show_local_scores,             "only_show_local_scores"
+    TYPE_STRING,
+    &setup.scores_in_highscore_list,           "scores_in_highscore_list"
   },
   {
     TYPE_STRING,
@@ -10030,10 +10101,22 @@ static struct TokenInfo server_setup_tokens[] =
     TYPE_SWITCH,
     &setup.ask_for_uploading_tapes, TEST_PREFIX        "ask_for_uploading_tapes"
   },
+  {
+    TYPE_SWITCH,
+    &setup.ask_for_remaining_tapes, TEST_PREFIX        "ask_for_remaining_tapes"
+  },
   {
     TYPE_SWITCH,
     &setup.provide_uploading_tapes, TEST_PREFIX        "provide_uploading_tapes"
   },
+  {
+    TYPE_SWITCH,
+    &setup.ask_for_using_api_server,TEST_PREFIX        "ask_for_using_api_server"
+  },
+  {
+    TYPE_SWITCH,
+    &setup.has_remaining_tapes,     TEST_PREFIX        "has_remaining_tapes"
+  },
 };
 
 static struct TokenInfo editor_setup_tokens[] =
@@ -10603,7 +10686,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->small_game_graphics = FALSE;
   si->show_load_save_buttons = FALSE;
   si->show_undo_redo_buttons = FALSE;
-  si->only_show_local_scores = FALSE;
+  si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
 
   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
@@ -10835,7 +10918,10 @@ static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
   si->ask_for_uploading_tapes = TRUE;
+  si->ask_for_remaining_tapes = FALSE;
   si->provide_uploading_tapes = TRUE;
+  si->ask_for_using_api_server = TRUE;
+  si->has_remaining_tapes = FALSE;
 }
 
 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)