Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
+ Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
PrintLine("-", 79);
}
+void DumpLevels(void)
+{
+ static LevelDirTree *dumplevel_leveldir = NULL;
+
+ dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
+ global.dumplevel_leveldir);
+
+ if (dumplevel_leveldir == NULL)
+ Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
+
+ if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
+ global.dumplevel_level_nr > dumplevel_leveldir->last_level)
+ Fail("no such level number: %d", global.dumplevel_level_nr);
+
+ leveldir_current = dumplevel_leveldir;
+
+ LoadLevel(global.dumplevel_level_nr);
+ DumpLevel(&level);
+
+ CloseAllAndExit(0);
+}
+
// ============================================================================
// tape file functions
PrintLine("-", 79);
}
+void DumpTapes(void)
+{
+ static LevelDirTree *dumptape_leveldir = NULL;
+
+ dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
+ global.dumptape_leveldir);
+
+ if (dumptape_leveldir == NULL)
+ Fail("no such level identifier: '%s'", global.dumptape_leveldir);
+
+ if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
+ global.dumptape_level_nr > dumptape_leveldir->last_level)
+ Fail("no such level number: %d", global.dumptape_level_nr);
+
+ leveldir_current = dumptape_leveldir;
+
+ if (options.mytapes)
+ LoadTape(global.dumptape_level_nr);
+ else
+ LoadSolutionTape(global.dumptape_level_nr);
+
+ DumpTape(&tape);
+
+ CloseAllAndExit(0);
+}
+
// ============================================================================
// score file functions
scores->num_entries = 0;
scores->last_added = -1;
scores->last_added_local = -1;
+
+ scores->updated = FALSE;
+ scores->force_last_added = FALSE;
}
static void setScoreInfoToDefaults(void)
void SaveScore_OLD(int nr)
{
int i;
- int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
char *filename = getScoreFilename(nr);
FILE *file;
fclose(file);
- SetFilePermissions(filename, permissions);
+ SetFilePermissions(filename, PERMS_PRIVATE);
}
#endif
static void SaveScoreToFilename(char *filename)
{
FILE *file;
- int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
int info_chunk_size;
int name_chunk_size;
int scor_chunk_size;
fclose(file);
- SetFilePermissions(filename, permissions);
+ SetFilePermissions(filename, PERMS_PRIVATE);
}
void SaveScore(int nr)
SaveScoreToFilename(filename);
}
+static void ExecuteAsThread(SDL_ThreadFunction function, char *name, int data,
+ char *error)
+{
+ static int data_static;
+
+ data_static = data;
+
+ SDL_Thread *thread = SDL_CreateThread(function, name, &data_static);
+
+ 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);
+}
+
static void DownloadServerScoreToCacheExt(struct HttpRequest *request,
struct HttpResponse *response,
int nr)
{
- request->hostname = API_SERVER_HOSTNAME;
+ request->hostname = setup.api_server_hostname;
request->port = API_SERVER_PORT;
request->method = API_SERVER_METHOD;
request->uri = API_SERVER_URI_GET;
fclose(file);
SetFilePermissions(filename, PERMS_PRIVATE);
+
+ server_scores.updated = TRUE;
}
static void DownloadServerScoreToCache(int nr)
checked_free(response);
}
+static int DownloadServerScoreToCacheThread(void *data)
+{
+ DownloadServerScoreToCache(*(int *)data);
+
+ return 0;
+}
+
+static void DownloadServerScoreToCacheAsThread(int nr)
+{
+ ExecuteAsThread(DownloadServerScoreToCacheThread,
+ "DownloadServerScoreToCache", nr,
+ "download scores from server");
+}
+
static void LoadServerScoreFromCache(int nr)
{
struct ScoreEntry score_entry;
freeSetupFileHash(score_hash);
}
-void LoadServerScore(int nr)
+void LoadServerScore(int nr, boolean download_score)
{
+ if (!setup.api_server)
+ return;
+
// always start with reliable default values
setServerScoreInfoToDefaults();
- DownloadServerScoreToCache(nr);
+ // 1st step: load server scores from cache file (which may not exist)
+ // (this should prevent reading it while the thread is writing to it)
LoadServerScoreFromCache(nr);
+
+ if (download_score && runtime.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);
+ }
}
static char *get_file_base64(char *filename)
{
struct ScoreEntry *score_entry = &scores.entry[scores.last_added];
- request->hostname = API_SERVER_HOSTNAME;
+ request->hostname = setup.api_server_hostname;
request->port = API_SERVER_PORT;
request->method = API_SERVER_METHOD;
request->uri = API_SERVER_URI_ADD;
checked_free(response);
}
+static int UploadScoreToServerThread(void *data)
+{
+ UploadScoreToServer(*(int *)data);
+
+ return 0;
+}
+
+static void UploadScoreToServerAsThread(int nr)
+{
+ ExecuteAsThread(UploadScoreToServerThread,
+ "UploadScoreToServer", nr,
+ "upload score to server");
+}
+
void SaveServerScore(int nr)
{
- UploadScoreToServer(nr);
+ if (!runtime.api_server)
+ return;
+
+ UploadScoreToServerAsThread(nr);
}
-void LoadLocalAndServerScore(int nr)
+void LoadLocalAndServerScore(int nr, boolean download_score)
{
int last_added_local = scores.last_added_local;
// restore last added local score entry (before merging server scores)
scores.last_added = scores.last_added_local = last_added_local;
- LoadServerScore(nr);
+ if (setup.api_server && !setup.only_show_local_scores)
+ {
+ // load server scores from cache file and trigger update from server
+ LoadServerScore(nr, download_score);
- MergeServerScore();
+ // merge local scores with scores from server
+ MergeServerScore();
+ }
}
TYPE_SWITCH,
&setup.show_snapshot_buttons, "show_snapshot_buttons"
},
+ {
+ TYPE_SWITCH,
+ &setup.only_show_local_scores, "only_show_local_scores"
+ },
{
TYPE_STRING,
&setup.graphics_set, "graphics_set"
TYPE_STRING,
&setup.network_server_hostname, "network_server_hostname"
},
+ {
+ TYPE_SWITCH,
+ &setup.api_server, "api_server"
+ },
+ {
+ TYPE_STRING,
+ &setup.api_server_hostname, "api_server_hostname"
+ },
{
TYPE_STRING,
&setup.touch.control_type, "touch.control_type"
si->sp_show_border_elements = FALSE;
si->small_game_graphics = FALSE;
si->show_snapshot_buttons = FALSE;
+ si->only_show_local_scores = FALSE;
si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
si->network_player_nr = 0; // first player
si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
+ si->api_server = TRUE;
+ si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
+
si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
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.touch.control_type ||
global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
global_setup_tokens[i].value == &setup.touch.grid_xsize[1])