From 2bb02acd603a7f6961e5e9acd9040d041e67cdb5 Mon Sep 17 00:00:00 2001 From: Amos <48657826+Mauler125@users.noreply.github.com> Date: Mon, 17 Jan 2022 23:20:03 +0100 Subject: [PATCH] Implement autocomplete logic for in-game console. Autocompletes based on user input. Also shows ConVar's current value. --- r5dev/gameui/IConsole.cpp | 328 +++++++++++++++++++++++++------- r5dev/gameui/IConsole.h | 58 ++++-- r5dev/launcher/IApplication.cpp | 5 + r5dev/tier0/IConVar.cpp | 2 + r5dev/tier0/cvar.cpp | 3 + r5dev/tier0/cvar.h | 3 + 6 files changed, 315 insertions(+), 84 deletions(-) diff --git a/r5dev/gameui/IConsole.cpp b/r5dev/gameui/IConsole.cpp index b82c7587..6934785f 100644 --- a/r5dev/gameui/IConsole.cpp +++ b/r5dev/gameui/IConsole.cpp @@ -33,9 +33,9 @@ CConsole::CConsole() m_bScrollToBottom = false; m_bInitialized = false; - m_ivCommands.push_back("CLEAR"); - m_ivCommands.push_back("HELP"); - m_ivCommands.push_back("HISTORY"); + m_vsvCommands.push_back("CLEAR"); + m_vsvCommands.push_back("HELP"); + m_vsvCommands.push_back("HISTORY"); } //----------------------------------------------------------------------------- @@ -44,14 +44,11 @@ CConsole::CConsole() CConsole::~CConsole() { ClearLog(); - for (int i = 0; i < m_ivHistory.Size; i++) - { - free(m_ivHistory[i]); - } + m_vsvHistory.clear(); } //----------------------------------------------------------------------------- -// Purpose: draws the console frontend +// Purpose: game console main render loop //----------------------------------------------------------------------------- void CConsole::Draw(const char* pszTitle, bool* bDraw) { @@ -59,32 +56,69 @@ void CConsole::Draw(const char* pszTitle, bool* bDraw) { SetStyleVar(); m_bInitialized = true; + m_pszConsoleTitle = pszTitle; } - //ImGui::ShowStyleEditor(); - ImGui::SetNextWindowSize(ImVec2(1000, 600), ImGuiCond_FirstUseEver); - ImGui::SetWindowPos(ImVec2(-1000, 50), ImGuiCond_FirstUseEver); + { // BASEPANEL SETUP. + if (!*bDraw) + { + m_bActivate = false; + return; + } + ImGui::SetNextWindowSize(ImVec2(1000, 600), ImGuiCond_FirstUseEver); + ImGui::SetWindowPos(ImVec2(-1000, 50), ImGuiCond_FirstUseEver); - if (!ImGui::Begin(pszTitle, bDraw)) + BasePanel(bDraw); + } + + { // SUGGESTION PANEL SETUP + if (CanAutoComplete()) + { + SuggestPanel(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: runs tasks for the console while not being drawn +//----------------------------------------------------------------------------- +void CConsole::Think(void) +{ + if (g_pImGuiConfig->IConsole_Config.m_bAutoClear) // Check if Auto-Clear is enabled. + { + // Loop and clear the beginning of the vector until we are below our limit. + while (m_ivConLog.Size > g_pImGuiConfig->IConsole_Config.m_nAutoClearLimit) + { + m_ivConLog.erase(m_ivConLog.begin()); + } + while ((int)m_vsvHistory.size() > 512) + { + m_vsvHistory.erase(m_vsvHistory.begin()); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the console's main surface +//----------------------------------------------------------------------------- +void CConsole::BasePanel(bool* bDraw) +{ + if (!ImGui::Begin(m_pszConsoleTitle, bDraw)) { ImGui::End(); return; } - if (!*bDraw) - { - m_bActivate = false; - } // Reserve enough left-over height and width for 1 separator + 1 input text const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - const float footer_width_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetWindowWidth(); + const float footer_width_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetWindowWidth(); /////////////////////////////////////////////////////////////////////// ImGui::Separator(); if (ImGui::BeginPopup("Options")) { - Options(); + OptionsPanel(); } if (ImGui::Button("Options")) { @@ -123,15 +157,43 @@ void CConsole::Draw(const char* pszTitle, bool* bDraw) ImGui::PushItemWidth(footer_width_to_reserve - 80); if (ImGui::IsWindowAppearing()) { ImGui::SetKeyboardFocusHere(); } - ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; - if (ImGui::InputText("##input", m_szInputBuf, IM_ARRAYSIZE(m_szInputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) { - if (m_szInputBuf[0]) { ProcessCommand(m_szInputBuf); } - strcpy_s(m_szInputBuf, 1, ""); - m_bReclaimFocus = true; + if (m_nSuggestPos != -1) + { + // Remove the default value from ConVar before assigning it to the input buffer. + std::string svConVar = m_vsvSuggest[m_nSuggestPos].substr(0, m_vsvSuggest[m_nSuggestPos].find(' ')) + " "; + memmove(m_szInputBuf, svConVar.c_str(), svConVar.size() + 1); + + m_bSuggestActive = false; + m_nSuggestPos = -1; + m_bReclaimFocus = true; + } + else + { + if (m_szInputBuf[0]) { ProcessCommand(m_szInputBuf); } + strcpy_s(m_szInputBuf, 1, ""); + + m_bSuggestActive = false; + m_nSuggestPos = -1; + m_bReclaimFocus = true; + } } + // Auto-focus on window apparition. + ImGui::SetItemDefaultFocus(); + + // Auto-focus previous widget. + if (m_bReclaimFocus) + { + ImGui::SetKeyboardFocusHere(-1); + m_bReclaimFocus = false; + } + + m_vecSuggestWindowPos = ImGui::GetItemRectMin(); + m_vecSuggestWindowPos.y += ImGui::GetItemRectSize().y; + m_vecSuggestWindowSize = ImVec2(500, std::clamp((int)m_vsvSuggest.size() * 20, 0, 122)); + ImGui::SameLine(); if (ImGui::Button("Submit")) { @@ -139,24 +201,13 @@ void CConsole::Draw(const char* pszTitle, bool* bDraw) strcpy_s(m_szInputBuf, 1, ""); m_bReclaimFocus = true; } - - // Auto-focus on window apparition. - ImGui::SetItemDefaultFocus(); - - // Auto focus previous widget. - if (m_bReclaimFocus) - { - ImGui::SetKeyboardFocusHere(); - m_bReclaimFocus = false; - } - ImGui::End(); } //----------------------------------------------------------------------------- // Purpose: draws the options panel //----------------------------------------------------------------------------- -void CConsole::Options() +void CConsole::OptionsPanel(void) { ImGui::Checkbox("Auto-Scroll", &m_bAutoScroll); @@ -203,22 +254,119 @@ void CConsole::Options() } //----------------------------------------------------------------------------- -// Purpose: runs tasks for the console while not being drawn +// Purpose: draws the suggestion panel with results based on user input //----------------------------------------------------------------------------- -void CConsole::Think() +void CConsole::SuggestPanel(void) { - if (g_pImGuiConfig->IConsole_Config.m_bAutoClear) // Check if Auto-Clear is enabled. + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0.0f, 0.0f)); + + ImGui::SetNextWindowPos(m_vecSuggestWindowPos); + ImGui::SetNextWindowSize(m_vecSuggestWindowSize); + + ImGui::Begin("##suggest", nullptr, popup_window_flags); + ImGui::PushAllowKeyboardFocus(false); + + for (int i = 0; i < m_vsvSuggest.size(); i++) { - // Loop and clear the beginning of the vector until we are below our limit. - while (m_ivConLog.Size > g_pImGuiConfig->IConsole_Config.m_nAutoClearLimit) + bool bIsIndexActive = m_nSuggestPos == i; + + ImGui::PushID(i); + if (ImGui::Selectable(m_vsvSuggest[i].c_str(), bIsIndexActive)) { - m_ivConLog.erase(m_ivConLog.begin()); + ImGui::Separator(); + + // Remove the default value from ConVar before assigning it to the input buffer. + std::string svConVar = m_vsvSuggest[i].substr(0, m_vsvSuggest[i].find(' ')) + " "; + memmove(m_szInputBuf, svConVar.c_str(), svConVar.size() + 1); + + m_bSuggestActive = false; + m_nSuggestPos = -1; + m_bReclaimFocus = true; } - while (m_ivHistory.Size > 512) + ImGui::PopID(); + + if (bIsIndexActive) { - m_ivHistory.erase(m_ivHistory.begin()); + if (m_bSuggestMoved) + { + // Make sure we bring the currently 'active' item into view. + ImGui::SetScrollHereY(); + m_bSuggestMoved = false; + } } } + + ImGui::PopAllowKeyboardFocus(); + ImGui::End(); + ImGui::PopStyleVar(1); +} + +//----------------------------------------------------------------------------- +// purpose: checks if the console can autocomplete based on input +//----------------------------------------------------------------------------- +bool CConsole::CanAutoComplete(void) +{ + // Show ConVar/ConCommand suggestions when at least 2 characters have been entered. + if (strlen(m_szInputBuf) > 1) + { + // Update suggestions if input buffer changed. + if (strcmp(m_szInputBuf, m_szInputBufOld) != 0) + { + memmove(m_szInputBufOld, m_szInputBuf, strlen(m_szInputBuf) + 1); + FindFromPartial(); + } + if ((int)m_vsvSuggest.size() <= 0) + { + m_nSuggestPos = -1; + return false; + } + } + else + { + m_nSuggestPos = -1; + return false; + } + + // Don't suggest if user tries to assign value to ConVar or execute ConCommand. + if (strstr(m_szInputBuf, " ") || strstr(m_szInputBuf, ";")) + { + m_bSuggestActive = false; + m_nSuggestPos = -1; + return false; + } + m_bSuggestActive = true; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: find ConVars/ConCommands from user input and add to vector +//----------------------------------------------------------------------------- +void CConsole::FindFromPartial(void) +{ + m_vsvSuggest.clear(); + for (int i = 0; i < g_vsvAllConVars.size(); i++) + { + if (m_vsvSuggest.size() < con_suggestion_limit->GetInt()) + { + if (g_vsvAllConVars[i].find(m_szInputBuf) != std::string::npos) + { + if (std::find(m_vsvSuggest.begin(), m_vsvSuggest.end(), g_vsvAllConVars[i]) == m_vsvSuggest.end()); + { + std::string svValue; + ConVar* pConVar = g_pCVar->FindVar(g_vsvAllConVars[i].c_str()); + if (pConVar != nullptr) + { + // Assign default value to string if its a ConVar. + svValue = g_pCVar->FindVar(g_vsvAllConVars[i].c_str())->GetString(); + } + + m_vsvSuggest.push_back(g_vsvAllConVars[i] + " " + svValue + ""); + } + } + } + else { break; } + } } //----------------------------------------------------------------------------- @@ -235,17 +383,16 @@ void CConsole::ProcessCommand(const char* pszCommand) std::this_thread::sleep_for(std::chrono::milliseconds(1)); m_nHistoryPos = -1; - for (int i = m_ivHistory.Size - 1; i >= 0; i--) + for (int i = (int)m_vsvHistory.size() - 1; i >= 0; i--) { - if (Stricmp(m_ivHistory[i], pszCommand) == 0) + if (Stricmp(m_vsvHistory[i].c_str(), pszCommand) == 0) { - delete m_ivHistory[i]; - m_ivHistory.erase(m_ivHistory.begin() + i); + m_vsvHistory.erase(m_vsvHistory.begin() + i); break; } } - m_ivHistory.push_back(Strdup(pszCommand)); + m_vsvHistory.push_back(Strdup(pszCommand)); if (Stricmp(pszCommand, "CLEAR") == 0) { ClearLog(); @@ -253,9 +400,9 @@ void CConsole::ProcessCommand(const char* pszCommand) else if (Stricmp(pszCommand, "HELP") == 0) { AddLog("Commands:"); - for (int i = 0; i < m_ivCommands.Size; i++) + for (int i = 0; i < (int)m_vsvCommands.size(); i++) { - AddLog("- %s", m_ivCommands[i]); + AddLog("- %s", m_vsvCommands[i].c_str()); } AddLog("Log types:"); @@ -274,10 +421,10 @@ void CConsole::ProcessCommand(const char* pszCommand) } else if (Stricmp(pszCommand, "HISTORY") == 0) { - int first = m_ivHistory.Size - 10; - for (int i = first > 0 ? first : 0; i < m_ivHistory.Size; i++) + int nFirst = (int)m_vsvHistory.size() - 10; + for (int i = nFirst > 0 ? nFirst : 0; i < (int)m_vsvHistory.size(); i++) { - AddLog("%3d: %s\n", i, m_ivHistory[i]); + AddLog("%3d: %s\n", i, m_vsvHistory[i]); } } @@ -285,7 +432,7 @@ void CConsole::ProcessCommand(const char* pszCommand) } //----------------------------------------------------------------------------- -// Purpose: text edit callback +// Purpose: console input box callback //----------------------------------------------------------------------------- int CConsole::TextEditCallback(ImGuiInputTextCallbackData* data) { @@ -309,35 +456,71 @@ int CConsole::TextEditCallback(ImGuiInputTextCallbackData* data) } case ImGuiInputTextFlags_CallbackHistory: { - const int nPrevHistoryPos = m_nHistoryPos; - if (data->EventKey == ImGuiKey_UpArrow) + if (m_bSuggestActive) { - if (m_nHistoryPos == -1) { m_nHistoryPos = m_ivHistory.Size - 1; } - else if (m_nHistoryPos > 0) { m_nHistoryPos--; } - } - else if (data->EventKey == ImGuiKey_DownArrow) - { - if (m_nHistoryPos != -1) + if (data->EventKey == ImGuiKey_UpArrow && m_nSuggestPos > - 1) { - if (++m_nHistoryPos >= m_ivHistory.Size) + m_nSuggestPos--; + m_bSuggestMoved = true; + } + else if (data->EventKey == ImGuiKey_DownArrow) + { + if (m_nSuggestPos < (int)m_vsvSuggest.size() - 1) { - m_nHistoryPos = -1; + m_nSuggestPos++; + m_bSuggestMoved = true; } } } - if (nPrevHistoryPos != m_nHistoryPos) + else // Allow user to navigate through the history if suggest isn't drawn. { - const char* pszHistory = (m_nHistoryPos >= 0) ? m_ivHistory[m_nHistoryPos] : ""; - data->DeleteChars(0, data->BufTextLen); - data->InsertChars(0, pszHistory); + const int nPrevHistoryPos = m_nHistoryPos; + if (data->EventKey == ImGuiKey_UpArrow) + { + if (m_nHistoryPos == -1) + { + m_nHistoryPos = (int)m_vsvHistory.size() - 1; + } + else if (m_nHistoryPos > 0) + { + m_nHistoryPos--; + } + } + else if (data->EventKey == ImGuiKey_DownArrow) + { + if (m_nHistoryPos != -1) + { + if (++m_nHistoryPos >= (int)m_vsvHistory.size()) + { + m_nHistoryPos = -1; + } + } + } + if (nPrevHistoryPos != m_nHistoryPos) + { + std::string svHistory = (m_nHistoryPos >= 0) ? m_vsvHistory[m_nHistoryPos] : ""; + + if (!svHistory.empty()) + { + if (!strstr(m_vsvHistory[m_nHistoryPos].c_str(), " ")) + { + // Append whitespace to previous entered command if absent or no parameters where passed. + svHistory.append(" "); + } + } + + data->DeleteChars(0, data->BufTextLen); + data->InsertChars(0, svHistory.c_str()); + } } + break; } } return 0; } //----------------------------------------------------------------------------- -// Purpose: text edit callback stub +// Purpose: console input box callback stub //----------------------------------------------------------------------------- int CConsole::TextEditCallbackStub(ImGuiInputTextCallbackData* data) { @@ -362,7 +545,7 @@ void CConsole::AddLog(const char* fmt, ...) IM_FMTARGS(2) //----------------------------------------------------------------------------- // Purpose: clears the entire vector //----------------------------------------------------------------------------- -void CConsole::ClearLog() +void CConsole::ClearLog(void) { for (int i = 0; i < m_ivConLog.Size; i++) { free(m_ivConLog[i]); } m_ivConLog.clear(); @@ -371,7 +554,7 @@ void CConsole::ClearLog() //----------------------------------------------------------------------------- // Purpose: colors important logs //----------------------------------------------------------------------------- -void CConsole::ColorLog() +void CConsole::ColorLog(void) { for (int i = 0; i < m_ivConLog.Size; i++) { @@ -442,7 +625,7 @@ void CConsole::ColorLog() //----------------------------------------------------------------------------- // Purpose: sets the console front-end style //----------------------------------------------------------------------------- -void CConsole::SetStyleVar() +void CConsole::SetStyleVar(void) { ImGuiStyle& imStyle = ImGui::GetStyle(); ImVec4* imColor = imStyle.Colors; @@ -499,6 +682,7 @@ void CConsole::SetStyleVar() imStyle.ItemSpacing = ImVec2(4, 4); imStyle.WindowPadding = ImVec2(5, 5); + imStyle.WindowMinSize = ImVec2(510, 510); } CConsole* g_pIConsole = new CConsole(); diff --git a/r5dev/gameui/IConsole.h b/r5dev/gameui/IConsole.h index 46025246..44f73cf0 100644 --- a/r5dev/gameui/IConsole.h +++ b/r5dev/gameui/IConsole.h @@ -4,16 +4,43 @@ class CConsole { private: /////////////////////////////////////////////////////////////////////////// - char m_szInputBuf[512] = { 0 }; - ImVector<const char*> m_ivCommands; - ImVector<char*> m_ivHistory; + char m_szInputBuf[512] = { 0 }; + char m_szInputBufOld[512] = { 0 }; + const char* m_pszConsoleTitle = { 0 }; + + std::vector<std::string> m_vsvCommands; + std::vector<std::string> m_vsvHistory; int m_nHistoryPos = -1; ImGuiTextFilter m_itFilter; + bool m_bInitialized = false; + bool m_bReclaimFocus = false; bool m_bAutoScroll = true; bool m_bScrollToBottom = false; bool m_bCopyToClipBoard = false; - bool m_bReclaimFocus = false; - bool m_bInitialized = false; + + std::vector<std::string> m_vsvSuggest; + bool m_bSuggestActive = false; + bool m_bSuggestMoved = false; + int m_nSuggestPos = -1; + + ImVec2 m_vecSuggestWindowPos; + ImVec2 m_vecSuggestWindowSize; + + ImGuiInputTextFlags input_text_flags = + ImGuiInputTextFlags_AutoCaretEnd | + ImGuiInputTextFlags_CallbackCompletion | + ImGuiInputTextFlags_CallbackHistory | + ImGuiInputTextFlags_CallbackEdit | + ImGuiInputTextFlags_EnterReturnsTrue; + + ImGuiWindowFlags popup_window_flags = + ImGuiWindowFlags_HorizontalScrollbar | + ImGuiWindowFlags_NoMove | + /*ImGuiWindowFlags_NoResize |*/ + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_NoBringToFrontOnFocus; public: bool m_bActivate = false; @@ -23,21 +50,28 @@ public: CConsole(); ~CConsole(); - void Draw(const char* title, bool* bDraw); - void Options(); - void Think(); + void Draw(const char* pszTitle, bool* bDraw); + void Think(void); + + void BasePanel(bool* bDraw); + void OptionsPanel(void); + void SuggestPanel(void); + + + bool CanAutoComplete(void); + void FindFromPartial(void); + void ProcessCommand(const char* pszCommand); - void ProcessCommand(const char* command_line); int TextEditCallback(ImGuiInputTextCallbackData* data); static int TextEditCallbackStub(ImGuiInputTextCallbackData* data); /////////////////////////////////////////////////////////////////////////// void AddLog(const char* fmt, ...) IM_FMTARGS(2); - void ClearLog(); - void ColorLog(); + void ClearLog(void); + void ColorLog(void); /////////////////////////////////////////////////////////////////////////// - void SetStyleVar(); + void SetStyleVar(void); }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/launcher/IApplication.cpp b/r5dev/launcher/IApplication.cpp index ddeae038..7e1f98b3 100644 --- a/r5dev/launcher/IApplication.cpp +++ b/r5dev/launcher/IApplication.cpp @@ -1,4 +1,5 @@ #include "core/stdafx.h" +#include "tier0/cvar.h" #include "launcher/IApplication.h" #include "ebisusdk/EbisuSDK.h" @@ -21,6 +22,10 @@ bool HIApplication_Create(void* a1) // Also add cross-season support? * (uintptr_t*)0x162C61208 = 0x1; // g_bDedicated #endif // DEDICATED + for (auto& map : g_pCVar->DumpToMap()) + { + g_vsvAllConVars.push_back(map.first.c_str()); + } return IAppSystem_Create(a1); } diff --git a/r5dev/tier0/IConVar.cpp b/r5dev/tier0/IConVar.cpp index defa4071..73732f0a 100644 --- a/r5dev/tier0/IConVar.cpp +++ b/r5dev/tier0/IConVar.cpp @@ -75,6 +75,8 @@ void ConVar::Init(void) cl_showgpustats = new ConVar("cl_showgpustats", "0", FCVAR_DEVELOPMENTONLY, "Texture streaming debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); cl_gpustats_offset_x = new ConVar("cl_gpustats_offset_x", "1250", FCVAR_DEVELOPMENTONLY, "X offset for texture streaming debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); cl_gpustats_offset_y = new ConVar("cl_gpustats_offset_y", "900", FCVAR_DEVELOPMENTONLY, "Y offset for texture streaming debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); + + con_suggestion_limit = new ConVar("con_suggestion_limit", "40", FCVAR_DEVELOPMENTONLY, "Maximum number of suggestions the autocomplete window will show for the console.", false, 0.f, false, 0.f, nullptr, nullptr); //------------------------------------------------------------------------- // FILESYSTEM | fs_warning_level_native = new ConVar("fs_warning_level_native", "0", FCVAR_DEVELOPMENTONLY, "Set the filesystem warning level.", false, 0.f, false, 0.f, nullptr, nullptr); diff --git a/r5dev/tier0/cvar.cpp b/r5dev/tier0/cvar.cpp index 9c7e35b9..dce9c643 100644 --- a/r5dev/tier0/cvar.cpp +++ b/r5dev/tier0/cvar.cpp @@ -31,6 +31,8 @@ ConVar* cl_simstats_offset_y = new ConVar(); ConVar* cl_showgpustats = new ConVar(); ConVar* cl_gpustats_offset_x = new ConVar(); ConVar* cl_gpustats_offset_y = new ConVar(); + +ConVar* con_suggestion_limit = new ConVar(); //----------------------------------------------------------------------------- // FILESYSTEM | ConVar* fs_warning_level_native = new ConVar(); @@ -111,4 +113,5 @@ std::unordered_map<std::string, ConCommandBase*> CCVar::DumpToMap() } /////////////////////////////////////////////////////////////////////////////// +std::vector<std::string> g_vsvAllConVars; CCVar* g_pCVar = reinterpret_cast<CCVar*>(p_CEngineAPI_Connect.FindPatternSelf("48 8D 0D", ADDRESS::Direction::DOWN, 40).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); diff --git a/r5dev/tier0/cvar.h b/r5dev/tier0/cvar.h index 173535f9..c11e7e9d 100644 --- a/r5dev/tier0/cvar.h +++ b/r5dev/tier0/cvar.h @@ -43,6 +43,8 @@ extern ConVar* cl_simstats_offset_y; extern ConVar* cl_showgpustats;; extern ConVar* cl_gpustats_offset_x; extern ConVar* cl_gpustats_offset_y; + +extern ConVar* con_suggestion_limit; //------------------------------------------------------------------------- // FILESYSTEM | extern ConVar* fs_warning_level_native; @@ -86,6 +88,7 @@ public: }; /////////////////////////////////////////////////////////////////////////////// +extern std::vector<std::string> g_vsvAllConVars; extern CCVar* g_pCVar;