From 02507e564e505c18fa720d42ba59c4d86dd6f48c Mon Sep 17 00:00:00 2001 From: emlai Date: Sun, 2 Oct 2016 12:58:09 +0300 Subject: [PATCH 01/12] Global hall of fame initial commit - Add dependency on libcurl. - Highscore menu now has two views: local and global scores. The enter key toggles between them. - Global highscores are fetched from the server when entering the global highscore listing. The results are received in CSV format. - New local scores are submitted to highscore server. - The server URL is currently hardcoded to localhost. --- FeLib/CMakeLists.txt | 7 +- FeLib/Include/felibdef.h | 1 + FeLib/Include/hscore.h | 13 ++- FeLib/Source/felist.cpp | 9 +- FeLib/Source/hscore.cpp | 173 +++++++++++++++++++++++++++++++++++---- 5 files changed, 182 insertions(+), 21 deletions(-) diff --git a/FeLib/CMakeLists.txt b/FeLib/CMakeLists.txt index 80362a4be..1f28e718c 100644 --- a/FeLib/CMakeLists.txt +++ b/FeLib/CMakeLists.txt @@ -16,6 +16,9 @@ else() message(STATUS "Using SDL1") endif() +# libcurl, for sending/fetching high-scores to/from a global high-score server. +find_package(CURL) + if(MSVC) # Not very pretty solution. This finds SDL2.dll from SDL2.lib path, so that it can be installed where ivan.exe will end up. message("SDL2_LIBRARY ${SDL2_LIBRARY}") @@ -39,5 +42,5 @@ if(MINGW) endif() target_include_directories(FeLib PUBLIC Include) -target_include_directories(FeLib SYSTEM PUBLIC ${SDL2_INCLUDE_DIR}) -target_link_libraries(FeLib ${SDL2_LIBRARY}) +target_include_directories(FeLib SYSTEM PUBLIC ${SDL2_INCLUDE_DIR} ${CURL_INCLUDE_DIRS}) +target_link_libraries(FeLib ${SDL2_LIBRARY} ${CURL_LIBRARIES}) diff --git a/FeLib/Include/felibdef.h b/FeLib/Include/felibdef.h index 8c21d4920..fa492d1e1 100644 --- a/FeLib/Include/felibdef.h +++ b/FeLib/Include/felibdef.h @@ -214,6 +214,7 @@ inline int GetMinColor24(col24 Color) #define LIST_WAS_EMPTY 0xFFFF #define ESCAPED 0xFFFE #define NOTHING_SELECTED 0xFFFD +#define UNSELECTABLE_SELECT 0xFFFC #define NO_LIMIT 0xFFFF diff --git a/FeLib/Include/hscore.h b/FeLib/Include/hscore.h index 4caf3ff3d..b555dd13c 100644 --- a/FeLib/Include/hscore.h +++ b/FeLib/Include/hscore.h @@ -28,12 +28,18 @@ class festring; +enum highscoreview +{ + LOCAL, + GLOBAL +}; + class highscore { public: highscore(cfestring& = HIGH_SCORE_FILENAME); truth Add(long, cfestring&); - void Draw() const; + void Draw(); void Save(cfestring& = HIGH_SCORE_FILENAME) const; void Load(cfestring& = HIGH_SCORE_FILENAME); truth LastAddFailed() const; @@ -48,12 +54,17 @@ class highscore truth CheckVersion() const; private: truth Add(long, cfestring&, time_t, long); + void ToggleBetweenLocalAndGlobalView() { View = static_cast(!View); } std::vector Entry; std::vector Score; std::vector Time; std::vector RandomID; + std::vector GlobalEntry; + std::vector GlobalScore; + std::vector GlobalTime; int LastAdd; ushort Version; + static highscoreview View; }; #endif diff --git a/FeLib/Source/felist.cpp b/FeLib/Source/felist.cpp index 4a60b9567..5f5757a20 100644 --- a/FeLib/Source/felist.cpp +++ b/FeLib/Source/felist.cpp @@ -252,9 +252,14 @@ uint felist::Draw() continue; } - if(Flags & SELECTABLE && Pressed == KEY_ENTER) + if(Pressed == KEY_ENTER) { - Return = Selected; + if(Flags & SELECTABLE) + Return = Selected; + else + // Used by the hall of fame to toggle between local and global scores. + Return = UNSELECTABLE_SELECT; + break; } diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp index c4627fd55..73f553edc 100644 --- a/FeLib/Source/hscore.cpp +++ b/FeLib/Source/hscore.cpp @@ -10,15 +10,26 @@ * */ +#include + #include "hscore.h" #include "save.h" #include "felist.h" #include "feio.h" #include "femath.h" +static truth RetrieveHighScoresFromServer(std::vector&, + std::vector&, + std::vector&); +static void SubmitHighScoreToServer(long, cfestring&, time_t, long); + /* Increment this if changes make highscores incompatible */ #define HIGH_SCORE_VERSION 128 +#define HIGH_SCORE_SERVER "http://localhost:3332" + +highscoreview highscore::View = LOCAL; + cfestring& highscore::GetEntry(int I) const { return Entry[I]; } long highscore::GetScore(int I) const { return Score[I]; } long highscore::GetSize() const { return Entry.size(); } @@ -28,6 +39,8 @@ highscore::highscore(cfestring& File) : LastAdd(0xFF), Version(HIGH_SCORE_VERSIO truth highscore::Add(long NewScore, cfestring& NewEntry, time_t NewTime, long NewRandomID) { + SubmitHighScoreToServer(NewScore, NewEntry, NewTime, NewRandomID); + for(uint c = 0; c < Score.size(); ++c) if(Score[c] < NewScore) { @@ -64,7 +77,7 @@ truth highscore::Add(long NewScore, cfestring& NewEntry, } } -void highscore::Draw() const +void highscore::Draw() { if(Score.empty()) { @@ -79,23 +92,61 @@ void highscore::Draw() const return; } - felist List(CONST_S("Adventurers' Hall of Fame")); - festring Desc; - - for(uint c = 0; c < Score.size(); ++c) + for(;;) { - Desc.Empty(); - Desc << c + 1; - Desc.Resize(5, ' '); - Desc << Score[c]; - Desc.Resize(13, ' '); - Desc << Entry[c]; - List.AddEntry(Desc, c == uint(LastAdd) ? WHITE : LIGHT_GRAY, 13); - } + festring Title; + if(View == LOCAL) + Title = CONST_S("Adventurers' Hall of Fame " + "Press ENTER to view global highscores"); + else if(View == GLOBAL) + Title = CONST_S("Global Adventurers' Hall of Fame " + "Press ENTER to view local highscores"); + + felist List(Title); + festring Desc; + + if(View == LOCAL) + for(uint c = 0; c < Score.size(); ++c) + { + Desc.Empty(); + Desc << c + 1; + Desc.Resize(5, ' '); + Desc << Score[c]; + Desc.Resize(13, ' '); + Desc << Entry[c]; + List.AddEntry(Desc, c == uint(LastAdd) ? WHITE : LIGHT_GRAY, 13); + } + else if(View == GLOBAL) + { + RetrieveHighScoresFromServer(GlobalEntry, GlobalScore, GlobalTime); + + for(uint c = 0; c < GlobalScore.size(); ++c) + { + Desc.Empty(); + Desc << c + 1; + Desc.Resize(5, ' '); + Desc << GlobalScore[c]; + Desc.Resize(13, ' '); + Desc << GlobalEntry[c]; + List.AddEntry(Desc, LIGHT_GRAY, 13); + } + } + + List.SetFlags(FADE); + List.SetPageLength(40); - List.SetFlags(FADE); - List.SetPageLength(40); - List.Draw(); + cuint DrawResult = List.Draw(); + + if(DrawResult == UNSELECTABLE_SELECT) // Enter was pressed. + ToggleBetweenLocalAndGlobalView(); + else if(DrawResult == LIST_WAS_EMPTY) + { + iosystem::TextScreen(CONST_S("Couldn't fetch global highscores from the server.")); + View = LOCAL; + } + else + break; + } } void highscore::Save(cfestring& File) const @@ -183,3 +234,93 @@ truth highscore::CheckVersion() const { return Version == HIGH_SCORE_VERSION; } + +static void ParseHighScoresFromCSV(cfestring& CSV, + std::vector& GlobalEntry, + std::vector& GlobalScore, + std::vector& GlobalTime) +{ + for(festring::sizetype LastIndex = 0;;) + { + festring::csizetype ScoreIndex = CSV.Find("\n", LastIndex) + 1; + if(!ScoreIndex || ScoreIndex == festring::NPos) return; + GlobalScore.push_back(atol(CSV.CStr() + ScoreIndex)); + festring::csizetype EntryIndex = CSV.Find("\"", ScoreIndex) + 1; + festring::csizetype EntryLength = CSV.Find("\"", EntryIndex) - EntryIndex; + GlobalEntry.push_back(festring(CSV.CStr() + EntryIndex, EntryLength) + '\0'); + GlobalTime.push_back(0); // TODO + LastIndex = EntryIndex + EntryLength; + } +} + +static size_t WriteMemoryCallback(char* Contents, size_t Size, size_t Count, void* UserData) +{ + const size_t RealSize = Size * Count; + *static_cast(UserData) << cfestring(Contents, RealSize); + return RealSize; +} + +static truth RetrieveHighScoresFromServer(std::vector& GlobalEntry, + std::vector& GlobalScore, + std::vector& GlobalTime) +{ + if(curl_global_init(CURL_GLOBAL_ALL) != 0) + return false; + + truth Success = false; + + if(CURL* Curl = curl_easy_init()) + { + festring RetrievedData; + + curl_easy_setopt(Curl, CURLOPT_URL, HIGH_SCORE_SERVER "/highscores"); + curl_easy_setopt(Curl, CURLOPT_HTTPGET, 1); + curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, &WriteMemoryCallback); + curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &RetrievedData); + + if(curl_easy_perform(Curl) == CURLE_OK) + { + GlobalEntry.clear(); + GlobalScore.clear(); + GlobalTime.clear(); + ParseHighScoresFromCSV(RetrievedData, GlobalEntry, GlobalScore, GlobalTime); + Success = true; + } + + curl_easy_cleanup(Curl); + } + + curl_global_cleanup(); + return Success; +} + +static void SubmitHighScoreToServer(long NewScore, cfestring& NewEntry, + time_t NewTime, long NewRandomID) +{ + if(curl_global_init(CURL_GLOBAL_ALL) != 0) + return; + + if(CURL* Curl = curl_easy_init()) + { + curl_slist* const Headers = curl_slist_append(nullptr, "Content-Type: application/json"); + + festring Json; + Json << + "{" + "\"score\": " << NewScore << "," + "\"entry\": \"" << NewEntry << "\"" + "}"; + + curl_easy_setopt(Curl, CURLOPT_URL, HIGH_SCORE_SERVER "/submit_score"); + curl_easy_setopt(Curl, CURLOPT_POST, 1); + curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Headers); + curl_easy_setopt(Curl, CURLOPT_POSTFIELDS, Json.CStr()); + + curl_easy_perform(Curl); + + curl_slist_free_all(Headers); + curl_easy_cleanup(Curl); + } + + curl_global_cleanup(); +} From df83e5a501d3f48a1e7f9bb2f3707c370cf7144f Mon Sep 17 00:00:00 2001 From: emlai Date: Thu, 16 Mar 2017 22:20:36 +0200 Subject: [PATCH 02/12] Don't stall if server returns 404 --- FeLib/Source/hscore.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp index 73f553edc..0a332e149 100644 --- a/FeLib/Source/hscore.cpp +++ b/FeLib/Source/hscore.cpp @@ -277,8 +277,11 @@ static truth RetrieveHighScoresFromServer(std::vector& GlobalEntry, curl_easy_setopt(Curl, CURLOPT_HTTPGET, 1); curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, &WriteMemoryCallback); curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &RetrievedData); + CURLcode Res = curl_easy_perform(Curl); + long ResponseCode; + curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &ResponseCode); - if(curl_easy_perform(Curl) == CURLE_OK) + if(Res == CURLE_OK && ResponseCode == 200) { GlobalEntry.clear(); GlobalScore.clear(); From 59d32657f0e12783c6b1cb05ac8b11af40e239a9 Mon Sep 17 00:00:00 2001 From: emlai Date: Fri, 17 Mar 2017 23:06:56 +0200 Subject: [PATCH 03/12] Set high-score server to production Heroku instance by default Wrap in #ifndef to allow it to be easily redefined e.g. during development. --- FeLib/Source/hscore.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp index 0a332e149..881a80f18 100644 --- a/FeLib/Source/hscore.cpp +++ b/FeLib/Source/hscore.cpp @@ -26,7 +26,9 @@ static void SubmitHighScoreToServer(long, cfestring&, time_t, long); /* Increment this if changes make highscores incompatible */ #define HIGH_SCORE_VERSION 128 -#define HIGH_SCORE_SERVER "http://localhost:3332" +#ifndef HIGH_SCORE_SERVER +#define HIGH_SCORE_SERVER "https://ivan-hall-of-fame.herokuapp.com" +#endif highscoreview highscore::View = LOCAL; From 65a40da93d710d011822a004dd230e0eee15eef8 Mon Sep 17 00:00:00 2001 From: emlai Date: Sat, 18 Mar 2017 02:13:09 +0200 Subject: [PATCH 04/12] Move high-score server URL to config file TODO: Hide it from the in-game menu. --- FeLib/Include/hscore.h | 6 +++--- FeLib/Source/hscore.cpp | 42 +++++++++++++++++++++++------------------ Main/Include/iconf.h | 2 ++ Main/Source/char.cpp | 3 ++- Main/Source/game.cpp | 2 +- Main/Source/iconf.cpp | 4 ++++ Main/Source/main.cpp | 2 +- 7 files changed, 37 insertions(+), 24 deletions(-) diff --git a/FeLib/Include/hscore.h b/FeLib/Include/hscore.h index b555dd13c..934f32e54 100644 --- a/FeLib/Include/hscore.h +++ b/FeLib/Include/hscore.h @@ -38,8 +38,8 @@ class highscore { public: highscore(cfestring& = HIGH_SCORE_FILENAME); - truth Add(long, cfestring&); - void Draw(); + truth Add(long, cfestring&, cfestring&); + void Draw(cfestring&); void Save(cfestring& = HIGH_SCORE_FILENAME) const; void Load(cfestring& = HIGH_SCORE_FILENAME); truth LastAddFailed() const; @@ -53,7 +53,7 @@ class highscore void Clear(); truth CheckVersion() const; private: - truth Add(long, cfestring&, time_t, long); + truth Add(long, cfestring&, time_t, long, cfestring&); void ToggleBetweenLocalAndGlobalView() { View = static_cast(!View); } std::vector Entry; std::vector Score; diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp index 881a80f18..8901a85dd 100644 --- a/FeLib/Source/hscore.cpp +++ b/FeLib/Source/hscore.cpp @@ -18,18 +18,15 @@ #include "feio.h" #include "femath.h" -static truth RetrieveHighScoresFromServer(std::vector&, +static truth RetrieveHighScoresFromServer(cfestring&, + std::vector&, std::vector&, std::vector&); -static void SubmitHighScoreToServer(long, cfestring&, time_t, long); +static void SubmitHighScoreToServer(cfestring&, long, cfestring&, time_t, long); /* Increment this if changes make highscores incompatible */ #define HIGH_SCORE_VERSION 128 -#ifndef HIGH_SCORE_SERVER -#define HIGH_SCORE_SERVER "https://ivan-hall-of-fame.herokuapp.com" -#endif - highscoreview highscore::View = LOCAL; cfestring& highscore::GetEntry(int I) const { return Entry[I]; } @@ -38,10 +35,12 @@ long highscore::GetSize() const { return Entry.size(); } highscore::highscore(cfestring& File) : LastAdd(0xFF), Version(HIGH_SCORE_VERSION) { Load(File); } -truth highscore::Add(long NewScore, cfestring& NewEntry, - time_t NewTime, long NewRandomID) +truth highscore::Add(long NewScore, cfestring& NewEntry, time_t NewTime, + long NewRandomID, cfestring& HighScoreServerURL) { - SubmitHighScoreToServer(NewScore, NewEntry, NewTime, NewRandomID); + if (!HighScoreServerURL.IsEmpty()) + SubmitHighScoreToServer(HighScoreServerURL, NewScore, NewEntry, + NewTime, NewRandomID); for(uint c = 0; c < Score.size(); ++c) if(Score[c] < NewScore) @@ -79,7 +78,7 @@ truth highscore::Add(long NewScore, cfestring& NewEntry, } } -void highscore::Draw() +void highscore::Draw(cfestring& HighScoreServerURL) { if(Score.empty()) { @@ -120,7 +119,8 @@ void highscore::Draw() } else if(View == GLOBAL) { - RetrieveHighScoresFromServer(GlobalEntry, GlobalScore, GlobalTime); + RetrieveHighScoresFromServer(HighScoreServerURL, GlobalEntry, + GlobalScore, GlobalTime); for(uint c = 0; c < GlobalScore.size(); ++c) { @@ -191,16 +191,17 @@ truth highscore::MergeToFile(highscore* To) const for(uint c = 0; c < Score.size(); ++c) if(!To->Find(Score[c], Entry[c], Time[c], RandomID[c])) { - To->Add(Score[c], Entry[c], Time[c], RandomID[c]); + To->Add(Score[c], Entry[c], Time[c], RandomID[c], ""); MergedSomething = true; } return MergedSomething; } -truth highscore::Add(long NewScore, cfestring& NewEntry) +truth highscore::Add(long NewScore, cfestring& NewEntry, + cfestring& HighScoreServerURL) { - return Add(NewScore, NewEntry, time(0), RAND()); + return Add(NewScore, NewEntry, time(0), RAND(), HighScoreServerURL); } /* Because of major stupidity this return the number of NEXT @@ -262,7 +263,8 @@ static size_t WriteMemoryCallback(char* Contents, size_t Size, size_t Count, voi return RealSize; } -static truth RetrieveHighScoresFromServer(std::vector& GlobalEntry, +static truth RetrieveHighScoresFromServer(cfestring& HighScoreServerURL, + std::vector& GlobalEntry, std::vector& GlobalScore, std::vector& GlobalTime) { @@ -274,8 +276,9 @@ static truth RetrieveHighScoresFromServer(std::vector& GlobalEntry, if(CURL* Curl = curl_easy_init()) { festring RetrievedData; + festring HighScoreRetrieveURL = HighScoreServerURL + "/highscores"; - curl_easy_setopt(Curl, CURLOPT_URL, HIGH_SCORE_SERVER "/highscores"); + curl_easy_setopt(Curl, CURLOPT_URL, HighScoreRetrieveURL.CStr()); curl_easy_setopt(Curl, CURLOPT_HTTPGET, 1); curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, &WriteMemoryCallback); curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &RetrievedData); @@ -299,7 +302,8 @@ static truth RetrieveHighScoresFromServer(std::vector& GlobalEntry, return Success; } -static void SubmitHighScoreToServer(long NewScore, cfestring& NewEntry, +static void SubmitHighScoreToServer(cfestring& HighScoreServerURL, + long NewScore, cfestring& NewEntry, time_t NewTime, long NewRandomID) { if(curl_global_init(CURL_GLOBAL_ALL) != 0) @@ -316,7 +320,9 @@ static void SubmitHighScoreToServer(long NewScore, cfestring& NewEntry, "\"entry\": \"" << NewEntry << "\"" "}"; - curl_easy_setopt(Curl, CURLOPT_URL, HIGH_SCORE_SERVER "/submit_score"); + festring HighScoreSubmitURL = HighScoreServerURL + "/submit_score"; + + curl_easy_setopt(Curl, CURLOPT_URL, HighScoreSubmitURL.CStr()); curl_easy_setopt(Curl, CURLOPT_POST, 1); curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Headers); curl_easy_setopt(Curl, CURLOPT_POSTFIELDS, Json.CStr()); diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h index 77245f839..b3c272f3b 100644 --- a/Main/Include/iconf.h +++ b/Main/Include/iconf.h @@ -31,6 +31,7 @@ class ivanconfig static truth GetBeNice() { return BeNice.Value; } static long GetVolume() { return Volume.Value; } static long GetMIDIOutputDevice() { return MIDIOutputDevice.Value; } + static cfestring& GetHighScoreServerURL() { return HighScoreServerURL.Value; } #ifndef __DJGPP__ static truth GetFullScreenMode() { return FullScreenMode.Value; } static void SwitchModeHandler(); @@ -77,6 +78,7 @@ class ivanconfig static truthoption BeNice; static scrollbaroption Volume; static cycleoption MIDIOutputDevice; + static stringoption HighScoreServerURL; #ifndef __DJGPP__ static truthoption FullScreenMode; #endif diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 8bc638938..a52ad8886 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -2148,7 +2148,8 @@ void character::AddScoreEntry(cfestring& Description, double Multiplier, truth A Desc << " in " << game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex()); } - HScore.Add(long(game::GetScore() * Multiplier), Desc); + HScore.Add(long(game::GetScore() * Multiplier), Desc, + ivanconfig::GetHighScoreServerURL()); HScore.Save(); } } diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp index 22f52fd3c..dd3a8e23f 100644 --- a/Main/Source/game.cpp +++ b/Main/Source/game.cpp @@ -1775,7 +1775,7 @@ void game::End(festring DeathMessage, truth Permanently, truth AndGoToMenu) + GetPlayerName() + ", " + DeathMessage + "\nRIP"); } else - HScore.Draw(); + HScore.Draw(ivanconfig::GetHighScoreServerURL()); } if(AndGoToMenu) diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 49f44a109..b336830d7 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -72,6 +72,9 @@ cycleoption ivanconfig::MIDIOutputDevice( "MIDIOutputDevice", "select MIDI output device", 0, 0, // {default value, number of options to cycle through} &MIDIOutputDeviceDisplayer); +stringoption ivanconfig::HighScoreServerURL("HighScoreServerURL", + "server to use for global high-scores", + "https://ivan-hall-of-fame.herokuapp.com"); #ifndef __DJGPP__ truthoption ivanconfig::FullScreenMode( "FullScreenMode", "run the game in full screen mode", @@ -310,6 +313,7 @@ void ivanconfig::Initialize() MIDIOutputDevice.CycleCount = NumDevices+1; configsystem::AddOption(&MIDIOutputDevice); + configsystem::AddOption(&HighScoreServerURL); #ifndef __DJGPP__ configsystem::AddOption(&FullScreenMode); #endif diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp index 2ea458e2b..7c26d8afd 100644 --- a/Main/Source/main.cpp +++ b/Main/Source/main.cpp @@ -121,7 +121,7 @@ int main(int argc, char** argv) case 3: { highscore HScore; - HScore.Draw(); + HScore.Draw(ivanconfig::GetHighScoreServerURL()); break; } case 4: From 0dd92ad15f9fb99cd625bcd3d2e82b622a52d7bf Mon Sep 17 00:00:00 2001 From: emlai Date: Sun, 19 Mar 2017 23:47:46 +0200 Subject: [PATCH 05/12] Add options for high-score server username and password TODO: Encrypt password for persistent storage, and don't show it as plain-text in the options menu. --- FeLib/Include/hscore.h | 4 ++-- FeLib/Source/hscore.cpp | 23 +++++++++++++++++------ Main/Include/iconf.h | 4 ++++ Main/Source/char.cpp | 4 +++- Main/Source/iconf.cpp | 8 ++++++++ 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/FeLib/Include/hscore.h b/FeLib/Include/hscore.h index 934f32e54..243f9cedc 100644 --- a/FeLib/Include/hscore.h +++ b/FeLib/Include/hscore.h @@ -38,7 +38,7 @@ class highscore { public: highscore(cfestring& = HIGH_SCORE_FILENAME); - truth Add(long, cfestring&, cfestring&); + truth Add(long, cfestring&, cfestring&, cfestring&, cfestring&); void Draw(cfestring&); void Save(cfestring& = HIGH_SCORE_FILENAME) const; void Load(cfestring& = HIGH_SCORE_FILENAME); @@ -53,7 +53,7 @@ class highscore void Clear(); truth CheckVersion() const; private: - truth Add(long, cfestring&, time_t, long, cfestring&); + truth Add(long, cfestring&, time_t, long, cfestring&, cfestring&, cfestring&); void ToggleBetweenLocalAndGlobalView() { View = static_cast(!View); } std::vector Entry; std::vector Score; diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp index 8901a85dd..ef82252d0 100644 --- a/FeLib/Source/hscore.cpp +++ b/FeLib/Source/hscore.cpp @@ -22,7 +22,8 @@ static truth RetrieveHighScoresFromServer(cfestring&, std::vector&, std::vector&, std::vector&); -static void SubmitHighScoreToServer(cfestring&, long, cfestring&, time_t, long); +static void SubmitHighScoreToServer(cfestring&, cfestring&, cfestring&, + long, cfestring&, time_t, long); /* Increment this if changes make highscores incompatible */ #define HIGH_SCORE_VERSION 128 @@ -36,10 +37,13 @@ long highscore::GetSize() const { return Entry.size(); } highscore::highscore(cfestring& File) : LastAdd(0xFF), Version(HIGH_SCORE_VERSION) { Load(File); } truth highscore::Add(long NewScore, cfestring& NewEntry, time_t NewTime, - long NewRandomID, cfestring& HighScoreServerURL) + long NewRandomID, cfestring& HighScoreServerURL, + cfestring& HighScoreServerUsername, + cfestring& HighScoreServerPassword) { if (!HighScoreServerURL.IsEmpty()) - SubmitHighScoreToServer(HighScoreServerURL, NewScore, NewEntry, + SubmitHighScoreToServer(HighScoreServerURL, HighScoreServerUsername, + HighScoreServerPassword, NewScore, NewEntry, NewTime, NewRandomID); for(uint c = 0; c < Score.size(); ++c) @@ -191,7 +195,7 @@ truth highscore::MergeToFile(highscore* To) const for(uint c = 0; c < Score.size(); ++c) if(!To->Find(Score[c], Entry[c], Time[c], RandomID[c])) { - To->Add(Score[c], Entry[c], Time[c], RandomID[c], ""); + To->Add(Score[c], Entry[c], Time[c], RandomID[c], "", "", ""); MergedSomething = true; } @@ -199,9 +203,12 @@ truth highscore::MergeToFile(highscore* To) const } truth highscore::Add(long NewScore, cfestring& NewEntry, - cfestring& HighScoreServerURL) + cfestring& HighScoreServerURL, + cfestring& HighScoreServerUsername, + cfestring& HighScoreServerPassword) { - return Add(NewScore, NewEntry, time(0), RAND(), HighScoreServerURL); + return Add(NewScore, NewEntry, time(0), RAND(), HighScoreServerURL, + HighScoreServerUsername, HighScoreServerPassword); } /* Because of major stupidity this return the number of NEXT @@ -303,6 +310,8 @@ static truth RetrieveHighScoresFromServer(cfestring& HighScoreServerURL, } static void SubmitHighScoreToServer(cfestring& HighScoreServerURL, + cfestring& HighScoreServerUsername, + cfestring& HighScoreServerPassword, long NewScore, cfestring& NewEntry, time_t NewTime, long NewRandomID) { @@ -316,6 +325,8 @@ static void SubmitHighScoreToServer(cfestring& HighScoreServerURL, festring Json; Json << "{" + "\"username\": \"" << HighScoreServerUsername << "\"," + "\"password\": \"" << HighScoreServerPassword << "\"," "\"score\": " << NewScore << "," "\"entry\": \"" << NewEntry << "\"" "}"; diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h index b3c272f3b..53845bb75 100644 --- a/Main/Include/iconf.h +++ b/Main/Include/iconf.h @@ -32,6 +32,8 @@ class ivanconfig static long GetVolume() { return Volume.Value; } static long GetMIDIOutputDevice() { return MIDIOutputDevice.Value; } static cfestring& GetHighScoreServerURL() { return HighScoreServerURL.Value; } + static cfestring& GetHighScoreServerUsername() { return HighScoreServerUsername.Value; } + static cfestring& GetHighScoreServerPassword() { return HighScoreServerPassword.Value; } #ifndef __DJGPP__ static truth GetFullScreenMode() { return FullScreenMode.Value; } static void SwitchModeHandler(); @@ -79,6 +81,8 @@ class ivanconfig static scrollbaroption Volume; static cycleoption MIDIOutputDevice; static stringoption HighScoreServerURL; + static stringoption HighScoreServerUsername; + static stringoption HighScoreServerPassword; #ifndef __DJGPP__ static truthoption FullScreenMode; #endif diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index a52ad8886..56cea3115 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -2149,7 +2149,9 @@ void character::AddScoreEntry(cfestring& Description, double Multiplier, truth A } HScore.Add(long(game::GetScore() * Multiplier), Desc, - ivanconfig::GetHighScoreServerURL()); + ivanconfig::GetHighScoreServerURL(), + ivanconfig::GetHighScoreServerUsername(), + ivanconfig::GetHighScoreServerPassword()); HScore.Save(); } } diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index b336830d7..2a82203cc 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -75,6 +75,12 @@ cycleoption ivanconfig::MIDIOutputDevice( "MIDIOutputDevice", stringoption ivanconfig::HighScoreServerURL("HighScoreServerURL", "server to use for global high-scores", "https://ivan-hall-of-fame.herokuapp.com"); +stringoption ivanconfig::HighScoreServerUsername("HighScoreServerUsername", + "username for submitting global high-scores", + ""); +stringoption ivanconfig::HighScoreServerPassword("HighScoreServerPassword", + "password for submitting global high-scores", + ""); #ifndef __DJGPP__ truthoption ivanconfig::FullScreenMode( "FullScreenMode", "run the game in full screen mode", @@ -314,6 +320,8 @@ void ivanconfig::Initialize() configsystem::AddOption(&MIDIOutputDevice); configsystem::AddOption(&HighScoreServerURL); + configsystem::AddOption(&HighScoreServerUsername); + configsystem::AddOption(&HighScoreServerPassword); #ifndef __DJGPP__ configsystem::AddOption(&FullScreenMode); #endif From 5c1c1a4265f23f1a3bc6380d4eb867380d700288 Mon Sep 17 00:00:00 2001 From: emlai Date: Mon, 20 Mar 2017 00:00:56 +0200 Subject: [PATCH 06/12] Add SecretStringDisplayer that shows each letter as '*' --- FeLib/Include/config.h | 1 + FeLib/Source/config.cpp | 9 +++++++++ Main/Source/iconf.cpp | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/FeLib/Include/config.h b/FeLib/Include/config.h index 265e458b4..03fa9ba66 100644 --- a/FeLib/Include/config.h +++ b/FeLib/Include/config.h @@ -33,6 +33,7 @@ class configsystem static void Show(void (*)() = 0, void (*)(felist&) = 0, truth = false); static void AddOption(configoption*); static void NormalStringDisplayer(const stringoption*, festring&); + static void SecretStringDisplayer(const stringoption*, festring&); static void NormalNumberDisplayer(const numberoption*, festring&); static void NormalTruthDisplayer(const truthoption*, festring&); static void NormalCycleDisplayer(const cycleoption*, festring&); diff --git a/FeLib/Source/config.cpp b/FeLib/Source/config.cpp index 81f936f95..283ecedb9 100644 --- a/FeLib/Source/config.cpp +++ b/FeLib/Source/config.cpp @@ -181,6 +181,15 @@ void configsystem::NormalStringDisplayer(const stringoption* O, Entry << '-'; } +void configsystem::SecretStringDisplayer(const stringoption* O, + festring& Entry) +{ + if(!O->Value.IsEmpty()) + Entry << festring(O->Value.GetSize(), '*'); + else + Entry << '-'; +} + void configsystem::NormalNumberDisplayer(const numberoption* O, festring& Entry) { diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index 2a82203cc..7fec0bf71 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -80,7 +80,8 @@ stringoption ivanconfig::HighScoreServerUsername("HighScoreServerUsername", ""); stringoption ivanconfig::HighScoreServerPassword("HighScoreServerPassword", "password for submitting global high-scores", - ""); + "", + &configsystem::SecretStringDisplayer); #ifndef __DJGPP__ truthoption ivanconfig::FullScreenMode( "FullScreenMode", "run the game in full screen mode", From 49398599f960e9c6c63880e73c6f6ef618cf8ed6 Mon Sep 17 00:00:00 2001 From: emlai Date: Tue, 21 Mar 2017 01:16:38 +0200 Subject: [PATCH 07/12] Add assert to rawbitmap::Printf to fail fast Otherwise trying to print a newline here just results in a nasty segfault that's not immediately obvious. --- FeLib/Source/rawbit.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FeLib/Source/rawbit.cpp b/FeLib/Source/rawbit.cpp index a8aca922e..57efbd7da 100644 --- a/FeLib/Source/rawbit.cpp +++ b/FeLib/Source/rawbit.cpp @@ -11,6 +11,7 @@ */ #include +#include #include "allocate.h" #include "rawbit.h" @@ -422,6 +423,7 @@ void rawbitmap::Printf(bitmap* Bitmap, v2 Pos, packcol16 Color, cchar* Format, . for(int c = 0; Buffer[c]; ++c, B.Dest.X += 8) { + assert(Buffer[c] != '\n'); B.Src.X = ((Buffer[c] - 0x20) & 0xF) << 4; B.Src.Y = (Buffer[c] - 0x20) & 0xF0; //printf("'%c' -> X=%5d -- Y=%5d\n", Buffer[c], B.Src.X, B.Src.Y); From 388b7da4db31e0f91d804fe727bf49bf8f5a601d Mon Sep 17 00:00:00 2001 From: emlai Date: Mon, 17 Apr 2017 16:31:01 +0300 Subject: [PATCH 08/12] Submit version identifier to high-score server --- FeLib/Source/hscore.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp index ef82252d0..c4da01c9a 100644 --- a/FeLib/Source/hscore.cpp +++ b/FeLib/Source/hscore.cpp @@ -328,7 +328,8 @@ static void SubmitHighScoreToServer(cfestring& HighScoreServerURL, "\"username\": \"" << HighScoreServerUsername << "\"," "\"password\": \"" << HighScoreServerPassword << "\"," "\"score\": " << NewScore << "," - "\"entry\": \"" << NewEntry << "\"" + "\"entry\": \"" << NewEntry << "\"," + "\"version\": \"" << IVAN_VERSION << "\"" "}"; festring HighScoreSubmitURL = HighScoreServerURL + "/submit_score"; From 5ff0fb86ccaf4858293bc93ad3787529a95abc0d Mon Sep 17 00:00:00 2001 From: emlai Date: Mon, 17 Apr 2017 19:30:52 +0300 Subject: [PATCH 09/12] Show loading screen when fetching/submitting scores --- FeLib/Source/hscore.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp index c4da01c9a..c9eadb5e4 100644 --- a/FeLib/Source/hscore.cpp +++ b/FeLib/Source/hscore.cpp @@ -275,6 +275,10 @@ static truth RetrieveHighScoresFromServer(cfestring& HighScoreServerURL, std::vector& GlobalScore, std::vector& GlobalTime) { + iosystem::TextScreen(CONST_S("Fetching highscores from the server...\n\n" + "This may take some time, please wait."), + ZERO_V2, WHITE, false, true); + if(curl_global_init(CURL_GLOBAL_ALL) != 0) return false; @@ -315,6 +319,10 @@ static void SubmitHighScoreToServer(cfestring& HighScoreServerURL, long NewScore, cfestring& NewEntry, time_t NewTime, long NewRandomID) { + iosystem::TextScreen(CONST_S("Submitting highscore to the server...\n\n" + "This may take some time, please wait."), + ZERO_V2, WHITE, false, true); + if(curl_global_init(CURL_GLOBAL_ALL) != 0) return; From 646d7cf0084faca9a51e82fb261e69a2d69a2020 Mon Sep 17 00:00:00 2001 From: emlai Date: Thu, 20 Apr 2017 17:57:32 +0300 Subject: [PATCH 10/12] Store auth token in config file instead of cleartext password - Use HTTP Basic Auth to validate username+password combo and fetch auth token when changing password setting. - Send auth token when submitting high-scores for user authentication. --- FeLib/Include/config.h | 1 + FeLib/Include/feio.h | 2 +- FeLib/Include/hscore.h | 4 ++++ FeLib/Source/config.cpp | 6 ++--- FeLib/Source/feio.cpp | 10 +++++---- FeLib/Source/hscore.cpp | 50 ++++++++++++++++++++++++++++++++++++----- Main/Include/iconf.h | 5 +++-- Main/Source/char.cpp | 2 +- Main/Source/iconf.cpp | 45 ++++++++++++++++++++++++++++++++++--- 9 files changed, 104 insertions(+), 21 deletions(-) diff --git a/FeLib/Include/config.h b/FeLib/Include/config.h index 5d8cafd2e..b84c9479e 100644 --- a/FeLib/Include/config.h +++ b/FeLib/Include/config.h @@ -42,6 +42,7 @@ class configsystem static truth NormalTruthChangeInterface(truthoption*); static truth NormalCycleChangeInterface(cycleoption*); static void NormalStringChanger(stringoption*, cfestring&); + static void SecretStringChanger(stringoption*, cfestring&); static void NormalNumberChanger(numberoption*, long); static void NormalTruthChanger(truthoption*, truth); static void NormalCycleChanger(cycleoption*, long); diff --git a/FeLib/Include/feio.h b/FeLib/Include/feio.h index 901fd03b7..2a3a857f2 100644 --- a/FeLib/Include/feio.h +++ b/FeLib/Include/feio.h @@ -27,7 +27,7 @@ class iosystem static festring ContinueMenu(col16, col16, cfestring&); static int StringQuestion(festring&, cfestring&, v2, col16, festring::sizetype, festring::sizetype, - truth, truth, stringkeyhandler = 0); + truth, truth, stringkeyhandler = 0, truth = false); static long NumberQuestion(cfestring&, v2, col16, truth, truth = false); static long ScrollBarQuestion(cfestring&, v2, long, long, long, diff --git a/FeLib/Include/hscore.h b/FeLib/Include/hscore.h index 243f9cedc..38c35e086 100644 --- a/FeLib/Include/hscore.h +++ b/FeLib/Include/hscore.h @@ -67,4 +67,8 @@ class highscore static highscoreview View; }; +festring FetchAuthToken(cfestring& HighScoreServerURL, + cfestring& HighScoreServerUsername, + cfestring& HighScoreServerPassword); + #endif diff --git a/FeLib/Source/config.cpp b/FeLib/Source/config.cpp index 28af2e120..b54190b61 100644 --- a/FeLib/Source/config.cpp +++ b/FeLib/Source/config.cpp @@ -184,10 +184,8 @@ void configsystem::NormalStringDisplayer(const stringoption* O, void configsystem::SecretStringDisplayer(const stringoption* O, festring& Entry) { - if(!O->Value.IsEmpty()) - Entry << festring(O->Value.GetSize(), '*'); - else - Entry << '-'; + if(O->Value.IsEmpty()) + Entry << "not set"; } void configsystem::NormalNumberDisplayer(const numberoption* O, diff --git a/FeLib/Source/feio.cpp b/FeLib/Source/feio.cpp index 1500025ac..de3e4b784 100644 --- a/FeLib/Source/feio.cpp +++ b/FeLib/Source/feio.cpp @@ -286,7 +286,8 @@ int iosystem::StringQuestion(festring& Input, festring::sizetype MinLetters, festring::sizetype MaxLetters, truth Fade, truth AllowExit, - stringkeyhandler StringKeyHandler) + stringkeyhandler StringKeyHandler, + truth SecretInput) { v2 V(RES.X, 10); ///??????????? bitmap BackUp(V, 0); @@ -303,7 +304,8 @@ int iosystem::StringQuestion(festring& Input, bitmap Buffer(RES, 0); Buffer.ActivateFastFlag(); FONT->Printf(&Buffer, Pos, Color, "%s", Topic.CStr()); - FONT->Printf(&Buffer, v2(Pos.X, Pos.Y + 10), Color, "%s", Input.CStr()); + FONT->Printf(&Buffer, v2(Pos.X, Pos.Y + 10), Color, "%s", + SecretInput ? festring(Input.GetSize(), '*').CStr() : Input.CStr()); Buffer.FadeToScreen(); } else @@ -317,8 +319,8 @@ int iosystem::StringQuestion(festring& Input, { B.Bitmap = DOUBLE_BUFFER; BackUp.NormalBlit(B); - FONT->Printf(DOUBLE_BUFFER, v2(Pos.X, Pos.Y + 10), - Color, "%s", Input.CStr()); + FONT->Printf(DOUBLE_BUFFER, v2(Pos.X, Pos.Y + 10), Color, "%s", + SecretInput ? festring(Input.GetSize(), '*').CStr() : Input.CStr()); FONT->Printf(DOUBLE_BUFFER, v2(Pos.X, Pos.Y + 11), Color, "%*c", CursorPos + 1, '_'); diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp index c9eadb5e4..26195761a 100644 --- a/FeLib/Source/hscore.cpp +++ b/FeLib/Source/hscore.cpp @@ -39,11 +39,11 @@ highscore::highscore(cfestring& File) : LastAdd(0xFF), Version(HIGH_SCORE_VERSIO truth highscore::Add(long NewScore, cfestring& NewEntry, time_t NewTime, long NewRandomID, cfestring& HighScoreServerURL, cfestring& HighScoreServerUsername, - cfestring& HighScoreServerPassword) + cfestring& HighScoreServerAuthToken) { if (!HighScoreServerURL.IsEmpty()) SubmitHighScoreToServer(HighScoreServerURL, HighScoreServerUsername, - HighScoreServerPassword, NewScore, NewEntry, + HighScoreServerAuthToken, NewScore, NewEntry, NewTime, NewRandomID); for(uint c = 0; c < Score.size(); ++c) @@ -205,10 +205,10 @@ truth highscore::MergeToFile(highscore* To) const truth highscore::Add(long NewScore, cfestring& NewEntry, cfestring& HighScoreServerURL, cfestring& HighScoreServerUsername, - cfestring& HighScoreServerPassword) + cfestring& HighScoreServerAuthToken) { return Add(NewScore, NewEntry, time(0), RAND(), HighScoreServerURL, - HighScoreServerUsername, HighScoreServerPassword); + HighScoreServerUsername, HighScoreServerAuthToken); } /* Because of major stupidity this return the number of NEXT @@ -313,9 +313,47 @@ static truth RetrieveHighScoresFromServer(cfestring& HighScoreServerURL, return Success; } +/* Sends a HTTP request to the specified high-score server to validate the + given username and password combination. Returns the user's auth token + if authentication was successful, otherwise returns an empty string. */ + +festring FetchAuthToken(cfestring& HighScoreServerURL, + cfestring& HighScoreServerUsername, + cfestring& HighScoreServerPassword) +{ + if(curl_global_init(CURL_GLOBAL_ALL) != 0) + return ""; + + festring AuthToken; + + if(CURL* Curl = curl_easy_init()) + { + festring AuthTokenFetchURL = HighScoreServerURL + "/get_auth_token"; + + curl_easy_setopt(Curl, CURLOPT_URL, AuthTokenFetchURL.CStr()); + curl_easy_setopt(Curl, CURLOPT_HTTPGET, 1); + curl_easy_setopt(Curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_easy_setopt(Curl, CURLOPT_USERNAME, HighScoreServerUsername.CStr()); + curl_easy_setopt(Curl, CURLOPT_PASSWORD, HighScoreServerPassword.CStr()); + curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, &WriteMemoryCallback); + curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &AuthToken); + CURLcode Res = curl_easy_perform(Curl); + long ResponseCode; + curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &ResponseCode); + + if(Res != CURLE_OK || ResponseCode != 200) + AuthToken.Empty(); + + curl_easy_cleanup(Curl); + } + + curl_global_cleanup(); + return AuthToken; +} + static void SubmitHighScoreToServer(cfestring& HighScoreServerURL, cfestring& HighScoreServerUsername, - cfestring& HighScoreServerPassword, + cfestring& HighScoreServerAuthToken, long NewScore, cfestring& NewEntry, time_t NewTime, long NewRandomID) { @@ -334,7 +372,7 @@ static void SubmitHighScoreToServer(cfestring& HighScoreServerURL, Json << "{" "\"username\": \"" << HighScoreServerUsername << "\"," - "\"password\": \"" << HighScoreServerPassword << "\"," + "\"auth_token\": \"" << HighScoreServerAuthToken << "\"," "\"score\": " << NewScore << "," "\"entry\": \"" << NewEntry << "\"," "\"version\": \"" << IVAN_VERSION << "\"" diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h index 0be31cce0..bacd512ab 100644 --- a/Main/Include/iconf.h +++ b/Main/Include/iconf.h @@ -33,7 +33,7 @@ class ivanconfig static long GetMIDIOutputDevice() { return MIDIOutputDevice.Value; } static cfestring& GetHighScoreServerURL() { return HighScoreServerURL.Value; } static cfestring& GetHighScoreServerUsername() { return HighScoreServerUsername.Value; } - static cfestring& GetHighScoreServerPassword() { return HighScoreServerPassword.Value; } + static cfestring& GetHighScoreServerAuthToken() { return HighScoreServerAuthToken.Value; } #ifndef __DJGPP__ static int GetGraphicsScale() { return GraphicsScale.Value; } static truth GetFullScreenMode() { return FullScreenMode.Value; } @@ -63,6 +63,7 @@ class ivanconfig static void VolumeDisplayer(const numberoption*, festring&); static truth VolumeChangeInterface(numberoption*); static void VolumeChanger(numberoption*, long); + static truth PasswordChangeInterface(stringoption*); #ifndef __DJGPP__ static void GraphicsScaleDisplayer(const cycleoption*, festring&); static truth GraphicsScaleChangeInterface(cycleoption*); @@ -86,7 +87,7 @@ class ivanconfig static cycleoption MIDIOutputDevice; static stringoption HighScoreServerURL; static stringoption HighScoreServerUsername; - static stringoption HighScoreServerPassword; + static stringoption HighScoreServerAuthToken; #ifndef __DJGPP__ static cycleoption GraphicsScale; static truthoption FullScreenMode; diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp index 6e9485663..23d0d5a1c 100644 --- a/Main/Source/char.cpp +++ b/Main/Source/char.cpp @@ -2151,7 +2151,7 @@ void character::AddScoreEntry(cfestring& Description, double Multiplier, truth A HScore.Add(long(game::GetScore() * Multiplier), Desc, ivanconfig::GetHighScoreServerURL(), ivanconfig::GetHighScoreServerUsername(), - ivanconfig::GetHighScoreServerPassword()); + ivanconfig::GetHighScoreServerAuthToken()); HScore.Save(); } } diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index dea149469..c0dadb54f 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -18,6 +18,7 @@ #include "bitmap.h" #include "igraph.h" #include "audio.h" +#include "hscore.h" stringoption ivanconfig::DefaultName( "DefaultName", "player's default name", @@ -78,10 +79,11 @@ stringoption ivanconfig::HighScoreServerURL("HighScoreServerURL", stringoption ivanconfig::HighScoreServerUsername("HighScoreServerUsername", "username for submitting global high-scores", ""); -stringoption ivanconfig::HighScoreServerPassword("HighScoreServerPassword", +stringoption ivanconfig::HighScoreServerAuthToken("HighScoreServerAuthToken", "password for submitting global high-scores", "", - &configsystem::SecretStringDisplayer); + &configsystem::SecretStringDisplayer, + &PasswordChangeInterface); #ifndef __DJGPP__ cycleoption ivanconfig::GraphicsScale( "GraphicsScale", "select graphics scale factor", @@ -250,6 +252,43 @@ void ivanconfig::VolumeChanger(numberoption* O, long What) audio::SetVolumeLevel(What); } +truth ivanconfig::PasswordChangeInterface(stringoption* O) +{ + festring Password; + festring Topic = CONST_S("Enter password for submitting global high-scores:"); + truth FirstTry = true; + + while(iosystem::StringQuestion(Password, Topic, v2(30, 30), WHITE, 0, 80, + true, true, nullptr, true) == NORMAL_EXIT) + { + if(Password.IsEmpty()) + { + O->ChangeValue(""); + break; + } + + festring AuthToken = FetchAuthToken(ivanconfig::GetHighScoreServerURL(), + ivanconfig::GetHighScoreServerUsername(), + Password); + + if(!AuthToken.IsEmpty()) + { + O->ChangeValue(AuthToken); + break; + } + + if(FirstTry) + { + Topic.Insert(0, "Incorrect username or password! "); + FirstTry = false; + } + + Password.Empty(); + } + + return false; +} + #ifndef __DJGPP__ void ivanconfig::GraphicsScaleDisplayer(const cycleoption* O, festring& Entry) @@ -345,7 +384,7 @@ void ivanconfig::Initialize() configsystem::AddOption(&MIDIOutputDevice); configsystem::AddOption(&HighScoreServerURL); configsystem::AddOption(&HighScoreServerUsername); - configsystem::AddOption(&HighScoreServerPassword); + configsystem::AddOption(&HighScoreServerAuthToken); #ifndef __DJGPP__ configsystem::AddOption(&GraphicsScale); configsystem::AddOption(&FullScreenMode); From 6ab61297a76186abfa595a658d94628a57146cbe Mon Sep 17 00:00:00 2001 From: emlai Date: Thu, 20 Apr 2017 18:56:43 +0300 Subject: [PATCH 11/12] Add #ifdef checks to allow compilation without libcurl --- FeLib/CMakeLists.txt | 6 ++++++ FeLib/Source/felist.cpp | 2 ++ FeLib/Source/hscore.cpp | 28 ++++++++++++++++++++++++++++ Main/Source/iconf.cpp | 2 ++ 4 files changed, 38 insertions(+) diff --git a/FeLib/CMakeLists.txt b/FeLib/CMakeLists.txt index 58505d90d..427c942c7 100644 --- a/FeLib/CMakeLists.txt +++ b/FeLib/CMakeLists.txt @@ -18,6 +18,12 @@ endif() # libcurl, for sending/fetching high-scores to/from a global high-score server. find_package(CURL) +if(CURL_FOUND) + message(STATUS "Using libcurl ${CURL_VERSION_STRING}") + target_compile_definitions(FeLib PUBLIC USE_HIGHSCORE_SERVER) +else() + message(WARNING "libcurl not found, global high-score submissions will be disabled") +endif() if(MSVC) # Not very pretty solution. This finds SDL2.dll from SDL2.lib path, so that it can be installed where ivan.exe will end up. diff --git a/FeLib/Source/felist.cpp b/FeLib/Source/felist.cpp index 5f5757a20..7382cdff4 100644 --- a/FeLib/Source/felist.cpp +++ b/FeLib/Source/felist.cpp @@ -256,11 +256,13 @@ uint felist::Draw() { if(Flags & SELECTABLE) Return = Selected; +#ifdef USE_HIGHSCORE_SERVER else // Used by the hall of fame to toggle between local and global scores. Return = UNSELECTABLE_SELECT; break; +#endif } if(Pressed == KEY_ESC) diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp index 26195761a..2ef92ce9a 100644 --- a/FeLib/Source/hscore.cpp +++ b/FeLib/Source/hscore.cpp @@ -10,7 +10,9 @@ * */ +#ifdef USE_HIGHSCORE_SERVER #include +#endif #include "hscore.h" #include "save.h" @@ -18,12 +20,14 @@ #include "feio.h" #include "femath.h" +#ifdef USE_HIGHSCORE_SERVER static truth RetrieveHighScoresFromServer(cfestring&, std::vector&, std::vector&, std::vector&); static void SubmitHighScoreToServer(cfestring&, cfestring&, cfestring&, long, cfestring&, time_t, long); +#endif /* Increment this if changes make highscores incompatible */ #define HIGH_SCORE_VERSION 128 @@ -41,10 +45,12 @@ truth highscore::Add(long NewScore, cfestring& NewEntry, time_t NewTime, cfestring& HighScoreServerUsername, cfestring& HighScoreServerAuthToken) { +#ifdef USE_HIGHSCORE_SERVER if (!HighScoreServerURL.IsEmpty()) SubmitHighScoreToServer(HighScoreServerURL, HighScoreServerUsername, HighScoreServerAuthToken, NewScore, NewEntry, NewTime, NewRandomID); +#endif for(uint c = 0; c < Score.size(); ++c) if(Score[c] < NewScore) @@ -100,12 +106,17 @@ void highscore::Draw(cfestring& HighScoreServerURL) for(;;) { festring Title; + +#ifdef USE_HIGHSCORE_SERVER if(View == LOCAL) Title = CONST_S("Adventurers' Hall of Fame " "Press ENTER to view global highscores"); else if(View == GLOBAL) Title = CONST_S("Global Adventurers' Hall of Fame " "Press ENTER to view local highscores"); +#else + Title = CONST_S("Adventurers' Hall of Fame"); +#endif felist List(Title); festring Desc; @@ -121,6 +132,7 @@ void highscore::Draw(cfestring& HighScoreServerURL) Desc << Entry[c]; List.AddEntry(Desc, c == uint(LastAdd) ? WHITE : LIGHT_GRAY, 13); } +#ifdef USE_HIGHSCORE_SERVER else if(View == GLOBAL) { RetrieveHighScoresFromServer(HighScoreServerURL, GlobalEntry, @@ -137,10 +149,12 @@ void highscore::Draw(cfestring& HighScoreServerURL) List.AddEntry(Desc, LIGHT_GRAY, 13); } } +#endif List.SetFlags(FADE); List.SetPageLength(40); +#ifdef USE_HIGHSCORE_SERVER cuint DrawResult = List.Draw(); if(DrawResult == UNSELECTABLE_SELECT) // Enter was pressed. @@ -152,6 +166,10 @@ void highscore::Draw(cfestring& HighScoreServerURL) } else break; +#else + List.Draw(); + break; +#endif } } @@ -245,6 +263,7 @@ truth highscore::CheckVersion() const return Version == HIGH_SCORE_VERSION; } +#ifdef USE_HIGHSCORE_SERVER static void ParseHighScoresFromCSV(cfestring& CSV, std::vector& GlobalEntry, std::vector& GlobalScore, @@ -312,6 +331,7 @@ static truth RetrieveHighScoresFromServer(cfestring& HighScoreServerURL, curl_global_cleanup(); return Success; } +#endif /* Sends a HTTP request to the specified high-score server to validate the given username and password combination. Returns the user's auth token @@ -321,6 +341,7 @@ festring FetchAuthToken(cfestring& HighScoreServerURL, cfestring& HighScoreServerUsername, cfestring& HighScoreServerPassword) { +#ifdef USE_HIGHSCORE_SERVER if(curl_global_init(CURL_GLOBAL_ALL) != 0) return ""; @@ -349,8 +370,13 @@ festring FetchAuthToken(cfestring& HighScoreServerURL, curl_global_cleanup(); return AuthToken; +#else + return ""; +#endif } +#ifdef USE_HIGHSCORE_SERVER + static void SubmitHighScoreToServer(cfestring& HighScoreServerURL, cfestring& HighScoreServerUsername, cfestring& HighScoreServerAuthToken, @@ -393,3 +419,5 @@ static void SubmitHighScoreToServer(cfestring& HighScoreServerURL, curl_global_cleanup(); } + +#endif // USE_HIGHSCORE_SERVER diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp index c0dadb54f..dba141836 100644 --- a/Main/Source/iconf.cpp +++ b/Main/Source/iconf.cpp @@ -382,9 +382,11 @@ void ivanconfig::Initialize() MIDIOutputDevice.CycleCount = NumDevices+1; configsystem::AddOption(&MIDIOutputDevice); +#ifdef USE_HIGHSCORE_SERVER configsystem::AddOption(&HighScoreServerURL); configsystem::AddOption(&HighScoreServerUsername); configsystem::AddOption(&HighScoreServerAuthToken); +#endif #ifndef __DJGPP__ configsystem::AddOption(&GraphicsScale); configsystem::AddOption(&FullScreenMode); From 3b7de1efe28c1c578b7a4784708e62d4ad7ce542 Mon Sep 17 00:00:00 2001 From: emlai Date: Sat, 22 Apr 2017 01:18:23 +0300 Subject: [PATCH 12/12] Don't submit scores of 0 to the server --- FeLib/Source/hscore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp index 2ef92ce9a..ffc1b4e88 100644 --- a/FeLib/Source/hscore.cpp +++ b/FeLib/Source/hscore.cpp @@ -46,7 +46,7 @@ truth highscore::Add(long NewScore, cfestring& NewEntry, time_t NewTime, cfestring& HighScoreServerAuthToken) { #ifdef USE_HIGHSCORE_SERVER - if (!HighScoreServerURL.IsEmpty()) + if (!HighScoreServerURL.IsEmpty() && NewScore) SubmitHighScoreToServer(HighScoreServerURL, HighScoreServerUsername, HighScoreServerAuthToken, NewScore, NewEntry, NewTime, NewRandomID);