From 37f14ba80cea954cdaa977d0b5d20b1c9df51d1c Mon Sep 17 00:00:00 2001 From: Starz0r Date: Wed, 11 Mar 2020 08:15:09 -0500 Subject: [PATCH 01/28] Make internet ranking settings configurable Signed-off-by: Starz0r --- Main/include/GameConfig.hpp | 7 ++++++- Main/src/GameConfig.cpp | 5 +++++ Main/src/SettingsScreen.cpp | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Main/include/GameConfig.hpp b/Main/include/GameConfig.hpp index 26a984edc..78c8a43c3 100644 --- a/Main/include/GameConfig.hpp +++ b/Main/include/GameConfig.hpp @@ -149,7 +149,12 @@ DefineEnum(GameConfigKeys, // Gameplay options GaugeType, MirrorChart, - RandomizeChart) + RandomizeChart, + + // Internet Ranking + IRBaseURL, + IRUsername, + IRPassword) DefineEnum(GaugeTypes, Normal, diff --git a/Main/src/GameConfig.cpp b/Main/src/GameConfig.cpp index f258fdb21..28232208f 100644 --- a/Main/src/GameConfig.cpp +++ b/Main/src/GameConfig.cpp @@ -187,6 +187,11 @@ void GameConfig::InitDefaults() Set(GameConfigKeys::MultiplayerUsername, ""); Set(GameConfigKeys::EnableFancyHighwayRoll, true); + + // Internet Ranking + Set(GameConfigKeys::IRBaseURL, "https://api.orchestra.fm"); + Set(GameConfigKeys::IRUsername, ""); + Set(GameConfigKeys::IRPassword, ""); //Gameplay Set(GameConfigKeys::RandomizeChart, false); diff --git a/Main/src/SettingsScreen.cpp b/Main/src/SettingsScreen.cpp index 230defba1..dfa231dd7 100644 --- a/Main/src/SettingsScreen.cpp +++ b/Main/src/SettingsScreen.cpp @@ -199,6 +199,12 @@ class SettingsScreen_Impl : public SettingsScreen int m_multiplayerPasswordLen = 0; char m_multiplayerUsername[1024]; int m_multiplayerUsernameLen = 0; + char m_irBaseURL[1024]; + int m_irBaseURLLen = 0; + char m_irUsername[1024]; + int m_irUsernameLen = 0; + char m_irPassword[1024]; + int m_irPasswordLen = 0; const Vector* m_activeBTKeys = &m_keyboardKeys; const Vector* m_activeLaserKeys = &m_keyboardLaserKeys; bool m_useBTGamepad = false; @@ -282,6 +288,21 @@ class SettingsScreen_Impl : public SettingsScreen multiplayerUsername.TrimBack(' '); g_gameConfig.Set(GameConfigKeys::MultiplayerUsername, multiplayerUsername); + String irBaseURL = String(m_irBaseURL, m_irBaseURLLen); + irBaseURL.TrimBack('\n'); + irBaseURL.TrimBack(' '); + g_gameConfig.Set(GameConfigKeys::IRBaseURL, irBaseURL); + + String irUsername = String(m_irUsername, m_irUsernameLen); + irUsername.TrimBack('\n'); + irUsername.TrimBack(' '); + g_gameConfig.Set(GameConfigKeys::IRUsername, irUsername); + + String irPassword = String(m_irPassword, m_irPasswordLen); + irPassword.TrimBack('\n'); + irPassword.TrimBack(' '); + g_gameConfig.Set(GameConfigKeys::IRPassword, irPassword); + if (g_gameConfig.GetEnum(GameConfigKeys::ButtonInputDevice) == InputDevice::Mouse) { g_gameConfig.SetEnum(GameConfigKeys::ButtonInputDevice, InputDevice::Keyboard); From a6dde0dfb6597047e983a12b3555d244bbf00985 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Thu, 21 May 2020 23:33:30 -0500 Subject: [PATCH 02/28] Include CryptoPP dependencies in CMake build lists Signed-off-by: Starz0r --- CMakeLists.txt | 1 + Shared/CMakeLists.txt | 1 + build.windows | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9708ae48e..41579f450 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,7 @@ find_package(Vorbis REQUIRED) find_package(OGG REQUIRED) find_package(LibArchive REQUIRED) find_package(Iconv REQUIRED) +find_package(CryptoPP REQUIRED) # All projects use unicode define # this is mainly for windows functions either being defined to call A or W prefixed functions diff --git a/Shared/CMakeLists.txt b/Shared/CMakeLists.txt index 1760d05d2..eb8032206 100644 --- a/Shared/CMakeLists.txt +++ b/Shared/CMakeLists.txt @@ -45,6 +45,7 @@ target_include_directories(Shared PRIVATE ) target_link_libraries(Shared lua) +target_link_libraries(Shared cryptopp-static) target_link_libraries(Shared ${Iconv_LIBRARIES}) target_include_directories(Shared SYSTEM PRIVATE ${Iconv_INCLUDE_DIRS}) diff --git a/build.windows b/build.windows index 20e134526..69c2d8688 100644 --- a/build.windows +++ b/build.windows @@ -5,4 +5,5 @@ sdl2:x64-windows libjpeg-turbo:x64-windows libvorbis:x64-windows libarchive[core,bzip2,libxml2,lz4,lzma,lzo]:x64-windows -libiconv:x64-windows \ No newline at end of file +libiconv:x64-windows +cryptopp:x64-windows \ No newline at end of file From 0b26c6f6cd694462307a9e43fec1213f760b72a4 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Thu, 21 May 2020 23:56:12 -0500 Subject: [PATCH 03/28] Submit score data to specified IR Service API Signed-off-by: Starz0r --- Main/src/ScoreScreen.cpp | 53 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/Main/src/ScoreScreen.cpp b/Main/src/ScoreScreen.cpp index 6e03ce566..e4ab3c69c 100644 --- a/Main/src/ScoreScreen.cpp +++ b/Main/src/ScoreScreen.cpp @@ -15,6 +15,12 @@ #include #include "MultiplayerScreen.hpp" #include "ChatOverlay.hpp" +#include +#include "cryptopp/cryptlib.h" +#include "cryptopp/hex.h" +#include "cryptopp/sha3.h" +#include +#include class ScoreScreen_Impl : public ScoreScreen { @@ -220,7 +226,49 @@ class ScoreScreen_Impl : public ScoreScreen m_graphTex->SetWrap(Graphics::TextureWrap::Clamp, Graphics::TextureWrap::Clamp); m_numPlayersSeen = m_stats->size(); - m_displayId = static_cast((*m_stats)[m_displayIndex].value("uid","")); + m_displayId = static_cast((*m_stats)[m_displayIndex].value("uid", "")); + + } + + void SubmitScoreToIR(class Game* game) { + Scoring& m_scoring = game->GetScoring(); + + // check if we can login + String url = g_gameConfig.GetString(GameConfigKeys::IRBaseURL); + + // hash the file of the chart we played + CryptoPP::SHA3_512 hash; + String digest; + CryptoPP::FileSource f( + std::istringstream(game->GetDifficultyIndex().path), + true, + new CryptoPP::HashFilter( + hash, + new CryptoPP::HexEncoder( + new CryptoPP::StringSink(digest)))); + + // get the ids from the file hash + auto res = nlohmann::json::parse(cpr::Get(cpr::Url{ url + "/api/v0/board/sha3/" + digest }).text); + + uint64 track_id = res["track_id"]; + uint64 board_id = res["id"]; + + // post the score + nlohmann::json score_info = { + {"track", track_id, }, + {"board", board_id, }, + {"score", m_scoring.CalculateCurrentScore(), }, + {"combo", m_scoring.maxComboCounter, }, + {"rate", m_scoring.currentGauge, }, + {"criticals", m_scoring.categorizedHits[2], }, + {"nears", m_scoring.categorizedHits[1] , }, + {"errors", m_scoring.categorizedHits[0], }, + {"mods", m_flags, }, + //{"replaydata", 0, }, does usc have replays? + }; + cpr::Post(cpr::Url{ url + "/api/v0/score" }, + cpr::Body{ score_info.dump() }, + cpr::Header{ {"Content-Type", "application/json"} }); } @@ -265,6 +313,9 @@ class ScoreScreen_Impl : public ScoreScreen loadScoresFromGame(game); } + // TODO: check if Internet Ranking is enabled + SubmitScoreToIR(game); + for (HitStat* stat : scoring.hitStats) { if (!stat->forReplay) From 9736ecd5f8512a1a3859ca4dbe3c7693f14b3700 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Sun, 6 Sep 2020 17:03:27 -0500 Subject: [PATCH 04/28] Add Internet Ranking options to the settings menu --- Main/src/SettingsScreen.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Main/src/SettingsScreen.cpp b/Main/src/SettingsScreen.cpp index dfa231dd7..188b5fb01 100644 --- a/Main/src/SettingsScreen.cpp +++ b/Main/src/SettingsScreen.cpp @@ -539,6 +539,18 @@ class SettingsScreen_Impl : public SettingsScreen strcpy(m_multiplayerUsername, multiplayerUsername.c_str()); m_multiplayerUsernameLen = multiplayerUsername.length(); + String irBaseURL = g_gameConfig.GetString(GameConfigKeys::IRBaseURL); + strcpy(m_irBaseURL, irBaseURL.c_str()); + m_irBaseURLLen = irBaseURL.length(); + + String irUsername = g_gameConfig.GetString(GameConfigKeys::IRUsername); + strcpy(m_irUsername, irUsername.c_str()); + m_irUsernameLen = irUsername.length(); + + String irPassword = g_gameConfig.GetString(GameConfigKeys::IRPassword); + strcpy(m_irPassword, irPassword.c_str()); + m_irPasswordLen = irPassword.length(); + return true; } @@ -886,6 +898,15 @@ class SettingsScreen_Impl : public SettingsScreen nk_label(m_nctx, "Multiplayer Server Password:", nk_text_alignment::NK_TEXT_LEFT); nk_sdl_text(nk_edit_string(m_nctx, NK_EDIT_FIELD, m_multiplayerPassword, &m_multiplayerPasswordLen, 1024, nk_filter_default)); + + nk_label(m_nctx, "Internet Ranking URL:", nk_text_alignment::NK_TEXT_LEFT); + nk_sdl_text(nk_edit_string(m_nctx, NK_EDIT_FIELD, m_irBaseURL, &m_irBaseURLLen, 1024, nk_filter_default)); + + nk_label(m_nctx, "Account Username for Internet Ranking:", nk_text_alignment::NK_TEXT_LEFT); + nk_sdl_text(nk_edit_string(m_nctx, NK_EDIT_FIELD, m_irUsername, &m_irUsernameLen, 1024, nk_filter_default)); + + nk_label(m_nctx, "Account Password for Internet Ranking:", nk_text_alignment::NK_TEXT_LEFT); + nk_sdl_text(nk_edit_string(m_nctx, NK_EDIT_FIELD, m_irPassword, &m_irPasswordLen, 1024, nk_filter_default)); nk_tree_pop(m_nctx); } } From ece4f0256e9b05f4c597a6a2455a66c3fbd9d791 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Sun, 6 Sep 2020 17:04:22 -0500 Subject: [PATCH 05/28] Get JWT from IR and use it when submitting score to the server --- Main/src/ScoreScreen.cpp | 45 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/Main/src/ScoreScreen.cpp b/Main/src/ScoreScreen.cpp index e4ab3c69c..4d01f2cfc 100644 --- a/Main/src/ScoreScreen.cpp +++ b/Main/src/ScoreScreen.cpp @@ -231,6 +231,13 @@ class ScoreScreen_Impl : public ScoreScreen } void SubmitScoreToIR(class Game* game) { + // login to ir + // TODO: remove this in favor of a class that handles this + String bearer; bool err; + std::tie(bearer, err) = LoginToIR(); + if (err == true) {return;} + + // get scoring info Scoring& m_scoring = game->GetScoring(); // check if we can login @@ -239,13 +246,14 @@ class ScoreScreen_Impl : public ScoreScreen // hash the file of the chart we played CryptoPP::SHA3_512 hash; String digest; + CryptoPP::FileSource f( - std::istringstream(game->GetDifficultyIndex().path), + std::istringstream(game->GetChartIndex()->path), true, new CryptoPP::HashFilter( hash, new CryptoPP::HexEncoder( - new CryptoPP::StringSink(digest)))); + new CryptoPP::StringSink(digest), false))); // get the ids from the file hash auto res = nlohmann::json::parse(cpr::Get(cpr::Url{ url + "/api/v0/board/sha3/" + digest }).text); @@ -253,6 +261,8 @@ class ScoreScreen_Impl : public ScoreScreen uint64 track_id = res["track_id"]; uint64 board_id = res["id"]; + //auto replay = String(nlohmann::json::parse(m_simpleHitStats)); + // post the score nlohmann::json score_info = { {"track", track_id, }, @@ -264,12 +274,41 @@ class ScoreScreen_Impl : public ScoreScreen {"nears", m_scoring.categorizedHits[1] , }, {"errors", m_scoring.categorizedHits[0], }, {"mods", m_flags, }, - //{"replaydata", 0, }, does usc have replays? + //{"replaydata", replay, }, }; cpr::Post(cpr::Url{ url + "/api/v0/score" }, cpr::Body{ score_info.dump() }, + cpr::Header{ {"Content-Type", "application/json"}, + {"Authorization", "Bearer "+bearer }, + }); + } + + std::pair LoginToIR() { + // get internet ranking settings info + String base_url = g_gameConfig.GetString(GameConfigKeys::IRBaseURL); + String user = g_gameConfig.GetString(GameConfigKeys::IRUsername); + String pass = g_gameConfig.GetString(GameConfigKeys::IRPassword); + + nlohmann::json login_creds = { + {"username", user, }, + {"password", pass, }, + }; + + // attempt to login + cpr::Response resp = cpr::Post(cpr::Url{ base_url + "/api/v0/authorize/basic" }, + cpr::Body{ login_creds.dump() }, cpr::Header{ {"Content-Type", "application/json"} }); + // on success + if (resp.status_code == 202) { + nlohmann::json tkns = nlohmann::json::parse(resp.text); + String refresh = tkns["refresh"].get(); // TODO: use this for heartbeats + String bearer = tkns["bearer"].get(); + return { bearer, false }; + } + + // on failure + return { NULLPTR, true }; } ScoreScreen_Impl(class Game* game, MultiplayerScreen* multiplayer, From ff5ae59d0c5dd2c3e7716dada5678d0f4cce83aa Mon Sep 17 00:00:00 2001 From: Starz0r Date: Wed, 7 Oct 2020 22:14:31 -0500 Subject: [PATCH 06/28] NetworkingServices class to facilitate interactions over the network --- Main/include/NetworkingServices.hpp | 36 +++++++ Main/src/NetworkingServices.cpp | 158 ++++++++++++++++++++++++++++ Main/src/ScoreScreen.cpp | 83 --------------- 3 files changed, 194 insertions(+), 83 deletions(-) create mode 100644 Main/include/NetworkingServices.hpp create mode 100644 Main/src/NetworkingServices.cpp diff --git a/Main/include/NetworkingServices.hpp b/Main/include/NetworkingServices.hpp new file mode 100644 index 000000000..b3a2876bb --- /dev/null +++ b/Main/include/NetworkingServices.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include "GameConfig.hpp" +#include "Game.hpp" + +class NetworkingServices +{ +public: + NetworkingServices(); + void Init(GameConfig* config); + bool TryLogin(); + void QueueHeartbeat(); + virtual bool SubmitScore(class Game* game, GameFlags m_flags); + bool ConnectionStatus(); + +private: + bool Heartbeat(); + void WaitForHeartbeat(); + + // Configurable + String m_serviceUrl; + String m_username; + String m_password; + + // Connection Dependant + bool m_isConnected; + String m_refreshToken; + String m_bearerToken; + + // Static + std::thread::id m_heartbeat; +}; + +extern class NetworkingServices; \ No newline at end of file diff --git a/Main/src/NetworkingServices.cpp b/Main/src/NetworkingServices.cpp new file mode 100644 index 000000000..ec36270e5 --- /dev/null +++ b/Main/src/NetworkingServices.cpp @@ -0,0 +1,158 @@ +#include "stdafx.h" +#include "NetworkingServices.hpp" +#include "GameConfig.hpp" +#include +#include +#include +#include +#include "cryptopp/cryptlib.h" +#include "cryptopp/hex.h" +#include "cryptopp/sha3.h" +#include +#include +#include "Game.hpp" +#include "Scoring.hpp" + +NetworkingServices::NetworkingServices() +{ + m_isConnected = false; + m_refreshToken = ""; + m_bearerToken = ""; + + Logf("[NetworkingServices] Module Enabled", Logger::Severity::Normal); +} + +void NetworkingServices::Init(GameConfig* config) +{ + m_serviceUrl = config->GetString(GameConfigKeys::IRBaseURL); + m_username = config->GetString(GameConfigKeys::IRUsername); + m_password = config->GetString(GameConfigKeys::IRPassword); + + Logf("[NetworkingServices] Module Initialized", Logger::Severity::Normal); +} + +bool NetworkingServices::TryLogin() +{ + Logf("[NetworkingServices] Attempting to login", Logger::Severity::Normal); + // assemble json request body data + nlohmann::json login_creds = { + {"username", m_username, }, + {"password", m_password, }, + }; + + // attempt to login + cpr::Response resp = cpr::Post(cpr::Url{ m_serviceUrl + "/api/v0/authorize/basic" }, + cpr::Body{ login_creds.dump() }, + cpr::Header{ {"Content-Type", "application/json"} }); + Logf("[NetworkingServices] Login request sent.", Logger::Severity::Normal); + + // on success + if (resp.status_code == 202) { + nlohmann::json tkns = nlohmann::json::parse(resp.text); + m_refreshToken = tkns["refresh"].get(); + m_bearerToken = tkns["bearer"].get(); + Logf("[NetworkingServices] Logged In", Logger::Severity::Normal); + return m_isConnected = true; + } + + // on failure + return m_isConnected = false; +} + +bool NetworkingServices::Heartbeat() +{ + // assemble json request body data + nlohmann::json login_creds = { + {"refresh_token", m_refreshToken, }, + }; + + // attempt to refresh credentials + cpr::Response resp = cpr::Post(cpr::Url{ m_serviceUrl + "/api/v0/authorize/refresh" }, + cpr::Body{ login_creds.dump() }, + cpr::Header{ {"Content-Type", "application/json"} }); + + // on success + if (resp.status_code == 202) { + nlohmann::json tkns = nlohmann::json::parse(resp.text); + m_refreshToken = tkns["refresh"].get(); + m_bearerToken = tkns["bearer"].get(); + return m_isConnected = true; + } + + // on failure + return false; +} + +void NetworkingServices::WaitForHeartbeat() +{ + m_heartbeat = std::this_thread::get_id(); + std::this_thread::sleep_for(std::chrono::minutes(2)); + NetworkingServices::Heartbeat(); + std::thread(&NetworkingServices::WaitForHeartbeat, this).detach(); +} + +void NetworkingServices::QueueHeartbeat() +{ + std::thread (&NetworkingServices::WaitForHeartbeat, this).detach(); +} + +bool NetworkingServices::ConnectionStatus() +{ + return m_isConnected; +} + +bool NetworkingServices::SubmitScore(class Game* game, GameFlags m_flags) +{ + // get scoring info + Scoring& m_scoring = game->GetScoring(); + + // check if we can login + String url = m_serviceUrl; + + // hash the file of the chart we played + CryptoPP::SHA3_512 hash; + String digest; + + CryptoPP::FileSource f( + String(game->GetChartIndex()->path).c_str(), + true, + new CryptoPP::HashFilter( + hash, + new CryptoPP::HexEncoder( + new CryptoPP::StringSink(digest), false)), true); + + // get the ids from the file hash + auto res = nlohmann::json::parse(cpr::Get(cpr::Url{ url + "/api/v0/board/sha3/" + digest }).text); + + uint64 track_id = res["track_id"]; + uint64 board_id = res["id"]; + + //auto replay = String(nlohmann::json::parse(m_simpleHitStats)); + + // post the score + nlohmann::json score_info = { + {"track", track_id, }, + {"board", board_id, }, + {"score", m_scoring.CalculateCurrentScore(), }, + {"combo", m_scoring.maxComboCounter, }, + {"rate", m_scoring.currentGauge, }, + {"criticals", m_scoring.categorizedHits[2], }, + {"nears", m_scoring.categorizedHits[1] , }, + {"errors", m_scoring.categorizedHits[0], }, + {"mods", m_flags, }, + //{"replaydata", replay, }, + }; + cpr::Response resp = cpr::Post(cpr::Url{ url + "/api/v0/score" }, + cpr::Body{ score_info.dump() }, + cpr::Header{ {"Content-Type", "application/json"}, + {"Authorization", "Bearer " + m_bearerToken }, + }); + if (resp.status_code == 200) + { + return true; + } + else + { + return false; + } +} \ No newline at end of file diff --git a/Main/src/ScoreScreen.cpp b/Main/src/ScoreScreen.cpp index 651070756..0299af187 100644 --- a/Main/src/ScoreScreen.cpp +++ b/Main/src/ScoreScreen.cpp @@ -247,87 +247,6 @@ class ScoreScreen_Impl : public ScoreScreen m_displayId = static_cast((*m_stats)[m_displayIndex].value("uid", "")); } - - void SubmitScoreToIR(class Game* game) { - // login to ir - // TODO: remove this in favor of a class that handles this - String bearer; bool err; - std::tie(bearer, err) = LoginToIR(); - if (err == true) {return;} - - // get scoring info - Scoring& m_scoring = game->GetScoring(); - - // check if we can login - String url = g_gameConfig.GetString(GameConfigKeys::IRBaseURL); - - // hash the file of the chart we played - CryptoPP::SHA3_512 hash; - String digest; - - CryptoPP::FileSource f( - std::istringstream(game->GetChartIndex()->path), - true, - new CryptoPP::HashFilter( - hash, - new CryptoPP::HexEncoder( - new CryptoPP::StringSink(digest), false))); - - // get the ids from the file hash - auto res = nlohmann::json::parse(cpr::Get(cpr::Url{ url + "/api/v0/board/sha3/" + digest }).text); - - uint64 track_id = res["track_id"]; - uint64 board_id = res["id"]; - - //auto replay = String(nlohmann::json::parse(m_simpleHitStats)); - - // post the score - nlohmann::json score_info = { - {"track", track_id, }, - {"board", board_id, }, - {"score", m_scoring.CalculateCurrentScore(), }, - {"combo", m_scoring.maxComboCounter, }, - {"rate", m_scoring.currentGauge, }, - {"criticals", m_scoring.categorizedHits[2], }, - {"nears", m_scoring.categorizedHits[1] , }, - {"errors", m_scoring.categorizedHits[0], }, - {"mods", m_flags, }, - //{"replaydata", replay, }, - }; - cpr::Post(cpr::Url{ url + "/api/v0/score" }, - cpr::Body{ score_info.dump() }, - cpr::Header{ {"Content-Type", "application/json"}, - {"Authorization", "Bearer "+bearer }, - }); - } - - std::pair LoginToIR() { - // get internet ranking settings info - String base_url = g_gameConfig.GetString(GameConfigKeys::IRBaseURL); - String user = g_gameConfig.GetString(GameConfigKeys::IRUsername); - String pass = g_gameConfig.GetString(GameConfigKeys::IRPassword); - - nlohmann::json login_creds = { - {"username", user, }, - {"password", pass, }, - }; - - // attempt to login - cpr::Response resp = cpr::Post(cpr::Url{ base_url + "/api/v0/authorize/basic" }, - cpr::Body{ login_creds.dump() }, - cpr::Header{ {"Content-Type", "application/json"} }); - - // on success - if (resp.status_code == 202) { - nlohmann::json tkns = nlohmann::json::parse(resp.text); - String refresh = tkns["refresh"].get(); // TODO: use this for heartbeats - String bearer = tkns["bearer"].get(); - return { bearer, false }; - } - - // on failure - return { NULLPTR, true }; - } ScoreScreen_Impl(class Game* game, MultiplayerScreen* multiplayer, String uid, Vector const* multistats) @@ -372,8 +291,6 @@ class ScoreScreen_Impl : public ScoreScreen loadScoresFromGame(game); } - // TODO: check if Internet Ranking is enabled - SubmitScoreToIR(game); for (HitStat* stat : scoring.hitStats) { From ef8e87b62137eb52b9c159cb17b109b9fffa7c7b Mon Sep 17 00:00:00 2001 From: Starz0r Date: Wed, 7 Oct 2020 22:15:16 -0500 Subject: [PATCH 07/28] Enable and Initialize the NetworkingService class at program startup --- Main/include/Application.hpp | 1 + Main/src/Application.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/Main/include/Application.hpp b/Main/include/Application.hpp index 7edc81c35..32a8c4234 100644 --- a/Main/include/Application.hpp +++ b/Main/include/Application.hpp @@ -16,6 +16,7 @@ extern class JobSheduler* g_jobSheduler; extern class Input g_input; extern class SkinConfig* g_skinConfig; extern class TransitionScreen* g_transition; +extern class NetworkingServices* g_networkingServices; class Application { diff --git a/Main/src/Application.cpp b/Main/src/Application.cpp index 245e6bd35..947e54f9e 100644 --- a/Main/src/Application.cpp +++ b/Main/src/Application.cpp @@ -23,6 +23,7 @@ #include "SkinConfig.hpp" #include "SkinHttp.hpp" #include "ShadedMesh.hpp" +#include "NetworkingServices.hpp" #ifdef EMBEDDED #define NANOVG_GLES2_IMPLEMENTATION @@ -48,6 +49,7 @@ Graphics::Window *g_gameWindow = nullptr; Application *g_application = nullptr; JobSheduler *g_jobSheduler = nullptr; TransitionScreen *g_transition = nullptr; +NetworkingServices* g_networkingServices = nullptr; Input g_input; // Tickable queue @@ -880,6 +882,15 @@ bool Application::m_Init() Path::CreateDir(Path::Absolute("replays")); Path::CreateDir(Path::Absolute("crash_dumps")); + // Initalize NetworkServices + g_networkingServices = new NetworkingServices(); + g_networkingServices->Init(&g_gameConfig); + auto net_loggedin = g_networkingServices->TryLogin(); + if (net_loggedin) + { + g_networkingServices->QueueHeartbeat(); + } + return true; } void Application::m_MainLoop() From 2b95d62a4f35531340d27ce9642a939d2c5b8732 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Wed, 7 Oct 2020 22:19:31 -0500 Subject: [PATCH 08/28] Submit scoring when connected to Networking Services --- Main/src/ScoreScreen.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Main/src/ScoreScreen.cpp b/Main/src/ScoreScreen.cpp index 0299af187..144054d11 100644 --- a/Main/src/ScoreScreen.cpp +++ b/Main/src/ScoreScreen.cpp @@ -21,6 +21,7 @@ #include "cryptopp/sha3.h" #include #include +#include "NetworkingServices.hpp" class ScoreScreen_Impl : public ScoreScreen { @@ -291,6 +292,10 @@ class ScoreScreen_Impl : public ScoreScreen loadScoresFromGame(game); } + if (g_networkingServices->ConnectionStatus() == true) + { + g_networkingServices->SubmitScore(game, m_flags); + } for (HitStat* stat : scoring.hitStats) { From 85a2f5b1618fa254a920554c7a97607b4d9773b3 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Sun, 11 Oct 2020 08:35:09 -0500 Subject: [PATCH 09/28] Retrieve scores of a track from a specific SHA3 hash --- Main/include/NetworkingServices.hpp | 1 + Main/src/NetworkingServices.cpp | 171 ++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) diff --git a/Main/include/NetworkingServices.hpp b/Main/include/NetworkingServices.hpp index b3a2876bb..f655747b2 100644 --- a/Main/include/NetworkingServices.hpp +++ b/Main/include/NetworkingServices.hpp @@ -14,6 +14,7 @@ class NetworkingServices void QueueHeartbeat(); virtual bool SubmitScore(class Game* game, GameFlags m_flags); bool ConnectionStatus(); + int lGetScoresForTrack(lua_State* L); private: bool Heartbeat(); diff --git a/Main/src/NetworkingServices.cpp b/Main/src/NetworkingServices.cpp index ec36270e5..5fbe84427 100644 --- a/Main/src/NetworkingServices.cpp +++ b/Main/src/NetworkingServices.cpp @@ -12,6 +12,8 @@ #include #include "Game.hpp" #include "Scoring.hpp" +#include "lua.hpp" +#include "Shared/LuaBindable.hpp" NetworkingServices::NetworkingServices() { @@ -155,4 +157,173 @@ bool NetworkingServices::SubmitScore(class Game* game, GameFlags m_flags) { return false; } +} + +void m_PushStringToTable(lua_State* m_lua, const char* name, const char* data) +{ + lua_pushstring(m_lua, name); + lua_pushstring(m_lua, data); + lua_settable(m_lua, -3); +} + +void m_PushFloatToTable(lua_State* m_lua, const char* name, float data) +{ + lua_pushstring(m_lua, name); + lua_pushnumber(m_lua, data); + lua_settable(m_lua, -3); +} + +void m_PushIntToTable(lua_State* m_lua, const char* name, int data) +{ + lua_pushstring(m_lua, name); + lua_pushinteger(m_lua, data); + lua_settable(m_lua, -3); +} + +int NetworkingServices::lGetScoresForTrack(lua_State* L) +{ + // TODO: cache the info so we aren't always wasting bandwidth + // get track we are talking about + String hash = luaL_checkstring(L, 2); + cpr::Response respTrackInfo = cpr::Get(cpr::Url{ m_serviceUrl + "/api/v0/board/sha3/" + hash }); + if (respTrackInfo.status_code != 200) + { + return 0; + } + + auto res = nlohmann::json::parse(respTrackInfo.text); + uint64 track_id = res["track_id"]; + uint64 board_id = res["id"]; + + uint8 limit = luaL_checkinteger(L, 3); + uint16 offset = luaL_checkinteger(L, 4); + + // get scores from board + cpr::Response respScoreList = cpr::Get(cpr::Url{ m_serviceUrl + + "/api/v0/score?track=" + std::to_string(track_id) + + "&board=" + std::to_string(board_id) + + "&limit=" + std::to_string(limit) + + "&offset=" + std::to_string(offset)}); + + if (respScoreList.status_code != 200) + { + return 0; + } + + lua_newtable(L); + for (auto scoreEntry : nlohmann::json::parse(respScoreList.text)) + { + lua_newtable(L); + uint64 profileID = scoreEntry["profile"]; + cpr::Response respPlayerInfo = cpr::Post(cpr::Url{ m_serviceUrl + "/api/v0/profile/" + std::to_string(profileID) }); + String playerName; + if (respPlayerInfo.status_code != 200) + { + playerName = "UNINITALIZEZD"; + } + else + { + nlohmann::json resp = nlohmann::json::parse(respPlayerInfo.text); + String pplayerName = resp["name"]; // QUEST: Why do I need to do this and can't assign to playerName directly?? + playerName = pplayerName; + } + String dateCreated = scoreEntry["date_created"]; + float perfRating; + if (scoreEntry["performance"].is_null()) + { + perfRating = 0; + } + else + { + perfRating = scoreEntry["performance"]; + } + uint64 score; + if (scoreEntry["score"].is_null()) + { + score = 0; + } + else + { + score = scoreEntry["score"]; + } + uint32 combo; + if (scoreEntry["combo"].is_null()) + { + combo = 0; + } + else + { + combo = scoreEntry["combo"]; + } + uint8 status = scoreEntry["status"]; + float rate; + if (scoreEntry["rate"].is_null()) + { + rate = 0; + } + else + { + rate = scoreEntry["rate"]; + } + double accuracy; + if (scoreEntry["accuracy"].is_null()) + { + accuracy = 0; + } + else + { + accuracy = scoreEntry["accuracy"]; + } + uint32 crits; + if (scoreEntry["criticals"].is_null()) + { + crits = 0; + } + else + { + crits = scoreEntry["criticals"]; + } + uint32 nears; + if (scoreEntry["nears"].is_null()) + { + nears = 0; + } + else + { + nears = scoreEntry["nears"]; + } + uint32 errors; + if (scoreEntry["errors"].is_null()) + { + errors = 0; + } + else + { + errors = scoreEntry["errors"]; + } + Logf("[NetworkingServices] Date Created: %s", Logger::Severity::Normal, dateCreated); + Logf("[NetworkingServices] perfRating: %g", Logger::Severity::Normal, perfRating); + Logf("[NetworkingServices] Score: %d", Logger::Severity::Normal, score); + Logf("[NetworkingServices] Combo: %d", Logger::Severity::Normal, combo); + Logf("[NetworkingServices] Status: %d", Logger::Severity::Normal, status); + Logf("[NetworkingServices] Rate: %g", Logger::Severity::Normal, rate); + Logf("[NetworkingServices] Accuracy: %g", Logger::Severity::Normal, accuracy); + m_PushStringToTable(L, "player", playerName.c_str()); + m_PushStringToTable(L, "date_created", dateCreated.c_str()); + m_PushFloatToTable(L, "performance", perfRating); + m_PushIntToTable(L, "score", score); + m_PushIntToTable(L, "combo", combo); + m_PushIntToTable(L, "status", status); + m_PushFloatToTable(L, "rate", rate); + m_PushFloatToTable(L, "score", accuracy); + m_PushIntToTable(L, "criticals", crits); + m_PushIntToTable(L, "nears", nears); + m_PushIntToTable(L, "errors", errors); + + lua_settable(L, -3); + } + lua_settable(L, -3); + return 1; +} + } \ No newline at end of file From 107e79026638a39d4da8a0fee13bf973c7c1b214 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Sun, 11 Oct 2020 08:37:22 -0500 Subject: [PATCH 10/28] Calculate a SHA3-512 hash for songs when pushing songs to the Lua state --- Main/src/SongSelect.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Main/src/SongSelect.cpp b/Main/src/SongSelect.cpp index 7183b0e87..a25f0090e 100644 --- a/Main/src/SongSelect.cpp +++ b/Main/src/SongSelect.cpp @@ -21,6 +21,11 @@ #include "SongSort.hpp" #include "DBUpdateScreen.hpp" #include "PreviewPlayer.hpp" +#include "cryptopp/cryptlib.h" +#include "cryptopp/hex.h" +#include "cryptopp/sha3.h" +#include +#include class TextInput { @@ -839,6 +844,18 @@ class SelectionWheel m_PushStringToTable("effector", diff->effector.c_str()); m_PushStringToTable("illustrator", diff->illustrator.c_str()); m_PushIntToTable("topBadge", static_cast(Scoring::CalculateBestBadge(diff->scores))); + // TODO: Calculating a SHA3-512 hash everytime the wheel spins is going to be slow, we should cache this in the MapDatabase + CryptoPP::SHA3_512 hash; + String digest; + + CryptoPP::FileSource f( + String(diff->path).c_str(), + true, + new CryptoPP::HashFilter( + hash, + new CryptoPP::HexEncoder( + new CryptoPP::StringSink(digest), false)), true); + m_PushStringToTable("sha3_512", digest.c_str()); lua_pushstring(m_lua, "scores"); lua_newtable(m_lua); int scoreIndex = 0; From a0547673c328bc6f7212664a7d8edf47f89bc0d6 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Sun, 11 Oct 2020 08:39:32 -0500 Subject: [PATCH 11/28] Push NetServ Lua functions to the state --- Main/include/NetworkingServices.hpp | 5 +++++ Main/src/Application.cpp | 3 +++ Main/src/NetworkingServices.cpp | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/Main/include/NetworkingServices.hpp b/Main/include/NetworkingServices.hpp index f655747b2..55c4284fa 100644 --- a/Main/include/NetworkingServices.hpp +++ b/Main/include/NetworkingServices.hpp @@ -4,6 +4,7 @@ #include #include "GameConfig.hpp" #include "Game.hpp" +#include "lua.hpp" class NetworkingServices { @@ -14,6 +15,7 @@ class NetworkingServices void QueueHeartbeat(); virtual bool SubmitScore(class Game* game, GameFlags m_flags); bool ConnectionStatus(); + void PushLuaFunctions(lua_State* L); int lGetScoresForTrack(lua_State* L); private: @@ -32,6 +34,9 @@ class NetworkingServices // Static std::thread::id m_heartbeat; + + // Lua + Map m_boundStates; }; extern class NetworkingServices; \ No newline at end of file diff --git a/Main/src/Application.cpp b/Main/src/Application.cpp index 947e54f9e..d1dbf5b4a 100644 --- a/Main/src/Application.cpp +++ b/Main/src/Application.cpp @@ -2364,6 +2364,9 @@ void Application::SetLuaBindings(lua_State *state) //http m_skinHttp.PushFunctions(state); + + //netserv + g_networkingServices->PushLuaFunctions(state); } bool JacketLoadingJob::Run() diff --git a/Main/src/NetworkingServices.cpp b/Main/src/NetworkingServices.cpp index 5fbe84427..b60cb4f2c 100644 --- a/Main/src/NetworkingServices.cpp +++ b/Main/src/NetworkingServices.cpp @@ -326,4 +326,11 @@ int NetworkingServices::lGetScoresForTrack(lua_State* L) return 1; } +void NetworkingServices::PushLuaFunctions(lua_State* L) +{ + auto bindable = new LuaBindable(L, "NetServ"); + bindable->AddFunction("GetScoresForTrack", this, &NetworkingServices::lGetScoresForTrack); + bindable->Push(); + lua_settop(L, 0); + //m_boundStates.Add(L, bindable); } \ No newline at end of file From fb1b72465331cdc2ad3d268e0932bfa87b45b5e2 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Mon, 12 Oct 2020 00:39:22 -0500 Subject: [PATCH 12/28] Attempt to fix cross-platform building on macOS and GNU/Linux --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6d00ae0a..256760bc7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: - name: Install packages run: | sudo apt-get update - sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev + sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev libcrypto++-dev - name: cmake run: cmake -DCMAKE_BUILD_TYPE=Release . - name: make @@ -91,7 +91,7 @@ jobs: - name: Checkout submodules run: git submodule update --init --recursive - name: Install packages - run: brew install cmake freetype libvorbis sdl2 libpng jpeg libarchive + run: brew install cmake freetype libvorbis sdl2 libpng jpeg libarchive cryptopp - name: cmake run: cmake . -DLibArchive_LIBRARY=/usr/local/opt/libarchive/lib/libarchive.dylib -DLibArchive_INCLUDE_DIR=/usr/local/opt/libarchive/include -DCMAKE_BUILD_TYPE=Release - name: make From 8308de8b3dc134546901ac3c91ab0aedba67a02c Mon Sep 17 00:00:00 2001 From: Starz0r Date: Mon, 12 Oct 2020 02:08:55 -0500 Subject: [PATCH 13/28] Don't index a number value or something --- Main/src/NetworkingServices.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Main/src/NetworkingServices.cpp b/Main/src/NetworkingServices.cpp index b60cb4f2c..9cebfa28f 100644 --- a/Main/src/NetworkingServices.cpp +++ b/Main/src/NetworkingServices.cpp @@ -210,9 +210,12 @@ int NetworkingServices::lGetScoresForTrack(lua_State* L) return 0; } + lua_pushstring(L, "scores"); lua_newtable(L); + int idx = 0; for (auto scoreEntry : nlohmann::json::parse(respScoreList.text)) { + lua_pushinteger(L, ++idx); lua_newtable(L); uint64 profileID = scoreEntry["profile"]; cpr::Response respPlayerInfo = cpr::Post(cpr::Url{ m_serviceUrl + "/api/v0/profile/" + std::to_string(profileID) }); @@ -322,7 +325,7 @@ int NetworkingServices::lGetScoresForTrack(lua_State* L) lua_settable(L, -3); } - lua_settable(L, -3); + return 1; } From 41c50cfc7efe1177b829368119957bb8eac77426 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Mon, 12 Oct 2020 02:35:27 -0500 Subject: [PATCH 14/28] More Linux dependencies while macOS has to build CryptoPP from scratch --- .github/workflows/build.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 256760bc7..c0bd9935e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: - name: Install packages run: | sudo apt-get update - sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev libcrypto++-dev + sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev libcrypto++-dev libcrypto++8 libcrypto++8-dbg - name: cmake run: cmake -DCMAKE_BUILD_TYPE=Release . - name: make @@ -86,12 +86,22 @@ jobs: name: macOS build runs-on: macos-latest steps: + - name: Checkout CryptoPP + run: | + git clone https://github.com/weidai11/cryptopp cryptopp + cd cryptopp + git checkout CRYPTOPP_8_2_0 + - name: Build CryptoPP + run: | + make dynamic + make install + cd ... - name: Checkout repo uses: actions/checkout@v1 - name: Checkout submodules run: git submodule update --init --recursive - name: Install packages - run: brew install cmake freetype libvorbis sdl2 libpng jpeg libarchive cryptopp + run: brew install cmake freetype libvorbis sdl2 libpng jpeg libarchive - name: cmake run: cmake . -DLibArchive_LIBRARY=/usr/local/opt/libarchive/lib/libarchive.dylib -DLibArchive_INCLUDE_DIR=/usr/local/opt/libarchive/include -DCMAKE_BUILD_TYPE=Release - name: make From 4f3c250ec7f6a70edca866be979513186e7d45e2 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Mon, 12 Oct 2020 02:41:41 -0500 Subject: [PATCH 15/28] Ubuntu only has Libcrypto6 so build from scratch there too --- .github/workflows/build.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c0bd9935e..0f6fb1132 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,6 +55,17 @@ jobs: name: Linux build runs-on: ubuntu-latest steps: + - name: Checkout CryptoPP + run: | + git clone https://github.com/weidai11/cryptopp cryptopp + cd cryptopp + git checkout CRYPTOPP_8_2_0 + - name: Build CryptoPP + run: | + make + make static dynamic + make install + cd ... - name: Checkout repo uses: actions/checkout@v1 - name: Checkout submodules @@ -93,7 +104,8 @@ jobs: git checkout CRYPTOPP_8_2_0 - name: Build CryptoPP run: | - make dynamic + make + make static dynamic make install cd ... - name: Checkout repo From ebc58bd96164c6e1651014a649db71b291f23f33 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Mon, 12 Oct 2020 02:47:01 -0500 Subject: [PATCH 16/28] Check why macOS can't see the GNUmakefile for CryptoPP --- .github/workflows/build.yml | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f6fb1132..1508a11fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,17 +55,6 @@ jobs: name: Linux build runs-on: ubuntu-latest steps: - - name: Checkout CryptoPP - run: | - git clone https://github.com/weidai11/cryptopp cryptopp - cd cryptopp - git checkout CRYPTOPP_8_2_0 - - name: Build CryptoPP - run: | - make - make static dynamic - make install - cd ... - name: Checkout repo uses: actions/checkout@v1 - name: Checkout submodules @@ -73,7 +62,7 @@ jobs: - name: Install packages run: | sudo apt-get update - sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev libcrypto++-dev libcrypto++8 libcrypto++8-dbg + sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev libcrypto++-dev libcrypto++6 libcrypto++6-dbg - name: cmake run: cmake -DCMAKE_BUILD_TYPE=Release . - name: make @@ -97,17 +86,16 @@ jobs: name: macOS build runs-on: macos-latest steps: - - name: Checkout CryptoPP + - name: Checkout and Build CryptoPP run: | git clone https://github.com/weidai11/cryptopp cryptopp cd cryptopp git checkout CRYPTOPP_8_2_0 - - name: Build CryptoPP - run: | + ls make make static dynamic make install - cd ... + cd .. - name: Checkout repo uses: actions/checkout@v1 - name: Checkout submodules From 77c8b02b4712672dfeb43af70b320a45ac89bc79 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Mon, 12 Oct 2020 02:59:14 -0500 Subject: [PATCH 17/28] Try building CryptoPP with a different prefix --- .github/workflows/build.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1508a11fb..4dd4b840e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,6 +55,15 @@ jobs: name: Linux build runs-on: ubuntu-latest steps: + - name: Checkout and Build CryptoPP + run: | + git clone https://github.com/weidai11/cryptopp cryptopp + cd cryptopp + git checkout CRYPTOPP_8_2_0 + make + make static dynamic + make install PREFIX=/usr/lib + cd .. - name: Checkout repo uses: actions/checkout@v1 - name: Checkout submodules @@ -62,7 +71,7 @@ jobs: - name: Install packages run: | sudo apt-get update - sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev libcrypto++-dev libcrypto++6 libcrypto++6-dbg + sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev libcrypto++-dev - name: cmake run: cmake -DCMAKE_BUILD_TYPE=Release . - name: make @@ -91,10 +100,9 @@ jobs: git clone https://github.com/weidai11/cryptopp cryptopp cd cryptopp git checkout CRYPTOPP_8_2_0 - ls make make static dynamic - make install + make install PREFIX=/usr/local/opt cd .. - name: Checkout repo uses: actions/checkout@v1 From ca53f112bd809d20361c8e3277e22115472fbd5b Mon Sep 17 00:00:00 2001 From: Starz0r Date: Mon, 12 Oct 2020 03:16:09 -0500 Subject: [PATCH 18/28] Linux needs sudo to install libs and change prefix directory for macOS --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4dd4b840e..5d69087cf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: git checkout CRYPTOPP_8_2_0 make make static dynamic - make install PREFIX=/usr/lib + sudo make install PREFIX=/usr/lib cd .. - name: Checkout repo uses: actions/checkout@v1 @@ -102,7 +102,7 @@ jobs: git checkout CRYPTOPP_8_2_0 make make static dynamic - make install PREFIX=/usr/local/opt + make install cd .. - name: Checkout repo uses: actions/checkout@v1 @@ -111,7 +111,7 @@ jobs: - name: Install packages run: brew install cmake freetype libvorbis sdl2 libpng jpeg libarchive - name: cmake - run: cmake . -DLibArchive_LIBRARY=/usr/local/opt/libarchive/lib/libarchive.dylib -DLibArchive_INCLUDE_DIR=/usr/local/opt/libarchive/include -DCMAKE_BUILD_TYPE=Release + run: cmake . -DLibArchive_LIBRARY=/usr/local/opt/libarchive/lib/libarchive.dylib -DLibArchive_INCLUDE_DIR=/usr/local/opt/libarchive/include -DLibCryptoPP_LIBRARY=/usr/local/lib/libcryptopp.dylib -DLibCryptoPP_INCLUDE_DIR=/usr/local/include/cryptopp -DCMAKE_BUILD_TYPE=Release - name: make run: make - name: Upload artifact From 053fc16b33294abea8aaa256a31e068551b101a6 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Mon, 12 Oct 2020 03:29:24 -0500 Subject: [PATCH 19/28] Change Linux prefix and CryptoPP not LibCryptoPP --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d69087cf..d29389f22 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: git checkout CRYPTOPP_8_2_0 make make static dynamic - sudo make install PREFIX=/usr/lib + sudo make install PREFIX=/usr cd .. - name: Checkout repo uses: actions/checkout@v1 @@ -73,7 +73,7 @@ jobs: sudo apt-get update sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev libcrypto++-dev - name: cmake - run: cmake -DCMAKE_BUILD_TYPE=Release . + run: cmake -DCryptoPP_LIBRARY=/usr/lib/libcryptopp.so -DCryptoPP_INCLUDE_DIR=/usr/lib/include/cryptopp -DCMAKE_BUILD_TYPE=Release . - name: make run: make - name: Prepare for bundling AppImage @@ -111,7 +111,7 @@ jobs: - name: Install packages run: brew install cmake freetype libvorbis sdl2 libpng jpeg libarchive - name: cmake - run: cmake . -DLibArchive_LIBRARY=/usr/local/opt/libarchive/lib/libarchive.dylib -DLibArchive_INCLUDE_DIR=/usr/local/opt/libarchive/include -DLibCryptoPP_LIBRARY=/usr/local/lib/libcryptopp.dylib -DLibCryptoPP_INCLUDE_DIR=/usr/local/include/cryptopp -DCMAKE_BUILD_TYPE=Release + run: cmake . -DLibArchive_LIBRARY=/usr/local/opt/libarchive/lib/libarchive.dylib -DLibArchive_INCLUDE_DIR=/usr/local/opt/libarchive/include -DCryptoPP_LIBRARY=/usr/local/lib/libcryptopp.dylib -DCryptoPP_INCLUDE_DIR=/usr/local/include/cryptopp -DCMAKE_BUILD_TYPE=Release - name: make run: make - name: Upload artifact From fe3765c026e69840dbffdd98a289d773e9017a89 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Mon, 12 Oct 2020 04:00:43 -0500 Subject: [PATCH 20/28] Expose CryptoPP in CMakeLists file --- .github/workflows/build.yml | 3 ++- Main/CMakeLists.txt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d29389f22..6c54760da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,6 +63,7 @@ jobs: make make static dynamic sudo make install PREFIX=/usr + cp /usr/lib/libcryptopp.so /usr/lib/x86_64-linux-gnu/libcryptopp.so cd .. - name: Checkout repo uses: actions/checkout@v1 @@ -73,7 +74,7 @@ jobs: sudo apt-get update sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev libcrypto++-dev - name: cmake - run: cmake -DCryptoPP_LIBRARY=/usr/lib/libcryptopp.so -DCryptoPP_INCLUDE_DIR=/usr/lib/include/cryptopp -DCMAKE_BUILD_TYPE=Release . + run: cmake -DCryptoPP_LIBRARY=/usr/lib/libcryptopp.so -DCryptoPP_INCLUDE_DIR=/usr/include/cryptopp -DCMAKE_BUILD_TYPE=Release . - name: make run: make - name: Prepare for bundling AppImage diff --git a/Main/CMakeLists.txt b/Main/CMakeLists.txt index 6d0f01218..03d77333b 100644 --- a/Main/CMakeLists.txt +++ b/Main/CMakeLists.txt @@ -80,7 +80,9 @@ target_link_libraries(usc-game nlohmann_json) target_link_libraries(usc-game ${SDL2_LIBRARY}) target_link_libraries(usc-game ${LibArchive_LIBRARIES}) +target_link_libraries(usc-game ${CryptoPP_LIBRARIES}) target_include_directories(usc-game SYSTEM PRIVATE ${LibArchive_INCLUDE_DIRS}) +target_include_directories(usc-game SYSTEM PRIVATE ${CryptoPP_INCLUDE_DIRS}) if(WIN32) target_link_libraries(usc-game From 6fb2273f12072d9ee7f3fd34c8964bdb35bd0a95 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Fri, 11 Dec 2020 11:27:39 -0600 Subject: [PATCH 21/28] Revert build.yml changes --- .github/workflows/build.yml | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6c54760da..f63da0efb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,16 +55,6 @@ jobs: name: Linux build runs-on: ubuntu-latest steps: - - name: Checkout and Build CryptoPP - run: | - git clone https://github.com/weidai11/cryptopp cryptopp - cd cryptopp - git checkout CRYPTOPP_8_2_0 - make - make static dynamic - sudo make install PREFIX=/usr - cp /usr/lib/libcryptopp.so /usr/lib/x86_64-linux-gnu/libcryptopp.so - cd .. - name: Checkout repo uses: actions/checkout@v1 - name: Checkout submodules @@ -72,9 +62,9 @@ jobs: - name: Install packages run: | sudo apt-get update - sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev libcrypto++-dev + sudo apt-get install g++ libegl1-mesa-dev libfreetype6-dev libogg-dev libvorbis-dev libsdl2-dev libarchive-dev zlib1g-dev libjpeg-dev libpng-dev - name: cmake - run: cmake -DCryptoPP_LIBRARY=/usr/lib/libcryptopp.so -DCryptoPP_INCLUDE_DIR=/usr/include/cryptopp -DCMAKE_BUILD_TYPE=Release . + run: cmake -DCMAKE_BUILD_TYPE=Release . - name: make run: make - name: Prepare for bundling AppImage @@ -96,15 +86,6 @@ jobs: name: macOS build runs-on: macos-latest steps: - - name: Checkout and Build CryptoPP - run: | - git clone https://github.com/weidai11/cryptopp cryptopp - cd cryptopp - git checkout CRYPTOPP_8_2_0 - make - make static dynamic - make install - cd .. - name: Checkout repo uses: actions/checkout@v1 - name: Checkout submodules @@ -112,11 +93,11 @@ jobs: - name: Install packages run: brew install cmake freetype libvorbis sdl2 libpng jpeg libarchive - name: cmake - run: cmake . -DLibArchive_LIBRARY=/usr/local/opt/libarchive/lib/libarchive.dylib -DLibArchive_INCLUDE_DIR=/usr/local/opt/libarchive/include -DCryptoPP_LIBRARY=/usr/local/lib/libcryptopp.dylib -DCryptoPP_INCLUDE_DIR=/usr/local/include/cryptopp -DCMAKE_BUILD_TYPE=Release + run: cmake . -DLibArchive_LIBRARY=/usr/local/opt/libarchive/lib/libarchive.dylib -DLibArchive_INCLUDE_DIR=/usr/local/opt/libarchive/include -DCMAKE_BUILD_TYPE=Release - name: make run: make - name: Upload artifact uses: actions/upload-artifact@master with: name: Game_mac - path: bin + path: bin \ No newline at end of file From 31d2527c1e444444549239184d6c48bd113121f8 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Fri, 11 Dec 2020 11:28:46 -0600 Subject: [PATCH 22/28] Push the SHA3 hash of the current chart to the Lua data table --- Main/src/ScoreScreen.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Main/src/ScoreScreen.cpp b/Main/src/ScoreScreen.cpp index 144054d11..73b275125 100644 --- a/Main/src/ScoreScreen.cpp +++ b/Main/src/ScoreScreen.cpp @@ -431,6 +431,18 @@ class ScoreScreen_Impl : public ScoreScreen void updateLuaData() { + // hash the file of the chart we played + CryptoPP::SHA3_512 hash; + String digest; + + CryptoPP::FileSource f( + String(m_chartIndex->path).c_str(), + true, + new CryptoPP::HashFilter( + hash, + new CryptoPP::HexEncoder( + new CryptoPP::StringSink(digest), false)), true); + const bool isSelf = m_displayIndex == m_selfDisplayIndex; lua_newtable(m_lua); @@ -443,6 +455,7 @@ class ScoreScreen_Impl : public ScoreScreen m_PushIntToTable("maxCombo", m_maxCombo); m_PushIntToTable("level", m_beatmapSettings.level); m_PushIntToTable("difficulty", m_beatmapSettings.difficulty); + m_PushStringToTable("sha3", digest); if (m_multiplayer) { m_PushStringToTable("playerName", m_playerName); From c1d0e0d15edbf7a13b479b414b54f72f876bd061 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Fri, 11 Dec 2020 11:30:12 -0600 Subject: [PATCH 23/28] GET Request instead of POST for getting profile information --- Main/src/NetworkingServices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/src/NetworkingServices.cpp b/Main/src/NetworkingServices.cpp index 9cebfa28f..d7032d347 100644 --- a/Main/src/NetworkingServices.cpp +++ b/Main/src/NetworkingServices.cpp @@ -218,7 +218,7 @@ int NetworkingServices::lGetScoresForTrack(lua_State* L) lua_pushinteger(L, ++idx); lua_newtable(L); uint64 profileID = scoreEntry["profile"]; - cpr::Response respPlayerInfo = cpr::Post(cpr::Url{ m_serviceUrl + "/api/v0/profile/" + std::to_string(profileID) }); + cpr::Response respPlayerInfo = cpr::Get(cpr::Url{ m_serviceUrl + "/api/v0/profile/" + std::to_string(profileID) }); String playerName; if (respPlayerInfo.status_code != 200) { From 37a63d09472d71d16c4705db72fa86089ff79276 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Fri, 11 Dec 2020 11:30:59 -0600 Subject: [PATCH 24/28] Don't push score as the accuracy to the Lua data table --- Main/src/NetworkingServices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main/src/NetworkingServices.cpp b/Main/src/NetworkingServices.cpp index d7032d347..9241affd9 100644 --- a/Main/src/NetworkingServices.cpp +++ b/Main/src/NetworkingServices.cpp @@ -318,7 +318,7 @@ int NetworkingServices::lGetScoresForTrack(lua_State* L) m_PushIntToTable(L, "combo", combo); m_PushIntToTable(L, "status", status); m_PushFloatToTable(L, "rate", rate); - m_PushFloatToTable(L, "score", accuracy); + m_PushFloatToTable(L, "accuracy", accuracy); m_PushIntToTable(L, "criticals", crits); m_PushIntToTable(L, "nears", nears); m_PushIntToTable(L, "errors", errors); From 662277694db8747eb53b409992e6e516b5ddd2ca Mon Sep 17 00:00:00 2001 From: Starz0r Date: Fri, 11 Dec 2020 11:31:58 -0600 Subject: [PATCH 25/28] Lua API to query whether the current user is connected to a NetServ --- Main/include/NetworkingServices.hpp | 1 + Main/src/NetworkingServices.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/Main/include/NetworkingServices.hpp b/Main/include/NetworkingServices.hpp index 55c4284fa..f27c1b564 100644 --- a/Main/include/NetworkingServices.hpp +++ b/Main/include/NetworkingServices.hpp @@ -17,6 +17,7 @@ class NetworkingServices bool ConnectionStatus(); void PushLuaFunctions(lua_State* L); int lGetScoresForTrack(lua_State* L); + int lIsConnected(lua_State* L); private: bool Heartbeat(); diff --git a/Main/src/NetworkingServices.cpp b/Main/src/NetworkingServices.cpp index 9241affd9..c94f331e2 100644 --- a/Main/src/NetworkingServices.cpp +++ b/Main/src/NetworkingServices.cpp @@ -329,10 +329,17 @@ int NetworkingServices::lGetScoresForTrack(lua_State* L) return 1; } +int NetworkingServices::lIsConnected(lua_State* L) { + lua_pushboolean(L, ConnectionStatus()); + + return 1; +} + void NetworkingServices::PushLuaFunctions(lua_State* L) { auto bindable = new LuaBindable(L, "NetServ"); bindable->AddFunction("GetScoresForTrack", this, &NetworkingServices::lGetScoresForTrack); + bindable->AddFunction("IsConnected", this, &NetworkingServices::lIsConnected); bindable->Push(); lua_settop(L, 0); //m_boundStates.Add(L, bindable); From 27c37c363f37d93336d02db0a5a859ba730d0037 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Fri, 11 Dec 2020 11:32:28 -0600 Subject: [PATCH 26/28] Disable Multiplayer and Nautica downloads while NetServ is enabled --- bin/skins/Default/scripts/titlescreen.lua | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/bin/skins/Default/scripts/titlescreen.lua b/bin/skins/Default/scripts/titlescreen.lua index 84bff4084..16bd198e1 100644 --- a/bin/skins/Default/scripts/titlescreen.lua +++ b/bin/skins/Default/scripts/titlescreen.lua @@ -72,11 +72,17 @@ end function setButtons() if buttons == nil then buttons = {} - buttons[1] = {"Start", Menu.Start} - buttons[2] = {"Multiplayer", Menu.Multiplayer} - buttons[3] = {"Get Songs", Menu.DLScreen} - buttons[4] = {"Settings", Menu.Settings} - buttons[5] = {"Exit", Menu.Exit} + if NetServ.IsConnected() == true then + buttons[1] = {"Start", Menu.Start} + buttons[2] = {"Settings", Menu.Settings} + buttons[3] = {"Exit", Menu.Exit} + else + buttons[1] = {"Start", Menu.Start} + buttons[2] = {"Multiplayer", Menu.Multiplayer} + buttons[3] = {"Get Songs", Menu.DLScreen} + buttons[4] = {"Settings", Menu.Settings} + buttons[5] = {"Exit", Menu.Exit} + end end end From e4765e32fbe2de3e22f491c9125bf332a75cca82 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Fri, 11 Dec 2020 11:34:00 -0600 Subject: [PATCH 27/28] Show NetServ highscores on a track at the score screen --- bin/skins/Default/scripts/result.lua | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/bin/skins/Default/scripts/result.lua b/bin/skins/Default/scripts/result.lua index 4a481846e..1b05c3539 100644 --- a/bin/skins/Default/scripts/result.lua +++ b/bin/skins/Default/scripts/result.lua @@ -146,7 +146,24 @@ result_set = function() hitGraphHoverScale = 10 end - if result.uid == nil then --local scores + if NetServ.IsConnected() == true then + for i,s in ipairs(NetServ.GetScoresForTrack(result.sha3, 50, 0)) do + entry = { } + + game.Log(s.score, 2) + game.Log(s.player, 2) + game.Log(s.rate, 2) + game.Log(s.status, 2) + entry.score = s.score + entry.subtext = s.player + entry.badge = s.status + entry.badgeDesc = getScoreBadgeDesc(result) + entry.color = {255, 127, 0} + entry.xoff = 0 + + table.insert(highScores, entry) + end + elseif result.uid == nil then --local scores for i,s in ipairs(result.highScores) do newScore = { } if currentAdded == false and result.score > s.score then From 059179b20bf7c32c39d5912f63716b9447e16d92 Mon Sep 17 00:00:00 2001 From: Starz0r Date: Sun, 21 Feb 2021 21:28:52 -0600 Subject: [PATCH 28/28] Show local online record on the Song Wheel --- .../Default/scripts/songselect/songwheel.lua | 99 ++++++++++++++++--- 1 file changed, 83 insertions(+), 16 deletions(-) diff --git a/bin/skins/Default/scripts/songselect/songwheel.lua b/bin/skins/Default/scripts/songselect/songwheel.lua index d529a6c33..80e528573 100644 --- a/bin/skins/Default/scripts/songselect/songwheel.lua +++ b/bin/skins/Default/scripts/songselect/songwheel.lua @@ -51,6 +51,8 @@ local badges = { gfx.CreateSkinImage("badges/perfect.png", 0) } +local recordCache = {} + gfx.LoadSkinFont("NotoSans-Regular.ttf"); game.LoadSkinSample("menu_click") @@ -138,43 +140,108 @@ check_or_create_cache = function(song, loadJacket) end end +function get_record(hash) + if recordCache[hash] then + return recordCache[hash] + end + + -- TODO: Set a field for when this cache expires (every 5 minutes) + recordCache[hash] = NetServ.GetScoresForTrack(hash, 1, 0) +end + draw_scores = function(difficulty, x, y, w, h) -- draw the top score for this difficulty local xOffset = 5 local height = h/3 - 10 local ySpacing = h/3 local yOffset = h/3 + gfx.FontSize(30); gfx.TextAlign(gfx.TEXT_ALIGN_BOTTOM + gfx.TEXT_ALIGN_CENTER); - gfx.FastText("HIGH SCORE", x +(w/2), y+(h/2)) + gfx.FastText("LOCAL SCORE", x +(w/4), y+(h/2)) + gfx.FastText("ONLINE RECORD", x + (3/4 * w), y + (h/2)) + + -- local high score rect gfx.BeginPath() - gfx.Rect(x+xOffset,y+h/2,w-(xOffset*2),h/2) + gfx.Rect(x+xOffset,y+h/2,w/2-(xOffset*2),h/2) gfx.FillColor(30,30,30,10) gfx.StrokeColor(0,128,255) gfx.StrokeWidth(1) gfx.Fill() gfx.Stroke() - if difficulty.scores[1] ~= nil then - local highScore = difficulty.scores[1] - scoreLabel = gfx.CreateLabel(string.format("%08d",highScore.score), 40, 0) - for i,v in ipairs(grades) do - if v.max > highScore.score then - gfx.BeginPath() - iw,ih = gfx.ImageSize(v.image) - iar = iw / ih; - gfx.ImageRect(x+xOffset,y+h/2 +5, iar * (h/2-10),h/2-10, v.image, 1, 0) - break - end + + -- online record rect + gfx.BeginPath() + gfx.Rect(x + xOffset + w/2,y+h/2,w/2-(xOffset*2),h/2) + gfx.FillColor(30,30,30,10) + gfx.StrokeColor(0,128,255) + gfx.StrokeWidth(1) + gfx.Fill() + gfx.Stroke() + + if difficulty.scores[1] ~= nil then + local highScore = difficulty.scores[1] + scoreLabel = gfx.CreateLabel(string.format("%08d",highScore.score), 40, 0) + for i,v in ipairs(grades) do + if v.max > highScore.score then + gfx.BeginPath() + iw,ih = gfx.ImageSize(v.image) + iarr = ih / iw + oldheight = h/2 - 10 + newheight = iarr * (h/2-10) + centreoffset = (oldheight - newheight)/2 + 3 -- +3 is stupid but ehhh + gfx.ImageRect(x+xOffset, y+h/2 + centreoffset, oldheight, newheight, v.image, 1, 0) --this is nasty but it works for me + break + end end if difficulty.topBadge ~= 0 then gfx.BeginPath() - gfx.ImageRect(x+xOffset+w-h/2, y+h/2 +5, (h/2-10), h/2-10, badges[difficulty.topBadge], 1, 0) + gfx.ImageRect(x+xOffset+w/2-h/2, y+h/2 +5, (h/2-10), h/2-10, badges[difficulty.topBadge], 1, 0) end + gfx.FillColor(255,255,255) - gfx.FontSize(40); + gfx.FontSize(40); gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER); - gfx.DrawLabel(scoreLabel, x+(w/2),y+(h/4)*3,w) + gfx.DrawLabel(scoreLabel, x+(w/4),y+(h/4)*3,w/2) + end + + onlinerecord = get_record(difficulty.sha3_512) + + if onlinerecord == nil then --record not set, but can be tracked + recordLabel = gfx.CreateLabel(string.format("%08d", 0), 40, 0) + gfx.FillColor(170, 170, 170) + gfx.FontSize(40) + gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER); + gfx.DrawLabel(recordLabel, x+(w * 3/4),y+(h/4)*3,w/2) + else + for i,s in ipairs(onlinerecord) do + recordScoreLabel = gfx.CreateLabel(string.format("%08d", s.score), 26, 0) + recordPlayerLabel = gfx.CreateLabel(s.player, 26, 0) + + gfx.BeginPath() + gfx.ImageRect(x+xOffset+w-h/2, y+h/2 +5, (h/2-10), h/2-10, badges[s.status], 1, 0) + + for ii,v in ipairs(grades) do + if v.max > s.score then + gfx.BeginPath() + iw,ih = gfx.ImageSize(v.image) + iarr = ih / iw + oldheight = h/2 - 10 + newheight = iarr * (h/2-10) + centreoffset = (oldheight - newheight)/2 + 3 -- +3 is stupid but ehhh + gfx.ImageRect(x+xOffset+w/2, y+h/2 + centreoffset, oldheight, newheight, v.image, 1, 0) --this is nasty but it works for me + break + end + end + + gfx.FillColor(255, 255, 255) + gfx.FontSize(40) + gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER); + gfx.DrawLabel(recordPlayerLabel, x+(w * 3/4),y+(h/4)*2.55,w/2) + gfx.DrawLabel(recordScoreLabel, x+(w * 3/4),y+(h/4)*3.45,w/2) + end end + end draw_song = function(song, x, y, w, h, selected)