impl searching local mods

This commit is contained in:
HJfod 2024-02-28 19:38:20 +02:00
parent 65645fe753
commit 435484b6ae
3 changed files with 89 additions and 36 deletions

View file

@ -39,6 +39,14 @@ bool ModList::init(ModListSource* src, CCSize const& size) {
m_searchInput->setAnchorPoint({ 0, .5f });
m_searchInput->setTextAlign(TextInputAlign::Left);
m_searchInput->setCallback([this](auto const&) {
// If the source is already in memory, we can immediately update the
// search query
if (m_source->isInMemory()) {
m_source->setQuery(m_searchInput->getString());
this->gotoPage(0);
return;
}
// Otherwise buffer inputs by a bit
// This avoids spamming servers for every character typed,
// instead waiting for input to stop to actually do the search
std::thread([this] {

View file

@ -19,36 +19,66 @@ static bool weightedFuzzyMatch(std::string const& str, std::string const& kw, do
return false;
}
static std::pair<std::vector<Mod*>, size_t> getModsWithQuery(server::ModsQuery const& query) {
std::vector<std::pair<Mod*, double>> mods;
// Filter installed mods based on query
for (auto& mod : Loader::get()->getAllMods()) {
bool addToList = !query.query.has_value();
double weighted = 0;
if (query.query) {
addToList |= weightedFuzzyMatch(mod->getName(), *query.query, 2, weighted);
addToList |= weightedFuzzyMatch(mod->getID(), *query.query, 1, weighted);
for (auto& dev : mod->getDevelopers()) {
addToList |= weightedFuzzyMatch(dev, *query.query, 0.75, weighted);
}
if (auto details = mod->getDetails()) {
addToList |= weightedFuzzyMatch(*details, *query.query, 0.05, weighted);
}
if (auto desc = mod->getDescription()) {
addToList |= weightedFuzzyMatch(*desc, *query.query, 0.2, weighted);
}
if (weighted < 2) {
addToList = false;
}
}
if (addToList) {
mods.push_back({ mod, weighted });
}
}
// Sort list based on score
std::sort(mods.begin(), mods.end(), [](auto a, auto b) {
// Sort primarily by score
if (a.second != b.second) {
return a.second > b.second;
}
// Sort secondarily alphabetically
return a.first->getName() < b.first->getName();
});
// Pick out only the mods in the page and page size specified in the query
std::vector<Mod*> result {};
for (
size_t i = query.page * query.pageSize;
i < mods.size() && i < (query.page + 1) * query.pageSize;
i += 1
) {
result.push_back(mods.at(i).first);
}
// Return paged mods & total count of matching mods
return { result, mods.size() };
}
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();
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 });
// }
auto mods = getModsWithQuery(query);
for (auto& mod : mods.first) {
content.push_back(InstalledModItem::create(mod));
}
// Sort list based on score
std::sort(mods.begin(), mods.end(), [](auto a, auto b) {
return a.second < b.second;
});
for (
size_t i = query.page * query.pageSize;
i < mods.size() && i < (query.page + 1) * query.pageSize;
i += 1
) {
content.push_back(InstalledModItem::create(mods.at(i).first));
}
resolve({ content, mods.size() });
resolve({ content, mods.second });
});
});
}
@ -58,7 +88,7 @@ static auto loadServerModsPage(server::ModsQuery&& query) {
server::getMods(query)
.then([resolve, reject](server::ServerModsList list) {
auto content = ModListSource::Page();
for (auto mod : list.mods) {
for (auto& mod : list.mods) {
content.push_back(ServerModItem::create(mod));
}
resolve({ content, list.totalModCount });
@ -122,7 +152,7 @@ std::optional<size_t> ModListSource::getItemCount() const {
}
void ModListSource::reset() {
m_query.clear();
m_query = std::nullopt;
m_cachedPages.clear();
m_cachedItemCount = std::nullopt;
}
@ -130,15 +160,20 @@ void ModListSource::reset() {
void ModListSource::setQuery(std::string const& query) {
// Set query & reset cache
if (m_query != query) {
m_query = query;
m_query = query.empty() ? std::nullopt : std::optional(query);
m_cachedPages.clear();
m_cachedItemCount = std::nullopt;
}
}
ModListSource* ModListSource::create(Provider* provider) {
bool ModListSource::isInMemory() const {
return m_inMemory;
}
ModListSource* ModListSource::create(Provider* provider, bool inMemory) {
auto ret = new ModListSource();
ret->m_provider = provider;
ret->m_inMemory = inMemory;
ret->autorelease();
return ret;
}
@ -147,7 +182,7 @@ ModListSource* ModListSource::get(ModListSourceType type) {
switch (type) {
default:
case ModListSourceType::Installed: {
static auto inst = Ref(ModListSource::create(loadInstalledModsPage));
static auto inst = Ref(ModListSource::create(loadInstalledModsPage, true));
return inst;
} break;
@ -155,24 +190,24 @@ ModListSource* ModListSource::get(ModListSourceType type) {
static auto inst = Ref(ModListSource::create(+[](server::ModsQuery&& query) {
query.featured = true;
return loadServerModsPage(std::move(query));
}));
}, false));
return inst;
} break;
case ModListSourceType::Trending: {
static auto inst = Ref(ModListSource::create(nullptr));
static auto inst = Ref(ModListSource::create(nullptr, false));
return inst;
} break;
case ModListSourceType::ModPacks: {
static auto inst = Ref(ModListSource::create(nullptr));
static auto inst = Ref(ModListSource::create(nullptr, false));
return inst;
} break;
case ModListSourceType::All: {
static auto inst = Ref(ModListSource::create(+[](server::ModsQuery&& query) {
return loadServerModsPage(std::move(query));
}));
}, false));
return inst;
} break;
}

View file

@ -39,12 +39,13 @@ public:
protected:
std::unordered_map<size_t, Page> m_cachedPages;
std::optional<size_t> m_cachedItemCount;
std::string m_query;
std::optional<std::string> m_query;
Provider* m_provider = nullptr;
bool m_inMemory;
public:
// Create a new source with an arbitary provider
static ModListSource* create(Provider* provider);
static ModListSource* create(Provider* provider, bool inMemory);
// Get a standard source (lazily created static instance)
static ModListSource* get(ModListSourceType type);
@ -59,4 +60,13 @@ public:
PagePromise loadPage(size_t page, bool update = false);
std::optional<size_t> getPageCount() const;
std::optional<size_t> getItemCount() const;
/**
* True if the source is already fully loaded in memory (doesn't fetch
* from a server or filesystem)
*
* Used to determine whether things like searching should update the query
* instantaniously or buffer a bit to avoid spamming unnecessary requests
*/
bool isInMemory() const;
};