VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
PluginDependencies.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <format>
5#include <functional>
6#include <map>
7#include <set>
8#include <string>
9#include <utility>
10#include <vector>
11
12namespace VoltMod
13{
14
15using PluginsByName = std::map<std::string, const PluginManifest*, std::less<>>;
16using RefusalsByName = std::map<std::string, Error, std::less<>>;
17
18// Refuse plugins missing a dependency, then their dependents transitively; @p refused seeds the chain.
20{
21 std::vector<std::string> spreading;
22 for (const auto& [name, reason] : refused)
23 {
24 spreading.push_back(name);
25 }
26
27 const auto refuse = [&](const std::string& name, Error reason) {
28 if (refused.emplace(name, std::move(reason)).second)
29 {
30 spreading.push_back(name);
31 }
32 };
33
34 for (const auto& [name, manifest] : plugins)
35 {
36 for (const std::string& dependency : manifest->Dependencies)
37 {
38 if (!plugins.contains(dependency))
39 {
40 refuse(name, Error::NotFound(std::format("requires '{}', which is not installed", dependency)));
41 break;
42 }
43 }
44 }
45
46 while (!spreading.empty())
47 {
48 const std::string gone = std::move(spreading.back());
49 spreading.pop_back();
50
51 for (const auto& [name, manifest] : plugins)
52 {
53 if (!refused.contains(name) && std::ranges::contains(manifest->Dependencies, gone))
54 {
55 refuse(name, Error::NotReady(std::format("requires '{}', which the host refused", gone)));
56 }
57 }
58 }
59}
60
61LoadList PluginDependencies::Resolve(std::span<const PluginManifest> installed)
62{
65 for (const PluginManifest& manifest : installed)
66 {
67 if (!plugins.emplace(manifest.Name, &manifest).second)
68 {
69 refused.emplace(manifest.Name, Error::Invalid("installed more than once; each plugin directory needs its "
70 "own plugin name"));
71 }
72 }
73
75
77 for (const auto& [name, manifest] : plugins)
78 {
79 if (!refused.contains(name))
80 {
81 list.ToLoad.push_back(name);
82 }
83 }
84
85 for (auto& [name, reason] : refused)
86 {
87 list.Refused.push_back({name, std::move(reason)});
88 }
89
90 return list;
91}
92
93std::vector<std::string> PluginDependencies::RequiredDependents(std::string_view plugin,
94 std::span<const PluginManifest> loaded)
95{
96 std::set<std::string, std::less<>> dependents;
97 std::vector<std::string> spreading{std::string(plugin)};
98 while (!spreading.empty())
99 {
100 const std::string needed = std::move(spreading.back());
101 spreading.pop_back();
102
103 for (const PluginManifest& manifest : loaded)
104 {
105 if (manifest.Name != plugin && !dependents.contains(manifest.Name) &&
106 std::ranges::contains(manifest.Dependencies, needed))
107 {
108 dependents.insert(manifest.Name);
109 spreading.push_back(manifest.Name);
110 }
111 }
112 }
113
114 return {dependents.begin(), dependents.end()};
115}
116
117} // namespace VoltMod
LoadList Resolve(std::span< const PluginManifest > installed)
Decide which of installed the host loads.
std::vector< std::string > RequiredDependents(std::string_view plugin, std::span< const PluginManifest > loaded)
static void RefuseUnsatisfied(const PluginsByName &plugins, RefusalsByName &refused)
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56
std::map< std::string, const PluginManifest *, std::less<> > PluginsByName
std::map< std::string, Error, std::less<> > RefusalsByName
One failure: a code to branch on, text for the log, and an optional translation key.
Definition Result.hpp:42
static Error Invalid(std::string detail)
Definition Result.hpp:51
static Error NotFound(std::string detail)
Definition Result.hpp:49
static Error NotReady(std::string detail)
Definition Result.hpp:50
std::vector< std::string > ToLoad