Skip to content

Commit

Permalink
RIP commit histories
Browse files Browse the repository at this point in the history
  • Loading branch information
hiimjustin000 committed Nov 1, 2024
1 parent b14f665 commit ef2b4e1
Show file tree
Hide file tree
Showing 15 changed files with 313 additions and 295 deletions.
12 changes: 7 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64")
set(CMAKE_CXX_VISIBILITY_PRESET hidden)

project(IntegratedDemonlist VERSION 1.6.5)
project(IntegratedDemonlist VERSION 1.7.0)

add_library(${PROJECT_NAME} SHARED
src/IDListLayer.cpp
src/IDPackCell.cpp
src/IDPackLayer.cpp
src/classes/IDListLayer.cpp
src/classes/IDPackCell.cpp
src/classes/IDPackLayer.cpp
src/hooks/LevelBrowserLayer.cpp
src/hooks/LevelCell.cpp
src/hooks/LevelSearchLayer.cpp
src/IntegratedDemonlist.cpp
src/main.cpp
)

if (NOT DEFINED ENV{GEODE_SDK})
Expand Down
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Integrated Demonlist Changelog
## v1.7.0 (2024-11-01)
- Changed ranking text to load individually on cell load rather than all at once in the main menu

## v1.6.5 (2024-10-25)
- Fixed two-player demons not appearing in the demonlist twice (Someone wanted this for some reason)
- Permanently fixed the Pemonlist issue (New API)
Expand Down
4 changes: 2 additions & 2 deletions mod.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"geode": "3.8.1",
"geode": "3.9.0",
"gd": {
"android": "2.206",
"win": "2.206",
"mac": "2.206"
},
"version": "v1.6.5",
"version": "v1.7.0",
"id": "hiimjustin000.integrated_demonlist",
"name": "Integrated Demonlist",
"developer": "hiimjustin000",
Expand Down
200 changes: 82 additions & 118 deletions src/IntegratedDemonlist.cpp
Original file line number Diff line number Diff line change
@@ -1,96 +1,59 @@
#include "IntegratedDemonlist.hpp"

using namespace geode::prelude;

#define AREDL_URL "https://api.aredl.net/api/aredl/levels"
#define AREDL_PACKS_URL "https://api.aredl.net/api/aredl/packs"
#define PEMONLIST_UPTIME_URL "https://pemonlist.com/api/uptime?version=2"
#define PEMONLIST_URL "https://pemonlist.com/api/list?limit=500&version=2"

void IntegratedDemonlist::initializeAREDL(web::WebResponse* res) {
AREDL.clear();
auto str = res->string().value();
std::string error;
auto json = matjson::parse(str, error).value_or(matjson::Array());
if (!error.empty()) log::error("Failed to parse AREDL: {}", error);
if (json.is_array()) for (auto const& level : json.as_array()) {
if (level.contains("legacy") && level["legacy"].is_bool() && level["legacy"].as_bool()) continue;
if (!level.contains("level_id") || !level["level_id"].is_number()) continue;
if (!level.contains("name") || !level["name"].is_string()) continue;
if (!level.contains("position") || !level["position"].is_number()) continue;

AREDL.push_back({
level["level_id"].as_int(),
level["name"].as_string(),
level["position"].as_int()
});
}
}

void IntegratedDemonlist::initializePemonlist(web::WebResponse* res) {
PEMONLIST.clear();
auto str = res->string().value();
std::string error;
auto json = matjson::parse(str, error).value_or(matjson::Object { { "data", matjson::Array() } });
if (!error.empty()) log::error("Failed to parse Pemonlist: {}", error);
if (json.is_object() && json.contains("data") && json["data"].is_array()) for (auto const& level : json["data"].as_array()) {
if (!level.contains("level_id") || !level["level_id"].is_number()) continue;
if (!level.contains("name") || !level["name"].is_string()) continue;
if (!level.contains("placement") || !level["placement"].is_number()) continue;

PEMONLIST.push_back({
level["level_id"].as_int(),
level["name"].as_string(),
level["placement"].as_int()
});
}
}

void IntegratedDemonlist::isOk(std::string const& url, EventListener<web::WebTask>&& listenerRef, utils::MiniFunction<void(bool, int)> callback) {
void IntegratedDemonlist::isOk(std::string const& url, EventListener<web::WebTask>&& listenerRef, std::function<void(bool, int)> const& callback) {
auto&& listener = std::move(listenerRef);
listener.bind([callback](web::WebTask::Event* e) {
if (auto res = e->getValue()) callback(res->ok(), res->code());
});
listenerRef.setFilter(web::WebRequest().downloadRange({ 0, 0 }).get(url));
}

void IntegratedDemonlist::loadAREDL() {
static std::optional<web::WebTask> task = std::nullopt;
static std::optional<web::WebTask> okTask = std::nullopt;
okTask = web::WebRequest().downloadRange({ 0, 0 }).get(AREDL_URL).map([](web::WebResponse* res) {
if (!res->ok()) {
log::error("Failed to load AREDL with status code {}", res->code());
okTask = std::nullopt;
return *res;
}

task = web::WebRequest().get(AREDL_URL).map([](web::WebResponse* res2) {
if (res2->ok()) initializeAREDL(res2);
else log::error("Failed to load AREDL with status code {}", res2->code());

task = std::nullopt;
return *res2;
});

okTask = std::nullopt;
return *res;
});
}

void IntegratedDemonlist::loadAREDL(
EventListener<web::WebTask>&& listenerRef, EventListener<web::WebTask>&& okListener,
LoadingCircle* circle, utils::MiniFunction<void()> callback
LoadingCircle* circle, std::function<void()> const& callback
) {
auto&& listener = std::move(listenerRef);
listener.bind([callback](web::WebTask::Event* e) {
listener.bind([callback, circle](web::WebTask::Event* e) {
if (auto res = e->getValue()) {
if (res->ok()) {
initializeAREDL(res);
callback();
if (!res->ok()) {
queueInMainThread([circle, res] {
FLAlertLayer::create(fmt::format("Load Failed ({})", res->code()).c_str(), "Failed to load AREDL. Please try again later.", "OK")->show();
circle->setVisible(false);
});
return;
}

AREDL_LOADED = true;
AREDL.clear();
auto str = res->string().value();
std::string error;
auto json = matjson::parse(str, error).value_or(matjson::Array());
if (!error.empty()) log::error("Failed to parse AREDL: {}", error);
if (json.is_array()) for (auto const& level : json.as_array()) {
if (level.contains("legacy") && level["legacy"].is_bool() && level["legacy"].as_bool()) continue;
if (!level.contains("level_id") || !level["level_id"].is_number()) continue;
if (!level.contains("name") || !level["name"].is_string()) continue;
if (!level.contains("position") || !level["position"].is_number()) continue;

AREDL.push_back({
level["level_id"].as_int(),
level["name"].as_string(),
level["position"].as_int()
});
}
callback();
}
});
isOk(AREDL_URL, std::move(okListener), [&listener, circle](bool ok, int code) {
if (ok) listener.setFilter(web::WebRequest().get(AREDL_URL));
else Loader::get()->queueInMainThread([circle, code] {
else queueInMainThread([circle, code] {
FLAlertLayer::create(fmt::format("Load Failed ({})", code).c_str(), "Failed to load AREDL. Please try again later.", "OK")->show();
circle->setVisible(false);
});
Expand All @@ -99,82 +62,83 @@ void IntegratedDemonlist::loadAREDL(

void IntegratedDemonlist::loadAREDLPacks(
EventListener<web::WebTask>&& listenerRef, EventListener<web::WebTask>&& okListener,
LoadingCircle* circle, utils::MiniFunction<void()> callback
LoadingCircle* circle, std::function<void()> const& callback
) {
auto&& listener = std::move(listenerRef);
listener.bind([callback](web::WebTask::Event* e) {
listener.bind([callback, circle](web::WebTask::Event* e) {
if (auto res = e->getValue()) {
if (res->ok()) {
AREDL_PACKS.clear();
for (auto const& pack : res->json().value().as_array()) {
std::vector<int> levels;
for (auto const& level : pack["levels"].as_array()) levels.push_back(level["level_id"].as_int());
AREDL_PACKS.push_back({
pack["name"].as_string(),
pack["points"].as_double(),
levels
});
}
std::sort(AREDL_PACKS.begin(), AREDL_PACKS.end(), [](auto const& a, auto const& b) {
return a.points < b.points;
if (!res->ok()) {
queueInMainThread([circle, res] {
FLAlertLayer::create(fmt::format("Load Failed ({})", res->code()).c_str(), "Failed to load AREDL packs. Please try again later.", "OK")->show();
circle->setVisible(false);
});
callback();
return;
}

AREDL_PACKS.clear();
for (auto const& pack : res->json().value().as_array()) {
std::vector<int> levels;
for (auto const& level : pack["levels"].as_array()) levels.push_back(level["level_id"].as_int());
AREDL_PACKS.push_back({
pack["name"].as_string(),
pack["points"].as_double(),
levels
});
}
std::sort(AREDL_PACKS.begin(), AREDL_PACKS.end(), [](auto const& a, auto const& b) {
return a.points < b.points;
});
callback();
}
});
isOk(AREDL_PACKS_URL, std::move(okListener), [&listener, circle](bool ok, int code) {
if (ok) listener.setFilter(web::WebRequest().get(AREDL_PACKS_URL));
else Loader::get()->queueInMainThread([circle, code] {
else queueInMainThread([circle, code] {
FLAlertLayer::create(fmt::format("Load Failed ({})", code).c_str(), "Failed to load AREDL packs. Please try again later.", "OK")->show();
circle->setVisible(false);
});
});
}

void IntegratedDemonlist::loadPemonlist() {
static std::optional<web::WebTask> task = std::nullopt;
static std::optional<web::WebTask> okTask = std::nullopt;
okTask = web::WebRequest().downloadRange({ 0, 0 }).get(PEMONLIST_UPTIME_URL).map([](web::WebResponse* res) {
if (!res->ok()) {
log::error("Failed to load Pemonlist with status code {}", res->code());
okTask = std::nullopt;
return *res;
}

task = web::WebRequest().get(PEMONLIST_URL).map([](web::WebResponse* res2) {
if (res2->ok()) initializePemonlist(res2);
else log::error("Failed to load Pemonlist with status code {}", res2->code());

task = std::nullopt;
return *res2;
});

okTask = std::nullopt;
return *res;
});
}

void IntegratedDemonlist::loadPemonlist(
EventListener<web::WebTask>&& listenerRef, EventListener<web::WebTask>&& okListener,
LoadingCircle* circle, utils::MiniFunction<void()> callback
LoadingCircle* circle, std::function<void()> const& callback
) {
auto&& listener = std::move(listenerRef);
listener.bind([callback, circle](web::WebTask::Event* e) {
if (auto res = e->getValue()) {
if (res->ok()) {
initializePemonlist(res);
callback();
if (!res->ok()) {
queueInMainThread([circle, res] {
FLAlertLayer::create(fmt::format("Load Failed ({})", res->code()).c_str(), "Failed to load Pemonlist. Please try again later.", "OK")->show();
circle->setVisible(false);
});
return;
}
else Loader::get()->queueInMainThread([circle, res] {
FLAlertLayer::create(fmt::format("Load Failed ({})", res->code()).c_str(), "Failed to load Pemonlist. Please try again later.", "OK")->show();
circle->setVisible(false);
});

PEMONLIST_LOADED = true;
PEMONLIST.clear();
auto str = res->string().value();
std::string error;
auto json = matjson::parse(str, error).value_or(matjson::Object { { "data", matjson::Array() } });
if (!error.empty()) log::error("Failed to parse Pemonlist: {}", error);
if (json.is_object() && json.contains("data") && json["data"].is_array()) for (auto const& level : json["data"].as_array()) {
if (!level.contains("level_id") || !level["level_id"].is_number()) continue;
if (!level.contains("name") || !level["name"].is_string()) continue;
if (!level.contains("placement") || !level["placement"].is_number()) continue;

PEMONLIST.push_back({
level["level_id"].as_int(),
level["name"].as_string(),
level["placement"].as_int()
});
}
callback();
}
});

isOk(PEMONLIST_UPTIME_URL, std::move(okListener), [&listener, circle](bool ok, int code) {
if (ok) listener.setFilter(web::WebRequest().get(PEMONLIST_URL));
else Loader::get()->queueInMainThread([circle, code] {
else queueInMainThread([circle, code] {
FLAlertLayer::create(fmt::format("Load Failed ({})", code).c_str(), "Failed to load Pemonlist. Please try again later.", "OK")->show();
circle->setVisible(false);
});
Expand Down
34 changes: 22 additions & 12 deletions src/IntegratedDemonlist.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#include <Geode/Geode.hpp>
#pragma once
#include <Geode/utils/web.hpp>

using namespace geode::prelude;

struct IDListDemon {
int id;
std::string name;
Expand All @@ -20,14 +18,26 @@ class IntegratedDemonlist {
inline static std::vector<IDListDemon> AREDL = {};
inline static std::vector<IDDemonPack> AREDL_PACKS = {};
inline static std::vector<IDListDemon> PEMONLIST = {};
inline static bool TRIED_LOADING = false;
inline static bool AREDL_LOADED = false;
inline static bool PEMONLIST_LOADED = false;

static void initializeAREDL(web::WebResponse*);
static void initializePemonlist(web::WebResponse*);
static void isOk(std::string const&, EventListener<web::WebTask>&&, MiniFunction<void(bool, int)> callback);
static void loadAREDL();
static void loadAREDL(EventListener<web::WebTask>&&, EventListener<web::WebTask>&&, LoadingCircle*, MiniFunction<void()> callback);
static void loadAREDLPacks(EventListener<web::WebTask>&&, EventListener<web::WebTask>&&, LoadingCircle*, MiniFunction<void()> callback);
static void loadPemonlist();
static void loadPemonlist(EventListener<web::WebTask>&&, EventListener<web::WebTask>&&, LoadingCircle*, MiniFunction<void()> callback);
static void isOk(std::string const&, geode::EventListener<geode::utils::web::WebTask>&&, std::function<void(bool, int)> const&);
static void loadAREDL(
geode::EventListener<geode::utils::web::WebTask>&&,
geode::EventListener<geode::utils::web::WebTask>&&,
LoadingCircle*,
std::function<void()> const&
);
static void loadAREDLPacks(
geode::EventListener<geode::utils::web::WebTask>&&,
geode::EventListener<geode::utils::web::WebTask>&&,
LoadingCircle*,
std::function<void()> const&
);
static void loadPemonlist(
geode::EventListener<geode::utils::web::WebTask>&&,
geode::EventListener<geode::utils::web::WebTask>&&,
LoadingCircle*,
std::function<void()> const&
);
};
Loading

0 comments on commit ef2b4e1

Please sign in to comment.