diff --git a/mod.json b/mod.json index 0295d59..d2b2157 100644 --- a/mod.json +++ b/mod.json @@ -227,6 +227,25 @@ "slider": true }, "enable-if": "show-sent && enable-sent-cache" + }, + "custom-sends-endpoint": { + "name": "Custom Sends Endpoint", + "description": "Only modify this if you're on a GDPS and host a custom SendDB instance (or something with a similar API response)", + "type": "string", + "filter": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:/.-_%?&=[]@", + "default": "" + }, + "disable-gdps-warning": { + "name": "Disable GDPS Warning", + "description": "Disable the warning you get when you're on a GDPS and have the sent indicator toggled on", + "type": "bool", + "default": false + } + }, + "dependencies": { + "km7dev.server_api": { + "version": "v4.0.0", + "required": false } } } diff --git a/src/hooks/LevelInfoLayer.cpp b/src/hooks/LevelInfoLayer.cpp index 57ba23d..a3b832f 100644 --- a/src/hooks/LevelInfoLayer.cpp +++ b/src/hooks/LevelInfoLayer.cpp @@ -189,7 +189,7 @@ class $modify(MyLevelInfoLayer, LevelInfoLayer) { labelContent << "Has LDM: " << (level->m_lowDetailMode ? "Yes" : "No") << std::endl; - if (SettingsManager::Toggles.sent) { + if (SettingsManager::Toggles.sent && (!Utils::IsOnGdps() || !SettingsManager::Other.customSendsEndpoint.empty())) { if (level->m_stars == 0) { if (auto cached = SettingsManager::Other.enableSentCache ? SentCacheManager::GetLevel(level->m_levelID) : std::nullopt) { labelContent << "Sent: " << (cached.value() ? "Yes" : "No") @@ -201,46 +201,7 @@ class $modify(MyLevelInfoLayer, LevelInfoLayer) { auto levelID = static_cast(level->m_levelID); async::spawn( - [levelID]() -> arc::Future> { - // First try requesting to my own cache API specifically for - // the sent state - auto req = co_await utils::web::WebRequest() - .userAgent(Utils::GetUserAgent()) - .timeout(std::chrono::seconds(3)) - .get(fmt::format("https://sdbc.m336.dev/level/{}", levelID)); - auto body = req.json().unwrapOrDefault(); - auto error = body["error"].asString().unwrapOrDefault(); - - // If that doesn't work, fallback to the original SendDB API - if (!req.ok() || body.size() <= 0 || error.size() > 0) { - log::warn( - "Failed requesting to the cache API ({}), fallback to the original SendDB API", - error.size() > 0 ? error : req.errorMessage() - ); - - req = co_await utils::web::WebRequest() - .userAgent(Utils::GetUserAgent()) - .timeout(std::chrono::seconds(3)) - .get(fmt::format("https://api.senddb.dev/api/v1/level/{}", levelID)); - body = req.json().unwrapOrDefault(); - error = req.errorMessage(); - - // If that still doesn't work, don't go further - if (!req.ok() || body.size() <= 0) { - log::error( - "Failed requesting to the SendDB API: {}", - error.size() > 0 ? error : req.string().unwrap() - ); - co_return Err(); - } - - // For the SendDB API, just check if the sends object is more than 0 - co_return Ok(body["sends"].size() > 0 ? true : false); - } - - // For the cache, it's directly indicated in the sent boolean - co_return Ok(body["sent"].asBool().unwrap()); - }, + Utils::CheckIfLevelSent(levelID), [self, levelID](Result result) { if (!self->m_fields->m_label) return; diff --git a/src/hooks/MenuLayer.cpp b/src/hooks/MenuLayer.cpp new file mode 100644 index 0000000..b7b719a --- /dev/null +++ b/src/hooks/MenuLayer.cpp @@ -0,0 +1,34 @@ +#include +#include "../managers/SettingsManager.h" +#include "../utils/Utils.h" + +using namespace geode::prelude; + +class $modify(MenuLayer) { + bool init() { + if (!MenuLayer::init()) + return false; + + if (SettingsManager::Toggles.sent && + Utils::IsOnGdps() && + SettingsManager::Other.customSendsEndpoint.empty() && + SettingsManager::Other.showGDPSWarning && + !SettingsManager::ShowedGDPSWarning + ) { + Notification::create( + "Level Info's sent indicator was disabled (you're on a GDPS)", + NotificationIcon::Warning, + 2.f + )->show(); + Notification::create( + "Change the sent indicator URL/disable this warning in its settings", + NotificationIcon::Warning, + 2.f + )->show(); + + SettingsManager::ShowedGDPSWarning = true; + } + + return true; + }; +}; \ No newline at end of file diff --git a/src/managers/SettingsManager.cpp b/src/managers/SettingsManager.cpp index 0fba752..634d1bc 100644 --- a/src/managers/SettingsManager.cpp +++ b/src/managers/SettingsManager.cpp @@ -36,7 +36,9 @@ CustomStruct::ToggleSettings SettingsManager::Toggles = { CustomStruct::OtherSettings SettingsManager::Other = { Mod::get()->getSettingValue("enable-sent-cache"), Mod::get()->getSettingValue("sent-cache-limit"), - Mod::get()->getSettingValue("sent-cache-expiration") * 60 + Mod::get()->getSettingValue("sent-cache-expiration") * 60, + Mod::get()->getSettingValue("custom-sends-endpoint"), + !Mod::get()->getSettingValue("disable-gdps-warning") }; // There is DEFINITELY a better way to do this but if it works it works @@ -132,4 +134,12 @@ CustomStruct::OtherSettings SettingsManager::Other = { listenForSettingChanges("sent-cache-expiration", [](int expiration) { SettingsManager::Other.maxSentCacheExpiration = expiration * 60; }); -}; \ No newline at end of file + listenForSettingChanges("custom-sends-endpoint", [](std::string url) { + SettingsManager::Other.customSendsEndpoint = url; + }); + listenForSettingChanges("disable-gdps-warning", [](bool enabled) { + SettingsManager::Other.showGDPSWarning = !enabled; + }); +}; + +bool SettingsManager::ShowedGDPSWarning = false; \ No newline at end of file diff --git a/src/managers/SettingsManager.h b/src/managers/SettingsManager.h index 3978514..7c9a840 100644 --- a/src/managers/SettingsManager.h +++ b/src/managers/SettingsManager.h @@ -6,4 +6,6 @@ class SettingsManager { static CustomStruct::DisplaySettings Display; static CustomStruct::ToggleSettings Toggles; static CustomStruct::OtherSettings Other; + + static bool ShowedGDPSWarning; }; \ No newline at end of file diff --git a/src/utils/CustomStruct.h b/src/utils/CustomStruct.h index 6b923a2..68c850a 100644 --- a/src/utils/CustomStruct.h +++ b/src/utils/CustomStruct.h @@ -49,6 +49,8 @@ namespace CustomStruct { bool enableSentCache; int maxSentCacheLimit; int maxSentCacheExpiration; + std::string customSendsEndpoint; + bool showGDPSWarning; }; struct SentCacheEntry { diff --git a/src/utils/Utils.cpp b/src/utils/Utils.cpp index e7ef19a..3639450 100644 --- a/src/utils/Utils.cpp +++ b/src/utils/Utils.cpp @@ -1,11 +1,52 @@ #include "Utils.h" +#include #include "../managers/SettingsManager.h" using namespace geode::prelude; -std::string const& Utils::GetUserAgent() { - static const auto userAgent = "LevelInfo/" + Mod::get()->getVersion().toNonVString(); - return userAgent; +std::string const& Utils::RequestUserAgent = "LevelInfo/" + Mod::get()->getVersion().toNonVString(); +std::chrono::seconds const Utils::RequestTimeout = std::chrono::seconds(3); + +arc::Future> Utils::CheckIfLevelSent(int levelID) { + if (!Utils::IsOnGdps() && SettingsManager::Other.customSendsEndpoint.empty()) { + auto req = co_await utils::web::WebRequest() + .userAgent(Utils::RequestUserAgent) + .timeout(Utils::RequestTimeout) + .get(fmt::format("https://sdbc.m336.dev/level/{}", levelID)); + + auto body = req.json().unwrapOrDefault(); + auto error = body["error"].asString().unwrapOrDefault(); + + if (req.ok() && body.size() > 0 && error.empty()) + co_return Ok(body["sent"].asBool().unwrap()); + else + log::warn( + "Failed requesting to the SendDB cache API: {}. Falling back to the original SendDB API", + error.size() > 0 ? error : req.errorMessage() + ); + } + + auto req = co_await utils::web::WebRequest() + .userAgent(Utils::RequestUserAgent) + .timeout(Utils::RequestTimeout) + .get(SettingsManager::Other.customSendsEndpoint.empty() + ? "https://api.senddb.dev/api/v1/level/" + : SettingsManager::Other.customSendsEndpoint + + std::to_string(levelID)); + + auto body = req.json().unwrapOrDefault(); + + if (!req.ok() || body.size() <= 0) { + auto error = req.errorMessage(); + + log::error( + "Failed requesting to the SendDB API: {}", + error.size() > 0 ? error : req.string().unwrap() + ); + co_return Err(); + } + + co_return Ok(body["sends"].size() > 0 ? true : false); }; std::unordered_map Utils::GameVersions = { @@ -17,6 +58,40 @@ std::unordered_map Utils::GameVersions = { { 10, "1.7" } }; +// https://github.com/hiimjasmine00/jasmine-tools/blob/6b6a3b00536a341791eb0de33e53b63c49baa8df/src/jasmine.cpp#L39-L66 +bool Utils::IsOnGdps() { + static const bool isOnGdps = []() -> bool { + std::string url; + + if (Loader::get()->isModLoaded("km7dev.server_api")) { + url = ServerAPIEvents::getCurrentServer().url; + if (!url.empty() && url!= "NONE_REGISTERED") { + while (url.ends_with("/")) url.pop_back(); + } else { + url = ""; + } + } + + if (url.empty()) { + static_assert(GEODE_COMP_GD_VERSION == 22081, "Incompatible GD version for GDPS check"); + url = std::string( + reinterpret_cast(base::get() + + GEODE_WINDOWS(0x558b70) + GEODE_ARM_MAC(0x77d709) + GEODE_INTEL_MAC(0x868df0) + GEODE_ANDROID64(0xeccf90) + GEODE_ANDROID32(0x96c0db) + GEODE_IOS(0x6b8cc2) + ), 34 + ); + } + + return url.find("://www.boomlings.com/database") == std::string::npos; + }(); + + return isOnGdps; +} + std::string_view Utils::GetGameVersion(int gameVersion) { if (Utils::GameVersions.count(gameVersion)) return Utils::GameVersions.at(gameVersion); diff --git a/src/utils/Utils.h b/src/utils/Utils.h index bdb2d42..73a50f3 100644 --- a/src/utils/Utils.h +++ b/src/utils/Utils.h @@ -2,13 +2,18 @@ class Utils { private: + static std::string const& RequestUserAgent; + static std::chrono::seconds const RequestTimeout; + static std::unordered_map GameVersions; public: - static std::string const& GetUserAgent(); + static arc::Future> CheckIfLevelSent(int levelID); static std::string_view GetGameVersion(int gameVersion); + static bool IsOnGdps(); + static std::string FormatNumber(size_t number); static std::string FormatTime(std::chrono::seconds seconds);