From e4b2e4f943c20af98956d30fd3014888e9f75416 Mon Sep 17 00:00:00 2001 From: Holger Schemel Date: Sun, 25 Apr 2021 01:56:20 +0200 Subject: [PATCH] added loading high scores from score server --- src/files.c | 157 +++++++++++++++++++++++++++++++++++++++++++ src/files.h | 1 + src/game.c | 20 ++++++ src/game.h | 2 + src/libgame/system.h | 1 + src/main.c | 2 +- src/main.h | 2 +- src/screens.c | 6 ++ 8 files changed, 189 insertions(+), 2 deletions(-) diff --git a/src/files.c b/src/files.c index eab83304..83cbc059 100644 --- a/src/files.c +++ b/src/files.c @@ -8445,6 +8445,11 @@ static void setScoreInfoToDefaults(void) setScoreInfoToDefaultsExt(&scores); } +static void setServerScoreInfoToDefaults(void) +{ + setScoreInfoToDefaultsExt(&server_scores); +} + static void LoadScore_OLD(int nr) { int i; @@ -8899,6 +8904,158 @@ void SaveScore(int nr) SaveScoreToFilename(filename); } +static void DownloadServerScoreToCacheExt(struct HttpRequest *request, + struct HttpResponse *response, + int nr) +{ + request->hostname = API_SERVER_HOSTNAME; + request->port = API_SERVER_PORT; + request->method = API_SERVER_METHOD; + request->uri = API_SERVER_URI_GET; + + snprintf(request->body, MAX_HTTP_BODY_SIZE, + "{\n" + " \"levelset_identifier\": \"%s\",\n" + " \"level_nr\": \"%d\",\n" + " \"rate_time_over_score\": \"%d\"\n" + "}\n", + levelset.identifier, nr, level.rate_time_over_score); + + if (!DoHttpRequest(request, response)) + { + Error("HTTP request failed: %s", GetHttpError()); + + return; + } + + if (!HTTP_SUCCESS(response->status_code)) + { + Error("server failed to handle request: %d %s", + response->status_code, + response->status_text); + + return; + } + + if (response->body_size == 0) + { + // no scores available for this level + + return; + } + + ConvertHttpResponseBodyToClientEncoding(response); + + char *filename = getScoreCacheFilename(nr); + FILE *file; + int i; + + // used instead of "leveldir_current->subdir" (for network games) + InitScoreCacheDirectory(levelset.identifier); + + if (!(file = fopen(filename, MODE_WRITE))) + { + Warn("cannot save score cache file '%s'", filename); + + return; + } + + for (i = 0; i < response->body_size; i++) + fputc(response->body[i], file); + + fclose(file); + + SetFilePermissions(filename, PERMS_PRIVATE); +} + +static void DownloadServerScoreToCache(int nr) +{ + struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest)); + struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); + + DownloadServerScoreToCacheExt(request, response, nr); + + checked_free(request); + checked_free(response); +} + +static void LoadServerScoreFromCache(int nr) +{ + struct ScoreEntry score_entry; + struct + { + void *value; + boolean is_string; + int string_size; + } + score_mapping[] = + { + { &score_entry.score, FALSE, 0 }, + { &score_entry.time, FALSE, 0 }, + { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN }, + { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN }, + + { NULL, FALSE, 0 } + }; + char *filename = getScoreCacheFilename(nr); + SetupFileHash *score_hash = loadSetupFileHash(filename); + int i, j; + + server_scores.num_entries = 0; + + if (score_hash == NULL) + return; + + for (i = 0; i < MAX_SCORE_ENTRIES; i++) + { + score_entry = server_scores.entry[i]; + + for (j = 0; score_mapping[j].value != NULL; j++) + { + char token[10]; + + sprintf(token, "%02d.%d", i, j); + + char *value = getHashEntry(score_hash, token); + + if (value == NULL) + continue; + + if (score_mapping[j].is_string) + { + char *score_value = (char *)score_mapping[j].value; + int value_size = score_mapping[j].string_size; + + strncpy(score_value, value, value_size); + score_value[value_size] = '\0'; + } + else + { + int *score_value = (int *)score_mapping[j].value; + + *score_value = atoi(value); + } + + server_scores.num_entries = i + 1; + } + + server_scores.entry[i] = score_entry; + } + + freeSetupFileHash(score_hash); +} + +void LoadServerScore(int nr) +{ + // always start with reliable default values + setServerScoreInfoToDefaults(); + + DownloadServerScoreToCache(nr); + LoadServerScoreFromCache(nr); + + MergeServerScore(); +} + static char *get_file_base64(char *filename) { struct stat file_status; diff --git a/src/files.h b/src/files.h index 982f20ec..803762db 100644 --- a/src/files.h +++ b/src/files.h @@ -62,6 +62,7 @@ boolean SaveTapeChecked_LevelSolved(int); void LoadScore(int); void SaveScore(int); +void LoadServerScore(int); void SaveServerScore(int); void LoadUserNames(void); diff --git a/src/game.c b/src/game.c index 31ecdbdd..470ccf59 100644 --- a/src/game.c +++ b/src/game.c @@ -5066,6 +5066,10 @@ static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry) boolean entry_is_empty = (entry->score == 0 && entry->time == 0); + // prevent adding server score entries if also existing in local score file + if (strEqual(new_entry->tape_basename, entry->tape_basename)) + return -1; + if (is_better || entry_is_empty) { // player has made it to the hall of fame @@ -5135,6 +5139,22 @@ int NewHighScore(int level_nr) return scores.last_added; } +void MergeServerScore(void) +{ + int i; + + for (i = 0; i < server_scores.num_entries; i++) + { + int pos = addScoreEntry(&scores, &server_scores.entry[i]); + + if (pos >= 0 && pos <= scores.last_added) + scores.last_added++; + } + + if (scores.last_added >= MAX_SCORE_ENTRIES) + scores.last_added = -1; +} + static int getElementMoveStepsizeExt(int x, int y, int direction) { int element = Tile[x][y]; diff --git a/src/game.h b/src/game.h index 13bfdbc1..653a6454 100644 --- a/src/game.h +++ b/src/game.h @@ -417,6 +417,8 @@ void UpdateEngineValues(int, int, int, int); void GameWon(void); void GameEnd(void); +void MergeServerScore(void); + void InitPlayerGfxAnimation(struct PlayerInfo *, int, int); void Moving2Blocked(int, int, int *, int *); void Blocked2Moving(int, int, int *, int *); diff --git a/src/libgame/system.h b/src/libgame/system.h index 82657733..796db13e 100644 --- a/src/libgame/system.h +++ b/src/libgame/system.h @@ -110,6 +110,7 @@ #define API_SERVER_PORT 80 #define API_SERVER_METHOD "POST" #define API_SERVER_URI_ADD "/api/scores/add" +#define API_SERVER_URI_GET "/api/scores/get" // values for touch control #define TOUCH_CONTROL_OFF "off" diff --git a/src/main.c b/src/main.c index 8d8a61a0..49f32600 100644 --- a/src/main.c +++ b/src/main.c @@ -128,7 +128,7 @@ boolean network_player_action_received = FALSE; struct LevelInfo level, level_template; struct PlayerInfo stored_player[MAX_PLAYERS], *local_player = NULL; -struct ScoreInfo scores; +struct ScoreInfo scores, server_scores; struct TapeInfo tape; struct GameInfo game; struct GlobalInfo global; diff --git a/src/main.h b/src/main.h index fbd20906..2d4e61d6 100644 --- a/src/main.h +++ b/src/main.h @@ -3766,7 +3766,7 @@ extern boolean network_player_action_received; extern int graphics_action_mapping[]; extern struct LevelInfo level, level_template; -extern struct ScoreInfo scores; +extern struct ScoreInfo scores, server_scores; extern struct TapeInfo tape; extern struct GlobalInfo global; extern struct BorderInfo border; diff --git a/src/screens.c b/src/screens.c index c60881af..d86fa572 100644 --- a/src/screens.c +++ b/src/screens.c @@ -5051,6 +5051,12 @@ void DrawHallOfFame(int level_nr, int highlight_position) else SetAnimStatus(GAME_MODE_PSEUDO_SCORESNEW); + LoadServerScore(level_nr); + + // correct highlight position after adding server scores + if (highlight_position >= 0) + highlight_position = scores.last_added; + FadeSetEnterScreen(); FadeOut(fade_mask); -- 2.34.1