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.
+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;
struct ApiAddScoreThreadData
{
int level_nr;
boolean tape_saved;
char *score_tape_filename;
struct ScoreEntry score_entry;
};
char *score_tape_filename;
struct ScoreEntry score_entry;
};
data->level_nr = nr;
data->tape_saved = 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_tape_filename = getStringCopy(score_tape_filename);
+ data->score_entry = *score_entry;
{
struct ApiAddScoreThreadData *data = data_raw;
{
struct ApiAddScoreThreadData *data = data_raw;
+ checked_free(data->leveldir_subdir);
checked_free(data->score_tape_filename);
checked_free(data);
}
checked_free(data->score_tape_filename);
checked_free(data);
}
server_scores.uploaded = TRUE;
}
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)
#if defined(PLATFORM_EMSCRIPTEN)
static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw,
void *buffer, unsigned int size)
else
{
Error("server response too large to handle (%d bytes)", size);
else
{
Error("server response too large to handle (%d bytes)", size);
+
+ HandleFailure_ApiAddScore(data_raw);
}
FreeThreadData_ApiAddScore(data_raw);
}
FreeThreadData_ApiAddScore(data_raw);
{
Error("server failed to handle request: %d %s", code, status);
{
Error("server failed to handle request: %d %s", code, status);
+ HandleFailure_ApiAddScore(data_raw);
+
FreeThreadData_ApiAddScore(data_raw);
}
FreeThreadData_ApiAddScore(data_raw);
}
{
Error("HTTP request failed: %s", GetHttpError());
{
Error("HTTP request failed: %s", GetHttpError());
+ HandleFailure_ApiAddScore(data_raw);
+
response->status_code,
response->status_text);
response->status_code,
response->status_text);
+ HandleFailure_ApiAddScore(data_raw);
+
void SaveServerScore(int nr, boolean tape_saved)
{
if (!runtime.use_api_server)
void SaveServerScore(int nr, boolean tape_saved)
{
if (!runtime.use_api_server)
+ {
+ PrepareScoreTapesForUpload(leveldir_current->subdir);
+
ApiAddScoreAsThread(nr, tape_saved, NULL);
}
ApiAddScoreAsThread(nr, tape_saved, NULL);
}
TYPE_SWITCH,
&setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
},
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.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.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[] =
};
static struct TokenInfo editor_setup_tokens[] =
si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
si->ask_for_uploading_tapes = TRUE;
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->provide_uploading_tapes = TRUE;
si->ask_for_using_api_server = TRUE;
+ si->has_remaining_tapes = FALSE;
}
static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
}
static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
print_timestamp_done("OpenAll");
print_timestamp_done("OpenAll");
+ if (setup.ask_for_remaining_tapes)
+ setup.ask_for_uploading_tapes = TRUE;
+
+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
// ----------------------------------------------------------------------------
// functions for directory handling
int getByteFromFile(File *);
char *getStringFromFile(File *, char *, int);
int copyFile(char *, char *);
int getByteFromFile(File *);
char *getStringFromFile(File *, char *, int);
int copyFile(char *, char *);
+boolean touchFile(char *);
Directory *openDirectory(char *);
int closeDirectory(Directory *);
Directory *openDirectory(char *);
int closeDirectory(Directory *);
return NULL; // cannot find specified artwork file anywhere
}
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)
{
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);
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)
}
void InitScoreDirectory(char *level_subdir)
char *getCustomArtworkLevelConfigFilename(int);
char *getCustomMusicDirectory(void);
char *getCustomArtworkLevelConfigFilename(int);
char *getCustomMusicDirectory(void);
+void MarkTapeDirectoryUploadsAsComplete(char *);
+void MarkTapeDirectoryUploadsAsIncomplete(char *);
+boolean CheckTapeDirectoryUploadsComplete(char *);
+
void InitTapeDirectory(char *);
void InitScoreDirectory(char *);
void InitScoreCacheDirectory(char *);
void InitTapeDirectory(char *);
void InitScoreDirectory(char *);
void InitScoreCacheDirectory(char *);
#define MUSICINFO_FILENAME "musicinfo.conf"
#define ARTWORKINFO_CACHE_FILE "artworkinfo.cache"
#define LEVELTEMPLATE_FILENAME "template.level"
#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"
#define LEVELFILE_EXTENSION "level"
#define TAPEFILE_EXTENSION "tape"
#define SCOREFILE_EXTENSION "score"
char *api_server_hostname;
char *api_server_password;
boolean ask_for_uploading_tapes;
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 provide_uploading_tapes;
boolean ask_for_using_api_server;
+ boolean has_remaining_tapes;
struct SetupAutoSetupInfo auto_setup;
struct SetupLevelSetupInfo level_setup;
struct SetupAutoSetupInfo auto_setup;
struct SetupLevelSetupInfo level_setup;
{ TYPE_SWITCH, &setup.use_api_server, "Use Highscore Server:" },
{ TYPE_ENTER_LIST, execSetupChooseScoresType,"Scores in Highscore List:" },
{ TYPE_STRING, &scores_type_text, "" },
{ 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:" },
{ 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:" },
static boolean OfferUploadTapes(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();
return FALSE;
int num_tapes_uploaded = UploadTapes();
if (num_tapes_uploaded < 0)
{
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();
Request(message, REQ_CONFIRM);
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;
// after all tapes have been uploaded, remove entry from setup menu
setup.provide_uploading_tapes = FALSE;
+ setup.has_remaining_tapes = FALSE;
if (!setup.ask_for_uploading_tapes)
return;
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_uploading_tapes = FALSE;
+ setup.ask_for_remaining_tapes = FALSE;
if (directoryExists(getTapeDir(NULL)))
{
boolean tapes_uploaded = OfferUploadTapes();
if (!tapes_uploaded)
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!",
init_level_set = FALSE;
}
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++;
if (global.autoplay_mode != AUTOPLAY_MODE_FIX || patch_nr == 0)
level_nr = autoplay.level_nr++;
if (!autoplay.all_levelsets)
break;
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);
// continue with next level set
autoplay.leveldir = getNextValidAutoPlayEntry(autoplay.leveldir);
+ num_tapes = -num_tapes;