Skip to content

Commit

Permalink
Introduced Linked Distribution initial outline.
Browse files Browse the repository at this point in the history
  • Loading branch information
adya committed Mar 18, 2024
1 parent 8bb55e5 commit 0065c94
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 15 deletions.
1 change: 1 addition & 0 deletions SPID/cmake/headerlist.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set(headers ${headers}
include/ExclusiveGroups.h
include/FormData.h
include/KeywordDependencies.h
include/LinkedDistribution.h
include/LogBuffer.h
include/LookupConfigs.h
include/LookupFilters.h
Expand Down
1 change: 1 addition & 0 deletions SPID/cmake/sourcelist.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(sources ${sources}
src/ExclusiveGroups.cpp
src/FormData.cpp
src/KeywordDependencies.cpp
src/LinkedDistribution.cpp
src/LogBuffer.cpp
src/LookupConfigs.cpp
src/LookupFilters.cpp
Expand Down
6 changes: 3 additions & 3 deletions SPID/include/ExclusiveGroups.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ namespace ExclusiveGroups
///
/// As a result this method configures Manager with discovered valid exclusive groups.
/// </summary>
/// <param name=""></param>
/// <param name="rawExclusiveGroups">A raw exclusive group entries that should be processed/</param>
/// <param name="dataHandler">A DataHandler that will perform the actual lookup.</param>
/// <param name="rawExclusiveGroups">A raw exclusive group entries that should be processed.</param>
void LookupExclusiveGroups(RE::TESDataHandler* const dataHandler, INI::ExclusiveGroupsVec& rawExclusiveGroups);

/// <summary>
/// Gets a set of all forms that are in the same exclusive group as the given form.
/// Note that a form can appear in multiple exclusive groups, all of those groups are returned.
/// </summary>
/// <param name="form"></param>
/// <param name="form">A form for which mutually exclusive forms will be returned.</param>
/// <returns>A union of all groups that contain a given form.</returns>
std::unordered_set<RE::TESForm*> MutuallyExclusiveFormsForForm(RE::TESForm* form) const;

Expand Down
79 changes: 79 additions & 0 deletions SPID/include/LinkedDistribution.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#pragma once
#include "FormData.h"

namespace LinkedDistribution
{
namespace INI {

struct RawLinkedItem
{
FormOrEditorID rawForm{};

/// Raw filters in RawLinkedItem only use MATCH, there is no meaning for ALL or NOT, so they are ignored.
Filters<FormOrEditorID> rawFormFilters{};

RandomCount count{ 1, 1 };
Chance chance{ 100 };

std::string path{};
};

using LinkedItemsVec = std::vector<RawLinkedItem>;

inline LinkedItemsVec linkedItems{};

namespace Parser
{
bool TryParse(const std::string& a_key, const std::string& a_value, const std::string& a_path);
}
}

template<class Form>
using DataSet = std::set<Forms::Data<Form>>;

template<class T>
using LinkedForms = std::unordered_map<RE::TESForm*, DataSet<T>>;

class Manager : public ISingleton<Manager>
{
private:
template <class Form>
const DataSet<Form>& LinkedFormsForForm(const RE::TESForm* form, const LinkedForms<Form>& linkedForms) const
{
if (const auto it = linkedForms.find(form); it != linkedForms.end()) {
return it->second;
} else {
static std::set<RE::TESForm*> empty{};
return empty;
}
}

public:
/// <summary>
/// Does a forms lookup similar to what Filters do.
///
/// As a result this method configures Manager with discovered valid linked items.
/// </summary>
/// <param name="dataHandler">A DataHandler that will perform the actual lookup.</param>
/// <param name="rawLinkedDistribution">A raw linked item entries that should be processed.</param>
void LookupLinkedItems(RE::TESDataHandler* const dataHandler, INI::LinkedItemsVec& rawLinkedItems);



private:

LinkedForms<RE::SpellItem> spells{ RECORD::kSpell };
LinkedForms<RE::BGSPerk> perks{ RECORD::kPerk };
LinkedForms<RE::TESBoundObject> items{ RECORD::kItem };
LinkedForms<RE::TESShout> shouts{ RECORD::kShout };
LinkedForms<RE::TESLevSpell> levSpells{ RECORD::kLevSpell };
LinkedForms<RE::TESForm> packages{ RECORD::kPackage };
LinkedForms<RE::BGSOutfit> outfits{ RECORD::kOutfit };
LinkedForms<RE::BGSKeyword> keywords{ RECORD::kKeyword };
LinkedForms<RE::TESBoundObject> deathItems{ RECORD::kDeathItem };
LinkedForms<RE::TESFaction> factions{ RECORD::kFaction };
LinkedForms<RE::BGSOutfit> sleepOutfits{ RECORD::kSleepOutfit };
LinkedForms<RE::TESObjectARMO> skins{ RECORD::kSkin };

};
}
6 changes: 1 addition & 5 deletions SPID/src/ExclusiveGroups.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ void ExclusiveGroups::Manager::LookupExclusiveGroups(RE::TESDataHandler* const d
}

// Remove empty groups
for (auto& [name, forms] : groups) {
if (forms.empty()) {
groups.erase(name);
}
}
std::erase_if(groups, [](const auto& pair) { return pair.second.empty(); });

for (auto& [name, forms] : groups) {
for (auto& form : forms) {
Expand Down
82 changes: 82 additions & 0 deletions SPID/src/LinkedDistribution.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include "LinkedDistribution.h"
#include "FormData.h"

#pragma region Parsing
bool LinkedDistribution::INI::Parser::TryParse(const std::string& a_key, const std::string& a_value, const std::string& a_path)
{
if (a_key != "LinkedItem") {
return false;
}

const auto sections = string::split(a_value, "|");
const auto size = sections.size();

if (size < 2) {
logger::warn("IGNORED: LinkedItem must have a form and at least one filter name: {} = {}"sv, a_key, a_value);
return false;
}

auto split_IDs = distribution::split_entry(sections[1]);

if (split_IDs.empty()) {
logger::warn("ExclusiveGroup must have at least one Form Filter : {} = {}"sv, a_key, a_value);
return false;
}

LinkedDistribution::INI::RawLinkedItem item{};
item.rawForm = sections[0];
item.path = a_path;

for (auto& IDs : split_IDs) {
item.rawFormFilters.MATCH.push_back(distribution::get_record(IDs));
}
}
#pragma endregion

#pragma region Lookup
void LinkedDistribution::Manager::LookupLinkedItems(RE::TESDataHandler* const dataHandler, INI::LinkedItemsVec& rawLinkedItems)
{
using namespace Forms;

// TODO: Figure out templates here.


for (auto& [rawForm, filterIDs, count, chance, path] : rawLinkedItems) {
try {
if (const auto form = Forms::detail::get_form(dataHandler, rawForm, path); form) {
/*auto& forms = linkedForms[form];
FormVec match{};
if (Forms::detail::formID_to_form(dataHandler, filterIDs.MATCH, match, path, false, false)) {
for (const auto& form : match) {
if (std::holds_alternative<RE::TESForm*>(form)) {
forms.insert(std::get<RE::TESForm*>(form));
}
}
}*/
}
} catch (const Lookup::UnknownFormIDException& e) {
buffered_logger::error("\t[{}] [0x{:X}] ({}) FAIL - formID doesn't exist", e.path, e.formID, e.modName.value_or(""));
} catch (const Lookup::InvalidKeywordException& e) {
buffered_logger::error("\t[{}] [0x{:X}] ({}) FAIL - keyword does not have a valid editorID", e.path, e.formID, e.modName.value_or(""));
} catch (const Lookup::KeywordNotFoundException& e) {
if (e.isDynamic) {
buffered_logger::critical("\t[{}] {} FAIL - couldn't create keyword", e.path, e.editorID);
} else {
buffered_logger::critical("\t[{}] {} FAIL - couldn't get existing keyword", e.path, e.editorID);
}
} catch (const Lookup::UnknownEditorIDException& e) {
buffered_logger::error("\t[{}] ({}) FAIL - editorID doesn't exist", e.path, e.editorID);
} catch (const Lookup::MalformedEditorIDException& e) {
buffered_logger::error("\t[{}] FAIL - editorID can't be empty", e.path);
} catch (const Lookup::InvalidFormTypeException& e) {
// Whitelisting is disabled, so this should not occur
} catch (const Lookup::UnknownPluginException& e) {
// Likewise, we don't expect plugin names in linked forms.
}
}

// Remove empty linked forms
//std::erase_if(linkedForms, [](const auto& pair) { return pair.second.empty(); });
}
#pragma endregion
14 changes: 7 additions & 7 deletions SPID/src/LookupConfigs.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "LookupConfigs.h"
#include "LinkedDistribution.h"

namespace INI
{
Expand Down Expand Up @@ -55,13 +56,8 @@ namespace INI
const auto sections = string::split(a_value, "|");
const auto size = sections.size();

if (size == 0) {
logger::warn("IGNORED: ExclusiveGroup must have a name: {} = {}"sv, a_key, a_value);
return std::nullopt;
}

if (size == 1) {
logger::warn("IGNORED: ExclusiveGroup must have at least one filter name: {} = {}"sv, a_key, a_value);
if (size < 2) {
logger::warn("IGNORED: ExclusiveGroup must have a name and at least one Form Filter: {} = {}"sv, a_key, a_value);
return std::nullopt;
}

Expand Down Expand Up @@ -326,6 +322,10 @@ namespace INI
continue;
}

if (LinkedDistribution::INI::Parser::TryParse(key.pItem, entry, truncatedPath)) {
continue;
}

auto [data, sanitized_str] = detail::parse_ini(key.pItem, entry, truncatedPath);
configs[key.pItem].emplace_back(data);

Expand Down

0 comments on commit 0065c94

Please sign in to comment.