XConsole.cpp 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. // Description : implementation of the CXConsole class.
  9. #include "CrySystem_precompiled.h"
  10. #include "XConsole.h"
  11. #include "XConsoleVariable.h"
  12. #include "System.h"
  13. #include "ConsoleBatchFile.h"
  14. #include <IRenderer.h>
  15. #include <ISystem.h>
  16. #include <ILog.h>
  17. #include <IFont.h>
  18. #include <ITexture.h>
  19. #include "ConsoleHelpGen.h" // CConsoleHelpGen
  20. #include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
  21. #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
  22. #include <AzCore/std/string/conversions.h>
  23. #include <AzCore/std/algorithm.h>
  24. #include <AzCore/Serialization/Locale.h>
  25. #include <AzCore/Time/ITime.h>
  26. //#define DEFENCE_CVAR_HASH_LOGGING
  27. // s should point to a buffer at least 65 chars long
  28. inline void BitsAlpha64(uint64 n, char* s)
  29. {
  30. for (int i = 0; n != 0; n >>= 1, i++)
  31. {
  32. if (n & 1)
  33. {
  34. *s++ = i < 32 ? static_cast<char>(i + 'z' - 31) : static_cast<char>(i + 'Z' - 63);
  35. }
  36. }
  37. *s++ = '\0';
  38. }
  39. static inline void AssertName([[maybe_unused]] const char* szName)
  40. {
  41. #ifdef _DEBUG
  42. assert(szName);
  43. // test for good console variable / command name
  44. const char* p = szName;
  45. bool bFirstChar = true;
  46. while (*p)
  47. {
  48. assert((*p >= 'a' && *p <= 'z')
  49. || (*p >= 'A' && *p <= 'Z')
  50. || (*p >= '0' && *p <= '9' && !bFirstChar)
  51. || *p == '_'
  52. || *p == '.');
  53. ++p;
  54. bFirstChar = false;
  55. }
  56. #endif
  57. }
  58. void ResetCVars(IConsoleCmdArgs*)
  59. {
  60. if (gEnv->pSystem)
  61. {
  62. CXConsole* pConsole = static_cast<CXConsole*>(gEnv->pSystem->GetIConsole());
  63. if (pConsole)
  64. {
  65. pConsole->ResetCVarsToDefaults();
  66. }
  67. }
  68. }
  69. //////////////////////////////////////////////////////////////////////////
  70. // user defined comparison - for nicer printout
  71. inline int GetCharPrio(char x)
  72. {
  73. if (x >= 'a' && x <= 'z')
  74. {
  75. x += 'A' - 'a'; // make upper case
  76. }
  77. if (x == '_')
  78. {
  79. return 300;
  80. }
  81. else
  82. {
  83. return x;
  84. }
  85. }
  86. void Command_SetWaitSeconds(IConsoleCmdArgs* pCmd)
  87. {
  88. CXConsole* pConsole = (CXConsole*)gEnv->pConsole;
  89. if (pCmd->GetArgCount() > 1)
  90. {
  91. // console commands are interpreted in the invarant locale as they come from cfg files which need to be
  92. // portable.
  93. AZ::Locale::ScopedSerializationLocale scopedLocale;
  94. pConsole->m_waitSeconds.SetSeconds(atof(pCmd->GetArg(1)));
  95. const AZ::TimeMs elaspedTimeMs = AZ::GetRealElapsedTimeMs();
  96. pConsole->m_waitSeconds += CTimeValue(AZ::TimeMsToSecondsDouble(elaspedTimeMs));
  97. }
  98. }
  99. void Command_SetWaitFrames(IConsoleCmdArgs* pCmd)
  100. {
  101. CXConsole* pConsole = (CXConsole*)gEnv->pConsole;
  102. if (pCmd->GetArgCount() > 1)
  103. {
  104. pConsole->m_waitFrames = max(0, atoi(pCmd->GetArg(1)));
  105. }
  106. }
  107. void ConsoleShow(IConsoleCmdArgs*)
  108. {
  109. gEnv->pConsole->ShowConsole(true);
  110. }
  111. void ConsoleHide(IConsoleCmdArgs*)
  112. {
  113. gEnv->pConsole->ShowConsole(false);
  114. }
  115. void Bind(IConsoleCmdArgs* cmdArgs)
  116. {
  117. if (cmdArgs->GetArgCount() >= 3)
  118. {
  119. AZStd::string arg;
  120. for (int i = 2; i < cmdArgs->GetArgCount(); ++i)
  121. {
  122. arg += cmdArgs->GetArg(i);
  123. arg += " ";
  124. }
  125. gEnv->pConsole->CreateKeyBind(cmdArgs->GetArg(1), arg.c_str());
  126. }
  127. }
  128. //////////////////////////////////////////////////////////////////////////
  129. int CXConsole::con_display_last_messages = 0;
  130. int CXConsole::con_line_buffer_size = 500;
  131. int CXConsole::con_showonload = 0;
  132. int CXConsole::con_debug = 0;
  133. int CXConsole::con_restricted = 0;
  134. //////////////////////////////////////////////////////////////////////
  135. // Construction/Destruction
  136. //////////////////////////////////////////////////////////////////////
  137. CXConsole::CXConsole()
  138. {
  139. m_fRepeatTimer = 0;
  140. m_pSysDeactivateConsole = 0;
  141. m_pImage = NULL;
  142. m_nCursorPos = 0;
  143. m_nScrollPos = 0;
  144. m_nScrollMax = 300;
  145. m_nTempScrollMax = m_nScrollMax;
  146. m_nScrollLine = 0;
  147. m_nHistoryPos = -1;
  148. m_nTabCount = 0;
  149. m_bConsoleActive = false;
  150. m_bActivationKeyEnable = true;
  151. m_bIsProcessingGroup = false;
  152. m_bIsConsoleKeyPressed = false;
  153. m_sdScrollDir = sdNONE;
  154. m_pSystem = NULL;
  155. m_bDrawCursor = true;
  156. m_fCursorBlinkTimer = 0;
  157. m_nCheatHashRangeFirst = 0;
  158. m_nCheatHashRangeLast = 0;
  159. m_bCheatHashDirty = false;
  160. m_nCheatHash = 0;
  161. m_bStaticBackground = false;
  162. m_nProgress = 0;
  163. m_nProgressRange = 0;
  164. m_nLoadingBackTexID = 0;
  165. m_deferredExecution = false;
  166. m_waitFrames = 0;
  167. m_waitSeconds = 0.0f;
  168. m_blockCounter = 0;
  169. m_intWrappers.reserve(128);
  170. m_floatWrappers.reserve(128);
  171. m_stringWrappers.reserve(128);
  172. AzFramework::ConsoleRequestBus::Handler::BusConnect();
  173. AzFramework::CommandRegistrationBus::Handler::BusConnect();
  174. AddCommand("resetcvars", (ConsoleCommandFunc)ResetCVars, 0, "Resets all cvars to their initial values");
  175. }
  176. //////////////////////////////////////////////////////////////////////////
  177. CXConsole::~CXConsole()
  178. {
  179. AzFramework::ConsoleRequestBus::Handler::BusDisconnect();
  180. AzFramework::CommandRegistrationBus::Handler::BusDisconnect();
  181. if (gEnv->pSystem)
  182. {
  183. gEnv->pSystem->GetIRemoteConsole()->UnregisterListener(this);
  184. }
  185. if (!m_mapVariables.empty())
  186. {
  187. while (!m_mapVariables.empty())
  188. {
  189. m_mapVariables.begin()->second->Release();
  190. }
  191. m_mapVariables.clear();
  192. }
  193. }
  194. //////////////////////////////////////////////////////////////////////////
  195. void CXConsole::FreeRenderResources()
  196. {
  197. }
  198. //////////////////////////////////////////////////////////////////////////
  199. void CXConsole::Release()
  200. {
  201. delete this;
  202. }
  203. #if ALLOW_AUDIT_CVARS
  204. void Command_AuditCVars(IConsoleCmdArgs* pArg)
  205. {
  206. CXConsole* pConsole = (CXConsole*)gEnv->pConsole;
  207. if (pConsole != NULL)
  208. {
  209. pConsole->AuditCVars(pArg);
  210. }
  211. }
  212. #endif // ALLOW_AUDIT_CVARS
  213. #if !defined(_RELEASE) && !defined(LINUX) && !defined(APPLE)
  214. void Command_DumpCommandsVars(IConsoleCmdArgs* Cmd)
  215. {
  216. const char* arg = "";
  217. if (Cmd->GetArgCount() > 1)
  218. {
  219. arg = Cmd->GetArg(1);
  220. }
  221. CXConsole* pConsole = (CXConsole*)gEnv->pConsole;
  222. // txt
  223. pConsole->DumpCommandsVarsTxt(arg);
  224. #if defined(WIN32) || defined(WIN64)
  225. // HTML
  226. {
  227. CConsoleHelpGen Generator(*pConsole);
  228. Generator.Work();
  229. }
  230. #endif
  231. }
  232. void Command_DumpVars(IConsoleCmdArgs* Cmd)
  233. {
  234. bool includeCheat = false;
  235. if (Cmd->GetArgCount() > 1)
  236. {
  237. const char* arg = Cmd->GetArg(1);
  238. int incCheat = atoi(arg);
  239. if (incCheat == 1)
  240. {
  241. includeCheat = true;
  242. }
  243. }
  244. CXConsole* pConsole = (CXConsole*)gEnv->pConsole;
  245. // txt
  246. pConsole->DumpVarsTxt(includeCheat);
  247. }
  248. #endif
  249. //////////////////////////////////////////////////////////////////////////
  250. void CXConsole::Init(ISystem* pSystem)
  251. {
  252. m_pSystem = static_cast<CSystem*>(pSystem);
  253. #if defined(_RELEASE)
  254. static const int kDeactivateConsoleDefault = 1;
  255. #else
  256. static const int kDeactivateConsoleDefault = 0;
  257. #endif
  258. m_pSysDeactivateConsole = REGISTER_INT("sys_DeactivateConsole", kDeactivateConsoleDefault, 0,
  259. "0: normal console behavior\n"
  260. "1: hide the console");
  261. REGISTER_CVAR(con_display_last_messages, 0, VF_NULL, ""); // keep default at 1, needed for gameplay
  262. REGISTER_CVAR(con_line_buffer_size, 1000, VF_NULL, "The number of lines to buffer in the console output window");
  263. REGISTER_CVAR(con_showonload, 0, VF_NULL, "Show console on level loading");
  264. REGISTER_CVAR(con_debug, 0, VF_CHEAT, "Log call stack on every GetCVar call");
  265. REGISTER_CVAR(con_restricted, con_restricted, VF_RESTRICTEDMODE, "0=normal mode / 1=restricted access to the console"); // later on VF_RESTRICTEDMODE should be removed (to 0)
  266. if (m_pSystem->IsDevMode() // unrestricted console for -DEVMODE
  267. || gEnv->IsDedicated()) // unrestricted console for dedicated server
  268. {
  269. con_restricted = 0;
  270. }
  271. m_nLoadingBackTexID = -1;
  272. REGISTER_COMMAND("ConsoleShow", &ConsoleShow, VF_NULL, "Opens the console");
  273. REGISTER_COMMAND("ConsoleHide", &ConsoleHide, VF_NULL, "Closes the console");
  274. #if ALLOW_AUDIT_CVARS
  275. REGISTER_COMMAND("audit_cvars", &Command_AuditCVars, VF_NULL, "Logs all console commands and cvars");
  276. #endif // ALLOW_AUDIT_CVARS
  277. #if !defined(_RELEASE) && !defined(LINUX) && !defined(APPLE)
  278. REGISTER_COMMAND("DumpCommandsVars", &Command_DumpCommandsVars, VF_NULL,
  279. "This console command dumps all console variables and commands to disk\n"
  280. "DumpCommandsVars [prefix]");
  281. REGISTER_COMMAND("DumpVars", &Command_DumpVars, VF_NULL,
  282. "This console command dumps all console variables to disk\n"
  283. "DumpVars [IncludeCheatCvars]");
  284. #endif
  285. REGISTER_COMMAND("Bind", &Bind, VF_NULL, "");
  286. REGISTER_COMMAND("wait_seconds", &Command_SetWaitSeconds, VF_BLOCKFRAME,
  287. "Forces the console to wait for a given number of seconds before the next deferred command is processed\n"
  288. "Works only in deferred command mode");
  289. REGISTER_COMMAND("wait_frames", &Command_SetWaitFrames, VF_BLOCKFRAME,
  290. "Forces the console to wait for a given number of frames before the next deferred command is processed\n"
  291. "Works only in deferred command mode");
  292. CConsoleBatchFile::Init();
  293. if (con_showonload)
  294. {
  295. ShowConsole(true);
  296. }
  297. pSystem->GetIRemoteConsole()->RegisterListener(this, "CXConsole");
  298. }
  299. void CXConsole::LogChangeMessage(const char* name, const bool isConst, const bool isCheat, const bool isReadOnly, const bool isDeprecated,
  300. const char* oldValue, const char* newValue, [[maybe_unused]] const bool isProcessingGroup, const bool allowChange)
  301. {
  302. AZStd::string logMessage = AZStd::string::format
  303. ("[CVARS]: [%s] variable [%s] from [%s] to [%s]%s; Marked as%s%s%s%s",
  304. (allowChange) ? "CHANGED" : "IGNORED CHANGE",
  305. name,
  306. oldValue,
  307. newValue,
  308. (m_bIsProcessingGroup) ? " as part of a cvar group" : "",
  309. (isConst) ? " [VF_CONST_CVAR]" : "",
  310. (isCheat) ? " [VF_CHEAT]" : "",
  311. (isReadOnly) ? " [VF_READONLY]" : "",
  312. (isDeprecated) ? " [VF_DEPRECATED]" : "");
  313. if (allowChange)
  314. {
  315. gEnv->pLog->LogWarning("%s", logMessage.c_str());
  316. gEnv->pLog->LogWarning("Modifying marked variables will not be allowed in Release mode!");
  317. }
  318. else
  319. {
  320. gEnv->pLog->LogError("%s", logMessage.c_str());
  321. }
  322. }
  323. //////////////////////////////////////////////////////////////////////////
  324. void CXConsole::RegisterVar(ICVar* pCVar, ConsoleVarFunc pChangeFunc)
  325. {
  326. // first register callback so setting the value from m_configVars
  327. // is calling pChangeFunc (that would be more correct but to not introduce new problems this code was not changed)
  328. // if (pChangeFunc)
  329. // pCVar->SetOnChangeCallback(pChangeFunc);
  330. bool isConst = pCVar->IsConstCVar();
  331. bool isCheat = ((pCVar->GetFlags() & (VF_CHEAT | VF_CHEAT_NOCHECK | VF_CHEAT_ALWAYS_CHECK)) != 0);
  332. bool isReadOnly = ((pCVar->GetFlags() & VF_READONLY) != 0);
  333. bool isDeprecated = ((pCVar->GetFlags() & VF_DEPRECATED) != 0);
  334. ConfigVars::iterator it = m_configVars.find(pCVar->GetName());
  335. if (it != m_configVars.end())
  336. {
  337. SConfigVar& var = it->second;
  338. bool allowChange = true;
  339. bool wasProcessingGroup = GetIsProcessingGroup();
  340. SetProcessingGroup(var.m_partOfGroup);
  341. if (
  342. #if CVAR_GROUPS_ARE_PRIVILEGED
  343. !m_bIsProcessingGroup &&
  344. #endif // !CVAR_GROUPS_ARE_PRIVILEGED
  345. (isConst || isCheat || isReadOnly || isDeprecated))
  346. {
  347. allowChange = !isDeprecated && ((gEnv->pSystem->IsDevMode()) || (gEnv->IsEditor()));
  348. if ((strcmp(pCVar->GetString(), var.m_value.c_str()) != 0) && (!(gEnv->IsEditor()) || isDeprecated))
  349. {
  350. #if LOG_CVAR_INFRACTIONS
  351. LogChangeMessage(pCVar->GetName(), isConst, isCheat,
  352. isReadOnly, isDeprecated, pCVar->GetString(), var.m_value.c_str(), m_bIsProcessingGroup, allowChange);
  353. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  354. gEnv->pSystem->debug_LogCallStack();
  355. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  356. #endif // LOG_CVAR_INFRACTIONS
  357. }
  358. }
  359. if (allowChange || ALLOW_CONST_CVAR_MODIFICATIONS)
  360. {
  361. pCVar->Set(var.m_value.c_str());
  362. pCVar->SetFlags(pCVar->GetFlags() | VF_WASINCONFIG);
  363. }
  364. SetProcessingGroup(wasProcessingGroup);
  365. }
  366. else
  367. {
  368. // Variable is not modified when just registered.
  369. pCVar->ClearFlags(VF_MODIFIED);
  370. }
  371. if (pChangeFunc)
  372. {
  373. pCVar->SetOnChangeCallback(pChangeFunc);
  374. }
  375. ConsoleVariablesMapItor::value_type value = ConsoleVariablesMapItor::value_type(pCVar->GetName(), pCVar);
  376. m_mapVariables.insert(value);
  377. if (auto consoleInterface = AZ::Interface<AZ::IConsole>::Get();
  378. consoleInterface != nullptr && !consoleInterface->HasCommand(pCVar->GetName(), AZ::ConsoleFunctorFlags::Null))
  379. {
  380. if (pCVar->GetType() == CVAR_INT)
  381. {
  382. m_intWrappers.emplace_back(pCVar->GetName(), pCVar->GetHelp(), pCVar->GetIVal());
  383. }
  384. else if (pCVar->GetType() == CVAR_FLOAT)
  385. {
  386. m_floatWrappers.emplace_back(pCVar->GetName(), pCVar->GetHelp(), pCVar->GetFVal());
  387. }
  388. else if (pCVar->GetType() == CVAR_STRING)
  389. {
  390. m_stringWrappers.emplace_back(pCVar->GetName(), pCVar->GetHelp(), pCVar->GetString());
  391. }
  392. }
  393. int flags = pCVar->GetFlags();
  394. if (flags & VF_CHEAT_ALWAYS_CHECK)
  395. {
  396. AddCheckedCVar(m_alwaysCheckedVariables, value);
  397. }
  398. else if ((flags & (VF_CHEAT | VF_CHEAT_NOCHECK)) == VF_CHEAT)
  399. {
  400. AddCheckedCVar(m_randomCheckedVariables, value);
  401. }
  402. }
  403. //////////////////////////////////////////////////////////////////////////
  404. void CXConsole::AddCheckedCVar(ConsoleVariablesVector& vector, const ConsoleVariablesVector::value_type& value)
  405. {
  406. ConsoleVariablesVector::iterator it = std::lower_bound(vector.begin(), vector.end(), value, CVarNameLess);
  407. if ((it == vector.end()) || strcmp(it->first, value.first))
  408. {
  409. vector.insert(it, value);
  410. }
  411. }
  412. //////////////////////////////////////////////////////////////////////////
  413. bool CXConsole::CVarNameLess(const std::pair<const char*, ICVar*>& lhs, const std::pair<const char*, ICVar*>& rhs)
  414. {
  415. return strcmp(lhs.first, rhs.first) < 0;
  416. }
  417. //////////////////////////////////////////////////////////////////////////
  418. void CXConsole::LoadConfigVar(const char* sVariable, const char* sValue)
  419. {
  420. ICVar* pCVar = GetCVar(sVariable);
  421. if (pCVar)
  422. {
  423. bool isConst = pCVar->IsConstCVar();
  424. bool isCheat = ((pCVar->GetFlags() & (VF_CHEAT | VF_CHEAT_NOCHECK | VF_CHEAT_ALWAYS_CHECK)) != 0);
  425. bool isReadOnly = ((pCVar->GetFlags() & VF_READONLY) != 0);
  426. bool isDeprecated = ((pCVar->GetFlags() & VF_DEPRECATED) != 0);
  427. bool allowChange = true;
  428. if (
  429. #if CVAR_GROUPS_ARE_PRIVILEGED
  430. !m_bIsProcessingGroup &&
  431. #endif // !CVAR_GROUPS_ARE_PRIVILEGED
  432. (isConst || isCheat || isReadOnly) || isDeprecated)
  433. {
  434. allowChange = !isDeprecated && (gEnv->pSystem->IsDevMode()) || (gEnv->IsEditor());
  435. if (!(gEnv->IsEditor()) || isDeprecated)
  436. {
  437. #if LOG_CVAR_INFRACTIONS
  438. LogChangeMessage(pCVar->GetName(), isConst, isCheat,
  439. isReadOnly, isDeprecated, pCVar->GetString(), sValue, m_bIsProcessingGroup, allowChange);
  440. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  441. gEnv->pSystem->debug_LogCallStack();
  442. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  443. #endif // LOG_CVAR_INFRACTIONS
  444. }
  445. }
  446. if (allowChange || ALLOW_CONST_CVAR_MODIFICATIONS)
  447. {
  448. pCVar->Set(sValue);
  449. pCVar->SetFlags(pCVar->GetFlags() | VF_WASINCONFIG);
  450. }
  451. return;
  452. }
  453. SConfigVar temp;
  454. temp.m_value = sValue;
  455. temp.m_partOfGroup = m_bIsProcessingGroup;
  456. m_configVars[sVariable] = temp;
  457. }
  458. //////////////////////////////////////////////////////////////////////////
  459. void CXConsole::EnableActivationKey(bool bEnable)
  460. {
  461. m_bActivationKeyEnable = bEnable;
  462. }
  463. //////////////////////////////////////////////////////////////////////////
  464. void CXConsole::SetClientDataProbeString(const char* pName, const char* pValue)
  465. {
  466. ICVar* pCVar = GetCVar(pName);
  467. if (pCVar)
  468. {
  469. pCVar->SetDataProbeString(pValue);
  470. }
  471. }
  472. //////////////////////////////////////////////////////////////////////////
  473. ICVar* CXConsole::Register(const char* sName, int* src, int iValue, int nFlags, const char* help, ConsoleVarFunc pChangeFunc, bool allowModify)
  474. {
  475. AssertName(sName);
  476. ICVar* pCVar = stl::find_in_map(m_mapVariables, sName, NULL);
  477. if (pCVar)
  478. {
  479. gEnv->pLog->LogError("[CVARS]: [DUPLICATE] CXConsole::Register(int): variable [%s] is already registered", pCVar->GetName());
  480. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  481. gEnv->pSystem->debug_LogCallStack();
  482. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  483. return pCVar;
  484. }
  485. if (!allowModify)
  486. {
  487. nFlags |= VF_CONST_CVAR;
  488. }
  489. pCVar = new CXConsoleVariableIntRef(this, sName, src, nFlags, help);
  490. *src = iValue;
  491. RegisterVar(pCVar, pChangeFunc);
  492. return pCVar;
  493. }
  494. //////////////////////////////////////////////////////////////////////////
  495. ICVar* CXConsole::Register(const char* sName, float* src, float fValue, int nFlags, const char* help, ConsoleVarFunc pChangeFunc, bool allowModify)
  496. {
  497. AssertName(sName);
  498. ICVar* pCVar = stl::find_in_map(m_mapVariables, sName, NULL);
  499. if (pCVar)
  500. {
  501. gEnv->pLog->Log("[CVARS]: [DUPLICATE] CXConsole::Register(float): variable [%s] is already registered", pCVar->GetName());
  502. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  503. gEnv->pSystem->debug_LogCallStack();
  504. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  505. return pCVar;
  506. }
  507. if (!allowModify)
  508. {
  509. nFlags |= VF_CONST_CVAR;
  510. }
  511. pCVar = new CXConsoleVariableFloatRef(this, sName, src, nFlags, help);
  512. *src = fValue;
  513. RegisterVar(pCVar, pChangeFunc);
  514. return pCVar;
  515. }
  516. //////////////////////////////////////////////////////////////////////////
  517. ICVar* CXConsole::Register(const char* sName, const char** src, const char* defaultValue, int nFlags, const char* help, ConsoleVarFunc pChangeFunc, bool allowModify)
  518. {
  519. AssertName(sName);
  520. ICVar* pCVar = stl::find_in_map(m_mapVariables, sName, NULL);
  521. if (pCVar)
  522. {
  523. gEnv->pLog->Log("[CVARS]: [DUPLICATE] CXConsole::Register(const char*): variable [%s] is already registered", pCVar->GetName());
  524. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  525. gEnv->pSystem->debug_LogCallStack();
  526. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  527. return pCVar;
  528. }
  529. if (!allowModify)
  530. {
  531. nFlags |= VF_CONST_CVAR;
  532. }
  533. pCVar = new CXConsoleVariableStringRef(this, sName, src, defaultValue, nFlags, help);
  534. RegisterVar(pCVar, pChangeFunc);
  535. return pCVar;
  536. }
  537. //////////////////////////////////////////////////////////////////////////
  538. ICVar* CXConsole::RegisterString(const char* sName, const char* sValue, int nFlags, const char* help, ConsoleVarFunc pChangeFunc)
  539. {
  540. AssertName(sName);
  541. ICVar* pCVar = stl::find_in_map(m_mapVariables, sName, NULL);
  542. if (pCVar)
  543. {
  544. gEnv->pLog->Log("[CVARS]: [DUPLICATE] CXConsole::RegisterString(const char*): variable [%s] is already registered", pCVar->GetName());
  545. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  546. gEnv->pSystem->debug_LogCallStack();
  547. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  548. return pCVar;
  549. }
  550. pCVar = new CXConsoleVariableString(this, sName, sValue, nFlags, help);
  551. RegisterVar(pCVar, pChangeFunc);
  552. return pCVar;
  553. }
  554. //////////////////////////////////////////////////////////////////////////
  555. ICVar* CXConsole::RegisterFloat(const char* sName, float fValue, int nFlags, const char* help, ConsoleVarFunc pChangeFunc)
  556. {
  557. AssertName(sName);
  558. ICVar* pCVar = stl::find_in_map(m_mapVariables, sName, NULL);
  559. if (pCVar)
  560. {
  561. gEnv->pLog->Log("[CVARS]: [DUPLICATE] CXConsole::RegisterFloat(): variable [%s] is already registered", pCVar->GetName());
  562. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  563. gEnv->pSystem->debug_LogCallStack();
  564. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  565. return pCVar;
  566. }
  567. pCVar = new CXConsoleVariableFloat(this, sName, fValue, nFlags, help);
  568. RegisterVar(pCVar, pChangeFunc);
  569. return pCVar;
  570. }
  571. //////////////////////////////////////////////////////////////////////////
  572. ICVar* CXConsole::RegisterInt(const char* sName, int iValue, int nFlags, const char* help, ConsoleVarFunc pChangeFunc)
  573. {
  574. AssertName(sName);
  575. ICVar* pCVar = stl::find_in_map(m_mapVariables, sName, NULL);
  576. if (pCVar)
  577. {
  578. gEnv->pLog->Log("[CVARS]: [DUPLICATE] CXConsole::RegisterInt(): variable [%s] is already registered", pCVar->GetName());
  579. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  580. gEnv->pSystem->debug_LogCallStack();
  581. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  582. return pCVar;
  583. }
  584. pCVar = new CXConsoleVariableInt(this, sName, iValue, nFlags, help);
  585. RegisterVar(pCVar, pChangeFunc);
  586. return pCVar;
  587. }
  588. //////////////////////////////////////////////////////////////////////////
  589. void CXConsole::UnregisterVariable(const char* sVarName, [[maybe_unused]] bool bDelete)
  590. {
  591. ConsoleVariablesMapItor itor;
  592. itor = m_mapVariables.find(sVarName);
  593. if (itor == m_mapVariables.end())
  594. {
  595. return;
  596. }
  597. ICVar* pCVar = itor->second;
  598. int32 flags = pCVar->GetFlags();
  599. if (flags & VF_CHEAT_ALWAYS_CHECK)
  600. {
  601. RemoveCheckedCVar(m_alwaysCheckedVariables, *itor);
  602. }
  603. else if ((flags & (VF_CHEAT | VF_CHEAT_NOCHECK)) == VF_CHEAT)
  604. {
  605. RemoveCheckedCVar(m_randomCheckedVariables, *itor);
  606. }
  607. m_mapVariables.erase(sVarName);
  608. delete pCVar;
  609. }
  610. void CXConsole::RemoveCheckedCVar(ConsoleVariablesVector& vector, const ConsoleVariablesVector::value_type& value)
  611. {
  612. ConsoleVariablesVector::iterator it = std::lower_bound(vector.begin(), vector.end(), value, CVarNameLess);
  613. if ((it != vector.end()) && !strcmp(it->first, value.first))
  614. {
  615. vector.erase(it);
  616. }
  617. }
  618. //////////////////////////////////////////////////////////////////////////
  619. void CXConsole::SetScrollMax(int value)
  620. {
  621. m_nScrollMax = value;
  622. m_nTempScrollMax = m_nScrollMax;
  623. }
  624. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  625. void CXConsole::ShowConsole(bool show, const int iRequestScrollMax)
  626. {
  627. if (m_pSysDeactivateConsole->GetIVal())
  628. {
  629. show = false;
  630. }
  631. SetStatus(show);
  632. if (iRequestScrollMax > 0)
  633. {
  634. m_nTempScrollMax = iRequestScrollMax; // temporary user request
  635. }
  636. else
  637. {
  638. m_nTempScrollMax = m_nScrollMax; // reset
  639. }
  640. if (m_bConsoleActive)
  641. {
  642. m_sdScrollDir = sdDOWN;
  643. }
  644. else
  645. {
  646. m_sdScrollDir = sdUP;
  647. }
  648. }
  649. //////////////////////////////////////////////////////////////////////////
  650. void CXConsole::CreateKeyBind(const char* sCmd, const char* sRes)
  651. {
  652. m_mapBinds.insert(ConsoleBindsMapItor::value_type(sCmd, sRes));
  653. }
  654. //////////////////////////////////////////////////////////////////////////
  655. void CXConsole::DumpKeyBinds(IKeyBindDumpSink* pCallback)
  656. {
  657. for (ConsoleBindsMap::iterator it = m_mapBinds.begin(); it != m_mapBinds.end(); ++it)
  658. {
  659. pCallback->OnKeyBindFound(it->first.c_str(), it->second.c_str());
  660. }
  661. }
  662. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  663. const char* CXConsole::FindKeyBind(const char* sCmd) const
  664. {
  665. ConsoleBindsMap::const_iterator it = m_mapBinds.find(sCmd);
  666. if (it != m_mapBinds.end())
  667. {
  668. return it->second.c_str();
  669. }
  670. return 0;
  671. }
  672. //////////////////////////////////////////////////////////////////////////
  673. void CXConsole::DumpCVars(ICVarDumpSink* pCallback, unsigned int nFlagsFilter)
  674. {
  675. ConsoleVariablesMapItor It = m_mapVariables.begin();
  676. while (It != m_mapVariables.end())
  677. {
  678. if ((nFlagsFilter == 0) || ((nFlagsFilter != 0) && (It->second->GetFlags() & nFlagsFilter)))
  679. {
  680. pCallback->OnElementFound(It->second);
  681. }
  682. ++It;
  683. }
  684. }
  685. //////////////////////////////////////////////////////////////////////////
  686. ICVar* CXConsole::GetCVar(const char* sName)
  687. {
  688. assert(sName);
  689. if (con_debug)
  690. {
  691. // Log call stack on get cvar.
  692. CryLog("GetCVar(\"%s\") called", sName);
  693. m_pSystem->debug_LogCallStack();
  694. }
  695. // Fast map lookup for case-sensitive match.
  696. ConsoleVariablesMapItor it;
  697. it = m_mapVariables.find(sName);
  698. if (it != m_mapVariables.end())
  699. {
  700. return it->second;
  701. }
  702. /*
  703. if(!bCaseSensitive)
  704. {
  705. // Much slower but allows names with wrong case (use only where performance doesn't matter).
  706. for(it=m_mapVariables.begin(); it!=m_mapVariables.end(); ++it)
  707. {
  708. if(azstricmp(it->first,sName)==0)
  709. return it->second;
  710. }
  711. }
  712. test else
  713. {
  714. for(it=m_mapVariables.begin(); it!=m_mapVariables.end(); ++it)
  715. {
  716. if(azstricmp(it->first,sName)==0)
  717. {
  718. CryFatalError("Error: Wrong case for '%s','%s'",it->first,sName);
  719. }
  720. }
  721. }
  722. */
  723. return NULL; // haven't found this name
  724. }
  725. //////////////////////////////////////////////////////////////////////////
  726. char* CXConsole::GetVariable([[maybe_unused]] const char* szVarName, [[maybe_unused]] const char* szFileName, [[maybe_unused]] const char* def_val)
  727. {
  728. assert(m_pSystem);
  729. return 0;
  730. }
  731. //////////////////////////////////////////////////////////////////////////
  732. float CXConsole::GetVariable([[maybe_unused]] const char* szVarName, [[maybe_unused]] const char* szFileName, [[maybe_unused]] float def_val)
  733. {
  734. assert(m_pSystem);
  735. return 0;
  736. }
  737. //////////////////////////////////////////////////////////////////////////
  738. bool CXConsole::GetStatus()
  739. {
  740. return m_bConsoleActive;
  741. }
  742. //////////////////////////////////////////////////////////////////////////
  743. void CXConsole::Clear()
  744. {
  745. m_dqConsoleBuffer.clear();
  746. }
  747. //////////////////////////////////////////////////////////////////////////
  748. void CXConsole::Update()
  749. {
  750. if (!m_pSystem)
  751. {
  752. return;
  753. }
  754. if (m_bIsConsoleKeyPressed)
  755. {
  756. m_sInputBuffer.clear();
  757. m_nCursorPos = 0;
  758. m_bIsConsoleKeyPressed = false;
  759. }
  760. // Execute the deferred commands
  761. ExecuteDeferredCommands();
  762. }
  763. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  764. void CXConsole::OnConsoleCommand(const char* cmd)
  765. {
  766. ExecuteString(cmd, false);
  767. }
  768. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  769. const char* CXConsole::GetHistoryElement(const bool bUpOrDown)
  770. {
  771. if (bUpOrDown)
  772. {
  773. if (!m_dqHistory.empty())
  774. {
  775. if (m_nHistoryPos < (int)(m_dqHistory.size() - 1))
  776. {
  777. m_nHistoryPos++;
  778. m_sReturnString = m_dqHistory[m_nHistoryPos];
  779. return m_sReturnString.c_str();
  780. }
  781. }
  782. }
  783. else
  784. {
  785. if (m_nHistoryPos > 0)
  786. {
  787. m_nHistoryPos--;
  788. m_sReturnString = m_dqHistory[m_nHistoryPos];
  789. return m_sReturnString.c_str();
  790. }
  791. }
  792. return 0;
  793. }
  794. //////////////////////////////////////////////////////////////////////////
  795. bool CXConsole::GetLineNo(const int indwLineNo, char* outszBuffer, const int indwBufferSize) const
  796. {
  797. assert(outszBuffer);
  798. assert(indwBufferSize > 0);
  799. outszBuffer[0] = 0;
  800. ConsoleBuffer::const_reverse_iterator ritor = m_dqConsoleBuffer.rbegin();
  801. ritor += indwLineNo;
  802. if (indwLineNo >= (int)m_dqConsoleBuffer.size())
  803. {
  804. return false;
  805. }
  806. const char* buf = ritor->c_str();// GetBuf(k);
  807. if (*buf > 0 && *buf < 32)
  808. {
  809. buf++; // to jump over verbosity level character
  810. }
  811. azstrcpy(outszBuffer, indwBufferSize, buf);
  812. return true;
  813. }
  814. //////////////////////////////////////////////////////////////////////////
  815. int CXConsole::GetLineCount() const
  816. {
  817. return static_cast<int>(m_dqConsoleBuffer.size());
  818. }
  819. //////////////////////////////////////////////////////////////////////////
  820. void CXConsole::ScrollConsole()
  821. {
  822. }
  823. //////////////////////////////////////////////////////////////////////////
  824. bool CXConsole::AddCommand(const char* sCommand, ConsoleCommandFunc func, int nFlags, const char* sHelp)
  825. {
  826. AssertName(sCommand);
  827. if (m_mapCommands.find(sCommand) == m_mapCommands.end())
  828. {
  829. CConsoleCommand cmd;
  830. cmd.m_sName = sCommand;
  831. cmd.m_func = func;
  832. if (sHelp)
  833. {
  834. cmd.m_sHelp = sHelp;
  835. }
  836. cmd.m_nFlags = nFlags;
  837. m_mapCommands.insert(std::make_pair(cmd.m_sName, cmd));
  838. return true;
  839. }
  840. else
  841. {
  842. gEnv->pLog->LogError("[CVARS]: [DUPLICATE] CXConsole::AddCommand(): console command [%s] is already registered", sCommand);
  843. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  844. gEnv->pSystem->debug_LogCallStack();
  845. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  846. return false;
  847. }
  848. }
  849. //////////////////////////////////////////////////////////////////////////
  850. bool CXConsole::AddCommand(const char* sCommand, const char* sScriptFunc, int nFlags, const char* sHelp)
  851. {
  852. AssertName(sCommand);
  853. if (m_mapCommands.find(sCommand) == m_mapCommands.end())
  854. {
  855. CConsoleCommand cmd;
  856. cmd.m_sName = sCommand;
  857. cmd.m_sCommand = sScriptFunc;
  858. if (sHelp)
  859. {
  860. cmd.m_sHelp = sHelp;
  861. }
  862. cmd.m_nFlags = nFlags;
  863. m_mapCommands.insert(std::make_pair(cmd.m_sName, cmd));
  864. return true;
  865. }
  866. else
  867. {
  868. gEnv->pLog->LogError("[CVARS]: [DUPLICATE] CXConsole::AddCommand(): script command [%s] is already registered", sCommand);
  869. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  870. gEnv->pSystem->debug_LogCallStack();
  871. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  872. return false;
  873. }
  874. }
  875. //////////////////////////////////////////////////////////////////////////
  876. void CXConsole::RemoveCommand(const char* sName)
  877. {
  878. ConsoleCommandsMap::iterator ite = m_mapCommands.find(sName);
  879. if (ite != m_mapCommands.end())
  880. {
  881. m_mapCommands.erase(ite);
  882. }
  883. }
  884. //////////////////////////////////////////////////////////////////////////
  885. inline bool hasprefix(const char* s, const char* prefix)
  886. {
  887. while (*prefix)
  888. {
  889. if (*prefix++ != *s++)
  890. {
  891. return false;
  892. }
  893. }
  894. return true;
  895. }
  896. //////////////////////////////////////////////////////////////////////////
  897. const char* CXConsole::GetFlagsString(const uint32 dwFlags)
  898. {
  899. static char sFlags[256];
  900. // hiding this makes it a bit more difficult for cheaters
  901. // if(dwFlags&VF_CHEAT) azstrcat( sFlags,"CHEAT, ");
  902. azstrcpy(sFlags, AZ_ARRAY_SIZE(sFlags), "");
  903. if (dwFlags & VF_READONLY)
  904. {
  905. azstrcat(sFlags, AZ_ARRAY_SIZE(sFlags), "READONLY, ");
  906. }
  907. if (dwFlags & VF_DEPRECATED)
  908. {
  909. azstrcat(sFlags, AZ_ARRAY_SIZE(sFlags), "DEPRECATED, ");
  910. }
  911. if (dwFlags & VF_DUMPTODISK)
  912. {
  913. azstrcat(sFlags, AZ_ARRAY_SIZE(sFlags), "DUMPTODISK, ");
  914. }
  915. if (dwFlags & VF_REQUIRE_LEVEL_RELOAD)
  916. {
  917. azstrcat(sFlags, AZ_ARRAY_SIZE(sFlags), "REQUIRE_LEVEL_RELOAD, ");
  918. }
  919. if (dwFlags & VF_REQUIRE_APP_RESTART)
  920. {
  921. azstrcat(sFlags, AZ_ARRAY_SIZE(sFlags), "REQUIRE_APP_RESTART, ");
  922. }
  923. if (dwFlags & VF_RESTRICTEDMODE)
  924. {
  925. azstrcat(sFlags, AZ_ARRAY_SIZE(sFlags), "RESTRICTEDMODE, ");
  926. }
  927. if (sFlags[0] != 0)
  928. {
  929. sFlags[strlen(sFlags) - 2] = 0; // remove ending ", "
  930. }
  931. return sFlags;
  932. }
  933. #if ALLOW_AUDIT_CVARS
  934. void CXConsole::AuditCVars(IConsoleCmdArgs* pArg)
  935. {
  936. int numArgs = pArg->GetArgCount();
  937. int cheatMask = VF_CHEAT | VF_CHEAT_NOCHECK | VF_CHEAT_ALWAYS_CHECK;
  938. int constMask = VF_CONST_CVAR;
  939. int readOnlyMask = VF_READONLY;
  940. int devOnlyMask = VF_DEV_ONLY;
  941. int dediOnlyMask = VF_DEDI_ONLY;
  942. int excludeMask = cheatMask | constMask | readOnlyMask | devOnlyMask | dediOnlyMask;
  943. if (numArgs > 1)
  944. {
  945. while (numArgs > 1)
  946. {
  947. const char* arg = pArg->GetArg(numArgs - 1);
  948. if (azstricmp(arg, "cheat") == 0)
  949. {
  950. excludeMask &= ~cheatMask;
  951. }
  952. if (azstricmp(arg, "const") == 0)
  953. {
  954. excludeMask &= ~constMask;
  955. }
  956. if (azstricmp(arg, "readonly") == 0)
  957. {
  958. excludeMask &= ~readOnlyMask;
  959. }
  960. if (azstricmp(arg, "dev") == 0)
  961. {
  962. excludeMask &= ~devOnlyMask;
  963. }
  964. if (azstricmp(arg, "dedi") == 0)
  965. {
  966. excludeMask &= ~dediOnlyMask;
  967. }
  968. --numArgs;
  969. }
  970. }
  971. int commandCount = 0;
  972. int cvarCount = 0;
  973. CryLogAlways("[CVARS]: [BEGIN AUDIT]");
  974. for (ConsoleCommandsMapItor it = m_mapCommands.begin(); it != m_mapCommands.end(); ++it)
  975. {
  976. CConsoleCommand& command = it->second;
  977. int cheatFlags = (command.m_nFlags & cheatMask);
  978. int devOnlyFlags = (command.m_nFlags & devOnlyMask);
  979. int dediOnlyFlags = (command.m_nFlags & dediOnlyMask);
  980. bool shouldLog = ((cheatFlags | devOnlyFlags | dediOnlyFlags) == 0) || (((cheatFlags | devOnlyFlags | dediOnlyFlags) & ~excludeMask) != 0);
  981. if (shouldLog)
  982. {
  983. CryLogAlways("[CVARS]: [COMMAND] %s%s%s%s%s",
  984. command.m_sName.c_str(),
  985. (cheatFlags != 0) ? " [VF_CHEAT]" : "",
  986. (devOnlyFlags != 0) ? " [VF_DEV_ONLY]" : "",
  987. (dediOnlyFlags != 0) ? " [VF_DEDI_ONLY]" : "",
  988. ""
  989. );
  990. ++commandCount;
  991. }
  992. }
  993. for (ConsoleVariablesMapItor it = m_mapVariables.begin(); it != m_mapVariables.end(); ++it)
  994. {
  995. ICVar* pVariable = it->second;
  996. int flags = pVariable->GetFlags();
  997. int cheatFlags = (flags & cheatMask);
  998. int constFlags = (flags & constMask);
  999. int readOnlyFlags = (flags & readOnlyMask);
  1000. int devOnlyFlags = (flags & devOnlyMask);
  1001. int dediOnlyFlags = (flags & dediOnlyMask);
  1002. bool shouldLog = ((cheatFlags | constFlags | readOnlyFlags | devOnlyFlags | dediOnlyFlags) == 0) || (((cheatFlags | constFlags | readOnlyFlags | devOnlyFlags | dediOnlyFlags) & ~excludeMask) != 0);
  1003. if (shouldLog)
  1004. {
  1005. CryLogAlways("[CVARS]: [VARIABLE] %s%s%s%s%s%s%s",
  1006. pVariable->GetName(),
  1007. (cheatFlags != 0) ? " [VF_CHEAT]" : "",
  1008. (constFlags != 0) ? " [VF_CONST_CVAR]" : "",
  1009. (readOnlyFlags != 0) ? " [VF_READONLY]" : "",
  1010. (devOnlyFlags != 0) ? " [VF_DEV_ONLY]" : "",
  1011. (dediOnlyFlags != 0) ? " [VF_DEDI_ONLY]" : "",
  1012. ""
  1013. );
  1014. ++cvarCount;
  1015. }
  1016. }
  1017. CryLogAlways("[CVARS]: [END AUDIT] (commands %d/%" PRISIZE_T "; variables %d/%" PRISIZE_T ")", commandCount, m_mapCommands.size(), cvarCount, m_mapVariables.size());
  1018. }
  1019. #endif // ALLOW_AUDIT_CVARS
  1020. //////////////////////////////////////////////////////////////////////////
  1021. #ifndef _RELEASE
  1022. void CXConsole::DumpCommandsVarsTxt(const char* prefix)
  1023. {
  1024. FILE* f0 = nullptr;
  1025. azfopen(&f0, "consolecommandsandvars.txt", "w");
  1026. if (!f0)
  1027. {
  1028. return;
  1029. }
  1030. ConsoleCommandsMapItor itrCmd, itrCmdEnd = m_mapCommands.end();
  1031. ConsoleVariablesMapItor itrVar, itrVarEnd = m_mapVariables.end();
  1032. fprintf(f0, " CHEAT: stays in the default value if cheats are not disabled\n");
  1033. fprintf(f0, " REQUIRE_NET_SYNC: cannot be changed on client and when connecting it's sent to the client\n");
  1034. fprintf(f0, " SAVEGAME: stored when saving a savegame\n");
  1035. fprintf(f0, " READONLY: can not be changed by the user\n");
  1036. fprintf(f0, "-------------------------\n");
  1037. fprintf(f0, "\n");
  1038. for (itrCmd = m_mapCommands.begin(); itrCmd != itrCmdEnd; ++itrCmd)
  1039. {
  1040. CConsoleCommand& cmd = itrCmd->second;
  1041. if (hasprefix(cmd.m_sName.c_str(), prefix))
  1042. {
  1043. const char* sFlags = GetFlagsString(cmd.m_nFlags);
  1044. fprintf(f0, "Command: %s %s\nscript: %s\nhelp: %s\n\n", cmd.m_sName.c_str(), sFlags, cmd.m_sCommand.c_str(), cmd.m_sHelp.c_str());
  1045. }
  1046. }
  1047. for (itrVar = m_mapVariables.begin(); itrVar != itrVarEnd; ++itrVar)
  1048. {
  1049. ICVar* var = itrVar->second;
  1050. const char* types[] = { "?", "int", "float", "string", "?" };
  1051. var->GetRealIVal(); // assert inside checks consistency for all cvars
  1052. if (hasprefix(var->GetName(), prefix))
  1053. {
  1054. const char* sFlags = GetFlagsString(var->GetFlags());
  1055. fprintf(f0, "variable: %s %s\ntype: %s\ncurrent: %s\nhelp: %s\n\n", var->GetName(), sFlags, types[var->GetType()], var->GetString(), var->GetHelp());
  1056. }
  1057. }
  1058. fclose(f0);
  1059. ConsoleLogInputResponse("successfully wrote consolecommandsandvars.txt");
  1060. }
  1061. void CXConsole::DumpVarsTxt(const bool includeCheat)
  1062. {
  1063. FILE* f0 = nullptr;
  1064. azfopen(&f0, "consolevars.txt", "w");
  1065. if (!f0)
  1066. {
  1067. return;
  1068. }
  1069. ConsoleVariablesMapItor itrVar, itrVarEnd = m_mapVariables.end();
  1070. fprintf(f0, " REQUIRE_NET_SYNC: cannot be changed on client and when connecting it's sent to the client\n");
  1071. fprintf(f0, " SAVEGAME: stored when saving a savegame\n");
  1072. fprintf(f0, " READONLY: can not be changed by the user\n");
  1073. fprintf(f0, "-------------------------\n");
  1074. fprintf(f0, "\n");
  1075. for (itrVar = m_mapVariables.begin(); itrVar != itrVarEnd; ++itrVar)
  1076. {
  1077. ICVar* var = itrVar->second;
  1078. const char* types[] = { "?", "int", "float", "string", "?" };
  1079. var->GetRealIVal(); // assert inside checks consistency for all cvars
  1080. const int flags = var->GetFlags();
  1081. if ((includeCheat == true) || (flags & VF_CHEAT) == 0)
  1082. {
  1083. const char* sFlags = GetFlagsString(flags);
  1084. fprintf(f0, "variable: %s %s\ntype: %s\ncurrent: %s\nhelp: %s\n\n", var->GetName(), sFlags, types[var->GetType()], var->GetString(), var->GetHelp());
  1085. }
  1086. }
  1087. fclose(f0);
  1088. ConsoleLogInputResponse("successfully wrote consolevars.txt");
  1089. }
  1090. #endif
  1091. //////////////////////////////////////////////////////////////////////////
  1092. void CXConsole::DisplayHelp(const char* help, const char* name)
  1093. {
  1094. if (help == 0 || *help == 0)
  1095. {
  1096. ConsoleLogInputResponse("No help available for $3%s", name);
  1097. }
  1098. else
  1099. {
  1100. auto PrintHelpLine = [this](AZStd::string_view line)
  1101. {
  1102. ConsoleLogInputResponse(" $3%.*s", AZ_STRING_ARG(line));
  1103. };
  1104. AZ::StringFunc::TokenizeVisitor(help, PrintHelpLine, '\n');
  1105. }
  1106. }
  1107. void CXConsole::ExecuteString(const char* command, const bool bSilentMode, const bool bDeferExecution)
  1108. {
  1109. if (!m_deferredExecution && !bDeferExecution)
  1110. {
  1111. // This is a regular mode
  1112. ExecuteStringInternal(command, false, bSilentMode); // not from console
  1113. return;
  1114. }
  1115. // Store the string commands into a list and defer the execution for later.
  1116. // The commands will be processed in CXConsole::Update()
  1117. AZStd::string str(command);
  1118. AZ::StringFunc::TrimWhiteSpace(str, true, false);
  1119. // Unroll the exec command
  1120. bool unroll = (0 == AZ::StringFunc::Find(str, "exec", 0, false, false));
  1121. if (unroll)
  1122. {
  1123. bool oldDeferredExecution = m_deferredExecution;
  1124. // Make sure that the unrolled commands are processed with deferred mode on
  1125. m_deferredExecution = true;
  1126. ExecuteStringInternal(str.c_str(), false, bSilentMode);
  1127. // Restore to the previous setting
  1128. m_deferredExecution = oldDeferredExecution;
  1129. }
  1130. else
  1131. {
  1132. m_deferredCommands.push_back(SDeferredCommand(str.c_str(), bSilentMode));
  1133. }
  1134. }
  1135. // This method is used by the ConsoleRequestBus to allow executing of console commands
  1136. // This can be used from anywhere in code or via script since the bus is relected to the behavior context
  1137. void CXConsole::ExecuteConsoleCommand(const char* command)
  1138. {
  1139. ExecuteString(command, true, true);
  1140. }
  1141. void CXConsole::ResetCVarsToDefaults()
  1142. {
  1143. ConsoleVariablesMapItor It = m_mapVariables.begin();
  1144. while (It != m_mapVariables.end())
  1145. {
  1146. It->second->Reset();
  1147. ++It;
  1148. }
  1149. }
  1150. void CXConsole::SplitCommands(const char* line, std::list<AZStd::string>& split)
  1151. {
  1152. const char* start = line;
  1153. AZStd::string working;
  1154. while (true)
  1155. {
  1156. char ch = *line++;
  1157. switch (ch)
  1158. {
  1159. case '\'':
  1160. case '\"':
  1161. while ((*line++ != ch) && *line)
  1162. {
  1163. ;
  1164. }
  1165. break;
  1166. case '\n':
  1167. case '\r':
  1168. case ';':
  1169. case '\0':
  1170. {
  1171. working.assign(start, line - 1);
  1172. AZ::StringFunc::TrimWhiteSpace(working, true, true);
  1173. if (!working.empty())
  1174. {
  1175. split.push_back(working);
  1176. }
  1177. start = line;
  1178. if (ch == '\0')
  1179. {
  1180. return;
  1181. }
  1182. }
  1183. break;
  1184. }
  1185. }
  1186. }
  1187. //////////////////////////////////////////////////////////////////////////
  1188. void CXConsole::ExecuteStringInternal(const char* command, const bool bFromConsole, const bool bSilentMode)
  1189. {
  1190. AzFramework::ConsoleNotificationBus::Broadcast(&AzFramework::ConsoleNotificationBus::Events::OnConsoleCommandExecuted, command);
  1191. assert(command);
  1192. assert(command[0] != '\\'); // caller should remove leading "\\"
  1193. ///////////////////////////
  1194. //Execute as string
  1195. if (command[0] == '#' || command[0] == '@')
  1196. {
  1197. if (!con_restricted || !bFromConsole) // in restricted mode we allow only VF_RESTRICTEDMODE CVars&CCmd
  1198. {
  1199. AddLine(command);
  1200. if (m_pSystem->IsDevMode())
  1201. {
  1202. m_bDrawCursor = 0;
  1203. }
  1204. else
  1205. {
  1206. // Warning.
  1207. // No Cheat warnings. ConsoleWarning("Console execution is cheat protected");
  1208. }
  1209. return;
  1210. }
  1211. }
  1212. ConsoleCommandsMapItor itrCmd;
  1213. ConsoleVariablesMapItor itrVar;
  1214. std::list<AZStd::string> lineCommands;
  1215. SplitCommands(command, lineCommands);
  1216. AZStd::string sTemp;
  1217. AZStd::string sCommand, sLineCommand;
  1218. while (!lineCommands.empty())
  1219. {
  1220. AZStd::string::size_type nPos;
  1221. {
  1222. sTemp = lineCommands.front();
  1223. sCommand = lineCommands.front();
  1224. sLineCommand = sCommand;
  1225. lineCommands.pop_front();
  1226. if (!bSilentMode)
  1227. {
  1228. if (GetStatus())
  1229. {
  1230. AddLine(sTemp.c_str());
  1231. }
  1232. }
  1233. nPos = sTemp.find_first_of('=');
  1234. if (nPos != AZStd::string::npos)
  1235. {
  1236. sCommand = sTemp.substr(0, nPos);
  1237. }
  1238. else if ((nPos = sTemp.find_first_of(' ')) != AZStd::string::npos)
  1239. {
  1240. sCommand = sTemp.substr(0, nPos);
  1241. }
  1242. else
  1243. {
  1244. sCommand = sTemp;
  1245. }
  1246. AZ::StringFunc::TrimWhiteSpace(sCommand, true, true);
  1247. //////////////////////////////////////////
  1248. // Search for CVars
  1249. if (sCommand.length() > 1 && sCommand[0] == '?')
  1250. {
  1251. sTemp = sCommand.substr(1);
  1252. FindVar(sTemp.c_str());
  1253. continue;
  1254. }
  1255. }
  1256. //////////////////////////////////////////
  1257. //Check if is a command
  1258. itrCmd = m_mapCommands.find(sCommand);
  1259. if (itrCmd != m_mapCommands.end())
  1260. {
  1261. if (((itrCmd->second).m_nFlags & VF_RESTRICTEDMODE) || !con_restricted || !bFromConsole) // in restricted mode we allow only VF_RESTRICTEDMODE CVars&CCmd
  1262. {
  1263. if (itrCmd->second.m_nFlags & VF_BLOCKFRAME)
  1264. {
  1265. m_blockCounter++;
  1266. }
  1267. {
  1268. sTemp = sLineCommand;
  1269. }
  1270. ExecuteCommand((itrCmd->second), sTemp);
  1271. continue;
  1272. }
  1273. }
  1274. //////////////////////////////////////////
  1275. //Check if is a variable
  1276. itrVar = m_mapVariables.find(sCommand.c_str());
  1277. if (itrVar != m_mapVariables.end())
  1278. {
  1279. ICVar* pCVar = itrVar->second;
  1280. if ((pCVar->GetFlags() & VF_RESTRICTEDMODE) || !con_restricted || !bFromConsole) // in restricted mode we allow only VF_RESTRICTEDMODE CVars&CCmd
  1281. {
  1282. if (pCVar->GetFlags() & VF_BLOCKFRAME)
  1283. {
  1284. m_blockCounter++;
  1285. }
  1286. if (nPos != AZStd::string::npos)
  1287. {
  1288. sTemp = sTemp.substr(nPos + 1); // remove the command from sTemp
  1289. AZ::StringFunc::StripEnds(sTemp, " \t\r\n\"\'");
  1290. if (sTemp == "?")
  1291. {
  1292. ICVar* v = itrVar->second;
  1293. DisplayHelp(v->GetHelp(), sCommand.c_str());
  1294. return;
  1295. }
  1296. if (!sTemp.empty() || (pCVar->GetType() == CVAR_STRING))
  1297. {
  1298. pCVar->Set(sTemp.c_str());
  1299. }
  1300. }
  1301. // the following line calls AddLine() indirectly
  1302. if (!bSilentMode)
  1303. {
  1304. DisplayVarValue(pCVar);
  1305. }
  1306. //ConsoleLogInputResponse("%s=%s",pCVar->GetName(),pCVar->GetString());
  1307. continue;
  1308. }
  1309. }
  1310. if (!bSilentMode)
  1311. {
  1312. ConsoleWarning("Unknown command: %s", sCommand.c_str());
  1313. }
  1314. }
  1315. }
  1316. //////////////////////////////////////////////////////////////////////////
  1317. void CXConsole::ExecuteDeferredCommands()
  1318. {
  1319. TDeferredCommandList::iterator it;
  1320. if (m_waitFrames)
  1321. {
  1322. --m_waitFrames;
  1323. return;
  1324. }
  1325. if (m_waitSeconds.GetValue())
  1326. {
  1327. const AZ::TimeMs elaspsedTimeMs = AZ::GetRealElapsedTimeMs();
  1328. const double elaspedTimeSec = AZ::TimeMsToSecondsDouble(elaspsedTimeMs);
  1329. if (m_waitSeconds > CTimeValue(elaspedTimeSec))
  1330. {
  1331. return;
  1332. }
  1333. // Help to avoid overflow problems
  1334. m_waitSeconds.SetValue(0);
  1335. }
  1336. const int blockCounter = m_blockCounter;
  1337. // Signal the console that we executing a deferred command
  1338. //m_deferredExecution = true;
  1339. while (!m_deferredCommands.empty())
  1340. {
  1341. it = m_deferredCommands.begin();
  1342. ExecuteStringInternal(it->command.c_str(), false, it->silentMode);
  1343. m_deferredCommands.pop_front();
  1344. // A blocker command was executed
  1345. if (m_blockCounter != blockCounter)
  1346. {
  1347. break;
  1348. }
  1349. }
  1350. }
  1351. //////////////////////////////////////////////////////////////////////////
  1352. void CXConsole::ExecuteCommand(CConsoleCommand& cmd, AZStd::string& str, bool bIgnoreDevMode)
  1353. {
  1354. CryLog ("[CONSOLE] Executing console command '%s'", str.c_str());
  1355. INDENT_LOG_DURING_SCOPE();
  1356. std::vector<AZStd::string> args;
  1357. size_t t;
  1358. {
  1359. t = 1;
  1360. const char* start = str.c_str();
  1361. const char* commandLine = start;
  1362. while (char ch = *commandLine++)
  1363. {
  1364. switch (ch)
  1365. {
  1366. case '\'':
  1367. case '\"':
  1368. {
  1369. while ((*commandLine++ != ch) && *commandLine)
  1370. {
  1371. ;
  1372. }
  1373. args.push_back(AZStd::string(start + 1, commandLine - 1));
  1374. start = commandLine;
  1375. break;
  1376. }
  1377. case ' ':
  1378. start = commandLine;
  1379. break;
  1380. default:
  1381. {
  1382. if ((*commandLine == ' ') || !*commandLine)
  1383. {
  1384. args.push_back(AZStd::string(start, commandLine));
  1385. start = commandLine + 1;
  1386. }
  1387. }
  1388. break;
  1389. }
  1390. }
  1391. if (args.size() >= 2 && args[1] == "?")
  1392. {
  1393. DisplayHelp(cmd.m_sHelp.c_str(), cmd.m_sName.c_str());
  1394. return;
  1395. }
  1396. if (((cmd.m_nFlags & (VF_CHEAT | VF_CHEAT_NOCHECK | VF_CHEAT_ALWAYS_CHECK)) != 0) && !(gEnv->IsEditor()))
  1397. {
  1398. #if LOG_CVAR_INFRACTIONS
  1399. gEnv->pLog->LogError("[CVARS]: [EXECUTE] command %s is marked [VF_CHEAT]", cmd.m_sName.c_str());
  1400. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  1401. gEnv->pSystem->debug_LogCallStack();
  1402. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  1403. #endif // LOG_CVAR_INFRACTIONS
  1404. if (!(gEnv->IsEditor()) && !(m_pSystem->IsDevMode()) && !bIgnoreDevMode)
  1405. {
  1406. return;
  1407. }
  1408. }
  1409. }
  1410. if (cmd.m_func)
  1411. {
  1412. // This is function command, execute it with a list of parameters.
  1413. CConsoleCommandArgs cmdArgs(str, args);
  1414. cmd.m_func(&cmdArgs);
  1415. return;
  1416. }
  1417. AZStd::string buf;
  1418. {
  1419. // only do this for commands with script implementation
  1420. for (;; )
  1421. {
  1422. t = str.find_first_of("\\", t);
  1423. if (t == AZStd::string::npos)
  1424. {
  1425. break;
  1426. }
  1427. str.replace(t, 1, "\\\\", 2);
  1428. t += 2;
  1429. }
  1430. for (t = 1;; )
  1431. {
  1432. t = str.find_first_of("\"", t);
  1433. if (t == AZStd::string::npos)
  1434. {
  1435. break;
  1436. }
  1437. str.replace(t, 1, "\\\"", 2);
  1438. t += 2;
  1439. }
  1440. buf = cmd.m_sCommand;
  1441. size_t pp = buf.find("%%");
  1442. if (pp != AZStd::string::npos)
  1443. {
  1444. AZStd::string list = "";
  1445. for (unsigned int i = 1; i < args.size(); i++)
  1446. {
  1447. list += "\"" + args[i] + "\"";
  1448. if (i < args.size() - 1)
  1449. {
  1450. list += ",";
  1451. }
  1452. }
  1453. buf.replace(pp, 2, list);
  1454. }
  1455. else if ((pp = buf.find("%line")) != AZStd::string::npos)
  1456. {
  1457. AZStd::string tmp = "\"" + str.substr(str.find(" ") + 1) + "\"";
  1458. if (args.size() > 1)
  1459. {
  1460. buf.replace(pp, 5, tmp);
  1461. }
  1462. else
  1463. {
  1464. buf.replace(pp, 5, "");
  1465. }
  1466. }
  1467. else
  1468. {
  1469. for (unsigned int i = 1; i <= args.size(); i++)
  1470. {
  1471. char pat[10];
  1472. azsnprintf(pat, AZ_ARRAY_SIZE(pat), "%%%d", i);
  1473. size_t pos = buf.find(pat);
  1474. if (pos == AZStd::string::npos)
  1475. {
  1476. if (i != args.size())
  1477. {
  1478. ConsoleWarning("Too many arguments for: %s", cmd.m_sName.c_str());
  1479. return;
  1480. }
  1481. }
  1482. else
  1483. {
  1484. if (i == args.size())
  1485. {
  1486. ConsoleWarning("Not enough arguments for: %s", cmd.m_sName.c_str());
  1487. return;
  1488. }
  1489. AZStd::string arg = "\"" + args[i] + "\"";
  1490. buf.replace(pos, strlen(pat), arg);
  1491. }
  1492. }
  1493. }
  1494. }
  1495. m_bDrawCursor = 0;
  1496. }
  1497. //////////////////////////////////////////////////////////////////////////
  1498. void CXConsole::Exit(const char* szExitComments, ...)
  1499. {
  1500. char sResultMessageText[1024] = "";
  1501. if (szExitComments)
  1502. {
  1503. // make result string
  1504. va_list arglist;
  1505. va_start(arglist, szExitComments);
  1506. vsprintf_s(sResultMessageText, szExitComments, arglist);
  1507. va_end(arglist);
  1508. }
  1509. else
  1510. {
  1511. azstrcpy(sResultMessageText, AZ_ARRAY_SIZE(sResultMessageText), "No comments from application");
  1512. }
  1513. CryFatalError("%s", sResultMessageText);
  1514. }
  1515. //////////////////////////////////////////////////////////////////////////
  1516. void CXConsole::RegisterAutoComplete(const char* sVarOrCommand, IConsoleArgumentAutoComplete* pArgAutoComplete)
  1517. {
  1518. m_mapArgumentAutoComplete[sVarOrCommand] = pArgAutoComplete;
  1519. }
  1520. //////////////////////////////////////////////////////////////////////////
  1521. void CXConsole::UnRegisterAutoComplete(const char* sVarOrCommand)
  1522. {
  1523. ArgumentAutoCompleteMap::iterator it;
  1524. it = m_mapArgumentAutoComplete.find(sVarOrCommand);
  1525. if (it != m_mapArgumentAutoComplete.end())
  1526. {
  1527. m_mapArgumentAutoComplete.erase(it);
  1528. }
  1529. }
  1530. //////////////////////////////////////////////////////////////////////////
  1531. void CXConsole::ResetAutoCompletion()
  1532. {
  1533. m_nTabCount = 0;
  1534. m_sPrevTab = "";
  1535. }
  1536. //////////////////////////////////////////////////////////////////////////
  1537. const char* CXConsole::ProcessCompletion(const char* szInputBuffer)
  1538. {
  1539. m_sInputBuffer = szInputBuffer;
  1540. int offset = (szInputBuffer[0] == '\\' ? 1 : 0); // legacy support
  1541. if ((m_sPrevTab.size() > strlen(szInputBuffer + offset)) || _strnicmp(m_sPrevTab.c_str(), (szInputBuffer + offset), m_sPrevTab.size()))
  1542. {
  1543. m_nTabCount = 0;
  1544. m_sPrevTab = "";
  1545. }
  1546. if (m_sInputBuffer.empty())
  1547. {
  1548. return (char*)m_sInputBuffer.c_str();
  1549. }
  1550. int nMatch = 0;
  1551. ConsoleCommandsMapItor itrCmds;
  1552. ConsoleVariablesMapItor itrVars;
  1553. bool showlist = !m_nTabCount && m_sPrevTab == "";
  1554. if (m_nTabCount == 0)
  1555. {
  1556. if (m_sInputBuffer.size() > 0)
  1557. {
  1558. if (m_sInputBuffer[0] == '\\')
  1559. {
  1560. m_sPrevTab = &m_sInputBuffer.c_str()[1]; // legacy support
  1561. }
  1562. else
  1563. {
  1564. m_sPrevTab = m_sInputBuffer;
  1565. }
  1566. }
  1567. else
  1568. {
  1569. m_sPrevTab = "";
  1570. }
  1571. }
  1572. //try to search in command list
  1573. bool bArgumentAutoComplete = false;
  1574. std::vector<AZStd::string> matches;
  1575. if (m_sPrevTab.find(' ') != AZStd::string::npos)
  1576. {
  1577. bool bProcessAutoCompl = true;
  1578. // Find command.
  1579. AZStd::string sVar = m_sPrevTab.substr(0, m_sPrevTab.find(' '));
  1580. ICVar* pCVar = GetCVar(sVar.c_str());
  1581. if (pCVar)
  1582. {
  1583. if (!(pCVar->GetFlags() & VF_RESTRICTEDMODE) && con_restricted) // in restricted mode we allow only VF_RESTRICTEDMODE CVars&CCmd
  1584. {
  1585. bProcessAutoCompl = false;
  1586. }
  1587. }
  1588. ConsoleCommandsMap::iterator it = m_mapCommands.find(sVar);
  1589. if (it != m_mapCommands.end())
  1590. {
  1591. CConsoleCommand& ccmd = it->second;
  1592. if (!(ccmd.m_nFlags & VF_RESTRICTEDMODE) && con_restricted) // in restricted mode we allow only VF_RESTRICTEDMODE CVars&CCmd
  1593. {
  1594. bProcessAutoCompl = false;
  1595. }
  1596. }
  1597. if (bProcessAutoCompl)
  1598. {
  1599. IConsoleArgumentAutoComplete* pArgumentAutoComplete = stl::find_in_map(m_mapArgumentAutoComplete, sVar, 0);
  1600. if (pArgumentAutoComplete)
  1601. {
  1602. int nMatches = pArgumentAutoComplete->GetCount();
  1603. for (int i = 0; i < nMatches; i++)
  1604. {
  1605. AZStd::string cmd = AZStd::string(sVar) + " " + pArgumentAutoComplete->GetValue(i);
  1606. if (_strnicmp(m_sPrevTab.c_str(), cmd.c_str(), m_sPrevTab.length()) == 0)
  1607. {
  1608. {
  1609. bArgumentAutoComplete = true;
  1610. matches.push_back(cmd);
  1611. }
  1612. }
  1613. }
  1614. }
  1615. }
  1616. }
  1617. if (!bArgumentAutoComplete)
  1618. {
  1619. itrCmds = m_mapCommands.begin();
  1620. while (itrCmds != m_mapCommands.end())
  1621. {
  1622. CConsoleCommand& cmd = itrCmds->second;
  1623. if ((cmd.m_nFlags & VF_RESTRICTEDMODE) || !con_restricted) // in restricted mode we allow only VF_RESTRICTEDMODE CVars&CCmd
  1624. {
  1625. if (_strnicmp(m_sPrevTab.c_str(), itrCmds->first.c_str(), m_sPrevTab.length()) == 0)
  1626. {
  1627. {
  1628. matches.push_back((char* const)itrCmds->first.c_str());
  1629. }
  1630. }
  1631. }
  1632. ++itrCmds;
  1633. }
  1634. // try to search in console variables
  1635. itrVars = m_mapVariables.begin();
  1636. while (itrVars != m_mapVariables.end())
  1637. {
  1638. ICVar* pVar = itrVars->second;
  1639. if ((pVar->GetFlags() & VF_RESTRICTEDMODE) || !con_restricted) // in restricted mode we allow only VF_RESTRICTEDMODE CVars&CCmd
  1640. {//if(itrVars->first.compare(0,m_sPrevTab.length(),m_sPrevTab)==0)
  1641. if (_strnicmp(m_sPrevTab.c_str(), itrVars->first, m_sPrevTab.length()) == 0)
  1642. {
  1643. {
  1644. matches.push_back((char* const)itrVars->first);
  1645. }
  1646. }
  1647. }
  1648. ++itrVars;
  1649. }
  1650. }
  1651. if (!matches.empty())
  1652. {
  1653. std::sort(matches.begin(), matches.end()); // to sort commands with variables
  1654. }
  1655. if (showlist && !matches.empty())
  1656. {
  1657. ConsoleLogInput(" "); // empty line before auto completion
  1658. for (std::vector<AZStd::string>::iterator i = matches.begin(); i != matches.end(); ++i)
  1659. {
  1660. // List matching variables
  1661. const char* sVar = i->c_str();
  1662. ICVar* pVar = GetCVar(sVar);
  1663. if (pVar)
  1664. {
  1665. DisplayVarValue(pVar);
  1666. }
  1667. else
  1668. {
  1669. ConsoleLogInputResponse(" $3%s $6(Command)", sVar);
  1670. }
  1671. }
  1672. }
  1673. for (std::vector<AZStd::string>::iterator i = matches.begin(); i != matches.end(); ++i)
  1674. {
  1675. if (m_nTabCount <= nMatch)
  1676. {
  1677. m_sInputBuffer = *i;
  1678. m_sInputBuffer += " ";
  1679. m_nTabCount = nMatch + 1;
  1680. return (char*)m_sInputBuffer.c_str();
  1681. }
  1682. nMatch++;
  1683. }
  1684. if (m_nTabCount > 0)
  1685. {
  1686. m_nTabCount = 0;
  1687. m_sInputBuffer = m_sPrevTab;
  1688. m_sInputBuffer = ProcessCompletion(m_sInputBuffer.c_str());
  1689. }
  1690. return (char*)m_sInputBuffer.c_str();
  1691. }
  1692. //////////////////////////////////////////////////////////////////////////
  1693. void CXConsole::DisplayVarValue(ICVar* pVar)
  1694. {
  1695. if (!pVar)
  1696. {
  1697. return;
  1698. }
  1699. const char* sFlagsString = GetFlagsString(pVar->GetFlags());
  1700. AZStd::string sValue = (pVar->GetFlags() & VF_INVISIBLE) ? "" : pVar->GetString();
  1701. AZStd::string sVar = pVar->GetName();
  1702. char szRealState[40] = "";
  1703. if (pVar->GetType() == CVAR_INT)
  1704. {
  1705. int iRealState = pVar->GetRealIVal();
  1706. if (iRealState != pVar->GetIVal())
  1707. {
  1708. if (iRealState == -1)
  1709. {
  1710. azstrcpy(szRealState, AZ_ARRAY_SIZE(szRealState), " RealState=Custom");
  1711. }
  1712. else
  1713. {
  1714. sprintf_s(szRealState, " RealState=%d", iRealState);
  1715. }
  1716. }
  1717. }
  1718. if (pVar->GetFlags() & VF_BITFIELD)
  1719. {
  1720. uint64 val64 = pVar->GetI64Val();
  1721. uint64 alphaBits = val64 & ~63LL;
  1722. uint32 nonAlphaBits = val64 & 63;
  1723. if (alphaBits != 0)
  1724. {
  1725. // the bottom 6 bits can't be set by char entry, so show them separately
  1726. char alphaChars[65]; // 1 char per bit + '\0'
  1727. BitsAlpha64(alphaBits, alphaChars);
  1728. sValue += " (";
  1729. if (nonAlphaBits != 0)
  1730. {
  1731. char nonAlphaChars[3] = { 0 }; // 1..63 + '\0'
  1732. azitoa(nonAlphaBits, nonAlphaChars, AZ_ARRAY_SIZE(nonAlphaChars), 10);
  1733. sValue += nonAlphaChars;
  1734. sValue += ", ";
  1735. }
  1736. sValue += alphaChars;
  1737. sValue += ")";
  1738. }
  1739. }
  1740. if (gEnv->IsEditor())
  1741. {
  1742. ConsoleLogInputResponse("%s=%s [ %s ]%s", sVar.c_str(), sValue.c_str(), sFlagsString, szRealState);
  1743. }
  1744. else
  1745. {
  1746. ConsoleLogInputResponse(" $3%s = $6%s $5[%s]$4%s", sVar.c_str(), sValue.c_str(), sFlagsString, szRealState);
  1747. }
  1748. }
  1749. //////////////////////////////////////////////////////////////////////////
  1750. bool CXConsole::IsOpened()
  1751. {
  1752. return m_nScrollPos == m_nTempScrollMax;
  1753. }
  1754. //////////////////////////////////////////////////////////////////////////
  1755. void CXConsole::PrintLine(AZStd::string_view s)
  1756. {
  1757. AddLine(s);
  1758. }
  1759. //////////////////////////////////////////////////////////////////////////
  1760. void CXConsole::PrintLineAppendWithPrevLine(AZStd::string_view s)
  1761. {
  1762. AddLineAppendWithPrevLine(s);
  1763. }
  1764. //////////////////////////////////////////////////////////////////////////
  1765. void CXConsole::AddLine(AZStd::string_view inputStr)
  1766. {
  1767. if (inputStr.empty())
  1768. {
  1769. return;
  1770. }
  1771. // split out each line
  1772. auto ParseLine = [this](AZStd::string line)
  1773. {
  1774. m_dqConsoleBuffer.push_back(line);
  1775. // Remove any lines that are larger than the console line buffer size
  1776. // set via the console variable "con_line_buffer_size"
  1777. while (static_cast<int>(m_dqConsoleBuffer.size()) > con_line_buffer_size)
  1778. {
  1779. m_dqConsoleBuffer.pop_front();
  1780. }
  1781. // tell everyone who is interested (e.g. dedicated server printout)
  1782. for (IOutputPrintSink* outputSink : m_OutputSinks)
  1783. {
  1784. outputSink->Print(line.c_str());
  1785. }
  1786. };
  1787. AZ::StringFunc::TokenizeVisitor(inputStr, ParseLine, "\r\n");
  1788. }
  1789. //////////////////////////////////////////////////////////////////////////
  1790. void CXConsole::AddOutputPrintSink(IOutputPrintSink* inpSink)
  1791. {
  1792. assert(inpSink);
  1793. m_OutputSinks.push_back(inpSink);
  1794. }
  1795. //////////////////////////////////////////////////////////////////////////
  1796. void CXConsole::RemoveOutputPrintSink(IOutputPrintSink* inpSink)
  1797. {
  1798. assert(inpSink);
  1799. int nCount = static_cast<int>(m_OutputSinks.size());
  1800. for (int i = 0; i < nCount; i++)
  1801. {
  1802. if (m_OutputSinks[i] == inpSink)
  1803. {
  1804. if (nCount <= 1)
  1805. {
  1806. m_OutputSinks.clear();
  1807. }
  1808. else
  1809. {
  1810. m_OutputSinks[i] = m_OutputSinks.back();
  1811. m_OutputSinks.pop_back();
  1812. }
  1813. return;
  1814. }
  1815. }
  1816. assert(0);
  1817. }
  1818. //////////////////////////////////////////////////////////////////////////
  1819. void CXConsole::AddLineAppendWithPrevLine(AZStd::string_view inputStr)
  1820. {
  1821. if (m_dqConsoleBuffer.empty())
  1822. {
  1823. // Append is only allowed if there was an existing previous line
  1824. return;
  1825. }
  1826. // Replace line separators with spaces
  1827. auto ParseLine = [this, firstIteration = true](AZStd::string_view line) mutable
  1828. {
  1829. // Add <space> between lines
  1830. if (!firstIteration)
  1831. {
  1832. m_dqConsoleBuffer.back() += ' ';
  1833. }
  1834. firstIteration = false;
  1835. // Append the now space separated string into with the last line
  1836. // in the console buffer double ended queue
  1837. m_dqConsoleBuffer.back() += line;
  1838. };
  1839. AZ::StringFunc::TokenizeVisitor(inputStr, ParseLine, "\r\n");
  1840. // tell everyone who is interested (e.g. dedicated server printout)
  1841. for (IOutputPrintSink* outputSink : m_OutputSinks)
  1842. {
  1843. outputSink->Print(m_dqConsoleBuffer.back().c_str());
  1844. }
  1845. }
  1846. //////////////////////////////////////////////////////////////////////////
  1847. void CXConsole::AddInputUTF8(const AZStd::string& textUTF8)
  1848. {
  1849. // Ignore control characters like backspace and tab
  1850. AZStd::string textUTF8ToInsert;
  1851. for (int i = 0; i < textUTF8.length(); ++i)
  1852. {
  1853. const char charToInsert = textUTF8.at(i);
  1854. if (!AZStd::is_cntrl(charToInsert))
  1855. {
  1856. textUTF8ToInsert += charToInsert;
  1857. }
  1858. }
  1859. const char* utf8_buf = textUTF8ToInsert.c_str();
  1860. if (m_nCursorPos < (int)(m_sInputBuffer.length()))
  1861. {
  1862. m_sInputBuffer.insert(m_nCursorPos, utf8_buf);
  1863. }
  1864. else
  1865. {
  1866. m_sInputBuffer = m_sInputBuffer + utf8_buf;
  1867. }
  1868. m_nCursorPos += strlen(utf8_buf);
  1869. }
  1870. //////////////////////////////////////////////////////////////////////////
  1871. void CXConsole::ExecuteInputBuffer()
  1872. {
  1873. AZStd::string sTemp = m_sInputBuffer;
  1874. if (m_sInputBuffer.empty())
  1875. {
  1876. return;
  1877. }
  1878. m_sInputBuffer = "";
  1879. AddCommandToHistory(sTemp.c_str());
  1880. {
  1881. ExecuteStringInternal(sTemp.c_str(), true); // from console
  1882. }
  1883. m_nCursorPos = 0;
  1884. }
  1885. //////////////////////////////////////////////////////////////////////////
  1886. void CXConsole::RemoveInputChar(bool bBackSpace)
  1887. {
  1888. if (m_sInputBuffer.empty())
  1889. {
  1890. return;
  1891. }
  1892. if (bBackSpace)
  1893. {
  1894. if (m_nCursorPos > 0)
  1895. {
  1896. const char* const pBase = m_sInputBuffer.c_str();
  1897. const char* pCursor = pBase + m_nCursorPos;
  1898. const char* const pEnd = pCursor;
  1899. pCursor -= Utf8::Internal::sequence_length(pCursor); // Remove one UCS code-point, doesn't account for combining diacritics
  1900. size_t length = pEnd - pCursor;
  1901. m_sInputBuffer.erase(pCursor - pBase, length);
  1902. m_nCursorPos -= length;
  1903. }
  1904. }
  1905. else
  1906. {
  1907. if (m_nCursorPos < (int)(m_sInputBuffer.length()))
  1908. {
  1909. const char* const pBase = m_sInputBuffer.c_str();
  1910. const char* pCursor = pBase + m_nCursorPos;
  1911. const char* const pBegin = pCursor;
  1912. pCursor -= Utf8::Internal::sequence_length(pCursor); // Remove one UCS code-point, doesn't account for combining diacritics
  1913. size_t length = pCursor - pBegin;
  1914. m_sInputBuffer.erase(pBegin - pBase, length);
  1915. }
  1916. }
  1917. }
  1918. //////////////////////////////////////////////////////////////////////////
  1919. void CXConsole::AddCommandToHistory(const char* szCommand)
  1920. {
  1921. assert(szCommand);
  1922. m_nHistoryPos = -1;
  1923. if (!m_dqHistory.empty())
  1924. {
  1925. // add only if the command is != than the last
  1926. if (m_dqHistory.front() != szCommand)
  1927. {
  1928. m_dqHistory.push_front(szCommand);
  1929. }
  1930. }
  1931. else
  1932. {
  1933. m_dqHistory.push_front(szCommand);
  1934. }
  1935. while (m_dqHistory.size() > MAX_HISTORY_ENTRIES)
  1936. {
  1937. m_dqHistory.pop_back();
  1938. }
  1939. }
  1940. //////////////////////////////////////////////////////////////////////////
  1941. void CXConsole::Copy()
  1942. {
  1943. #ifdef AZ_PLATFORM_WINDOWS
  1944. if (m_sInputBuffer.empty())
  1945. {
  1946. return;
  1947. }
  1948. if (!OpenClipboard(NULL))
  1949. {
  1950. return;
  1951. }
  1952. size_t stringLength = m_sInputBuffer.length();
  1953. size_t requiredBufferSize = stringLength + 1; // for the null
  1954. if (HGLOBAL hGlobal = GlobalAlloc(GHND, requiredBufferSize))
  1955. {
  1956. if (LPVOID pGlobal = GlobalLock(hGlobal))
  1957. {
  1958. azstrcpy((char*)pGlobal, requiredBufferSize, m_sInputBuffer.c_str());
  1959. GlobalUnlock(hGlobal);
  1960. EmptyClipboard();
  1961. SetClipboardData(CF_TEXT, hGlobal);
  1962. CloseClipboard();
  1963. }
  1964. }
  1965. return;
  1966. #endif //AZ_PLATFORM_WINDOWS
  1967. }
  1968. //////////////////////////////////////////////////////////////////////////
  1969. void CXConsole::Paste()
  1970. {
  1971. #if defined(AZ_PLATFORM_WINDOWS)
  1972. if (OpenClipboard(NULL) != 0)
  1973. {
  1974. AZStd::string data;
  1975. const HANDLE wideData = GetClipboardData(CF_UNICODETEXT);
  1976. if (wideData)
  1977. {
  1978. const LPCWSTR pWideData = (LPCWSTR)GlobalLock(wideData);
  1979. if (pWideData)
  1980. {
  1981. AZStd::to_string(data, pWideData);
  1982. GlobalUnlock(wideData);
  1983. }
  1984. }
  1985. CloseClipboard();
  1986. Utf8::Unchecked::octet_iterator end(data.end());
  1987. for (Utf8::Unchecked::octet_iterator it(data.begin()); it != end; ++it)
  1988. {
  1989. const wchar_t cp = static_cast<wchar_t>(*it);
  1990. if (cp != '\r')
  1991. {
  1992. // Convert UCS code-point into UTF-8 string
  1993. AZStd::fixed_string<5> utf8_buf = {0};
  1994. AZStd::to_string(utf8_buf.data(), 5, { &cp, 1 });
  1995. AddInputUTF8(utf8_buf.c_str());
  1996. }
  1997. }
  1998. }
  1999. #endif //AZ_PLATFORM_WINDOWS
  2000. }
  2001. //////////////////////////////////////////////////////////////////////////
  2002. int CXConsole::GetNumVars()
  2003. {
  2004. return static_cast<int>(m_mapVariables.size());
  2005. }
  2006. //////////////////////////////////////////////////////////////////////////
  2007. int CXConsole::GetNumVisibleVars()
  2008. {
  2009. int numVars = 0;
  2010. for (auto& v : m_mapVariables)
  2011. {
  2012. if ((v.second->GetFlags() & VF_INVISIBLE) == 0)
  2013. ++numVars;
  2014. }
  2015. return numVars;
  2016. }
  2017. //////////////////////////////////////////////////////////////////////////
  2018. size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, const char* szPrefix)
  2019. {
  2020. // This method used to insert instead of push_back, so we need to clear first
  2021. pszArray.clear();
  2022. size_t iPrefixLen = szPrefix ? strlen(szPrefix) : 0;
  2023. // variables
  2024. {
  2025. ConsoleVariablesMap::const_iterator it, end = m_mapVariables.end();
  2026. for (it = m_mapVariables.begin(); it != end; ++it)
  2027. {
  2028. if (szPrefix)
  2029. {
  2030. if (_strnicmp(it->first, szPrefix, iPrefixLen) != 0)
  2031. {
  2032. continue;
  2033. }
  2034. }
  2035. if (it->second->GetFlags() & VF_INVISIBLE)
  2036. {
  2037. continue;
  2038. }
  2039. pszArray.push_back(it->first);
  2040. }
  2041. }
  2042. // commands
  2043. {
  2044. ConsoleCommandsMap::iterator it, end = m_mapCommands.end();
  2045. for (it = m_mapCommands.begin(); it != end; ++it)
  2046. {
  2047. if (szPrefix)
  2048. {
  2049. if (_strnicmp(it->first.c_str(), szPrefix, iPrefixLen) != 0)
  2050. {
  2051. continue;
  2052. }
  2053. }
  2054. if (it->second.m_nFlags & VF_INVISIBLE)
  2055. {
  2056. continue;
  2057. }
  2058. pszArray.push_back(it->first.c_str());
  2059. }
  2060. }
  2061. std::sort(pszArray.begin(), pszArray.end());
  2062. return pszArray.size();
  2063. }
  2064. //////////////////////////////////////////////////////////////////////////
  2065. void CXConsole::FindVar(const char* substr)
  2066. {
  2067. AZStd::vector<AZStd::string_view> cmds;
  2068. size_t cmdCount = GetSortedVars(cmds);
  2069. for (size_t i = 0; i < cmdCount; i++)
  2070. {
  2071. if (AZ::StringFunc::Find(cmds[i], substr) != AZStd::string::npos)
  2072. {
  2073. ICVar* pCvar = gEnv->pConsole->GetCVar(cmds[i].data());
  2074. if (pCvar)
  2075. {
  2076. DisplayVarValue(pCvar);
  2077. }
  2078. else
  2079. {
  2080. ConsoleLogInputResponse(" $3%.*s $6(Command)", aznumeric_cast<int>(cmds[i].size()), cmds[i].data());
  2081. }
  2082. }
  2083. }
  2084. }
  2085. //////////////////////////////////////////////////////////////////////////
  2086. const char* CXConsole::AutoComplete(const char* substr)
  2087. {
  2088. // following code can be optimized
  2089. AZStd::vector<AZStd::string_view> cmds;
  2090. size_t cmdCount = GetSortedVars(cmds);
  2091. size_t substrLen = substr ? strlen(substr) : 0;
  2092. // If substring is empty return first command.
  2093. if (substrLen == 0 && cmdCount > 0)
  2094. {
  2095. return cmds[0].data();
  2096. }
  2097. // find next
  2098. for (size_t i = 0; i < cmdCount; i++)
  2099. {
  2100. const char* szCmd = cmds[i].data();
  2101. size_t cmdlen = cmds[i].size();
  2102. if (cmdlen >= substrLen && memcmp(szCmd, substr, substrLen) == 0)
  2103. {
  2104. if (substrLen == cmdlen)
  2105. {
  2106. i++;
  2107. if (i < cmdCount)
  2108. {
  2109. return cmds[i].data();
  2110. }
  2111. return cmds[i - 1].data();
  2112. }
  2113. return cmds[i].data();
  2114. }
  2115. }
  2116. // then first matching case insensitive
  2117. for (size_t i = 0; i < cmdCount; i++)
  2118. {
  2119. const char* szCmd = cmds[i].data();
  2120. size_t cmdlen = cmds[i].size();
  2121. if (cmdlen >= substrLen && azstrnicmp(szCmd, substr, substrLen) == 0)
  2122. {
  2123. if (substrLen == cmdlen)
  2124. {
  2125. i++;
  2126. if (i < cmdCount)
  2127. {
  2128. return cmds[i].data();
  2129. }
  2130. return cmds[i - 1].data();
  2131. }
  2132. return cmds[i].data();
  2133. }
  2134. }
  2135. // Not found.
  2136. return "";
  2137. }
  2138. void CXConsole::SetInputLine(const char* szLine)
  2139. {
  2140. assert(szLine);
  2141. m_sInputBuffer = szLine;
  2142. m_nCursorPos = m_sInputBuffer.size();
  2143. }
  2144. //////////////////////////////////////////////////////////////////////////
  2145. const char* CXConsole::AutoCompletePrev(const char* substr)
  2146. {
  2147. AZStd::vector<AZStd::string_view> cmds;
  2148. GetSortedVars(cmds);
  2149. // If substring is empty return last command.
  2150. if (strlen(substr) == 0 && !cmds.empty())
  2151. {
  2152. return cmds.back().data();
  2153. }
  2154. for (const AZStd::string_view& cmd : cmds)
  2155. {
  2156. if (azstricmp(substr, cmd.data()) == 0)
  2157. {
  2158. return cmd.data();
  2159. }
  2160. }
  2161. return AutoComplete(substr);
  2162. }
  2163. //////////////////////////////////////////////////////////////////////////
  2164. inline size_t sizeOf (const AZStd::string& str)
  2165. {
  2166. return str.capacity() + 1;
  2167. }
  2168. //////////////////////////////////////////////////////////////////////////
  2169. inline size_t sizeOf (const char* sz)
  2170. {
  2171. return sz ? strlen(sz) + 1 : 0;
  2172. }
  2173. //////////////////////////////////////////////////////////////////////////
  2174. void CXConsole::ConsoleLogInputResponse(const char* format, ...)
  2175. {
  2176. va_list args;
  2177. va_start(args, format);
  2178. gEnv->pLog->LogV(ILog::eInputResponse, format, args);
  2179. va_end(args);
  2180. }
  2181. //////////////////////////////////////////////////////////////////////////
  2182. void CXConsole::ConsoleLogInput(const char* format, ...)
  2183. {
  2184. va_list args;
  2185. va_start(args, format);
  2186. gEnv->pLog->LogV(ILog::eInput, format, args);
  2187. va_end(args);
  2188. }
  2189. //////////////////////////////////////////////////////////////////////////
  2190. void CXConsole::ConsoleWarning(const char* format, ...)
  2191. {
  2192. va_list args;
  2193. va_start(args, format);
  2194. gEnv->pLog->LogV(ILog::eWarningAlways, format, args);
  2195. va_end(args);
  2196. }
  2197. //////////////////////////////////////////////////////////////////////////
  2198. bool CXConsole::OnBeforeVarChange(ICVar* pVar, const char* sNewValue)
  2199. {
  2200. bool isConst = pVar->IsConstCVar();
  2201. bool isCheat = ((pVar->GetFlags() & (VF_CHEAT | VF_CHEAT_NOCHECK | VF_CHEAT_ALWAYS_CHECK)) != 0);
  2202. bool isReadOnly = ((pVar->GetFlags() & VF_READONLY) != 0);
  2203. bool isDeprecated = ((pVar->GetFlags() & VF_DEPRECATED) != 0);
  2204. if (
  2205. #if CVAR_GROUPS_ARE_PRIVILEGED
  2206. !m_bIsProcessingGroup &&
  2207. #endif // !CVAR_GROUPS_ARE_PRIVILEGED
  2208. (isConst || isCheat || isReadOnly || isDeprecated))
  2209. {
  2210. bool allowChange = !isDeprecated && ((gEnv->pSystem->IsDevMode()) || (gEnv->IsEditor()));
  2211. if (!(gEnv->IsEditor()) || isDeprecated)
  2212. {
  2213. #if LOG_CVAR_INFRACTIONS
  2214. LogChangeMessage(pVar->GetName(), isConst, isCheat,
  2215. isReadOnly, isDeprecated, pVar->GetString(), sNewValue, m_bIsProcessingGroup, allowChange);
  2216. #if LOG_CVAR_INFRACTIONS_CALLSTACK
  2217. gEnv->pSystem->debug_LogCallStack();
  2218. #endif // LOG_CVAR_INFRACTIONS_CALLSTACK
  2219. #endif // LOG_CVAR_INFRACTIONS
  2220. }
  2221. if (!allowChange && !ALLOW_CONST_CVAR_MODIFICATIONS)
  2222. {
  2223. return false;
  2224. }
  2225. }
  2226. if (!m_consoleVarSinks.empty())
  2227. {
  2228. ConsoleVarSinks::iterator it, next;
  2229. for (it = m_consoleVarSinks.begin(); it != m_consoleVarSinks.end(); it = next)
  2230. {
  2231. next = it;
  2232. next++;
  2233. if (!(*it)->OnBeforeVarChange(pVar, sNewValue))
  2234. {
  2235. return false;
  2236. }
  2237. }
  2238. }
  2239. return true;
  2240. }
  2241. //////////////////////////////////////////////////////////////////////////
  2242. void CXConsole::OnAfterVarChange(ICVar* pVar)
  2243. {
  2244. if (!m_consoleVarSinks.empty())
  2245. {
  2246. ConsoleVarSinks::iterator it, next;
  2247. for (it = m_consoleVarSinks.begin(); it != m_consoleVarSinks.end(); it = next)
  2248. {
  2249. next = it;
  2250. next++;
  2251. (*it)->OnAfterVarChange(pVar);
  2252. }
  2253. }
  2254. }
  2255. //////////////////////////////////////////////////////////////////////////
  2256. void CXConsole_ExecuteCommandTrampoline(IConsoleCmdArgs* args)
  2257. {
  2258. if (gEnv && gEnv->pSystem && gEnv->pSystem->GetIConsole())
  2259. {
  2260. CXConsole* pConsole = static_cast<CXConsole*>(gEnv->pSystem->GetIConsole());
  2261. pConsole->ExecuteRegisteredCommand(args);
  2262. }
  2263. }
  2264. //////////////////////////////////////////////////////////////////////////
  2265. void CXConsole::ExecuteRegisteredCommand(IConsoleCmdArgs* args)
  2266. {
  2267. if (args->GetArgCount() == 0)
  2268. {
  2269. AZ_Error("console", false, "Invalid number of args sent");
  2270. return;
  2271. }
  2272. const char* commandIdentifier = args->GetArg(0);
  2273. auto itCommandEntry = m_commandRegistrationMap.find(commandIdentifier);
  2274. if (itCommandEntry == m_commandRegistrationMap.end())
  2275. {
  2276. AZ_Error("console", false, "Command %s not found in the command registry", commandIdentifier);
  2277. return;
  2278. }
  2279. AZStd::vector<AZStd::string_view> input;
  2280. input.reserve(args->GetArgCount());
  2281. for (int i = 0; i < args->GetArgCount(); ++i)
  2282. {
  2283. input.push_back(args->GetArg(i));
  2284. }
  2285. CommandRegistrationEntry& entry = itCommandEntry->second;
  2286. const AzFramework::CommandResult output = entry.m_callback(input);
  2287. if (output != AzFramework::CommandResult::Success)
  2288. {
  2289. if (output == AzFramework::CommandResult::ErrorWrongNumberOfArguments)
  2290. {
  2291. AZ_Warning("console", false, "Command does not have the right number of arguments (send = %d)", input.size());
  2292. }
  2293. else
  2294. {
  2295. AZ_Warning("console", false, "Command returned a generic error");
  2296. }
  2297. }
  2298. }
  2299. //////////////////////////////////////////////////////////////////////////
  2300. bool CXConsole::RegisterCommand(AZStd::string_view identifier, AZStd::string_view helpText, AZ::u32 commandFlags, AzFramework::CommandFunction callback)
  2301. {
  2302. if (identifier.empty())
  2303. {
  2304. AZ_Error("console", false, "RegisterCommand() requires a valid identifier");
  2305. return false;
  2306. }
  2307. if (!callback)
  2308. {
  2309. AZ_Error("console", false, "RegisterCommand() requires a valid callback");
  2310. return false;
  2311. }
  2312. if (m_commandRegistrationMap.find(identifier) != m_commandRegistrationMap.end())
  2313. {
  2314. AZ_Warning("console", false, "Command '%.*s' already found in the command registry.", static_cast<int>(identifier.size()), identifier.data());
  2315. return false;
  2316. }
  2317. // command flags should match "enum EVarFlags" values
  2318. const int flags = static_cast<int>(commandFlags);
  2319. CommandRegistrationEntry entry;
  2320. entry.m_callback = callback;
  2321. entry.m_id = identifier;
  2322. if (!helpText.empty())
  2323. {
  2324. entry.m_helpText = helpText;
  2325. }
  2326. if (!AddCommand(entry.m_id.c_str(), CXConsole_ExecuteCommandTrampoline, flags, entry.m_helpText.empty() ? nullptr : entry.m_helpText.c_str()))
  2327. {
  2328. AZ_Warning("console", false, "Command %s already found in the command registry.", entry.m_id.c_str());
  2329. return false;
  2330. }
  2331. m_commandRegistrationMap.insert(AZStd::make_pair(entry.m_id, entry));
  2332. return true;
  2333. }
  2334. //////////////////////////////////////////////////////////////////////////
  2335. bool CXConsole::UnregisterCommand(AZStd::string_view identifier)
  2336. {
  2337. if (m_commandRegistrationMap.erase(identifier) == 1)
  2338. {
  2339. RemoveCommand(identifier.data());
  2340. return true;
  2341. }
  2342. return false;
  2343. }
  2344. //////////////////////////////////////////////////////////////////////////
  2345. void CXConsole::AddConsoleVarSink(IConsoleVarSink* pSink)
  2346. {
  2347. m_consoleVarSinks.push_back(pSink);
  2348. }
  2349. //////////////////////////////////////////////////////////////////////////
  2350. void CXConsole::RemoveConsoleVarSink(IConsoleVarSink* pSink)
  2351. {
  2352. m_consoleVarSinks.remove(pSink);
  2353. }