added runtime flag to store if high score server is available
[rocksndiamonds.git] / src / files.c
index 83cbc05947dbb181f793d4b7c2610d3154541b61..d5118422d940e2021debdd88ac70a7793830aaf3 100644 (file)
@@ -8438,6 +8438,9 @@ static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
 
   scores->num_entries = 0;
   scores->last_added = -1;
+  scores->last_added_local = -1;
+
+  scores->updated = FALSE;
 }
 
 static void setScoreInfoToDefaults(void)
@@ -8904,6 +8907,24 @@ 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)
@@ -8966,6 +8987,8 @@ static void DownloadServerScoreToCacheExt(struct HttpRequest *request,
   fclose(file);
 
   SetFilePermissions(filename, PERMS_PRIVATE);
+
+  server_scores.updated = TRUE;
 }
 
 static void DownloadServerScoreToCache(int nr)
@@ -8979,6 +9002,20 @@ 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;
@@ -9045,15 +9082,24 @@ static void LoadServerScoreFromCache(int nr)
   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);
 
-  MergeServerScore();
+  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)
@@ -9159,6 +9205,8 @@ static void UploadScoreToServerExt(struct HttpRequest *request,
           score_entry->tape_basename,
           tape_base64);
 
+  checked_free(tape_base64);
+
   ConvertHttpRequestBodyToServerEncoding(request);
 
   if (!DoHttpRequest(request, response))
@@ -9189,9 +9237,45 @@ static void UploadScoreToServer(int nr)
   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, boolean download_score)
+{
+  int last_added_local = scores.last_added_local;
+
+  LoadScore(nr);
+
+  // restore last added local score entry (before merging server scores)
+  scores.last_added = scores.last_added_local = last_added_local;
+
+  if (setup.api_server)
+  {
+    // load server scores from cache file and trigger update from server
+    LoadServerScore(nr, download_score);
+
+    // merge local scores with scores from server
+    MergeServerScore();
+  }
 }
 
 
@@ -9428,6 +9512,14 @@ static struct TokenInfo global_setup_tokens[] =
     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"
@@ -10061,6 +10153,9 @@ 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->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
@@ -10676,6 +10771,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.touch.control_type       ||
        global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
        global_setup_tokens[i].value == &setup.touch.grid_xsize[1])