1 | #ifndef GAME_EDITOR_EDITOR_SERVER_SETTINGS_H |
2 | #define GAME_EDITOR_EDITOR_SERVER_SETTINGS_H |
3 | |
4 | #include "component.h" |
5 | #include "editor_ui.h" |
6 | |
7 | #include <map> |
8 | #include <string> |
9 | #include <vector> |
10 | |
11 | class CEditor; |
12 | struct SMapSettingInt; |
13 | struct SMapSettingCommand; |
14 | struct IMapSetting; |
15 | class CLineInput; |
16 | |
17 | struct CEditorMapSetting |
18 | { |
19 | char m_aCommand[256]; |
20 | |
21 | CEditorMapSetting(const char *pCommand) |
22 | { |
23 | str_copy(dst&: m_aCommand, src: pCommand); |
24 | } |
25 | }; |
26 | |
27 | // A parsed map setting argument, storing the name and the type |
28 | // Used for validation and to display arguments names |
29 | struct SParsedMapSettingArg |
30 | { |
31 | char m_aName[32]; |
32 | char m_Type; |
33 | }; |
34 | |
35 | // An argument for the current setting |
36 | struct SCurrentSettingArg |
37 | { |
38 | char m_aValue[256]; // Value of the argument |
39 | float m_X; // The X position |
40 | size_t m_Start; // Start offset within the input string |
41 | size_t m_End; // End offset within the input string |
42 | bool m_Error; // If the argument is wrong or not |
43 | char m_ExpectedType; // The expected type |
44 | }; |
45 | |
46 | struct SPossibleValueMatch |
47 | { |
48 | const char *m_pValue; // Possible value string |
49 | int m_ArgIndex; // Argument for that possible value |
50 | const void *m_pData; // Generic pointer to pass specific data |
51 | }; |
52 | |
53 | struct SCommandParseError |
54 | { |
55 | char m_aMessage[256]; |
56 | int m_ArgIndex; |
57 | |
58 | enum EErrorType |
59 | { |
60 | ERROR_NONE = 0, |
61 | ERROR_TOO_MANY_ARGS, |
62 | ERROR_INVALID_VALUE, |
63 | ERROR_UNKNOWN_VALUE, |
64 | ERROR_INCOMPLETE, |
65 | ERROR_OUT_OF_RANGE, |
66 | ERROR_UNKNOWN |
67 | }; |
68 | EErrorType m_Type; |
69 | }; |
70 | |
71 | struct SInvalidSetting |
72 | { |
73 | enum |
74 | { |
75 | TYPE_INVALID = 1 << 0, |
76 | TYPE_DUPLICATE = 1 << 1 |
77 | }; |
78 | int m_Index; // Index of the command in the loaded map settings list |
79 | char m_aSetting[256]; // String of the setting |
80 | int m_Type; // Type of that invalid setting |
81 | int m_CollidingIndex; // The colliding line index in case type is TYPE_DUPLICATE |
82 | bool m_Unknown; // Is the command unknown |
83 | |
84 | struct SContext |
85 | { |
86 | bool m_Fixed; |
87 | bool m_Deleted; |
88 | bool m_Chosen; |
89 | } m_Context; |
90 | |
91 | SInvalidSetting(int Index, const char *pSetting, int Type, int CollidingIndex, bool Unknown) : |
92 | m_Index(Index), m_Type(Type), m_CollidingIndex(CollidingIndex), m_Unknown(Unknown), m_Context() |
93 | { |
94 | str_copy(dst&: m_aSetting, src: pSetting); |
95 | } |
96 | }; |
97 | |
98 | // -------------------------------------- |
99 | // Builder classes & methods to generate list of possible values |
100 | // easily for specific settings and arguments. |
101 | // It uses a container stored inside CMapSettingsBackend. |
102 | // Usage: |
103 | // CValuesBuilder Builder(&m_Container); |
104 | // // Either do it in one go: |
105 | // Builder("tune").Argument(0).Add("value_1").Add("value_2"); |
106 | // // Or reference the builder (useful when using in a loop): |
107 | // auto TuneBuilder = Builder("tune").Argument(0); |
108 | // TuneBuilder.Add("value_1"); |
109 | // TuneBuilder.Add("value_2"); |
110 | // // ... |
111 | |
112 | using TArgumentValuesList = std::vector<const char *>; // List of possible values |
113 | using TSettingValues = std::map<int, TArgumentValuesList>; // Possible values per argument |
114 | using TSettingsArgumentValues = std::map<std::string, TSettingValues>; // Possible values per argument, per command/setting name |
115 | |
116 | class CValuesBuilder; |
117 | class CSettingValuesBuilder; |
118 | class CArgumentValuesListBuilder |
119 | { |
120 | public: |
121 | CArgumentValuesListBuilder &Add(const char *pString) |
122 | { |
123 | m_pContainer->emplace_back(args&: pString); |
124 | return *this; |
125 | } |
126 | |
127 | private: |
128 | CArgumentValuesListBuilder(std::vector<const char *> *pContainer) : |
129 | m_pContainer(pContainer) {} |
130 | |
131 | std::vector<const char *> *m_pContainer; |
132 | friend class CSettingValuesBuilder; |
133 | }; |
134 | |
135 | class CSettingValuesBuilder |
136 | { |
137 | public: |
138 | CArgumentValuesListBuilder Argument(int Arg) const |
139 | { |
140 | return CArgumentValuesListBuilder(&(*m_pContainer)[Arg]); |
141 | } |
142 | |
143 | private: |
144 | CSettingValuesBuilder(TSettingValues *pContainer) : |
145 | m_pContainer(pContainer) {} |
146 | |
147 | friend class CValuesBuilder; |
148 | TSettingValues *m_pContainer; |
149 | }; |
150 | |
151 | class CValuesBuilder |
152 | { |
153 | public: |
154 | CValuesBuilder(TSettingsArgumentValues *pContainer) : |
155 | m_pContainer(pContainer) |
156 | { |
157 | } |
158 | |
159 | CSettingValuesBuilder operator()(const char *pSettingName) const |
160 | { |
161 | return CSettingValuesBuilder(&(*m_pContainer)[pSettingName]); |
162 | } |
163 | |
164 | private: |
165 | TSettingsArgumentValues *m_pContainer; |
166 | }; |
167 | |
168 | // -------------------------------------- |
169 | |
170 | struct SValueLoader |
171 | { |
172 | static void LoadTuneValues(const CSettingValuesBuilder &TuneBuilder); |
173 | static void LoadTuneZoneValues(const CSettingValuesBuilder &TuneZoneBuilder); |
174 | static void LoadMapBugs(const CSettingValuesBuilder &BugBuilder); |
175 | static void LoadArgumentTuneValues(CArgumentValuesListBuilder &&ArgBuilder); |
176 | }; |
177 | |
178 | enum class EValidationResult |
179 | { |
180 | VALID = 0, |
181 | ERROR, |
182 | INCOMPLETE, |
183 | UNKNOWN, |
184 | OUT_OF_RANGE, |
185 | }; |
186 | |
187 | enum class ECollisionCheckResult |
188 | { |
189 | ERROR, |
190 | REPLACE, |
191 | ADD |
192 | }; |
193 | |
194 | class CMapSettingsBackend : public CEditorComponent |
195 | { |
196 | typedef void (*FLoaderFunction)(const CSettingValuesBuilder &); |
197 | |
198 | public: // General methods |
199 | CMapSettingsBackend() = default; |
200 | |
201 | void Init(CEditor *pEditor) override; |
202 | bool OnInput(const IInput::CEvent &Event) override; |
203 | void OnUpdate() override; |
204 | void OnMapLoad() override; |
205 | |
206 | public: // Constraints methods |
207 | enum class EArgConstraint |
208 | { |
209 | DEFAULT = 0, |
210 | UNIQUE, |
211 | MULTIPLE, |
212 | }; |
213 | |
214 | EArgConstraint ArgConstraint(const char *pSettingName, int Arg) const |
215 | { |
216 | return m_ArgConstraintsPerCommand.at(k: pSettingName).at(k: Arg); |
217 | } |
218 | |
219 | public: // Backend methods |
220 | const std::vector<SParsedMapSettingArg> &ParsedArgs(const std::shared_ptr<IMapSetting> &pSetting) const |
221 | { |
222 | return m_ParsedCommandArgs.at(k: pSetting); |
223 | } |
224 | |
225 | public: // CContext |
226 | class CContext |
227 | { |
228 | static const ColorRGBA ms_ArgumentStringColor; |
229 | static const ColorRGBA ms_ArgumentNumberColor; |
230 | static const ColorRGBA ms_ArgumentUnknownColor; |
231 | static const ColorRGBA ; |
232 | static const ColorRGBA ms_ErrorColor; |
233 | |
234 | friend class CMapSettingsBackend; |
235 | |
236 | public: |
237 | bool CommandIsValid() const { return m_pCurrentSetting != nullptr; } |
238 | int CurrentArg() const { return m_CursorArgIndex; } |
239 | const char *CurrentArgName() const { return (!m_pCurrentSetting || m_CursorArgIndex < 0 || m_CursorArgIndex >= (int)m_pBackend->m_ParsedCommandArgs.at(k: m_pCurrentSetting).size()) ? nullptr : m_pBackend->m_ParsedCommandArgs.at(k: m_pCurrentSetting).at(n: m_CursorArgIndex).m_aName; } |
240 | float CurrentArgPos() const { return (m_CursorArgIndex < 0 || m_CursorArgIndex >= (int)m_vCurrentArgs.size()) ? 0 : m_vCurrentArgs[m_CursorArgIndex].m_X; } |
241 | size_t CurrentArgOffset() const { return m_CursorArgIndex == -1 ? 0 : m_vCurrentArgs[m_CursorArgIndex].m_Start; } |
242 | const char *CurrentArgValue() const { return m_CursorArgIndex == -1 ? m_aCommand : m_vCurrentArgs[m_CursorArgIndex].m_aValue; } |
243 | const std::vector<SPossibleValueMatch> &PossibleMatches() const { return m_vPossibleMatches; } |
244 | bool HasError() const { return m_Error.m_aMessage[0] != '\0'; } |
245 | size_t ErrorOffset() const { return m_Error.m_ArgIndex < 0 ? 0 : m_vCurrentArgs.at(n: m_Error.m_ArgIndex).m_Start; } |
246 | const char *Error() const { return m_Error.m_aMessage; } |
247 | int ArgCount() const { return (int)m_vCurrentArgs.size(); } |
248 | const SCurrentSettingArg &Arg(int Index) const { return m_vCurrentArgs.at(n: Index); } |
249 | const std::shared_ptr<IMapSetting> &Setting() const { return m_pCurrentSetting; } |
250 | CLineInput *LineInput() const { return m_pLineInput; } |
251 | void SetFontSize(float FontSize) { m_FontSize = FontSize; } |
252 | int () const { return m_CommentOffset; }; |
253 | |
254 | int CheckCollision(ECollisionCheckResult &Result) const; |
255 | int CheckCollision(const std::vector<CEditorMapSetting> &vSettings, ECollisionCheckResult &Result) const; |
256 | int CheckCollision(const char *pInputString, const std::vector<CEditorMapSetting> &vSettings, ECollisionCheckResult &Result) const; |
257 | |
258 | void Update(); |
259 | void UpdateFromString(const char *pStr); |
260 | bool UpdateCursor(bool Force = false); |
261 | void Reset(); |
262 | void GetCommandHelpText(char *pStr, int Length) const; |
263 | bool Valid() const; |
264 | void ColorArguments(std::vector<STextColorSplit> &vColorSplits) const; |
265 | |
266 | bool m_AllowUnknownCommands; |
267 | SEditBoxDropdownContext m_DropdownContext; |
268 | int m_CurrentCompletionIndex; |
269 | |
270 | private: // Methods |
271 | CContext(CMapSettingsBackend *pMaster, CLineInput *pLineinput) : |
272 | m_DropdownContext(), m_pLineInput(pLineinput), m_pBackend(pMaster) |
273 | { |
274 | m_AllowUnknownCommands = false; |
275 | Reset(); |
276 | } |
277 | |
278 | void ClearError(); |
279 | EValidationResult ValidateArg(int Index, const char *pArg); |
280 | void UpdatePossibleMatches(); |
281 | void ParseArgs(const char *pLineInputStr, const char *pStr); |
282 | bool OnInput(const IInput::CEvent &Event); |
283 | const char *InputString() const; |
284 | void UpdateCompositionString(); |
285 | |
286 | template<int N> |
287 | void FormatDisplayValue(const char *pValue, char (&aOut)[N]); |
288 | |
289 | private: // Fields |
290 | std::shared_ptr<IMapSetting> m_pCurrentSetting; // Current setting, can be nullptr in case of invalid setting name |
291 | std::vector<SCurrentSettingArg> m_vCurrentArgs; // Current parsed arguments from lineinput string |
292 | int m_CursorArgIndex; // The current argument the cursor is over |
293 | std::vector<SPossibleValueMatch> m_vPossibleMatches; // The current matches from cursor argument |
294 | size_t m_LastCursorOffset; // Last cursor offset |
295 | CLineInput *m_pLineInput; |
296 | char m_aCommand[128]; // The current command, not necessarily valid |
297 | SCommandParseError m_Error; // Error |
298 | int ; // Position of the comment, if there is one |
299 | |
300 | CMapSettingsBackend *m_pBackend; |
301 | std::string m_CompositionStringBuffer; |
302 | float m_FontSize; |
303 | }; |
304 | |
305 | CContext NewContext(CLineInput *pLineInput) |
306 | { |
307 | return CContext(this, pLineInput); |
308 | } |
309 | |
310 | private: // Loader methods |
311 | void LoadAllMapSettings(); |
312 | void LoadCommand(const char *pName, const char *pArgs, const char *pHelp); |
313 | void LoadSettingInt(const std::shared_ptr<SMapSettingInt> &pSetting); |
314 | void LoadSettingCommand(const std::shared_ptr<SMapSettingCommand> &pSetting); |
315 | void InitValueLoaders(); |
316 | void LoadPossibleValues(const CSettingValuesBuilder &Builder, const std::shared_ptr<IMapSetting> &pSetting); |
317 | void RegisterLoader(const char *pSettingName, const FLoaderFunction &pfnLoader); |
318 | void LoadConstraints(); |
319 | |
320 | static void PossibleConfigVariableCallback(const struct SConfigVariable *pVariable, void *pUserData); |
321 | |
322 | private: // Argument constraints |
323 | using TArgumentConstraints = std::map<int, EArgConstraint>; // Constraint per argument index |
324 | using TCommandArgumentConstraints = std::map<std::string, TArgumentConstraints>; // Constraints per command/setting name |
325 | |
326 | // Argument constraints builder |
327 | // Used to define arguments constraints for specific commands |
328 | // It uses a container stored in CMapSettingsBackend. |
329 | // Usage: |
330 | // CCommandArgumentConstraintBuilder Command(&m_Container); |
331 | // Command("tune", 2).Unique(0); // Defines argument 0 of command "tune" having 2 args as UNIQUE |
332 | // Command("tune_zone", 3).Multiple(0).Unique(1); |
333 | // // ^ Multiple() currently is only for readable purposes. It can be omited: |
334 | // // Command("tune_zone", 3).Unique(1); |
335 | // |
336 | |
337 | class CCommandArgumentConstraintBuilder; |
338 | |
339 | class CArgumentConstraintsBuilder |
340 | { |
341 | friend class CCommandArgumentConstraintBuilder; |
342 | |
343 | private: |
344 | CArgumentConstraintsBuilder(TArgumentConstraints *pContainer) : |
345 | m_pContainer(pContainer){}; |
346 | |
347 | TArgumentConstraints *m_pContainer; |
348 | |
349 | public: |
350 | CArgumentConstraintsBuilder &Multiple(int Arg) |
351 | { |
352 | // Define a multiple argument constraint |
353 | (*m_pContainer)[Arg] = EArgConstraint::MULTIPLE; |
354 | return *this; |
355 | } |
356 | |
357 | CArgumentConstraintsBuilder &Unique(int Arg) |
358 | { |
359 | // Define a unique argument constraint |
360 | (*m_pContainer)[Arg] = EArgConstraint::UNIQUE; |
361 | return *this; |
362 | } |
363 | }; |
364 | |
365 | class CCommandArgumentConstraintBuilder |
366 | { |
367 | public: |
368 | CCommandArgumentConstraintBuilder(TCommandArgumentConstraints *pContainer) : |
369 | m_pContainer(pContainer) {} |
370 | |
371 | CArgumentConstraintsBuilder operator()(const char *pSettingName, int ArgCount) |
372 | { |
373 | for(int i = 0; i < ArgCount; i++) |
374 | (*m_pContainer)[pSettingName][i] = EArgConstraint::DEFAULT; |
375 | return CArgumentConstraintsBuilder(&(*m_pContainer)[pSettingName]); |
376 | } |
377 | |
378 | private: |
379 | TCommandArgumentConstraints *m_pContainer; |
380 | }; |
381 | |
382 | TCommandArgumentConstraints m_ArgConstraintsPerCommand; |
383 | |
384 | private: // Backend fields |
385 | std::vector<std::shared_ptr<IMapSetting>> m_vpMapSettings; |
386 | std::map<std::shared_ptr<IMapSetting>, std::vector<SParsedMapSettingArg>> m_ParsedCommandArgs; // Parsed available settings arguments, used for validation |
387 | TSettingsArgumentValues m_PossibleValuesPerCommand; |
388 | std::map<std::string, FLoaderFunction> m_LoaderFunctions; |
389 | |
390 | static CContext *ms_pActiveContext; |
391 | |
392 | friend class CEditor; |
393 | |
394 | private: // Map settings validation on load |
395 | struct SLoadedMapSettings |
396 | { |
397 | std::vector<SInvalidSetting> m_vSettingsInvalid; |
398 | std::vector<CEditorMapSetting> m_vSettingsValid; |
399 | std::map<int, std::vector<int>> m_SettingsDuplicate; |
400 | |
401 | SLoadedMapSettings() : |
402 | m_vSettingsInvalid(), m_vSettingsValid(), m_SettingsDuplicate() |
403 | { |
404 | } |
405 | |
406 | void Reset() |
407 | { |
408 | m_vSettingsInvalid.clear(); |
409 | m_vSettingsValid.clear(); |
410 | m_SettingsDuplicate.clear(); |
411 | } |
412 | |
413 | } m_LoadedMapSettings; |
414 | }; |
415 | |
416 | #endif |
417 | |