lxgui
Loading...
Searching...
No Matches
gui_addon_registry.cpp
1#include "lxgui/gui_addon_registry.hpp"
2
3#include "lxgui/gui_event_emitter.hpp"
4#include "lxgui/gui_localizer.hpp"
5#include "lxgui/gui_out.hpp"
6#include "lxgui/utils_file_system.hpp"
7#include "lxgui/utils_range.hpp"
8#include "lxgui/utils_std.hpp"
9#include "lxgui/utils_string.hpp"
10
11#include <fstream>
12#include <lxgui/extern_sol2_state.hpp>
13
14namespace {
15// This should be incremented for each non-backward compatible change
16// to the GUI API, or when new elements are added to the API.
17const char* lxgui_ui_version = "0001";
18} // namespace
19
20namespace lxgui::gui {
21
23 sol::state& lua, localizer& loc, event_emitter& emitter, root& r, virtual_root& vr) :
24 lua_(lua), localizer_(loc), event_emitter_(emitter), root_(r), virtual_root_(vr) {}
25
26void addon_registry::load_addon_toc_(
27 const std::string& addon_name, const std::string& addon_directory) {
28 auto& addons = addon_list_[addon_directory];
29 if (addons.find(addon_name) != addons.end())
30 return;
31
32 addon a;
33 a.enabled = true;
34 a.main_directory = utils::cut(addon_directory, "/").back();
35 a.directory = addon_directory + "/" + addon_name;
36
37 std::string toc_file = a.directory + "/" + addon_name + ".toc";
38 std::ifstream file(toc_file);
39 if (!file.is_open())
40 return;
41
42 std::string line;
43 while (std::getline(file, line)) {
44 utils::replace(line, "\r", "");
45 if (line.empty())
46 continue;
47
48 std::string_view line_view = line;
49
50 if (line_view.size() >= 2 && line_view[0] == '#' && line_view[1] == '#') {
51 line_view = line_view.substr(2);
52 line_view = utils::trim(line_view, ' ');
53 auto args = utils::cut_first(line_view, ":");
54 if (!args.first.empty() && !args.second.empty()) {
55 std::string_view key = utils::trim(args.first, ' ');
56 std::string_view value = utils::trim(args.second, ' ');
57
58 if (key == "Interface") {
59 a.ui_version = value;
60
61 if (a.ui_version == lxgui_ui_version)
62 a.enabled = true;
63 else {
64 gui::out << gui::warning << "gui::manager: "
65 << "Wrong UI version for \"" << addon_name
66 << "\" (got: " << a.ui_version
67 << ", expected: " << lxgui_ui_version << "). AddOn disabled."
68 << std::endl;
69 a.enabled = false;
70 }
71 } else if (key == "Title")
72 a.name = value;
73 else if (key == "Version")
74 a.version = value;
75 else if (key == "Author")
76 a.author = value;
77 else if (key == "SavedVariables") {
78 for (auto var : utils::cut(value, ",")) {
79 var = utils::trim(var, ' ');
80 if (!utils::has_no_content(var))
81 a.saved_variable_list.push_back(std::string{var});
82 }
83 }
84 }
85 } else {
86 line_view = utils::trim(line_view, ' ');
87 if (!utils::has_no_content(line_view))
88 a.file_list.push_back(a.directory + "/" + std::string{line_view});
89 }
90 }
91
92 if (a.name.empty())
93 gui::out << gui::error << "gui::manager: Missing addon name in " << toc_file << "."
94 << std::endl;
95 else
96 addons[addon_name] = a;
97}
98
99void addon_registry::load_addon_files_(const addon& a) {
100 localizer_.load_translations(a.directory);
101
102 current_addon_ = &a;
103 for (const auto& file : a.file_list) {
104 const std::string extension = utils::get_file_extension(file);
105 if (extension == ".lua") {
106 auto result = lua_.do_file(file);
107 if (!result.valid()) {
108 std::string err = result.get<sol::error>().what();
109 gui::out << gui::error << err << std::endl;
110 event_emitter_.fire_event("LUA_ERROR", {err});
111 }
112 } else {
113 this->parse_layout_file_(file, a);
114 }
115 }
116
117 std::string saved_variables_file =
118 "saves/interface/" + a.main_directory + "/" + a.name + ".lua";
119
120 if (utils::file_exists(saved_variables_file)) {
121 auto result = lua_.do_file(saved_variables_file);
122 if (!result.valid()) {
123 std::string err = result.get<sol::error>().what();
124 gui::out << gui::error << err << std::endl;
125 event_emitter_.fire_event("LUA_ERROR", {err});
126 }
127 }
128
129 event_emitter_.fire_event("ADDON_LOADED", {a.name});
130}
131
132void addon_registry::load_addon_directory(const std::string& directory) {
133 for (const auto& sub_dir : utils::get_directory_list(directory))
134 this->load_addon_toc_(sub_dir, directory);
135
136 std::vector<addon*> core_addon_stack;
137 std::vector<addon*> addon_stack;
138 bool core = false;
139
140 auto& addons = addon_list_[directory];
141
142 std::ifstream file(directory + "/addons.txt");
143 if (file.is_open()) {
144 std::string line;
145 while (std::getline(file, line)) {
146 utils::replace(line, "\r", "");
147 if (line.empty())
148 continue;
149
150 std::string_view line_view = line;
151
152 if (line_view[0] == '#') {
153 line_view = line_view.substr(1);
154 line_view = utils::trim(line_view, ' ');
155 core = line_view == "Core";
156 } else {
157 auto args = utils::cut_first(line_view, ":");
158 if (!args.first.empty() && !args.second.empty()) {
159 std::string_view key = utils::trim(args.first, ' ');
160 std::string_view value = utils::trim(args.second, ' ');
161 auto iter = addons.find(std::string{key});
162 if (iter != addons.end()) {
163 if (core)
164 core_addon_stack.push_back(&iter->second);
165 else
166 addon_stack.push_back(&iter->second);
167
168 iter->second.enabled = value == "1";
169 }
170 }
171 }
172 }
173 file.close();
174 }
175
176 for (auto* a : core_addon_stack) {
177 if (a->enabled)
178 this->load_addon_files_(*a);
179 }
180
181 for (auto* a : addon_stack) {
182 if (a->enabled)
183 this->load_addon_files_(*a);
184 }
185
186 current_addon_ = nullptr;
187}
188
189std::string serialize(const std::string& tab, const sol::object& value) noexcept {
190 if (value.is<double>()) {
191 return utils::to_string(value.as<double>());
192 } else if (value.is<int>()) {
193 return utils::to_string(value.as<int>());
194 } else if (value.is<std::string>()) {
195 return "\"" + utils::to_string(value.as<std::string>()) + "\"";
196 } else if (value.is<sol::table>()) {
197 std::string result;
198 result += "{";
199
200 std::string content;
201 sol::table table = value.as<sol::table>();
202 for (const auto& key_value : table) {
203 content += tab + " [" + serialize("", key_value.first) +
204 "] = " + serialize(tab + " ", key_value.second) + ",\n";
205 }
206
207 if (!content.empty())
208 result += "\n" + content + tab;
209
210 result += "}";
211 return result;
212 }
213
214 return "nil";
215}
216
217std::string serialize_global(sol::state& lua, const std::string& variable) noexcept {
218 sol::object value = lua.globals()[variable];
219 return serialize("", value);
220}
221
223 for (const auto& directory : addon_list_) {
224 for (const auto& a : utils::range::value(directory.second))
225 save_variables_(a);
226 }
227}
228
229void addon_registry::save_variables_(const addon& a) const noexcept {
230 if (!a.saved_variable_list.empty()) {
231 if (!utils::make_directory("saves/interface/" + a.main_directory)) {
233 << "gui::addon_registry: unable to create directory 'saves/interface/"
234 << a.main_directory << "'" << std::endl;
235 return;
236 }
237
238 std::ofstream file("saves/interface/" + a.main_directory + "/" + a.name + ".lua");
239 for (const auto& variable : a.saved_variable_list) {
240 std::string serialized = serialize_global(lua_, variable);
241 if (!serialized.empty())
242 file << serialized << "\n";
243 }
244 }
245}
246
248 return current_addon_;
249}
250
252 current_addon_ = a;
253}
254
255} // namespace lxgui::gui
const addon * get_current_addon()
Returns the addon that is being parsed.
void save_variables() const
Save Lua variables registred for saving for all addons.
void set_current_addon(const addon *a)
Sets the current addon.
void load_addon_directory(const std::string &directory)
Parse all addons inside a directory.
addon_registry(sol::state &lua, localizer &loc, event_emitter &emitter, root &r, virtual_root &vr)
Constructor.
Generates events and keep tracks of registered callbacks.
void fire_event(const std::string &event_name, event_data data=event_data{})
Emmit a new event.
Utility class to translate strings for display in GUI.
void load_translations(const std::string &folder_path)
Loads new translations from a folder, selecting the language automatically.
Root of the UI object hierarchy.
Definition gui_root.hpp:39
Root of the virtual UI object hierarchy.
std::string serialize_global(sol::state &lua, const std::string &variable) noexcept
std::ostream out
std::string serialize(const std::string &tab, const sol::object &value) noexcept
const std::string warning
Definition gui_out.cpp:6
const std::string error
Definition gui_out.cpp:7
range_impl::value_range< T > value(T &container)
Expose the value rather than the (key,value) pair.
bool make_directory(const std::string &path)
string_vector get_directory_list(const std::string &rel_path)
std::string get_file_extension(const std::string &file)
bool file_exists(const std::string &file)
A piece of the user interface.
Definition gui_addon.hpp:12
std::string directory
Definition gui_addon.hpp:21
std::string version
Definition gui_addon.hpp:14
std::string author
Definition gui_addon.hpp:16
std::string ui_version
Definition gui_addon.hpp:15
std::string main_directory
Definition gui_addon.hpp:20
std::vector< std::string > saved_variable_list
Definition gui_addon.hpp:24
std::string name
Definition gui_addon.hpp:13
std::vector< std::string > file_list
Definition gui_addon.hpp:23