1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3
4#include <base/dbg.h>
5#include <base/io.h>
6#include <base/log.h>
7#include <base/str.h>
8
9#include <engine/config.h>
10#include <engine/console.h>
11#include <engine/shared/config.h>
12#include <engine/shared/console.h>
13#include <engine/shared/protocol.h>
14#include <engine/storage.h>
15
16CConfig g_Config;
17
18// ----------------------- Config Variables
19
20static void EscapeParam(char *pDst, const char *pSrc, int Size)
21{
22 str_escape(dst: &pDst, src: pSrc, end: pDst + Size);
23}
24
25void SConfigVariable::ExecuteLine(const char *pLine) const
26{
27 m_pConsole->ExecuteLine(pStr: pLine, ClientId: (m_Flags & CFGFLAG_GAME) != 0 ? IConsole::CLIENT_ID_GAME : IConsole::CLIENT_ID_UNSPECIFIED);
28}
29
30bool SConfigVariable::CheckReadOnly() const
31{
32 if(!m_ReadOnly)
33 return false;
34 char aBuf[IConsole::CMDLINE_LENGTH + 64];
35 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "The config variable '%s' cannot be changed right now.", m_pScriptName);
36 m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "config", pStr: aBuf);
37 return true;
38}
39
40// -----
41
42void SIntConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUserData)
43{
44 SIntConfigVariable *pData = static_cast<SIntConfigVariable *>(pUserData);
45
46 if(pResult->NumArguments())
47 {
48 if(pData->CheckReadOnly())
49 return;
50
51 int Value = pResult->GetInteger(Index: 0);
52
53 // do clamping
54 if(pData->m_Min != pData->m_Max)
55 {
56 if(Value < pData->m_Min)
57 Value = pData->m_Min;
58 if(pData->m_Max != 0 && Value > pData->m_Max)
59 Value = pData->m_Max;
60 }
61
62 *pData->m_pVariable = Value;
63 if(pResult->m_ClientId != IConsole::CLIENT_ID_GAME)
64 pData->m_OldValue = Value;
65 }
66 else
67 {
68 char aBuf[32];
69 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Value: %d", *pData->m_pVariable);
70 pData->m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "config", pStr: aBuf);
71 }
72}
73
74void SIntConfigVariable::Register()
75{
76 m_pConsole->Register(pName: m_pScriptName, pParams: "?i", Flags: m_Flags, pfnFunc: CommandCallback, pUser: this, pHelp: m_pHelp);
77}
78
79bool SIntConfigVariable::IsDefault() const
80{
81 return *m_pVariable == m_Default;
82}
83
84void SIntConfigVariable::Serialize(char *pOut, size_t Size, int Value) const
85{
86 str_format(buffer: pOut, buffer_size: Size, format: "%s %i", m_pScriptName, Value);
87}
88
89void SIntConfigVariable::Serialize(char *pOut, size_t Size) const
90{
91 Serialize(pOut, Size, Value: *m_pVariable);
92}
93
94void SIntConfigVariable::SetValue(int Value)
95{
96 if(CheckReadOnly())
97 return;
98 char aBuf[IConsole::CMDLINE_LENGTH];
99 Serialize(pOut: aBuf, Size: sizeof(aBuf), Value);
100 ExecuteLine(pLine: aBuf);
101}
102
103void SIntConfigVariable::ResetToDefault()
104{
105 SetValue(m_Default);
106}
107
108void SIntConfigVariable::ResetToOld()
109{
110 *m_pVariable = m_OldValue;
111}
112
113// -----
114
115void SColorConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUserData)
116{
117 SColorConfigVariable *pData = static_cast<SColorConfigVariable *>(pUserData);
118 char aBuf[IConsole::CMDLINE_LENGTH + 64];
119 if(pResult->NumArguments())
120 {
121 if(pData->CheckReadOnly())
122 return;
123
124 const auto Color = pResult->GetColor(Index: 0, DarkestLighting: pData->m_DarkestLighting);
125 const unsigned Value = Color.Pack(Darkest: pData->m_DarkestLighting, Alpha: pData->m_Alpha);
126
127 *pData->m_pVariable = Value;
128 if(pResult->m_ClientId != IConsole::CLIENT_ID_GAME)
129 pData->m_OldValue = Value;
130 }
131 else
132 {
133 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Value: %u", *pData->m_pVariable);
134 pData->m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "config", pStr: aBuf);
135
136 const ColorHSLA Hsla = ColorHSLA(*pData->m_pVariable, true).UnclampLighting(Darkest: pData->m_DarkestLighting);
137 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "H: %d°, S: %d%%, L: %d%%", round_to_int(f: Hsla.h * 360), round_to_int(f: Hsla.s * 100), round_to_int(f: Hsla.l * 100));
138 pData->m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "config", pStr: aBuf);
139
140 const ColorRGBA Rgba = color_cast<ColorRGBA>(hsl: Hsla);
141 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "R: %d, G: %d, B: %d, #%06X", round_to_int(f: Rgba.r * 255), round_to_int(f: Rgba.g * 255), round_to_int(f: Rgba.b * 255), Rgba.Pack(Alpha: false));
142 pData->m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "config", pStr: aBuf);
143
144 if(pData->m_Alpha)
145 {
146 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "A: %d%%", round_to_int(f: Hsla.a * 100));
147 pData->m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "config", pStr: aBuf);
148 }
149 }
150}
151
152void SColorConfigVariable::Register()
153{
154 m_pConsole->Register(pName: m_pScriptName, pParams: "?c", Flags: m_Flags, pfnFunc: CommandCallback, pUser: this, pHelp: m_pHelp);
155}
156
157bool SColorConfigVariable::IsDefault() const
158{
159 return *m_pVariable == m_Default;
160}
161
162void SColorConfigVariable::Serialize(char *pOut, size_t Size, unsigned Value) const
163{
164 str_format(buffer: pOut, buffer_size: Size, format: "%s %u", m_pScriptName, Value);
165}
166
167void SColorConfigVariable::Serialize(char *pOut, size_t Size) const
168{
169 Serialize(pOut, Size, Value: *m_pVariable);
170}
171
172void SColorConfigVariable::SetValue(unsigned Value)
173{
174 if(CheckReadOnly())
175 return;
176 char aBuf[IConsole::CMDLINE_LENGTH];
177 Serialize(pOut: aBuf, Size: sizeof(aBuf), Value);
178 ExecuteLine(pLine: aBuf);
179}
180
181void SColorConfigVariable::ResetToDefault()
182{
183 SetValue(m_Default);
184}
185
186void SColorConfigVariable::ResetToOld()
187{
188 *m_pVariable = m_OldValue;
189}
190
191// -----
192
193SStringConfigVariable::SStringConfigVariable(IConsole *pConsole, const char *pScriptName, EVariableType Type, int Flags, const char *pHelp, char *pStr, const char *pDefault, size_t MaxSize, char *pOldValue) :
194 SConfigVariable(pConsole, pScriptName, Type, Flags, pHelp),
195 m_pStr(pStr),
196 m_pDefault(pDefault),
197 m_MaxSize(MaxSize),
198 m_pOldValue(pOldValue)
199{
200 str_copy(dst: m_pStr, src: m_pDefault, dst_size: m_MaxSize);
201 str_copy(dst: m_pOldValue, src: m_pDefault, dst_size: m_MaxSize);
202}
203
204void SStringConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUserData)
205{
206 SStringConfigVariable *pData = static_cast<SStringConfigVariable *>(pUserData);
207
208 if(pResult->NumArguments())
209 {
210 if(pData->CheckReadOnly())
211 return;
212
213 const char *pString = pResult->GetString(Index: 0);
214 str_copy(dst: pData->m_pStr, src: pString, dst_size: pData->m_MaxSize);
215
216 if(pResult->m_ClientId != IConsole::CLIENT_ID_GAME)
217 str_copy(dst: pData->m_pOldValue, src: pData->m_pStr, dst_size: pData->m_MaxSize);
218 }
219 else
220 {
221 char aBuf[1024];
222 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Value: %s", pData->m_pStr);
223 pData->m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "config", pStr: aBuf);
224 }
225}
226
227void SStringConfigVariable::Register()
228{
229 m_pConsole->Register(pName: m_pScriptName, pParams: "?r", Flags: m_Flags, pfnFunc: CommandCallback, pUser: this, pHelp: m_pHelp);
230}
231
232bool SStringConfigVariable::IsDefault() const
233{
234 return str_comp(a: m_pStr, b: m_pDefault) == 0;
235}
236
237void SStringConfigVariable::Serialize(char *pOut, size_t Size, const char *pValue) const
238{
239 str_copy(dst: pOut, src: m_pScriptName, dst_size: Size);
240 str_append(dst: pOut, src: " \"", dst_size: Size);
241 const int OutLen = str_length(str: pOut);
242 EscapeParam(pDst: pOut + OutLen, pSrc: pValue, Size: Size - OutLen - 1); // -1 to ensure space for final quote
243 str_append(dst: pOut, src: "\"", dst_size: Size);
244}
245
246void SStringConfigVariable::Serialize(char *pOut, size_t Size) const
247{
248 Serialize(pOut, Size, pValue: m_pStr);
249}
250
251void SStringConfigVariable::SetValue(const char *pValue)
252{
253 if(CheckReadOnly())
254 return;
255 char aBuf[2048];
256 Serialize(pOut: aBuf, Size: sizeof(aBuf), pValue);
257 ExecuteLine(pLine: aBuf);
258}
259
260void SStringConfigVariable::ResetToDefault()
261{
262 SetValue(m_pDefault);
263}
264
265void SStringConfigVariable::ResetToOld()
266{
267 str_copy(dst: m_pStr, src: m_pOldValue, dst_size: m_MaxSize);
268}
269
270// ----------------------- Config Manager
271CConfigManager::CConfigManager()
272{
273 m_pConsole = nullptr;
274 m_pStorage = nullptr;
275 m_ConfigFile = nullptr;
276 m_Failed = false;
277}
278
279void CConfigManager::Init()
280{
281 m_pConsole = Kernel()->RequestInterface<IConsole>();
282 m_pStorage = Kernel()->RequestInterface<IStorage>();
283
284 const auto &&AddVariable = [this](SConfigVariable *pVariable) {
285 m_vpAllVariables.push_back(x: pVariable);
286 if((pVariable->m_Flags & CFGFLAG_GAME) != 0)
287 m_vpGameVariables.push_back(x: pVariable);
288 pVariable->Register();
289 };
290
291#define MACRO_CONFIG_INT(Name, ScriptName, Def, Min, Max, Flags, Desc) \
292 { \
293 const char *pScriptName = #ScriptName; \
294 dbg_assert(Min == 0 || Max == 0 || Min < Max, "MACRO_CONFIG_INT(%s): minimum (%d) must be less than maximum (%d)", pScriptName, Min, Max); \
295 dbg_assert((Min == 0 || Def >= Min) && (Max == 0 || Def <= Max), "MACRO_CONFIG_INT(%s): default (%d) must be in range of minimum (%d) and maximum (%d)", pScriptName, Def, Min, Max); \
296 char aHelp[512]; \
297 size_t HelpSize; \
298 if(Min == 0 && Max == 0) \
299 HelpSize = str_format(aHelp, sizeof(aHelp), "%s (default: %d)", Desc, Def); \
300 else if(Max == 0) \
301 HelpSize = str_format(aHelp, sizeof(aHelp), "%s (default: %d, min: %d)", Desc, Def, Min); \
302 else \
303 HelpSize = str_format(aHelp, sizeof(aHelp), "%s (default: %d, min: %d, max: %d)", Desc, Def, Min, Max); \
304 dbg_assert(HelpSize < sizeof(aHelp) - UTF8_BYTE_LENGTH - 1, "MACRO_CONFIG_INT(%s): help text possibly truncated. Increase size of aHelp.", pScriptName); \
305 AddVariable(m_ConfigHeap.Allocate<SIntConfigVariable>( \
306 m_pConsole, pScriptName, SConfigVariable::VAR_INT, Flags, m_ConfigHeap.StoreString(aHelp), &g_Config.m_##Name, Def, Min, Max)); \
307 }
308
309#define MACRO_CONFIG_COL(Name, ScriptName, Def, Flags, Desc) \
310 { \
311 const char *pScriptName = #ScriptName; \
312 const bool Alpha = ((Flags) & CFGFLAG_COLALPHA) != 0; \
313 char aHelp[512]; \
314 const size_t HelpSize = str_format(aHelp, sizeof(aHelp), "%s (default: $%0*X)", Desc, Alpha ? 8 : 6, color_cast<ColorRGBA>(ColorHSLA(Def, Alpha)).Pack(Alpha)); \
315 dbg_assert(HelpSize < sizeof(aHelp) - UTF8_BYTE_LENGTH - 1, "MACRO_CONFIG_COL(%s): help text possibly truncated. Increase size of aHelp.", pScriptName); \
316 AddVariable(m_ConfigHeap.Allocate<SColorConfigVariable>( \
317 m_pConsole, pScriptName, SConfigVariable::VAR_COLOR, Flags, m_ConfigHeap.StoreString(aHelp), &g_Config.m_##Name, Def)); \
318 }
319
320#define MACRO_CONFIG_STR(Name, ScriptName, Len, Def, Flags, Desc) \
321 { \
322 const char *pScriptName = #ScriptName; \
323 char aHelp[512]; \
324 const size_t HelpSize = str_format(aHelp, sizeof(aHelp), "%s (default: \"%s\", max length: %d)", Desc, Def, Len - 1); \
325 dbg_assert(HelpSize < sizeof(aHelp) - UTF8_BYTE_LENGTH - 1, "MACRO_CONFIG_STR(%s): help text possibly truncated. Increase size of aHelp.", pScriptName); \
326 char *pOldValue = static_cast<char *>(m_ConfigHeap.Allocate(Len)); \
327 AddVariable(m_ConfigHeap.Allocate<SStringConfigVariable>( \
328 m_pConsole, pScriptName, SConfigVariable::VAR_STRING, Flags, m_ConfigHeap.StoreString(aHelp), g_Config.m_##Name, Def, Len, pOldValue)); \
329 }
330
331#include "config_variables.h"
332
333#undef MACRO_CONFIG_INT
334#undef MACRO_CONFIG_COL
335#undef MACRO_CONFIG_STR
336
337 m_pConsole->Register(pName: "reset", pParams: "s[config-name]", Flags: CFGFLAG_SERVER | CFGFLAG_CLIENT | CFGFLAG_STORE, pfnFunc: Con_Reset, pUser: this, pHelp: "Reset a config to its default value");
338 m_pConsole->Register(pName: "toggle", pParams: "s[config-option] s[value 1] s[value 2]", Flags: CFGFLAG_SERVER | CFGFLAG_CLIENT, pfnFunc: Con_Toggle, pUser: this, pHelp: "Toggle config value");
339 m_pConsole->Register(pName: "+toggle", pParams: "s[config-option] s[value 1] s[value 2]", Flags: CFGFLAG_CLIENT, pfnFunc: Con_ToggleStroke, pUser: this, pHelp: "Toggle config value via keypress");
340}
341
342void CConfigManager::Reset(const char *pScriptName)
343{
344 for(SConfigVariable *pVariable : m_vpAllVariables)
345 {
346 if((pVariable->m_Flags & m_pConsole->FlagMask()) != 0 && str_comp(a: pScriptName, b: pVariable->m_pScriptName) == 0)
347 {
348 pVariable->ResetToDefault();
349 return;
350 }
351 }
352
353 char aBuf[IConsole::CMDLINE_LENGTH + 32];
354 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Invalid command: '%s'.", pScriptName);
355 m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "config", pStr: aBuf);
356}
357
358void CConfigManager::ResetGameSettings()
359{
360 for(SConfigVariable *pVariable : m_vpGameVariables)
361 {
362 pVariable->ResetToOld();
363 }
364}
365
366void CConfigManager::SetReadOnly(const char *pScriptName, bool ReadOnly)
367{
368 for(SConfigVariable *pVariable : m_vpAllVariables)
369 {
370 if(str_comp(a: pScriptName, b: pVariable->m_pScriptName) == 0)
371 {
372 pVariable->m_ReadOnly = ReadOnly;
373 return;
374 }
375 }
376 dbg_assert_failed("Invalid command for SetReadOnly: '%s'", pScriptName);
377}
378
379void CConfigManager::SetGameSettingsReadOnly(bool ReadOnly)
380{
381 for(SConfigVariable *pVariable : m_vpGameVariables)
382 {
383 pVariable->m_ReadOnly = ReadOnly;
384 }
385}
386
387bool CConfigManager::Save()
388{
389 if(!m_pStorage || !g_Config.m_ClSaveSettings)
390 return true;
391
392 char aConfigFileTmp[IO_MAX_PATH_LENGTH];
393 m_ConfigFile = m_pStorage->OpenFile(pFilename: IStorage::FormatTmpPath(aBuf: aConfigFileTmp, BufSize: sizeof(aConfigFileTmp), CONFIG_FILE), Flags: IOFLAG_WRITE, Type: IStorage::TYPE_SAVE);
394
395 if(!m_ConfigFile)
396 {
397 log_error("config", "ERROR: opening %s failed", aConfigFileTmp);
398 return false;
399 }
400
401 m_Failed = false;
402
403 char aLineBuf[2048];
404 for(const SConfigVariable *pVariable : m_vpAllVariables)
405 {
406 if((pVariable->m_Flags & CFGFLAG_SAVE) != 0 && !pVariable->IsDefault())
407 {
408 pVariable->Serialize(pOut: aLineBuf, Size: sizeof(aLineBuf));
409 WriteLine(pLine: aLineBuf);
410 }
411 }
412
413 for(const auto &Callback : m_vCallbacks)
414 {
415 Callback.m_pfnFunc(this, Callback.m_pUserData);
416 }
417
418 for(const char *pCommand : m_vpUnknownCommands)
419 {
420 WriteLine(pLine: pCommand);
421 }
422
423 if(m_Failed)
424 {
425 log_error("config", "ERROR: writing to %s failed", aConfigFileTmp);
426 }
427
428 if(io_sync(io: m_ConfigFile) != 0)
429 {
430 m_Failed = true;
431 log_error("config", "ERROR: synchronizing %s failed", aConfigFileTmp);
432 }
433
434 if(io_close(io: m_ConfigFile) != 0)
435 {
436 m_Failed = true;
437 log_error("config", "ERROR: closing %s failed", aConfigFileTmp);
438 }
439
440 m_ConfigFile = nullptr;
441
442 if(m_Failed)
443 {
444 return false;
445 }
446
447 if(!m_pStorage->RenameFile(pOldFilename: aConfigFileTmp, CONFIG_FILE, Type: IStorage::TYPE_SAVE))
448 {
449 log_error("config", "ERROR: renaming %s to " CONFIG_FILE " failed", aConfigFileTmp);
450 return false;
451 }
452
453 log_info("config", "saved to " CONFIG_FILE);
454 return true;
455}
456
457void CConfigManager::RegisterCallback(SAVECALLBACKFUNC pfnFunc, void *pUserData)
458{
459 m_vCallbacks.emplace_back(args&: pfnFunc, args&: pUserData);
460}
461
462void CConfigManager::WriteLine(const char *pLine)
463{
464 if(!m_ConfigFile ||
465 io_write(io: m_ConfigFile, buffer: pLine, size: str_length(str: pLine)) != static_cast<unsigned>(str_length(str: pLine)) ||
466 !io_write_newline(io: m_ConfigFile))
467 {
468 m_Failed = true;
469 }
470}
471
472void CConfigManager::StoreUnknownCommand(const char *pCommand)
473{
474 m_vpUnknownCommands.push_back(x: m_ConfigHeap.StoreString(pSrc: pCommand));
475}
476
477void CConfigManager::PossibleConfigVariables(const char *pStr, int FlagMask, POSSIBLECFGFUNC pfnCallback, void *pUserData)
478{
479 for(const SConfigVariable *pVariable : m_vpAllVariables)
480 {
481 if(pVariable->m_Flags & FlagMask)
482 {
483 if(str_find_nocase(haystack: pVariable->m_pScriptName, needle: pStr))
484 {
485 pfnCallback(pVariable, pUserData);
486 }
487 }
488 }
489}
490
491void CConfigManager::Con_Reset(IConsole::IResult *pResult, void *pUserData)
492{
493 static_cast<CConfigManager *>(pUserData)->Reset(pScriptName: pResult->GetString(Index: 0));
494}
495
496void CConfigManager::Con_Toggle(IConsole::IResult *pResult, void *pUserData)
497{
498 CConfigManager *pConfigManager = static_cast<CConfigManager *>(pUserData);
499 IConsole *pConsole = pConfigManager->m_pConsole;
500
501 const char *pScriptName = pResult->GetString(Index: 0);
502 for(SConfigVariable *pVariable : pConfigManager->m_vpAllVariables)
503 {
504 if((pVariable->m_Flags & pConsole->FlagMask()) == 0 ||
505 str_comp(a: pScriptName, b: pVariable->m_pScriptName) != 0)
506 {
507 continue;
508 }
509
510 if(pVariable->m_Type == SConfigVariable::VAR_INT)
511 {
512 SIntConfigVariable *pIntVariable = static_cast<SIntConfigVariable *>(pVariable);
513 const bool EqualToFirst = *pIntVariable->m_pVariable == pResult->GetInteger(Index: 1);
514 pIntVariable->SetValue(pResult->GetInteger(Index: EqualToFirst ? 2 : 1));
515 }
516 else if(pVariable->m_Type == SConfigVariable::VAR_COLOR)
517 {
518 SColorConfigVariable *pColorVariable = static_cast<SColorConfigVariable *>(pVariable);
519 const bool EqualToFirst = *pColorVariable->m_pVariable == pResult->GetColor(Index: 1, DarkestLighting: pColorVariable->m_DarkestLighting).Pack(Darkest: pColorVariable->m_DarkestLighting, Alpha: pColorVariable->m_Alpha);
520 const std::optional<ColorHSLA> Value = pResult->GetColor(Index: EqualToFirst ? 2 : 1, DarkestLighting: pColorVariable->m_DarkestLighting);
521 pColorVariable->SetValue(Value.value_or(u: ColorHSLA(0, 0, 0)).Pack(Darkest: pColorVariable->m_DarkestLighting, Alpha: pColorVariable->m_Alpha));
522 }
523 else if(pVariable->m_Type == SConfigVariable::VAR_STRING)
524 {
525 SStringConfigVariable *pStringVariable = static_cast<SStringConfigVariable *>(pVariable);
526 const bool EqualToFirst = str_comp(a: pStringVariable->m_pStr, b: pResult->GetString(Index: 1)) == 0;
527 pStringVariable->SetValue(pResult->GetString(Index: EqualToFirst ? 2 : 1));
528 }
529 return;
530 }
531
532 char aBuf[IConsole::CMDLINE_LENGTH + 32];
533 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Invalid command: '%s'.", pScriptName);
534 pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "config", pStr: aBuf);
535}
536
537void CConfigManager::Con_ToggleStroke(IConsole::IResult *pResult, void *pUserData)
538{
539 CConfigManager *pConfigManager = static_cast<CConfigManager *>(pUserData);
540 IConsole *pConsole = pConfigManager->m_pConsole;
541
542 const char *pScriptName = pResult->GetString(Index: 1);
543 for(SConfigVariable *pVariable : pConfigManager->m_vpAllVariables)
544 {
545 if((pVariable->m_Flags & pConsole->FlagMask()) == 0 ||
546 pVariable->m_Type != SConfigVariable::VAR_INT ||
547 str_comp(a: pScriptName, b: pVariable->m_pScriptName) != 0)
548 {
549 continue;
550 }
551
552 SIntConfigVariable *pIntVariable = static_cast<SIntConfigVariable *>(pVariable);
553 pIntVariable->SetValue(pResult->GetInteger(Index: 0) == 0 ? pResult->GetInteger(Index: 3) : pResult->GetInteger(Index: 2));
554 return;
555 }
556
557 char aBuf[IConsole::CMDLINE_LENGTH + 32];
558 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Invalid command: '%s'.", pScriptName);
559 pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "config", pStr: aBuf);
560}
561
562IConfigManager *CreateConfigManager() { return new CConfigManager; }
563