improved handling of failed tape uploads to score server 4.3.0.1
authorHolger Schemel <info@artsoft.org>
Thu, 11 Nov 2021 17:10:57 +0000 (18:10 +0100)
committerHolger Schemel <info@artsoft.org>
Thu, 11 Nov 2021 17:22:52 +0000 (18:22 +0100)
When initially mass-uploading personal tapes, a single failed upload
caused the whole process to be restarted from scratch, ignoring all
tapes that were already uploaded. This improvement marks completely
uploaded tape directories to be able to skip them after failures and
repeated upload attempts. (In addition, non-existing tape directories
are skipped right away, without checking for each single tape file.)

Another improvement remembers if tapes were not uploaded (either
because the score server was disabled, or because uploading the tape
failed). If this happens, the game adds an entry to upload tapes to
the setup menu, and asks for uploading missing tapes on next start.

src/files.c
src/init.c
src/libgame/misc.c
src/libgame/misc.h
src/libgame/setup.c
src/libgame/setup.h
src/libgame/system.h
src/screens.c
src/tape.c

index 28a19e67eefeaa78495d58e0f97a381fd001e4df..fa5b9e45de312059aed05884faf00b2da6e12c91 100644 (file)
@@ -9445,10 +9445,25 @@ 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;
 };
@@ -9465,8 +9480,9 @@ static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved,
 
   data->level_nr = nr;
   data->tape_saved = tape_saved;
-  data->score_entry = *score_entry;
+  data->leveldir_subdir = getStringCopy(leveldir_current->subdir);
   data->score_tape_filename = getStringCopy(score_tape_filename);
+  data->score_entry = *score_entry;
 
   return data;
 }
@@ -9475,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);
 }
@@ -9586,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)
@@ -9601,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);
@@ -9611,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);
 }
 
@@ -9653,6 +9681,8 @@ static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
   {
     Error("HTTP request failed: %s", GetHttpError());
 
+    HandleFailure_ApiAddScore(data_raw);
+
     return;
   }
 
@@ -9662,6 +9692,8 @@ static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
          response->status_code,
          response->status_text);
 
+    HandleFailure_ApiAddScore(data_raw);
+
     return;
   }
 
@@ -9713,7 +9745,11 @@ static void ApiAddScoreAsThread(int nr, boolean tape_saved,
 void SaveServerScore(int nr, boolean tape_saved)
 {
   if (!runtime.use_api_server)
+  {
+    PrepareScoreTapesForUpload(leveldir_current->subdir);
+
     return;
+  }
 
   ApiAddScoreAsThread(nr, tape_saved, NULL);
 }
@@ -10065,6 +10101,10 @@ 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"
@@ -10073,6 +10113,10 @@ static struct TokenInfo server_setup_tokens[] =
     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[] =
@@ -10874,8 +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)
index 8c6c7e12e1d6d7010cb9fdfea0e03a15dba24c9c..2d6b750dbd19b4307f116b607175a56f13ed5fc8 100644 (file)
@@ -6330,6 +6330,9 @@ void OpenAll(void)
 
   print_timestamp_done("OpenAll");
 
+  if (setup.ask_for_remaining_tapes)
+    setup.ask_for_uploading_tapes = TRUE;
+
   DrawMainMenu();
 
 #if 0
index 8bc457216de7448418ad5cebd885728e562b5b60..faf6c011f25a873980f7b42c1c0a0c24a3a858af 100644 (file)
@@ -2786,6 +2786,22 @@ int copyFile(char *filename_from, char *filename_to)
   return 0;
 }
 
+boolean touchFile(char *filename)
+{
+  FILE *file;
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Warn("cannot touch file '%s'", filename);
+
+    return FALSE;
+  }
+
+  fclose(file);
+
+  return TRUE;
+}
+
 
 // ----------------------------------------------------------------------------
 // functions for directory handling
index 83b7773018d21f24b374cac499885da11e1a2aac..27ae0f3df394bdacd632f9195c430295f7b4545d 100644 (file)
@@ -258,6 +258,7 @@ int seekFile(File *, long, int);
 int getByteFromFile(File *);
 char *getStringFromFile(File *, char *, int);
 int copyFile(char *, char *);
+boolean touchFile(char *);
 
 Directory *openDirectory(char *);
 int closeDirectory(Directory *);
index 0a406540224696e012e1243fe9944407a62a4863..9ea1754932adb1371c9d994aca8e8af497411e3a 100644 (file)
@@ -1155,11 +1155,44 @@ char *getCustomMusicDirectory(void)
   return NULL;         // cannot find specified artwork file anywhere
 }
 
+void MarkTapeDirectoryUploadsAsComplete(char *level_subdir)
+{
+  char *filename = getPath2(getTapeDir(level_subdir), UPLOADED_FILENAME);
+
+  touchFile(filename);
+
+  checked_free(filename);
+}
+
+void MarkTapeDirectoryUploadsAsIncomplete(char *level_subdir)
+{
+  char *filename = getPath2(getTapeDir(level_subdir), UPLOADED_FILENAME);
+
+  unlink(filename);
+
+  checked_free(filename);
+}
+
+boolean CheckTapeDirectoryUploadsComplete(char *level_subdir)
+{
+  char *filename = getPath2(getTapeDir(level_subdir), UPLOADED_FILENAME);
+  boolean success = fileExists(filename);
+
+  checked_free(filename);
+
+  return success;
+}
+
 void InitTapeDirectory(char *level_subdir)
 {
+  boolean new_tape_dir = !directoryExists(getTapeDir(level_subdir));
+
   createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
   createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
   createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
+
+  if (new_tape_dir)
+    MarkTapeDirectoryUploadsAsComplete(level_subdir);
 }
 
 void InitScoreDirectory(char *level_subdir)
index 68f67a7825b06360ec610cbf6ff0e0f6c10c2e74..ab52a7a8adcd82695f92f7fca3d2f5d57b2f6480 100644 (file)
@@ -288,6 +288,10 @@ char *getCustomArtworkConfigFilename(int);
 char *getCustomArtworkLevelConfigFilename(int);
 char *getCustomMusicDirectory(void);
 
+void MarkTapeDirectoryUploadsAsComplete(char *);
+void MarkTapeDirectoryUploadsAsIncomplete(char *);
+boolean CheckTapeDirectoryUploadsComplete(char *);
+
 void InitTapeDirectory(char *);
 void InitScoreDirectory(char *);
 void InitScoreCacheDirectory(char *);
index d9b42321ba46b4ee59fb9936ded83414a197a8b7..8ad5c08f8387b2cdc8e49f0187dfb9d46958ec8c 100644 (file)
 #define MUSICINFO_FILENAME     "musicinfo.conf"
 #define ARTWORKINFO_CACHE_FILE "artworkinfo.cache"
 #define LEVELTEMPLATE_FILENAME "template.level"
+#define UPLOADED_FILENAME      ".uploaded"
 #define LEVELFILE_EXTENSION    "level"
 #define TAPEFILE_EXTENSION     "tape"
 #define SCOREFILE_EXTENSION    "score"
@@ -1514,8 +1515,10 @@ struct SetupInfo
   char *api_server_hostname;
   char *api_server_password;
   boolean ask_for_uploading_tapes;
+  boolean ask_for_remaining_tapes;
   boolean provide_uploading_tapes;
   boolean ask_for_using_api_server;
+  boolean has_remaining_tapes;
 
   struct SetupAutoSetupInfo auto_setup;
   struct SetupLevelSetupInfo level_setup;
index cf8077f8ddb392eee40b9b26baed66089c0c29ba..e34a9df5cad6ab396cd922fad08bc0f8c24e7297 100644 (file)
@@ -7076,7 +7076,7 @@ static struct TokenInfo setup_info_game[] =
   { TYPE_SWITCH,       &setup.use_api_server,  "Use Highscore Server:" },
   { TYPE_ENTER_LIST,   execSetupChooseScoresType,"Scores in Highscore List:" },
   { TYPE_STRING,       &scores_type_text,      ""                      },
-  { TYPE_ENTER_LIST,   execOfferUploadTapes,   "Upload All Tapes to Server" },
+  { TYPE_ENTER_LIST,   execOfferUploadTapes,   "Upload Tapes to Server" },
   { TYPE_SWITCH,       &setup.multiple_users,  "Multiple Users/Teams:" },
   { TYPE_YES_NO,       &setup.input_on_focus,  "Only Move Focussed Player:" },
   { TYPE_SWITCH,       &setup.time_limit,      "Time Limit:"           },
@@ -10072,7 +10072,9 @@ static int UploadTapes(void)
 
 static boolean OfferUploadTapes(void)
 {
-  if (!Request("Upload all your tapes to the high score server now?", REQ_ASK))
+  if (!Request(setup.has_remaining_tapes ?
+              "Upload missing tapes to the high score server now?" :
+              "Upload all your tapes to the high score server now?", REQ_ASK))
     return FALSE;
 
   int num_tapes_uploaded = UploadTapes();
@@ -10080,7 +10082,23 @@ static boolean OfferUploadTapes(void)
 
   if (num_tapes_uploaded < 0)
   {
-    Request("Cannot upload tapes to score server!", REQ_CONFIRM);
+    num_tapes_uploaded = -num_tapes_uploaded - 1;
+
+    if (num_tapes_uploaded == 0)
+      sprintf(message, "Upload failed! No tapes uploaded!");
+    else if (num_tapes_uploaded == 1)
+      sprintf(message, "Upload failed! Only 1 tape uploaded!");
+    else
+      sprintf(message, "Upload failed! Only %d tapes uploaded!",
+             num_tapes_uploaded);
+
+    Request(message, REQ_CONFIRM);
+
+    // if uploading tapes failed, add tape upload entry to setup menu
+    setup.provide_uploading_tapes = TRUE;
+    setup.has_remaining_tapes = TRUE;
+
+    SaveSetup_ServerSetup();
 
     return FALSE;
   }
@@ -10094,8 +10112,12 @@ static boolean OfferUploadTapes(void)
 
   Request(message, REQ_CONFIRM);
 
+  if (num_tapes_uploaded > 0)
+    Request("New scores will be visible after a few minutes!", REQ_CONFIRM);
+
   // after all tapes have been uploaded, remove entry from setup menu
   setup.provide_uploading_tapes = FALSE;
+  setup.has_remaining_tapes = FALSE;
 
   SaveSetup_ServerSetup();
 
@@ -10107,16 +10129,21 @@ void CheckUploadTapes(void)
   if (!setup.ask_for_uploading_tapes)
     return;
 
-  // after asking for uploading all tapes once, do not ask again
+  // after asking for uploading tapes, do not ask again
   setup.ask_for_uploading_tapes = FALSE;
+  setup.ask_for_remaining_tapes = FALSE;
 
   if (directoryExists(getTapeDir(NULL)))
   {
     boolean tapes_uploaded = OfferUploadTapes();
 
     if (!tapes_uploaded)
-      Request("You can upload your tapes from the setup menu later!",
+    {
+      Request(setup.has_remaining_tapes ?
+             "You can upload missing tapes from the setup menu later!" :
+             "You can upload your tapes from the setup menu later!",
              REQ_CONFIRM);
+    }
   }
   else
   {
index 03ecf68944bc6cec9726be31cd6e6ff808d70b49..2a1a3ec345e5b65e85279bae23234da2ed97f801 100644 (file)
@@ -1668,6 +1668,41 @@ static int AutoPlayTapesExt(boolean initialize)
       init_level_set = FALSE;
     }
 
+    if (autoplay.all_levelsets && global.autoplay_mode == AUTOPLAY_MODE_UPLOAD)
+    {
+      boolean skip_levelset = FALSE;
+
+      if (!directoryExists(getTapeDir(autoplay.leveldir->subdir)))
+      {
+       Print("No tape directory for this level set found -- skipping.\n");
+
+       skip_levelset = TRUE;
+      }
+
+      if (CheckTapeDirectoryUploadsComplete(autoplay.leveldir->subdir))
+      {
+       Print("All tapes for this level set already uploaded -- skipping.\n");
+
+       skip_levelset = TRUE;
+      }
+
+      if (skip_levelset)
+      {
+       PrintTapeReplaySummary(&autoplay);
+
+       // continue with next level set
+       autoplay.leveldir = getNextValidAutoPlayEntry(autoplay.leveldir);
+
+       // all level sets processed
+       if (autoplay.leveldir == NULL)
+         break;
+
+       init_level_set = TRUE;
+
+       continue;
+      }
+    }
+
     if (global.autoplay_mode != AUTOPLAY_MODE_FIX || patch_nr == 0)
       level_nr = autoplay.level_nr++;
 
@@ -1681,6 +1716,9 @@ static int AutoPlayTapesExt(boolean initialize)
       if (!autoplay.all_levelsets)
        break;
 
+      if (global.autoplay_mode == AUTOPLAY_MODE_UPLOAD)
+       MarkTapeDirectoryUploadsAsComplete(autoplay.leveldir->subdir);
+
       // continue with next level set
       autoplay.leveldir = getNextValidAutoPlayEntry(autoplay.leveldir);
 
@@ -1841,7 +1879,7 @@ static int AutoPlayTapesExt(boolean initialize)
 
       if (!success)
       {
-       num_tapes = -1;
+       num_tapes = -num_tapes;
 
        break;
       }