Line data Source code
1 : /* Copyright (C) 2022 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : /*
19 : CConfigDB - Load, access and store configuration variables
20 :
21 : TDD : http://www.wildfiregames.com/forum/index.php?showtopic=1125
22 : OVERVIEW:
23 :
24 : JavaScript: Check this documentation: http://trac.wildfiregames.com/wiki/Exposed_ConfigDB_Functions
25 : */
26 :
27 : #ifndef INCLUDED_CONFIGDB
28 : #define INCLUDED_CONFIGDB
29 :
30 : #include "lib/file/vfs/vfs_path.h"
31 : #include "ps/CStr.h"
32 :
33 : #include <array>
34 : #include <map>
35 : #include <memory>
36 : #include <mutex>
37 : #include <vector>
38 :
39 : /**
40 : * Namespace priorities:
41 : * - Command line args override everything
42 : * - User supersedes HWDetect (let the user try crashing his system).
43 : * - HWDetect supersedes mods & default -> mods can mod hwdetect itself.
44 : * - SYSTEM is used for local.cfg and is basically for setting custom defaults.
45 : */
46 : enum EConfigNamespace
47 : {
48 : CFG_DEFAULT,
49 : CFG_MOD,
50 : CFG_SYSTEM,
51 : CFG_HWDETECT,
52 : CFG_USER,
53 : CFG_COMMAND,
54 : CFG_LAST
55 : };
56 :
57 : using CConfigValueSet = std::vector<CStr>;
58 :
59 : // Opaque data type so that callers that hook into ConfigDB can delete their hooks.
60 : // Would be defined in CConfigDB but then it couldn't be forward-declared, which is rather annoying.
61 : // Actually defined below - requires access to CConfigDB.
62 : class CConfigDBHook;
63 :
64 : #define g_ConfigDB (*CConfigDB::Instance())
65 :
66 : class CConfigDB
67 : {
68 : friend CConfigDBHook;
69 : public:
70 : CConfigDB();
71 : ~CConfigDB();
72 : CConfigDB(const CConfigDB&) = delete;
73 : CConfigDB(CConfigDB&&) = delete;
74 :
75 : static void Initialise();
76 : static void Shutdown();
77 : static bool IsInitialised();
78 : static CConfigDB* Instance();
79 :
80 : /**
81 : * Attempt to retrieve the value of a config variable with the given name;
82 : * will search CFG_COMMAND first, and then all namespaces from the specified
83 : * namespace down.
84 : */
85 : void GetValue(EConfigNamespace ns, const CStr& name, bool& value);
86 : ///@copydoc CConfigDB::GetValue
87 : void GetValue(EConfigNamespace ns, const CStr& name, int& value);
88 : ///@copydoc CConfigDB::GetValue
89 : void GetValue(EConfigNamespace ns, const CStr& name, u32& value);
90 : ///@copydoc CConfigDB::GetValue
91 : void GetValue(EConfigNamespace ns, const CStr& name, float& value);
92 : ///@copydoc CConfigDB::GetValue
93 : void GetValue(EConfigNamespace ns, const CStr& name, double& value);
94 : ///@copydoc CConfigDB::GetValue
95 : void GetValue(EConfigNamespace ns, const CStr& name, std::string& value);
96 :
97 : /**
98 : * Returns true if changed with respect to last write on file
99 : */
100 : bool HasChanges(EConfigNamespace ns) const;
101 :
102 : void SetChanges(EConfigNamespace ns, bool value);
103 :
104 : /**
105 : * Attempt to retrieve a vector of values corresponding to the given setting;
106 : * will search CFG_COMMAND first, and then all namespaces from the specified
107 : * namespace down.
108 : */
109 : void GetValues(EConfigNamespace ns, const CStr& name, CConfigValueSet& values) const;
110 :
111 : /**
112 : * Returns the namespace that the value returned by GetValues was defined in,
113 : * or CFG_LAST if it wasn't defined at all.
114 : */
115 : EConfigNamespace GetValueNamespace(EConfigNamespace ns, const CStr& name) const;
116 :
117 : /**
118 : * Retrieve a map of values corresponding to settings whose names begin
119 : * with the given prefix;
120 : * will search all namespaces from default up to the specified namespace.
121 : */
122 : std::map<CStr, CConfigValueSet> GetValuesWithPrefix(EConfigNamespace ns, const CStr& prefix) const;
123 :
124 : /**
125 : * Save a config value in the specified namespace. If the config variable
126 : * existed the value is replaced.
127 : */
128 : void SetValueString(EConfigNamespace ns, const CStr& name, const CStr& value);
129 :
130 : void SetValueBool(EConfigNamespace ns, const CStr& name, const bool value);
131 :
132 : void SetValueList(EConfigNamespace ns, const CStr& name, std::vector<CStr> values);
133 :
134 : /**
135 : * Remove a config value in the specified namespace.
136 : */
137 : bool RemoveValue(EConfigNamespace ns, const CStr& name);
138 :
139 : /**
140 : * Set the path to the config file used to populate the specified namespace
141 : * Note that this function does not actually load the config file. Use
142 : * the Reload() method if you want to read the config file at the same time.
143 : *
144 : * 'path': The path to the config file.
145 : */
146 : void SetConfigFile(EConfigNamespace ns, const VfsPath& path);
147 :
148 : /**
149 : * Reload the config file associated with the specified config namespace
150 : * (the last config file path set with SetConfigFile)
151 : *
152 : * Returns:
153 : * true: if the reload succeeded,
154 : * false: if the reload failed
155 : */
156 : bool Reload(EConfigNamespace);
157 :
158 : /**
159 : * Write the current state of the specified config namespace to the file
160 : * specified by 'path'
161 : *
162 : * Returns:
163 : * true: if the config namespace was successfully written to the file
164 : * false: if an error occurred
165 : */
166 : bool WriteFile(EConfigNamespace ns, const VfsPath& path) const;
167 :
168 : /**
169 : * Write the current state of the specified config namespace to the file
170 : * it was originally loaded from.
171 : *
172 : * Returns:
173 : * true: if the config namespace was successfully written to the file
174 : * false: if an error occurred
175 : */
176 : bool WriteFile(EConfigNamespace ns) const;
177 :
178 : /**
179 : * Write a config value to the file specified by 'path'
180 : *
181 : * Returns:
182 : * true: if the config value was successfully saved and written to the file
183 : * false: if an error occurred
184 : */
185 : bool WriteValueToFile(EConfigNamespace ns, const CStr& name, const CStr& value, const VfsPath& path);
186 :
187 : bool WriteValueToFile(EConfigNamespace ns, const CStr& name, const CStr& value);
188 :
189 : /**
190 : * Register a simple lambda that will be called anytime the value changes in any namespace
191 : * This is simple on purpose, the hook is responsible for checking if it should do something.
192 : * When RegisterHookAndCall is called, the hook is immediately triggered.
193 : * NB: CConfigDBHook will auto-unregister the hook when destroyed,
194 : * so you can use it to tie the lifetime of the hook to your object.
195 : * The hook will be deleted alongside ConfigDB anyways.
196 : */
197 : [[nodiscard]] CConfigDBHook RegisterHookAndCall(const CStr& name, std::function<void()> hook);
198 :
199 : void UnregisterHook(CConfigDBHook&& hook);
200 : void UnregisterHook(std::unique_ptr<CConfigDBHook> hook);
201 :
202 : private:
203 : std::array<std::map<CStr, CConfigValueSet>, CFG_LAST> m_Map;
204 : std::multimap<CStr, std::function<void()>> m_Hooks;
205 : std::array<VfsPath, CFG_LAST> m_ConfigFile;
206 : std::array<bool, CFG_LAST> m_HasChanges;
207 :
208 : mutable std::recursive_mutex m_Mutex;
209 : };
210 :
211 : class CConfigDBHook
212 : {
213 : friend class CConfigDB;
214 : public:
215 : CConfigDBHook() = delete;
216 : CConfigDBHook(const CConfigDBHook&) = delete;
217 : // Point the moved-from hook to end, which is checked for in UnregisterHook,
218 : // to avoid a double-erase error.
219 0 : CConfigDBHook(CConfigDBHook&& h) : m_ConfigDB(h.m_ConfigDB)
220 : {
221 0 : m_Ptr = std::move(h.m_Ptr);
222 0 : h.m_Ptr = m_ConfigDB.m_Hooks.end();
223 0 : }
224 : // Unregisters the hook. Must be called before the original ConfigDB gets deleted.
225 0 : ~CConfigDBHook()
226 0 : {
227 0 : m_ConfigDB.UnregisterHook(std::move(*this));
228 0 : }
229 : private:
230 0 : CConfigDBHook(CConfigDB& cdb, std::multimap<CStr, std::function<void()>>::iterator p)
231 0 : : m_ConfigDB(cdb), m_Ptr(p)
232 0 : {};
233 :
234 : std::multimap<CStr, std::function<void()>>::iterator m_Ptr;
235 : CConfigDB& m_ConfigDB;
236 : };
237 :
238 :
239 : // stores the value of the given key into <destination>. this quasi-template
240 : // convenience wrapper on top of GetValue simplifies user code
241 : #define CFG_GET_VAL(name, destination)\
242 : g_ConfigDB.GetValue(CFG_USER, name, destination)
243 :
244 : #endif // INCLUDED_CONFIGDB
|