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
11class CEditor;
12struct SMapSettingInt;
13struct SMapSettingCommand;
14struct IMapSetting;
15class CLineInput;
16
17struct 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
29struct SParsedMapSettingArg
30{
31 char m_aName[32];
32 char m_Type;
33};
34
35// An argument for the current setting
36struct 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
46struct 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
53struct 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
71struct 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
112using TArgumentValuesList = std::vector<const char *>; // List of possible values
113using TSettingValues = std::map<int, TArgumentValuesList>; // Possible values per argument
114using TSettingsArgumentValues = std::map<std::string, TSettingValues>; // Possible values per argument, per command/setting name
115
116class CValuesBuilder;
117class CSettingValuesBuilder;
118class CArgumentValuesListBuilder
119{
120public:
121 CArgumentValuesListBuilder &Add(const char *pString)
122 {
123 m_pContainer->emplace_back(args&: pString);
124 return *this;
125 }
126
127private:
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
135class CSettingValuesBuilder
136{
137public:
138 CArgumentValuesListBuilder Argument(int Arg) const
139 {
140 return CArgumentValuesListBuilder(&(*m_pContainer)[Arg]);
141 }
142
143private:
144 CSettingValuesBuilder(TSettingValues *pContainer) :
145 m_pContainer(pContainer) {}
146
147 friend class CValuesBuilder;
148 TSettingValues *m_pContainer;
149};
150
151class CValuesBuilder
152{
153public:
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
164private:
165 TSettingsArgumentValues *m_pContainer;
166};
167
168// --------------------------------------
169
170struct 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
178enum class EValidationResult
179{
180 VALID = 0,
181 ERROR,
182 INCOMPLETE,
183 UNKNOWN,
184 OUT_OF_RANGE,
185};
186
187enum class ECollisionCheckResult
188{
189 ERROR,
190 REPLACE,
191 ADD
192};
193
194class CMapSettingsBackend : public CEditorComponent
195{
196 typedef void (*FLoaderFunction)(const CSettingValuesBuilder &);
197
198public: // 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
206public: // 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
219public: // 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
225public: // 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 ms_CommentColor;
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 CommentOffset() 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 m_CommentOffset; // 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
310private: // 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
322private: // 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
384private: // 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
394private: // 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