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