mirror of
https://github.com/geode-sdk/geode.git
synced 2025-07-29 15:40:06 -04:00
impl searching on server
This commit is contained in:
parent
54ac66ef7c
commit
65645fe753
6 changed files with 102 additions and 31 deletions
loader/src
|
@ -211,7 +211,7 @@ std::string server::getServerUserAgent() {
|
|||
);
|
||||
}
|
||||
|
||||
ServerPromise<ServerModsList> server::getMods(ModsQuery query) {
|
||||
ServerPromise<ServerModsList> server::getMods(ModsQuery const& query) {
|
||||
auto req = web::WebRequest();
|
||||
req.userAgent(getServerUserAgent());
|
||||
|
||||
|
|
|
@ -76,6 +76,6 @@ namespace server {
|
|||
|
||||
std::string getServerAPIBaseURL();
|
||||
std::string getServerUserAgent();
|
||||
ServerPromise<ServerModsList> getMods(ModsQuery query);
|
||||
ServerPromise<ServerModsList> getMods(ModsQuery const& query);
|
||||
ServerPromise<ByteVector> getModLogo(std::string const& id);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "ModList.hpp"
|
||||
#include <Geode/ui/TextInput.hpp>
|
||||
#include <Geode/utils/ColorProvider.hpp>
|
||||
|
||||
bool ModList::init(ModListSource* src, CCSize const& size) {
|
||||
|
@ -10,6 +9,7 @@ bool ModList::init(ModListSource* src, CCSize const& size) {
|
|||
this->setAnchorPoint({ .5f, .5f });
|
||||
|
||||
m_source = src;
|
||||
src->reset();
|
||||
|
||||
m_list = ScrollLayer::create(size);
|
||||
m_list->m_contentLayer->setLayout(
|
||||
|
@ -34,11 +34,26 @@ bool ModList::init(ModListSource* src, CCSize const& size) {
|
|||
searchBG->ignoreAnchorPointForPosition(false);
|
||||
m_searchMenu->addChildAtPosition(searchBG, Anchor::Center);
|
||||
|
||||
auto searchInput = TextInput::create(size.width, "Search Mods");
|
||||
searchInput->setScale(.75f);
|
||||
searchInput->setAnchorPoint({ 0, .5f });
|
||||
searchInput->setTextAlign(TextInputAlign::Left);
|
||||
m_searchMenu->addChildAtPosition(searchInput, Anchor::Left, ccp(10, 0));
|
||||
m_searchInput = TextInput::create(size.width, "Search Mods");
|
||||
m_searchInput->setScale(.75f);
|
||||
m_searchInput->setAnchorPoint({ 0, .5f });
|
||||
m_searchInput->setTextAlign(TextInputAlign::Left);
|
||||
m_searchInput->setCallback([this](auto const&) {
|
||||
// This avoids spamming servers for every character typed,
|
||||
// instead waiting for input to stop to actually do the search
|
||||
std::thread([this] {
|
||||
m_searchInputThreads += 1;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
m_searchInputThreads -= 1;
|
||||
if (m_searchInputThreads == 0) {
|
||||
Loader::get()->queueInMainThread([this] {
|
||||
m_source->setQuery(m_searchInput->getString());
|
||||
this->gotoPage(0);
|
||||
});
|
||||
}
|
||||
}).detach();
|
||||
});
|
||||
m_searchMenu->addChildAtPosition(m_searchInput, Anchor::Left, ccp(10, 0));
|
||||
|
||||
// Do not add search menu; that's handled by onSearch
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <Geode/ui/General.hpp>
|
||||
#include <Geode/ui/ScrollLayer.hpp>
|
||||
#include <Geode/ui/TextArea.hpp>
|
||||
#include <Geode/ui/TextInput.hpp>
|
||||
#include "ModItem.hpp"
|
||||
#include "ModListSource.hpp"
|
||||
|
||||
|
@ -32,8 +33,10 @@ protected:
|
|||
CCMenuItemSpriteExtra* m_pagePrevBtn;
|
||||
CCMenuItemSpriteExtra* m_pageNextBtn;
|
||||
Ref<CCMenu> m_searchMenu;
|
||||
TextInput* m_searchInput;
|
||||
ModListPageUpdated m_pageUpdated = nullptr;
|
||||
bool m_bigSize = false;
|
||||
std::atomic<size_t> m_searchInputThreads = 0;
|
||||
|
||||
bool init(ModListSource* src, CCSize const& size);
|
||||
|
||||
|
|
|
@ -1,37 +1,61 @@
|
|||
#include "ModListSource.hpp"
|
||||
#include <server/Server.hpp>
|
||||
|
||||
static size_t PER_PAGE = 10;
|
||||
#define FTS_FUZZY_MATCH_IMPLEMENTATION
|
||||
#include <Geode/external/fts/fts_fuzzy_match.h>
|
||||
|
||||
static constexpr size_t PER_PAGE = 10;
|
||||
|
||||
static size_t ceildiv(size_t a, size_t b) {
|
||||
// https://stackoverflow.com/questions/2745074/fast-ceiling-of-an-integer-division-in-c-c
|
||||
return a / b + (a % b != 0);
|
||||
}
|
||||
|
||||
static auto loadInstalledModsPage(size_t page) {
|
||||
return ModListSource::ProviderPromise([page](auto resolve, auto, auto, auto const&) {
|
||||
Loader::get()->queueInMainThread([page, resolve = std::move(resolve)] {
|
||||
static bool weightedFuzzyMatch(std::string const& str, std::string const& kw, double weight, double& out) {
|
||||
int score;
|
||||
if (fts::fuzzy_match(kw.c_str(), str.c_str(), score)) {
|
||||
out = std::max(out, score * weight);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static auto loadInstalledModsPage(server::ModsQuery&& query) {
|
||||
return ModListSource::ProviderPromise([query = std::move(query)](auto resolve, auto, auto, auto const&) {
|
||||
Loader::get()->queueInMainThread([query = std::move(query), resolve = std::move(resolve)] {
|
||||
auto content = ModListSource::Page();
|
||||
auto all = Loader::get()->getAllMods();
|
||||
std::vector<std::pair<Mod*, double>> mods;
|
||||
|
||||
// todo: finish this
|
||||
for (auto& mod : Loader::get()->getAllMods()) {
|
||||
// bool someMatched = false;
|
||||
double weighted = 0;
|
||||
// if (query.query) {
|
||||
// someMatched += weightedFuzzyMatch(mod->getName(), *query.query, 2, weighted);
|
||||
// }
|
||||
// if (someMatched) {
|
||||
mods.push_back({ mod, weighted });
|
||||
// }
|
||||
}
|
||||
|
||||
// Sort list based on score
|
||||
std::sort(mods.begin(), mods.end(), [](auto a, auto b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
for (
|
||||
size_t i = page * PER_PAGE;
|
||||
i < all.size() && i < (page + 1) * PER_PAGE;
|
||||
size_t i = query.page * query.pageSize;
|
||||
i < mods.size() && i < (query.page + 1) * query.pageSize;
|
||||
i += 1
|
||||
) {
|
||||
content.push_back(InstalledModItem::create(all.at(i)));
|
||||
content.push_back(InstalledModItem::create(mods.at(i).first));
|
||||
}
|
||||
resolve({ content, all.size() });
|
||||
resolve({ content, mods.size() });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static auto loadServerModsPage(size_t page, bool featuredOnly) {
|
||||
return ModListSource::ProviderPromise([page, featuredOnly](auto resolve, auto reject, auto progress, auto cancelled) {
|
||||
server::getMods(server::ModsQuery {
|
||||
.featured = featuredOnly ? std::optional(true) : std::nullopt,
|
||||
.page = page,
|
||||
.pageSize = PER_PAGE,
|
||||
})
|
||||
static auto loadServerModsPage(server::ModsQuery&& query) {
|
||||
return ModListSource::ProviderPromise([query = std::move(query)](auto resolve, auto reject, auto progress, auto cancelled) {
|
||||
server::getMods(query)
|
||||
.then([resolve, reject](server::ServerModsList list) {
|
||||
auto content = ModListSource::Page();
|
||||
for (auto mod : list.mods) {
|
||||
|
@ -65,7 +89,12 @@ typename ModListSource::PagePromise ModListSource::loadPage(size_t page, bool up
|
|||
}
|
||||
m_cachedPages.erase(page);
|
||||
return PagePromise([this, page](auto resolve, auto reject, auto progress, auto cancelled) {
|
||||
m_provider(page)
|
||||
m_provider(server::ModsQuery {
|
||||
.query = m_query,
|
||||
.page = page,
|
||||
// todo: loader setting to change this
|
||||
.pageSize = PER_PAGE,
|
||||
})
|
||||
.then([page, this, resolve, reject](auto data) {
|
||||
if (data.second == 0 || data.first.empty()) {
|
||||
return reject(ModListSource::LoadPageError("No mods found :("));
|
||||
|
@ -92,6 +121,21 @@ std::optional<size_t> ModListSource::getItemCount() const {
|
|||
return m_cachedItemCount;
|
||||
}
|
||||
|
||||
void ModListSource::reset() {
|
||||
m_query.clear();
|
||||
m_cachedPages.clear();
|
||||
m_cachedItemCount = std::nullopt;
|
||||
}
|
||||
|
||||
void ModListSource::setQuery(std::string const& query) {
|
||||
// Set query & reset cache
|
||||
if (m_query != query) {
|
||||
m_query = query;
|
||||
m_cachedPages.clear();
|
||||
m_cachedItemCount = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
ModListSource* ModListSource::create(Provider* provider) {
|
||||
auto ret = new ModListSource();
|
||||
ret->m_provider = provider;
|
||||
|
@ -108,8 +152,9 @@ ModListSource* ModListSource::get(ModListSourceType type) {
|
|||
} break;
|
||||
|
||||
case ModListSourceType::Featured: {
|
||||
static auto inst = Ref(ModListSource::create(+[](size_t page) {
|
||||
return loadServerModsPage(page, true);
|
||||
static auto inst = Ref(ModListSource::create(+[](server::ModsQuery&& query) {
|
||||
query.featured = true;
|
||||
return loadServerModsPage(std::move(query));
|
||||
}));
|
||||
return inst;
|
||||
} break;
|
||||
|
@ -125,8 +170,8 @@ ModListSource* ModListSource::get(ModListSourceType type) {
|
|||
} break;
|
||||
|
||||
case ModListSourceType::All: {
|
||||
static auto inst = Ref(ModListSource::create(+[](size_t page) {
|
||||
return loadServerModsPage(page, false);
|
||||
static auto inst = Ref(ModListSource::create(+[](server::ModsQuery&& query) {
|
||||
return loadServerModsPage(std::move(query));
|
||||
}));
|
||||
return inst;
|
||||
} break;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/utils/Promise.hpp>
|
||||
#include <server/Server.hpp>
|
||||
#include "ModItem.hpp"
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
@ -33,11 +34,12 @@ public:
|
|||
using PagePromise = Promise<Page, LoadPageError, std::optional<uint8_t>>;
|
||||
|
||||
using ProviderPromise = Promise<std::pair<Page, size_t>, LoadPageError, std::optional<uint8_t>>;
|
||||
using Provider = ProviderPromise(size_t page);
|
||||
using Provider = ProviderPromise(server::ModsQuery&& query);
|
||||
|
||||
protected:
|
||||
std::unordered_map<size_t, Page> m_cachedPages;
|
||||
std::optional<size_t> m_cachedItemCount;
|
||||
std::string m_query;
|
||||
Provider* m_provider = nullptr;
|
||||
|
||||
public:
|
||||
|
@ -47,6 +49,12 @@ public:
|
|||
// Get a standard source (lazily created static instance)
|
||||
static ModListSource* get(ModListSourceType type);
|
||||
|
||||
// Reset all filters & cache
|
||||
void reset();
|
||||
|
||||
// Set a query; clears cache
|
||||
void setQuery(std::string const& query);
|
||||
|
||||
// Load page, uses cache if possible unless `update` is true
|
||||
PagePromise loadPage(size_t page, bool update = false);
|
||||
std::optional<size_t> getPageCount() const;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue