diff --git a/about.md b/about.md index 5623c04..9b6f737 100644 --- a/about.md +++ b/about.md @@ -2,7 +2,7 @@ Look through deleted and outdated levels using GDHistory or other sources! -## As of `v1.0.4` GDHistory is supported only. +## As of `v1.0.5` GDHistory is supported only. # How To Use diff --git a/changelog.md b/changelog.md index f901ce7..f141018 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,14 @@ +# v1.1.0 +## GDHistory Provider +- All requests are now cached + - This feature would decrease loading time in some cases and would make GDHistory's server load a bit lower. +## UI +- Added info button for all level providers +- Fixed a bug when 'Go to Level' button wasn't able to gray out *(reported by Reapavon)* +- URL for a level provider now can be changed on runtime +## Technical Changes +- Geode version has been switched to `3.2.0` + # v1.0.4 ## GDHistory Provider - Networking has been redone diff --git a/mod.json b/mod.json index 7c8d9bf..3483b08 100644 --- a/mod.json +++ b/mod.json @@ -1,10 +1,10 @@ { - "geode": "3.0.0-beta.1", + "geode": "3.2.0", "gd": { "android": "2.206", "win": "2.206" }, - "version": "v1.0.4", + "version": "v1.1.0", "id": "dogotrigger.level_history", "name": "Level History", "developer": "dogotrigger", diff --git a/src/GDHistoryProvider.cpp b/src/GDHistoryProvider.cpp index 0b7a318..4b370c6 100644 --- a/src/GDHistoryProvider.cpp +++ b/src/GDHistoryProvider.cpp @@ -3,17 +3,34 @@ #include #include #include "GeodeNetwork.hpp" +#include using namespace geode::prelude; #include "GDHistoryProvider.hpp" +#define PARSE_STRING(lvalue, rvalue) if (!rvalue.is_null()) lvalue = rvalue.get().c_str() +#define PARSE_INT(lvalue, rvalue) if (!rvalue.is_null()) lvalue = rvalue.get() +#define PARSE_BOOL(lvalue, rvalue) if (!rvalue.is_null()) lvalue = (int)(rvalue.get()) + std::string GDHistoryProvider::getName() { return "GDHistory"; } void GDHistoryProvider::downloadLevel(std::function onComplete) { + auto savedParams = _params; + cleanupLevels(false); + int cid = getCacheID(); + + if (cid != -1) { + struct Cached *cached = _cachedResults.data() + cid; + + parseResult(cached->_result, onComplete); + + return; + } + GeodeNetwork *network = new GeodeNetwork(); if (_params.count(LPFeatures::QueryID)) { @@ -38,7 +55,7 @@ void GDHistoryProvider::downloadLevel(std::functionsetOkCallback([this, network, onComplete](GeodeNetwork *) { + network->setOkCallback([this, network, onComplete, savedParams](GeodeNetwork *) { std::string catgirl = network->getResponse(); if (catgirl.find("Server Error") != std::string::npos) { @@ -50,110 +67,14 @@ void GDHistoryProvider::downloadLevel(std::function_currentError = "-8"; - onComplete(this, nullptr); - - delete network; - - return; - } - - nlohmann::json records = data.at("records"); - if (!records.is_array()) { - this->_currentError = "-9"; - onComplete(this, nullptr); - - delete network; + this->_params = savedParams; + this->parseResult(catgirl, onComplete, network); - return; - } - - int levels = records.size(); + struct Cached cached = { + _params, catgirl, (uint64_t)time(0) + }; - if (levels == 0) { - this->_currentError = "-10"; - onComplete(this, nullptr); - - delete network; - - return; - } - - for (int i = 0; i < levels; i++) { - nlohmann::json leveljson = records[i]; - GJGameLevel *level = GJGameLevel::create(); - -#define PARSE_STRING(lvalue, rvalue) if (!rvalue.is_null()) lvalue = rvalue.get().c_str() -#define PARSE_INT(lvalue, rvalue) if (!rvalue.is_null()) lvalue = rvalue.get() -#define PARSE_BOOL(lvalue, rvalue) if (!rvalue.is_null()) lvalue = (int)(rvalue.get()) - - PARSE_STRING(level->m_levelName, data["cache_level_name"]); - PARSE_STRING(level->m_levelDesc, leveljson["level_description"]); - PARSE_STRING(level->m_uploadDate, leveljson["real_date"]); - PARSE_STRING(level->m_creatorName, data["cache_username"]); - PARSE_STRING(level->m_songIDs, data["song_ids"]); - PARSE_STRING(level->m_sfxIDs, data["sfx_ids"]); - PARSE_INT(level->m_audioTrack, leveljson["official_song"]); - PARSE_INT(level->m_gameVersion, leveljson["game_version"]); - PARSE_INT(level->m_ratings, leveljson["rating"]); - PARSE_INT(level->m_ratingsSum, leveljson["rating_sum"]); - PARSE_INT(level->m_downloads, leveljson["downloads"]); - PARSE_INT(level->m_likes, leveljson["likes"]); - PARSE_INT(level->m_levelLength, leveljson["length"]); - PARSE_INT(level->m_userID, data["cache_user_id"]); - PARSE_INT(level->m_coins, leveljson["coins"]); - PARSE_INT(level->m_coinsVerified, leveljson["coins_verified"]); - PARSE_INT(level->m_rateStars, leveljson["stars"]); - PARSE_INT(level->m_accountID, leveljson["account_id"]); - PARSE_INT(level->m_levelID, data["online_id"]); - PARSE_BOOL(level->m_demon, leveljson["demon"]); - PARSE_BOOL(level->m_autoLevel, leveljson["auto"]); - PARSE_BOOL(level->m_isEditable, leveljson["level_string_available"]); - PARSE_INT(level->m_demonDifficulty, leveljson["demon_type"]); - PARSE_INT(level->m_demonVotes, leveljson["id"]); - PARSE_INT(level->m_featured, leveljson["feature_score"]); - - if (!leveljson["song"].is_null()) { - PARSE_INT(level->m_songID, leveljson["song"]["online_id"]); - } - PARSE_INT(level->m_isEpic, leveljson["epic"]); - - bool has_leveldata = false; - PARSE_BOOL(has_leveldata, leveljson["level_string_available"]); - if (has_leveldata) { - level->m_dislikes = 1; - level->m_likes++; - } else { - level->m_dislikes = 0; - } - - this->makeLevelCopyable(level); - - level->retain(); - - this->_serverResponseParsed.push_back(level); - } - - if (this->_params.count(LPFeatures::LimitLevelArray)) { - int max = std::get(this->_params[LPFeatures::LimitLevelArray]); - - if (max > 0) { - std::vector new_vec; - - for (int i = 0; i < max && i < _serverResponseParsed.size(); i++) { - new_vec.push_back(this->_serverResponseParsed.at(i)); - } - - this->_serverResponseParsed = new_vec; - } - } - - onComplete(this, this->_serverResponseParsed.at(0)); - - delete network; + this->_cachedResults.push_back(cached); return; }); @@ -197,7 +118,7 @@ void GDHistoryProvider::downloadLevel(std::functionsetOkCallback([this, network, onComplete](GeodeNetwork *) { + network->setOkCallback([this, network, onComplete, savedParams](GeodeNetwork *) { std::string catgirl = network->getResponse(); if (catgirl.find("Server Error") != std::string::npos) { @@ -209,85 +130,14 @@ void GDHistoryProvider::downloadLevel(std::function_params = savedParams; + this->parseResult(catgirl, onComplete, network); - if (!data.contains("hits")) { - this->_currentError = "-8"; - onComplete(this, nullptr); + struct Cached cached = { + _params, catgirl, (uint64_t)time(0) + }; - delete network; - - return; - } - - nlohmann::json records = data.at("hits"); - if (!records.is_array()) { - this->_currentError = "-9"; - onComplete(this, nullptr); - - delete network; - - return; - } - - int levels = records.size(); - - if (levels == 0) { - this->_currentError = "-10"; - onComplete(this, nullptr); - - delete network; - - return; - } - - for (int i = 0; i < levels; i++) { - nlohmann::json leveljson = records[i]; - GJGameLevel *level = GJGameLevel::create(); - - PARSE_STRING(level->m_levelName, leveljson["cache_level_name"]); - // PARSE_STRING(level->m_levelDesc, leveljson["level_description"]); - PARSE_STRING(level->m_uploadDate, leveljson["cache_submitted"]); - PARSE_STRING(level->m_creatorName, leveljson["cache_username"]); - // PARSE_INT(level->m_audioTrack, leveljson["official_song"]); - level->m_gameVersion = 22; - PARSE_INT(level->m_ratings, leveljson["cache_rating"]); - PARSE_INT(level->m_ratingsSum, leveljson["cache_rating_sum"]); - PARSE_BOOL(level->m_demon, leveljson["cache_demon"]); - PARSE_BOOL(level->m_autoLevel, leveljson["cache_auto"]); - PARSE_INT(level->m_demonDifficulty, leveljson["cache_demon_type"]); - PARSE_INT(level->m_downloads, leveljson["cache_downloads"]); - // PARSE_INT(level->m_difficulty, leveljson["cache_main_difficulty"]); - PARSE_INT(level->m_likes, leveljson["cache_likes"]); - PARSE_INT(level->m_levelLength, leveljson["cache_length"]); - PARSE_INT(level->m_userID, data["cache_user_id"]); - PARSE_INT(level->m_rateStars, leveljson["cache_stars"]); - PARSE_INT(level->m_levelID, leveljson["online_id"]); - PARSE_INT(level->m_demonVotes, leveljson["id"]); - PARSE_INT(level->m_featured, leveljson["cache_featured"]); - PARSE_INT(level->m_isEpic, leveljson["cache_epic"]); - - bool has_leveldata = false; - PARSE_BOOL(has_leveldata, leveljson["cache_level_string_available"]); - if (has_leveldata) { - log::info("(GDHistoryProvider) level {} by {} has leveldata", level->m_levelName, level->m_creatorName); - level->m_dislikes = 1; - level->m_likes++; - } else { - log::info("(GDHistoryProvider) level {} by {} does not has leveldata", level->m_levelName, level->m_creatorName); - level->m_dislikes = 0; - } - - this->makeLevelCopyable(level); - - level->retain(); - - this->_serverResponseParsed.push_back(level); - } - - onComplete(this, this->_serverResponseParsed.at(0)); - - delete network; + this->_cachedResults.push_back(cached); return; }); @@ -345,6 +195,8 @@ void GDHistoryProvider::getLevelData(int id, std::function(_params[LPFeatures::SpecificRecord]); } @@ -418,6 +270,21 @@ void GDHistoryProvider::getLevelData(int id, std::function_result, onComplete); + + return; + } + GeodeNetwork *network = new GeodeNetwork(); network->setURL(fmt::format("{}/level/{}/{}/download", _baseUrl, id, recid)); @@ -430,33 +297,269 @@ void GDHistoryProvider::getLevelData(int id, std::functionsetOkCallback([this, onComplete, network](GeodeNetwork *) { + network->setOkCallback([this, onComplete, network, savedParams](GeodeNetwork *) { struct LevelProvider::BasicLevelInformation info; std::string catgirl = network->getResponse(); - if (catgirl.find("Server Error") != std::string::npos) { + this->_params = savedParams; + this->parseResult(catgirl, onComplete, network); + + struct Cached cached = { + _params, catgirl, (uint64_t)time(0) + }; + + this->_cachedResults.push_back(cached); + }); + + network->send(); + + _params.clear(); +} + +std::string GDHistoryProvider::getErrorCodeDescription(std::string err) { + std::string res = "unknown error"; + + std::unordered_map errors = { + {"-1", "http error."}, + {"-2", "gmd api error. (dumped into console)"}, + {"-3", "invalid record id."}, + {"-4", "level not found."}, + {"-5", "level data cannot be downloaded for this level. Note that this issue will be fixed if level would have downloadable link for it in the future."}, + {"-6", "insufficient rights to download this level."}, + {"-7", "gd history's api is down."}, + {"-8", "api did not return record data."}, + {"-9", "api returned record data with invalid data type."}, + {"-10", "api returned an record array with zero levels in it."} + }; + + if (errors.count(err)) { + res = errors[err]; + } + + return res; +} + +std::string GDHistoryProvider::getDescription() { + return std::string("A Geometry Dash preservation project. ") + _baseUrl; +} + +void GDHistoryProvider::parseResult(std::string &result, std::function onComplete, GeodeNetwork *network) { + // log::info("(GDHistoryProvider) result={}", result); + + if (_params.count(LPFeatures::QueryID) && !_params.count(LPFeatures::TmpAskedLevel)) { + nlohmann::json data = nlohmann::json::parse(result); + + if (!data.contains("records")) { + this->_currentError = "-8"; + onComplete(this, nullptr); + + if (network != nullptr) delete network; + + return; + } + + nlohmann::json records = data.at("records"); + if (!records.is_array()) { + this->_currentError = "-9"; + onComplete(this, nullptr); + + if (network != nullptr) delete network; + + return; + } + + int levels = records.size(); + + if (levels == 0) { + this->_currentError = "-10"; + onComplete(this, nullptr); + + if (network != nullptr) delete network; + + return; + } + + for (int i = 0; i < levels; i++) { + nlohmann::json leveljson = records[i]; + GJGameLevel *level = GJGameLevel::create(); + + PARSE_STRING(level->m_levelName, data["cache_level_name"]); + PARSE_STRING(level->m_levelDesc, leveljson["level_description"]); + PARSE_STRING(level->m_uploadDate, leveljson["real_date"]); + PARSE_STRING(level->m_creatorName, data["cache_username"]); + PARSE_STRING(level->m_songIDs, data["song_ids"]); + PARSE_STRING(level->m_sfxIDs, data["sfx_ids"]); + PARSE_INT(level->m_audioTrack, leveljson["official_song"]); + PARSE_INT(level->m_gameVersion, leveljson["game_version"]); + PARSE_INT(level->m_ratings, leveljson["rating"]); + PARSE_INT(level->m_ratingsSum, leveljson["rating_sum"]); + PARSE_INT(level->m_downloads, leveljson["downloads"]); + PARSE_INT(level->m_likes, leveljson["likes"]); + PARSE_INT(level->m_levelLength, leveljson["length"]); + PARSE_INT(level->m_userID, data["cache_user_id"]); + PARSE_INT(level->m_coins, leveljson["coins"]); + PARSE_INT(level->m_coinsVerified, leveljson["coins_verified"]); + PARSE_INT(level->m_rateStars, leveljson["stars"]); + PARSE_INT(level->m_accountID, leveljson["account_id"]); + PARSE_INT(level->m_levelID, data["online_id"]); + PARSE_BOOL(level->m_demon, leveljson["demon"]); + PARSE_BOOL(level->m_autoLevel, leveljson["auto"]); + PARSE_BOOL(level->m_isEditable, leveljson["level_string_available"]); + PARSE_INT(level->m_demonDifficulty, leveljson["demon_type"]); + PARSE_INT(level->m_demonVotes, leveljson["id"]); + PARSE_INT(level->m_featured, leveljson["feature_score"]); + + if (!leveljson["song"].is_null()) { + PARSE_INT(level->m_songID, leveljson["song"]["online_id"]); + } + PARSE_INT(level->m_isEpic, leveljson["epic"]); + + bool has_leveldata = false; + PARSE_BOOL(has_leveldata, leveljson["level_string_available"]); + if (has_leveldata) { + level->m_dislikes = 1; + level->m_likes++; + } else { + level->m_dislikes = 0; + } + + this->makeLevelCopyable(level); + + level->retain(); + + this->_serverResponseParsed.push_back(level); + } + + if (this->_params.count(LPFeatures::LimitLevelArray) ) { + int max = std::get(this->_params[LPFeatures::LimitLevelArray]); + + if (max > 0) { + std::vector new_vec; + + for (int i = 0; i < max && i < _serverResponseParsed.size(); i++) { + new_vec.push_back(this->_serverResponseParsed.at(i)); + } + + this->_serverResponseParsed = new_vec; + } + } + + onComplete(this, this->_serverResponseParsed.at(0)); + + if (network != nullptr) delete network; + } else if (_params.count(LPFeatures::QueryLevelName) && !_params.count(LPFeatures::TmpAskedLevel)) { + + nlohmann::json data = nlohmann::json::parse(result); + + if (!data.contains("hits")) { + this->_currentError = "-8"; + onComplete(this, nullptr); + + delete network; + + return; + } + + nlohmann::json records = data.at("hits"); + if (!records.is_array()) { + this->_currentError = "-9"; + onComplete(this, nullptr); + + if (network != nullptr) delete network; + + return; + } + + int levels = records.size(); + + if (levels == 0) { + this->_currentError = "-10"; + onComplete(this, nullptr); + + if (network != nullptr) delete network; + + return; + } + + for (int i = 0; i < levels; i++) { + // log::info("{}", i); + + nlohmann::json leveljson = records[i]; + GJGameLevel *level = GJGameLevel::create(); + + PARSE_STRING(level->m_levelName, leveljson["cache_level_name"]); + // PARSE_STRING(level->m_levelDesc, leveljson["level_description"]); + PARSE_STRING(level->m_uploadDate, leveljson["cache_submitted"]); + PARSE_STRING(level->m_creatorName, leveljson["cache_username"]); + // PARSE_INT(level->m_audioTrack, leveljson["official_song"]); + level->m_gameVersion = 22; + PARSE_INT(level->m_ratings, leveljson["cache_rating"]); + PARSE_INT(level->m_ratingsSum, leveljson["cache_rating_sum"]); + PARSE_BOOL(level->m_demon, leveljson["cache_demon"]); + PARSE_BOOL(level->m_autoLevel, leveljson["cache_auto"]); + PARSE_INT(level->m_demonDifficulty, leveljson["cache_demon_type"]); + PARSE_INT(level->m_downloads, leveljson["cache_downloads"]); + // PARSE_INT(level->m_difficulty, leveljson["cache_main_difficulty"]); + PARSE_INT(level->m_likes, leveljson["cache_likes"]); + PARSE_INT(level->m_levelLength, leveljson["cache_length"]); + PARSE_INT(level->m_userID, data["cache_user_id"]); + PARSE_INT(level->m_rateStars, leveljson["cache_stars"]); + PARSE_INT(level->m_levelID, leveljson["online_id"]); + PARSE_INT(level->m_demonVotes, leveljson["id"]); + PARSE_INT(level->m_featured, leveljson["cache_featured"]); + PARSE_INT(level->m_isEpic, leveljson["cache_epic"]); + + bool has_leveldata = false; + PARSE_BOOL(has_leveldata, leveljson["cache_level_string_available"]); + if (has_leveldata) { + log::info("(GDHistoryProvider) level \"{}\" by \"{}\" has leveldata", level->m_levelName, level->m_creatorName); + level->m_dislikes = 1; + level->m_likes++; + } else { + log::info("(GDHistoryProvider) level \"{}\" by \"{}\" does not has leveldata", level->m_levelName, level->m_creatorName); + level->m_dislikes = 0; + } + + this->makeLevelCopyable(level); + + level->retain(); + + this->_serverResponseParsed.push_back(level); + } + + onComplete(this, this->_serverResponseParsed.at(0)); + + if (network != nullptr) delete network; + } +} + +void GDHistoryProvider::parseResult(std::string &result, std::function onComplete, GeodeNetwork *network) { + if (_params.count(LPFeatures::TmpAskedLevel)) { + struct LevelProvider::BasicLevelInformation info; + + if (result.find("Server Error") != std::string::npos) { this->_currentError = "-7"; onComplete(this, "-7", info); - delete network; + if (network != nullptr) delete network; return; } - if (catgirl.find("This record does not contain any level data.") != std::string::npos) { + if (result.find("This record does not contain any level data") != std::string::npos) { this->_currentError = "-5"; onComplete(this, "-5", info); - delete network; + if (network != nullptr) delete network; return; } - if (catgirl.find("You do not have the rights to download this record") != std::string::npos) { + if (result.find("You do not have the rights to download this record") != std::string::npos) { this->_currentError = "-6"; onComplete(this, "-6", info); - delete network; + if (network != nullptr) delete network; return; } @@ -467,7 +570,7 @@ void GDHistoryProvider::getLevelData(int id, std::functionretain(); auto str = level->m_levelString; @@ -505,37 +608,6 @@ void GDHistoryProvider::getLevelData(int id, std::functionsend(); - - _params.clear(); -} - -std::string GDHistoryProvider::getErrorCodeDescription(std::string err) { - std::string res = "unknown error"; - - std::unordered_map errors = { - {"-1", "http error."}, - {"-2", "gmd api error. (dumped into console)"}, - {"-3", "invalid record id."}, - {"-4", "level not found."}, - {"-5", "level data cannot be downloaded for this level. Note that this issue will be fixed if level would have downloadable link for it in the future."}, - {"-6", "insufficient rights to download this level."}, - {"-7", "gd history's api is down."}, - {"-8", "api did not return record data."}, - {"-9", "api returned record data with invalid data type."}, - {"-10", "api returned an record array with zero levels in it."} - }; - - if (errors.count(err)) { - res = errors[err]; + if (network != nullptr) delete network; } - - return res; -} - -std::string GDHistoryProvider::getDescription() { - return std::string("A Geometry Dash preservation project. ") + _baseUrl; } \ No newline at end of file diff --git a/src/GDHistoryProvider.hpp b/src/GDHistoryProvider.hpp index 9d0be13..22461b5 100644 --- a/src/GDHistoryProvider.hpp +++ b/src/GDHistoryProvider.hpp @@ -24,4 +24,7 @@ class GDHistoryProvider : public LevelProvider { virtual std::string getErrorCodeDescription(std::string err) override; std::string getDescription() override; + + void parseResult(std::string &result, std::function onComplete, GeodeNetwork *network = nullptr) override; + void parseResult(std::string &result, std::function onComplete, GeodeNetwork *network = nullptr); }; \ No newline at end of file diff --git a/src/LevelProvider.cpp b/src/LevelProvider.cpp index a44d027..2db5ec9 100644 --- a/src/LevelProvider.cpp +++ b/src/LevelProvider.cpp @@ -53,4 +53,65 @@ void LevelProvider::makeLevelCopyable(GJGameLevel *level) { std::string LevelProvider::getDescription() { return "(No description provided)"; +} + +#include + +bool LevelProvider::requestCached() { + return getCacheID() != -1; +} + +int LevelProvider::getCacheID() { + uint64_t timestamp = time(0); + + int i = 0; + + for (struct Cached &data : _cachedResults) { + if (_params == data._params && (timestamp - data._timestamp) < getTimestampMaxDiff()) { + return i; + } + + i++; + } + + return -1; +} + +bool LevelProvider::requestCached(std::string &result) { + struct Cached toCheck = {_params, result, 0}; + + for (const struct Cached &data : _cachedResults) { + if (toCheck == data) return true; + } + + return false; +} + +unsigned int LevelProvider::getTimestampMaxDiff() { + return 3 * 60; +} + +std::string LevelProvider::encodeParams() { + std::string result = ""; + + for (auto [key, val] : _params) { + result += std::to_string((int)key) + "_"; + } + + if (result.length() >= 1) { + result.pop_back(); + } + + return result; +} + +void LevelProvider::cleanupParameters() { + _params.clear(); +} + +std::string LevelProvider::getBaseURL() { + return _baseUrl; +} +void LevelProvider::setBaseURL(std::string url) { + _baseUrl = url; } \ No newline at end of file diff --git a/src/LevelProvider.hpp b/src/LevelProvider.hpp index c3ed2b0..9c3bc12 100644 --- a/src/LevelProvider.hpp +++ b/src/LevelProvider.hpp @@ -1,30 +1,54 @@ #pragma once class GJGameLevel; +class GeodeNetwork; #include #include #include #include #include +#include class LevelProvider { public: enum LPFeatures { QueryID, QueryLevelName, LimitLevelArray, SetLevelArrayPage, - ReturnMultipleLevels, SpecificRecord + ReturnMultipleLevels, SpecificRecord, + TmpAskedLevel + }; + + struct Cached { + std::unordered_map> _params; + std::string _result; + uint64_t _timestamp; + + bool operator ==(const struct Cached &ref) { + bool _member0 = _params == ref._params; + bool _member1 = _result == ref._result; + + return _member0 && _member1; + } }; protected: + std::string _currentError; + std::vector _cachedResults; std::string _baseUrl; - std::unordered_map> _params; std::string url_encode(const std::string value); + void makeLevelCopyable(GJGameLevel *level); - std::string _currentError; + bool requestCached(); + bool requestCached(std::string &result); + int getCacheID(); - void makeLevelCopyable(GJGameLevel *level); + virtual void parseResult(std::string &result, std::function onComplete, GeodeNetwork *network = nullptr) = 0; + + virtual unsigned int getTimestampMaxDiff(); + + std::string encodeParams(); public: struct BasicLevelInformation { int musicID = 0; @@ -41,6 +65,8 @@ class LevelProvider { virtual void setParameter(enum LPFeatures parameter, int value); virtual void setParameter(enum LPFeatures parameter, std::string value); + void cleanupParameters(); + virtual std::vector askMultipleLevels(); virtual void cleanupLevels(bool withRelease) = 0; @@ -51,4 +77,7 @@ class LevelProvider { virtual std::string getErrorCode(); virtual std::string getDescription(); + + std::string getBaseURL(); + void setBaseURL(std::string url); }; \ No newline at end of file diff --git a/src/ProviderPopup.cpp b/src/ProviderPopup.cpp index f4394cf..961f4fc 100644 --- a/src/ProviderPopup.cpp +++ b/src/ProviderPopup.cpp @@ -57,6 +57,21 @@ bool ProviderPopup::init(std::vector> providers) exitBtn, this, menu_selector(ProviderPopup::onExitButton) ); + auto infoBtn = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png"); + auto btn4 = CCMenuItemSpriteExtra::create( + infoBtn, this, menu_selector(ProviderPopup::onProviderInfoBtn) + ); + + CCMenu *men3 = CCMenu::create(); + + float padding = 20.f; + + men3->setPosition({ + winsize.width / 2 + spr1->getContentSize().width / 2 - padding, + winsize.height / 2 + spr1->getContentSize().height / 2 - padding + }); + men3->addChild(btn4); + CCMenu *menu = CCMenu::create(); CCLayer *infoLayer = CCLayer::create(); @@ -136,6 +151,7 @@ bool ProviderPopup::init(std::vector> providers) men2->addChild(btn3); objectSelector->addChild(men2, 2); + objectSelector->addChild(men3, 2); m_mainLayer->addChild(objectSelector); @@ -593,6 +609,7 @@ void ProviderPopup::onLevelIDSearch(CCObject *sender) { int id = std::stoi(si->_input->getString()); + si->_info->provider->cleanupParameters(); si->_info->provider->setParameter(LevelProvider::QueryID, id); si->_info->provider->setParameter(LevelProvider::LimitLevelArray, levels_limit); @@ -625,7 +642,7 @@ void ProviderPopup::onLevelIDSearch(CCObject *sender) { LevelCell *ProviderPopup::createLevelCell(GJGameLevel *level, CCLayer *page) { auto csz = page->getContentSize(); - log::info("1"); + // log::info("1"); // LevelCell *cell = LevelCell::create(0.f, 0.f); // log::info("2"); @@ -634,13 +651,13 @@ LevelCell *ProviderPopup::createLevelCell(GJGameLevel *level, CCLayer *page) { // cell->setPosition(0, csz.height / 2); // log::info("4"); LevelCell *cell = new LevelCell("a", 0.f, 0.f); - log::info("2"); + // log::info("2"); if (!cell->init()) return nullptr; - log::info("3"); + // log::info("3"); cell->autorelease(); - log::info("4"); + // log::info("4"); cell->loadFromLevel(level); - log::info("5"); + // log::info("5"); cell->setPosition(0, csz.height / 2); CCLayer *base = typeinfo_cast(cell->getChildByID("main-layer")); @@ -747,19 +764,27 @@ void ProviderPopup::onLevelPage(CCObject *sender) { for (auto cell : popup->_levelPage._cells) { cell->setVisible(false); } + + log::info("C: {}", level->m_dislikes); if (level->m_dislikes == 0) { + log::info("A"); + auto spr = typeinfo_cast(getChildByIDRecursive("play-level-spr")); auto btn = typeinfo_cast(getChildByIDRecursive("play-level-btn")); btn->setEnabled(false); - spr->setColor({64, 64, 64}); + // spr->setColor({64, 64, 64}); + setColorToButtonSprite(spr, {64, 64, 64}); } else { + log::info("B"); + auto spr = typeinfo_cast(getChildByIDRecursive("play-level-spr")); auto btn = typeinfo_cast(getChildByIDRecursive("play-level-btn")); btn->setEnabled(true); - spr->setColor({255, 255, 255}); + // spr->setColor({255, 255, 255}); + setColorToButtonSprite(spr, {255, 255, 255}); } // int id = level->m_levelID.value(); @@ -885,6 +910,8 @@ void ProviderPopup::setupSettingsPage(CCLayer *providerBox) { ProviderPopupInfo *info = (ProviderPopupInfo *)providerBox->getUserData(); auto *layout = RowLayout::create(); + layout->setGrowCrossAxis(true); + CCLayer *settings = CCLayer::create(); auto csz = page->getContentSize(); @@ -901,16 +928,6 @@ void ProviderPopup::setupSettingsPage(CCLayer *providerBox) { // feats[LevelProvider::LPFeatures::LimitLevelArray] || feats[LevelProvider::LPFeatures::SetLevelArrayPage] if (feats[LevelProvider::LPFeatures::LimitLevelArray]) { - // m_pFollowPlayer = CCMenuItemToggler::create(checkOff, checkOn, this, menu_selector(ProviderPopup::onToggler1PressMaybe)); - // CCLabelBMFont *men1_info = CCLabelBMFont::create("Follow Player", "bigFont.fnt"); - // men1_info->setAnchorPoint({0.5f, 0.5f}); - // men1_info->setScale(0.5f); - // men1_info->setPositionX(100.f); - // std::string _final = "Set level array size..."; - // if (!this->_levelArraySize.empty()) { - // _final = this->_levelArraySize; - // } - TextInput *in = TextInput::create(100, "Set level array size...", "chatFont.fnt"); in->setID("level-array-input"); @@ -941,7 +958,22 @@ void ProviderPopup::setupSettingsPage(CCLayer *providerBox) { settings->updateLayout(); } - settings->setPosition(csz.width / 2, (csz.height - settings->getContentSize().height) / 2 + 15); + { + TextInput *in = TextInput::create(128, "Set instance URL...", "chatFont.fnt"); + in->setID("instance-url-input"); + + if (!ProviderPopup::get()->_selectedProvider->getBaseURL().empty()) { + in->setString(ProviderPopup::get()->_selectedProvider->getBaseURL(), false); + } + in->setCallback([this](const std::string &value) { + ProviderPopup::get()->_selectedProvider->setBaseURL(value); + }); + + settings->addChild(in); + settings->updateLayout(); + } + + settings->setPosition(csz.width / 2, (csz.height - settings->getContentSize().height) / 2 + 30); } ProviderPopup *ProviderPopup::get() { @@ -1251,6 +1283,7 @@ void ProviderPopup::onGenericSearch(CCObject *sender) { lp = std::stoi(levelPageStr); } + si->_info->provider->cleanupParameters(); si->_info->provider->setParameter(LevelProvider::QueryLevelName, si->_input->getString()); si->_info->provider->setParameter(LevelProvider::LimitLevelArray, levels_limit); si->_info->provider->setParameter(LevelProvider::SetLevelArrayPage, lp); @@ -1317,6 +1350,7 @@ void ProviderPopup::onPlayLevelDownload(CCObject *sender) { auto level = popup->_levelPage._currentLevels[popup->_levelPage._currentLevelsIndex]; + popup->_selectedProvider->cleanupParameters(); popup->_selectedProvider->setParameter(LevelProvider::SpecificRecord, level->m_demonVotes); popup->_selectedProvider->getLevelData(level->m_levelID.value(), [popup, level, wait] (LevelProvider *prov, std::string data, struct LevelProvider::BasicLevelInformation info) { @@ -1467,4 +1501,43 @@ void ProviderPopup::scenePrintError(float delta) { FLAlertLayer::create("Error", ProviderPopup::_currentError, "OK")->show(); } -std::string ProviderPopup::_currentError = ""; \ No newline at end of file +std::string ProviderPopup::_currentError = ""; + +void ProviderPopup::setColorToButtonSprite(ButtonSprite *spr, _ccColor3B color) { + auto rgba = typeinfo_cast(spr); + + if (rgba != nullptr) { + spr->runAction(CCTintTo::create(0.25, color.r, color.g, color.b)); + } + + CCArray *nodeChildren = spr->getChildren(); + + if (nodeChildren == nullptr) return; + + for (int i = 0; i < nodeChildren->count(); i++) { + CCObject *child_obj = nodeChildren->objectAtIndex(i); + if (child_obj == nullptr) continue; + + CCRGBAProtocol *child_rgba = typeinfo_cast(child_obj); + if (child_rgba == nullptr) continue; + + CCNode *child_node = typeinfo_cast(child_obj); + + child_node->runAction(CCTintTo::create(0.1f, color.r, color.g, color.b)); + } +} + +void ProviderPopup::onProviderInfoBtn(CCObject *sender) { + auto popup = ProviderPopup::get(); + + if (popup->_selectedProvider == nullptr) { + FLAlertLayer::create("Level History", "Click on any available provider and try again.", "OK")->show(); + + return; + } + + std::string description = popup->_selectedProvider->getDescription(); + std::string name = popup->_selectedProvider->getName(); + + FLAlertLayer::create(name.c_str(), description, "OK")->show(); +} \ No newline at end of file diff --git a/src/ProviderPopup.hpp b/src/ProviderPopup.hpp index dc201c5..60a3eaa 100644 --- a/src/ProviderPopup.hpp +++ b/src/ProviderPopup.hpp @@ -41,6 +41,8 @@ class ProviderPopup : public FLAlertLayer { void setupSettingsPage(CCLayer *providerBox); void setupGenericSearchPage(CCLayer *providerBox); + static void setColorToButtonSprite(ButtonSprite *spr, cocos2d::_ccColor3B color); + bool _locked = false; public: static ProviderPopup *get(); @@ -90,6 +92,8 @@ class ProviderPopup : public FLAlertLayer { void removeLevelRatings(); void scenePrintError(float delta); + + void onProviderInfoBtn(CCObject *sender); // static void setButtonSpriteColor(ButtonSprite) // bool ccTouchBegan(cocos2d::CCTouch *touch, cocos2d::CCEvent *event) override; }; \ No newline at end of file