5724: Show console completion options for tunings, extend and rename commands according to upstream r=def- a=Robyt3

- Rename commands that list things according to upstream so they are consistent and improve command descriptions:
  - `dump_binds` -> `binds`
  - `tune_dump` -> `tunes`
  - `bans` (already named fittingly)
  - `dump_(local|remote)_console` (named correctly, `dump` should be used when writing to a file)
- Add success/error messages to `dump_(local|remote)_console` and improve description.
- Print error when using incorrect tuning names like `tune xyz 123`.
- Extend `tune_reset` so a single tuning value can be reset to default. For example `tune_reset player_collision` will reset just the `player_collision` tuning, whereas `tune_reset` will reset all to defaults like before.
- Get the actual tuning value after setting it, as the value that was applied may differ due to overflow or rounding errors.
- Extend `tune` so the current value of a given tuning variable is printed if no new value is given, so it works like commands in the console. For example `tune player_collision` will print the current value of the variable, whereas `tune player_collision 0` will change the value like before.
- Show completion options for tune params in console. Closes #5711.
  - Currently supports `tune`, `tune_reset` and `toggle_tune`.
  - For `tune_zone` the tune name is the second argument, so this is more difficult to handle.
  - Screenshot: 
![screenshot](https://user-images.githubusercontent.com/23437060/184015369-05853414-e45e-474d-a29a-90b82cf94159.png)

## Checklist

- [X] Tested the change ingame
- [X] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [X] Considered possible null pointers and out of bounds array indexing
- [X] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
bors[bot] 2022-08-11 21:29:06 +00:00 committed by GitHub
commit f10edfa589
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 259 additions and 121 deletions

View file

@ -82,14 +82,16 @@ public:
typedef void (*FTeeHistorianCommandCallback)(int ClientID, int FlagMask, const char *pCmd, IResult *pResult, void *pUser);
typedef void (*FPrintCallback)(const char *pStr, void *pUser, ColorRGBA PrintColor);
typedef void (*FPossibleCallback)(const char *pCmd, void *pUser);
typedef void (*FPossibleCallback)(int Index, const char *pCmd, void *pUser);
typedef void (*FCommandCallback)(IResult *pResult, void *pUserData);
typedef void (*FChainCommandCallback)(IResult *pResult, void *pUserData, FCommandCallback pfnCallback, void *pCallbackUserData);
static void EmptyPossibleCommandCallback(int Index, const char *pCmd, void *pUser) {}
virtual void Init() = 0;
virtual const CCommandInfo *FirstCommandInfo(int AccessLevel, int Flagmask) const = 0;
virtual const CCommandInfo *GetCommandInfo(const char *pName, int FlagMask, bool Temp) = 0;
virtual void PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser) = 0;
virtual int PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback = EmptyPossibleCommandCallback, void *pUser = nullptr) = 0;
virtual void ParseArguments(int NumArgs, const char **ppArguments) = 0;
virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) = 0;

View file

@ -551,16 +551,21 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientID, bo
}
}
void CConsole::PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser)
int CConsole::PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser)
{
int Index = 0;
for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
{
if(pCommand->m_Flags & FlagMask && pCommand->m_Temp == Temp)
{
if(str_find_nocase(pCommand->m_pName, pStr))
pfnCallback(pCommand->m_pName, pUser);
{
pfnCallback(Index, pCommand->m_pName, pUser);
Index++;
}
}
}
return Index;
}
CConsole::CCommand *CConsole::FindCommand(const char *pName, int FlagMask)

View file

@ -196,7 +196,7 @@ public:
void Init() override;
const CCommandInfo *FirstCommandInfo(int AccessLevel, int FlagMask) const override;
const CCommandInfo *GetCommandInfo(const char *pName, int FlagMask, bool Temp) override;
void PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser) override;
int PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser) override;
void ParseArguments(int NumArgs, const char **ppArguments) override;
void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) override;

View file

@ -254,7 +254,7 @@ void CBinds::OnConsoleInit()
pConfigManager->RegisterCallback(ConfigSaveCallback, this);
Console()->Register("bind", "s[key] r[command]", CFGFLAG_CLIENT, ConBind, this, "Bind key to execute the command");
Console()->Register("dump_binds", "?s[key]", CFGFLAG_CLIENT, ConDumpBinds, this, "Print command executed by this keybindind or all binds");
Console()->Register("binds", "?s[key]", CFGFLAG_CLIENT, ConBinds, this, "Print command executed by this keybinding or all binds");
Console()->Register("unbind", "s[key]", CFGFLAG_CLIENT, ConUnbind, this, "Unbind key");
Console()->Register("unbindall", "", CFGFLAG_CLIENT, ConUnbindAll, this, "Unbind all keys");
@ -280,7 +280,7 @@ void CBinds::ConBind(IConsole::IResult *pResult, void *pUserData)
pBinds->Bind(KeyID, pResult->GetString(1), false, Modifier);
}
void CBinds::ConDumpBinds(IConsole::IResult *pResult, void *pUserData)
void CBinds::ConBinds(IConsole::IResult *pResult, void *pUserData)
{
CBinds *pBinds = (CBinds *)pUserData;
if(pResult->NumArguments() == 1)

View file

@ -15,7 +15,7 @@ class CBinds : public CComponent
int GetKeyID(const char *pKeyName);
static void ConBind(IConsole::IResult *pResult, void *pUserData);
static void ConDumpBinds(IConsole::IResult *pResult, void *pUserData);
static void ConBinds(IConsole::IResult *pResult, void *pUserData);
static void ConUnbind(IConsole::IResult *pResult, void *pUserData);
static void ConUnbindAll(IConsole::IResult *pResult, void *pUserData);
class IConsole *GetConsole() const { return Console(); }

View file

@ -76,6 +76,13 @@ void CConsoleLogger::OnConsoleDeletion()
m_pConsole = nullptr;
}
// TODO: support "tune_zone", which has tuning as second argument
static const char *gs_apTuningCommands[] = {"tune ", "tune_reset ", "toggle_tune "};
static bool IsTuningCommandPrefix(const char *pStr)
{
return std::any_of(std::begin(gs_apTuningCommands), std::end(gs_apTuningCommands), [pStr](auto *pCmd) { return str_startswith_nocase(pStr, pCmd); });
}
CGameConsole::CInstance::CInstance(int Type)
{
m_pHistoryEntry = 0x0;
@ -83,15 +90,21 @@ CGameConsole::CInstance::CInstance(int Type)
m_Type = Type;
if(Type == CGameConsole::CONSOLETYPE_LOCAL)
{
m_pName = "local_console";
m_CompletionFlagmask = CFGFLAG_CLIENT;
}
else
{
m_pName = "remote_console";
m_CompletionFlagmask = CFGFLAG_SERVER;
}
m_aCompletionBuffer[0] = 0;
m_CompletionUsed = false;
m_CompletionChosen = -1;
m_CompletionRenderOffset = 0.0f;
m_ReverseTAB = false;
m_aCompletionBufferArgument[0] = 0;
m_CompletionChosenArgument = -1;
Reset();
m_aUser[0] = '\0';
m_UserGot = false;
@ -131,6 +144,11 @@ void CGameConsole::CInstance::ClearHistory()
m_pHistoryEntry = 0;
}
void CGameConsole::CInstance::Reset()
{
m_CompletionRenderOffset = 0.0f;
}
void CGameConsole::CInstance::ExecuteLine(const char *pLine)
{
if(m_Type == CGameConsole::CONSOLETYPE_LOCAL)
@ -155,12 +173,33 @@ void CGameConsole::CInstance::ExecuteLine(const char *pLine)
}
}
void CGameConsole::CInstance::PossibleCommandsCompleteCallback(const char *pStr, void *pUser)
void CGameConsole::CInstance::PossibleCommandsCompleteCallback(int Index, const char *pStr, void *pUser)
{
CGameConsole::CInstance *pInstance = (CGameConsole::CInstance *)pUser;
if(pInstance->m_CompletionChosen == pInstance->m_CompletionEnumerationCount)
if(pInstance->m_CompletionChosen == Index)
pInstance->m_Input.Set(pStr);
pInstance->m_CompletionEnumerationCount++;
}
static void StrCopyUntilSpace(char *pDest, size_t DestSize, const char *pSrc)
{
const char *pSpace = str_find(pSrc, " ");
str_copy(pDest, pSrc, minimum<size_t>(pSpace ? pSpace - pSrc + 1 : 1, DestSize));
}
void CGameConsole::CInstance::PossibleArgumentsCompleteCallback(int Index, const char *pStr, void *pUser)
{
CGameConsole::CInstance *pInstance = (CGameConsole::CInstance *)pUser;
if(pInstance->m_CompletionChosenArgument == Index)
{
// get command
char aBuf[512];
StrCopyUntilSpace(aBuf, sizeof(aBuf), pInstance->GetString());
str_append(aBuf, " ", sizeof(aBuf));
// append argument
str_append(aBuf, pStr, sizeof(aBuf));
pInstance->m_Input.Set(aBuf);
}
}
void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
@ -323,23 +362,46 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
}
else if(Event.m_Key == KEY_TAB)
{
const int Direction = m_pGameConsole->m_pClient->Input()->KeyIsPressed(KEY_LSHIFT) || m_pGameConsole->m_pClient->Input()->KeyIsPressed(KEY_RSHIFT) ? -1 : 1;
// command completion
if(m_Type == CGameConsole::CONSOLETYPE_LOCAL || m_pGameConsole->Client()->RconAuthed())
{
if(m_ReverseTAB && m_CompletionUsed)
m_CompletionChosen--;
else if(!m_ReverseTAB)
m_CompletionChosen++;
m_CompletionEnumerationCount = 0;
m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, m_Type != CGameConsole::CONSOLETYPE_LOCAL && m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands(), PossibleCommandsCompleteCallback, this);
m_CompletionUsed = true;
// handle wrapping
if(m_CompletionEnumerationCount && (m_CompletionChosen >= m_CompletionEnumerationCount || m_CompletionChosen < 0))
const bool UseTempCommands = m_Type == CGameConsole::CONSOLETYPE_REMOTE && m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands();
const int CompletionEnumerationCount = m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, UseTempCommands);
if(CompletionEnumerationCount)
{
m_CompletionChosen = (m_CompletionChosen + m_CompletionEnumerationCount) % m_CompletionEnumerationCount;
m_CompletionEnumerationCount = 0;
m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, m_Type != CGameConsole::CONSOLETYPE_LOCAL && m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands(), PossibleCommandsCompleteCallback, this);
if(m_CompletionChosen == -1 && Direction < 0)
m_CompletionChosen = 0;
m_CompletionChosen = (m_CompletionChosen + Direction + CompletionEnumerationCount) % CompletionEnumerationCount;
m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, UseTempCommands, PossibleCommandsCompleteCallback, this);
}
else if(m_CompletionChosen != -1)
{
m_CompletionChosen = -1;
Reset();
}
}
// argument completion (tuning, ...)
if(m_Type == CGameConsole::CONSOLETYPE_REMOTE && m_pGameConsole->Client()->RconAuthed())
{
const bool TuningCompletion = IsTuningCommandPrefix(GetString());
if(TuningCompletion)
{
int CompletionEnumerationCount = m_pGameConsole->m_pClient->m_aTuning[g_Config.m_ClDummy].PossibleTunings(m_aCompletionBufferArgument);
if(CompletionEnumerationCount)
{
if(m_CompletionChosenArgument == -1 && Direction < 0)
m_CompletionChosenArgument = 0;
m_CompletionChosenArgument = (m_CompletionChosenArgument + Direction + CompletionEnumerationCount) % CompletionEnumerationCount;
m_pGameConsole->m_pClient->m_aTuning[g_Config.m_ClDummy].PossibleTunings(m_aCompletionBufferArgument, PossibleArgumentsCompleteCallback, this);
}
else if(m_CompletionChosenArgument != -1)
{
m_CompletionChosenArgument = -1;
Reset();
}
}
}
}
@ -367,16 +429,6 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
m_BacklogCurPage = 0;
m_pGameConsole->m_HasSelection = false;
}
else if(Event.m_Key == KEY_LSHIFT)
{
m_ReverseTAB = true;
Handled = true;
}
}
if(Event.m_Flags & IInput::FLAG_RELEASE && Event.m_Key == KEY_LSHIFT)
{
m_ReverseTAB = false;
Handled = true;
}
if(!Handled)
@ -384,23 +436,27 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
if(Event.m_Flags & (IInput::FLAG_PRESS | IInput::FLAG_TEXT))
{
if((Event.m_Key != KEY_TAB) && (Event.m_Key != KEY_LSHIFT))
if(Event.m_Key != KEY_TAB && Event.m_Key != KEY_LSHIFT && Event.m_Key != KEY_RSHIFT)
{
m_CompletionUsed = false;
m_CompletionChosen = -1;
str_copy(m_aCompletionBuffer, m_Input.GetString());
m_CompletionRenderOffset = 0.0f;
for(const auto *pCmd : gs_apTuningCommands)
{
if(str_startswith_nocase(m_Input.GetString(), pCmd))
{
m_CompletionChosenArgument = -1;
str_copy(m_aCompletionBufferArgument, &m_Input.GetString()[str_length(pCmd)]);
}
}
Reset();
}
// find the current command
{
char aBuf[64] = {0};
const char *pSrc = GetString();
int i = 0;
for(; i < (int)sizeof(aBuf) - 1 && *pSrc && *pSrc != ' '; i++, pSrc++)
aBuf[i] = *pSrc;
aBuf[i] = 0;
char aBuf[sizeof(m_aCommandName)];
StrCopyUntilSpace(aBuf, sizeof(aBuf), GetString());
const IConsole::CCommandInfo *pCommand = m_pGameConsole->m_pConsole->GetCommandInfo(aBuf, m_CompletionFlagmask,
m_Type != CGameConsole::CONSOLETYPE_LOCAL && m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands());
if(pCommand)
@ -461,6 +517,7 @@ CGameConsole::CInstance *CGameConsole::CurrentConsole()
void CGameConsole::OnReset()
{
m_RemoteConsole.Reset();
}
// only defined for 0<=t<=1
@ -470,7 +527,7 @@ static float ConsoleScaleFunc(float t)
return sinf(acosf(1.0f - t));
}
struct CRenderInfo
struct CCompletionOptionRenderInfo
{
CGameConsole *m_pSelf;
CTextCursor m_Cursor;
@ -481,9 +538,9 @@ struct CRenderInfo
float m_Width;
};
void CGameConsole::PossibleCommandsRenderCallback(const char *pStr, void *pUser)
void CGameConsole::PossibleCommandsRenderCallback(int Index, const char *pStr, void *pUser)
{
CRenderInfo *pInfo = static_cast<CRenderInfo *>(pUser);
CCompletionOptionRenderInfo *pInfo = static_cast<CCompletionOptionRenderInfo *>(pUser);
if(pInfo->m_EnumCount == pInfo->m_WantedCompletion)
{
@ -618,15 +675,8 @@ void CGameConsole::OnRender()
float x = 3;
float y = ConsoleHeight - RowHeight - 5.0f;
CRenderInfo Info;
Info.m_pSelf = this;
Info.m_WantedCompletion = pConsole->m_CompletionUsed ? pConsole->m_CompletionChosen : -1;
Info.m_EnumCount = 0;
Info.m_Offset = pConsole->m_CompletionRenderOffset;
Info.m_Width = Screen.w;
Info.m_pCurrentCmd = pConsole->m_aCompletionBuffer;
TextRender()->SetCursor(&Info.m_Cursor, x + Info.m_Offset, y + RowHeight + 2.0f, FontSize, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END);
Info.m_Cursor.m_LineWidth = std::numeric_limits<float>::max();
const float InitialX = x;
const float InitialY = y;
// render prompt
CTextCursor Cursor;
@ -706,16 +756,33 @@ void CGameConsole::OnRender()
Input()->SetEditingPosition(Marker.m_X, Marker.m_Y + Marker.m_FontSize);
// render possible commands
if(m_ConsoleType == CONSOLETYPE_LOCAL || Client()->RconAuthed())
if((m_ConsoleType == CONSOLETYPE_LOCAL || Client()->RconAuthed()) && pConsole->m_Input.GetString()[0])
{
if(pConsole->m_Input.GetString()[0] != 0)
{
m_pConsole->PossibleCommands(pConsole->m_aCompletionBuffer, pConsole->m_CompletionFlagmask, m_ConsoleType != CGameConsole::CONSOLETYPE_LOCAL && Client()->RconAuthed() && Client()->UseTempRconCommands(), PossibleCommandsRenderCallback, &Info);
CCompletionOptionRenderInfo Info;
Info.m_pSelf = this;
Info.m_WantedCompletion = pConsole->m_CompletionChosen;
Info.m_EnumCount = 0;
Info.m_Offset = pConsole->m_CompletionRenderOffset;
Info.m_Width = Screen.w;
Info.m_pCurrentCmd = pConsole->m_aCompletionBuffer;
TextRender()->SetCursor(&Info.m_Cursor, InitialX + Info.m_Offset, InitialY + RowHeight + 2.0f, FontSize, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END);
Info.m_Cursor.m_LineWidth = std::numeric_limits<float>::max();
m_pConsole->PossibleCommands(Info.m_pCurrentCmd, pConsole->m_CompletionFlagmask, m_ConsoleType != CGameConsole::CONSOLETYPE_LOCAL && Client()->RconAuthed() && Client()->UseTempRconCommands(), PossibleCommandsRenderCallback, &Info);
pConsole->m_CompletionRenderOffset = Info.m_Offset;
if(Info.m_EnumCount <= 0)
if(Info.m_EnumCount <= 0 && pConsole->m_IsCommand)
{
if(pConsole->m_IsCommand)
const bool TuningCompletion = IsTuningCommandPrefix(Info.m_pCurrentCmd);
if(TuningCompletion)
{
Info.m_WantedCompletion = pConsole->m_CompletionChosenArgument;
Info.m_EnumCount = 0;
Info.m_pCurrentCmd = pConsole->m_aCompletionBufferArgument;
m_pClient->m_aTuning[g_Config.m_ClDummy].PossibleTunings(Info.m_pCurrentCmd, PossibleCommandsRenderCallback, &Info);
pConsole->m_CompletionRenderOffset = Info.m_Offset;
}
if(Info.m_EnumCount <= 0 && pConsole->m_IsCommand)
{
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_aCommandHelp);
@ -726,7 +793,6 @@ void CGameConsole::OnRender()
}
}
}
}
pConsole->m_BacklogLock.lock();
@ -922,23 +988,28 @@ void CGameConsole::Toggle(int Type)
void CGameConsole::Dump(int Type)
{
CInstance *pConsole = Type == CONSOLETYPE_REMOTE ? &m_RemoteConsole : &m_LocalConsole;
char aBuf[IO_MAX_PATH_LENGTH + 64];
char aFilename[IO_MAX_PATH_LENGTH];
char aDate[20];
str_timestamp(aDate, sizeof(aDate));
str_format(aFilename, sizeof(aFilename), "dumps/%s_dump_%s.txt", Type == CONSOLETYPE_REMOTE ? "remote_console" : "local_console", aDate);
IOHANDLE io = Storage()->OpenFile(aFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
if(io)
str_timestamp(aBuf, sizeof(aBuf));
str_format(aFilename, sizeof(aFilename), "dumps/%s_dump_%s.txt", pConsole->m_pName, aBuf);
IOHANDLE File = Storage()->OpenFile(aFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
if(File)
{
pConsole->m_BacklogLock.lock();
for(CInstance::CBacklogEntry *pEntry = pConsole->m_Backlog.First(); pEntry; pEntry = pConsole->m_Backlog.Next(pEntry))
{
io_write(io, pEntry->m_aText, str_length(pEntry->m_aText));
io_write_newline(io);
io_write(File, pEntry->m_aText, str_length(pEntry->m_aText));
io_write_newline(File);
}
pConsole->m_BacklogLock.unlock();
io_close(io);
io_close(File);
str_format(aBuf, sizeof(aBuf), "%s contents were written to '%s'", pConsole->m_pName, aFilename);
}
else
{
str_format(aBuf, sizeof(aBuf), "Failed to open '%s'", aFilename);
}
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "console", aBuf);
}
void CGameConsole::ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData)
@ -1014,8 +1085,8 @@ void CGameConsole::OnConsoleInit()
Console()->Register("toggle_remote_console", "", CFGFLAG_CLIENT, ConToggleRemoteConsole, this, "Toggle remote console");
Console()->Register("clear_local_console", "", CFGFLAG_CLIENT, ConClearLocalConsole, this, "Clear local console");
Console()->Register("clear_remote_console", "", CFGFLAG_CLIENT, ConClearRemoteConsole, this, "Clear remote console");
Console()->Register("dump_local_console", "", CFGFLAG_CLIENT, ConDumpLocalConsole, this, "Dump local console");
Console()->Register("dump_remote_console", "", CFGFLAG_CLIENT, ConDumpRemoteConsole, this, "Dump remote console");
Console()->Register("dump_local_console", "", CFGFLAG_CLIENT, ConDumpLocalConsole, this, "Write local console contents to a text file");
Console()->Register("dump_remote_console", "", CFGFLAG_CLIENT, ConDumpRemoteConsole, this, "Write remote console contents to a text file");
Console()->Register("console_page_up", "", CFGFLAG_CLIENT, ConConsolePageUp, this, "Previous page in console");
Console()->Register("console_page_down", "", CFGFLAG_CLIENT, ConConsolePageDown, this, "Next page in console");

View file

@ -38,18 +38,18 @@ class CGameConsole : public CComponent
char *m_pHistoryEntry;
CLineInput m_Input;
const char *m_pName;
int m_Type;
int m_CompletionEnumerationCount;
int m_BacklogCurPage;
CGameConsole *m_pGameConsole;
char m_aCompletionBuffer[128];
bool m_CompletionUsed;
int m_CompletionChosen;
char m_aCompletionBufferArgument[128];
int m_CompletionChosenArgument;
int m_CompletionFlagmask;
float m_CompletionRenderOffset;
bool m_ReverseTAB;
char m_aUser[32];
bool m_UserGot;
@ -66,6 +66,7 @@ class CGameConsole : public CComponent
void ClearBacklog();
void ClearBacklogYOffsets();
void ClearHistory();
void Reset();
void ExecuteLine(const char *pLine);
@ -73,7 +74,8 @@ class CGameConsole : public CComponent
void PrintLine(const char *pLine, int Len, ColorRGBA PrintColor);
const char *GetString() const { return m_Input.GetString(); }
static void PossibleCommandsCompleteCallback(const char *pStr, void *pUser);
static void PossibleCommandsCompleteCallback(int Index, const char *pStr, void *pUser);
static void PossibleArgumentsCompleteCallback(int Index, const char *pStr, void *pUser);
};
class IConsole *m_pConsole;
@ -105,7 +107,7 @@ class CGameConsole : public CComponent
void Toggle(int Type);
void Dump(int Type);
static void PossibleCommandsRenderCallback(const char *pStr, void *pUser);
static void PossibleCommandsRenderCallback(int Index, const char *pStr, void *pUser);
static void ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData);
static void ConToggleRemoteConsole(IConsole::IResult *pResult, void *pUserData);
static void ConClearLocalConsole(IConsole::IResult *pResult, void *pUserData);

View file

@ -119,7 +119,7 @@ void CDebugHud::RenderTuning()
TextRender()->Text(0x0, x - w, y + Count * 6, 5, aBuf, -1.0f);
x += 5.0f;
TextRender()->Text(0x0, x, y + Count * 6, 5, CTuningParams::ms_apNames[i], -1.0f);
TextRender()->Text(0x0, x, y + Count * 6, 5, CTuningParams::Name(i), -1.0f);
Count++;
}

View file

@ -163,9 +163,9 @@ void CGameClient::OnConsoleInit()
Console()->Register("kill", "", CFGFLAG_CLIENT, ConKill, this, "Kill yourself to restart");
// register server dummy commands for tab completion
Console()->Register("tune", "s[tuning] i[value]", CFGFLAG_SERVER, 0, 0, "Tune variable to value");
Console()->Register("tune_reset", "", CFGFLAG_SERVER, 0, 0, "Reset tuning");
Console()->Register("tune_dump", "", CFGFLAG_SERVER, 0, 0, "Dump tuning");
Console()->Register("tune", "s[tuning] ?i[value]", CFGFLAG_SERVER, 0, 0, "Tune variable to value or show current value");
Console()->Register("tune_reset", "?s[tuning]", CFGFLAG_SERVER, 0, 0, "Reset all or one tuning variable to default");
Console()->Register("tunes", "", CFGFLAG_SERVER, 0, 0, "List all tuning variables and their values");
Console()->Register("change_map", "?r[map]", CFGFLAG_SERVER, 0, 0, "Change map");
Console()->Register("restart", "?i[seconds]", CFGFLAG_SERVER, 0, 0, "Restart in x seconds");
Console()->Register("broadcast", "r[message]", CFGFLAG_SERVER, 0, 0, "Broadcast message");

View file

@ -34,7 +34,7 @@ bool CTuningParams::Get(int Index, float *pValue) const
bool CTuningParams::Set(const char *pName, float Value)
{
for(int i = 0; i < Num(); i++)
if(str_comp_nocase(pName, ms_apNames[i]) == 0)
if(str_comp_nocase(pName, Name(i)) == 0)
return Set(i, Value);
return false;
}
@ -42,12 +42,26 @@ bool CTuningParams::Set(const char *pName, float Value)
bool CTuningParams::Get(const char *pName, float *pValue) const
{
for(int i = 0; i < Num(); i++)
if(str_comp_nocase(pName, ms_apNames[i]) == 0)
if(str_comp_nocase(pName, Name(i)) == 0)
return Get(i, pValue);
return false;
}
int CTuningParams::PossibleTunings(const char *pStr, IConsole::FPossibleCallback pfnCallback, void *pUser)
{
int Index = 0;
for(int i = 0; i < Num(); i++)
{
if(str_find_nocase(Name(i), pStr))
{
pfnCallback(Index, Name(i), pUser);
Index++;
}
}
return Index;
}
float VelocityRamp(float Value, float Start, float Range, float Curvature)
{
if(Value < Start)

View file

@ -10,6 +10,7 @@
#include <set>
#include <vector>
#include <engine/console.h>
#include <engine/shared/protocol.h>
#include <game/generated/protocol.h>
@ -40,6 +41,8 @@ public:
class CTuningParams
{
static const char *ms_apNames[];
public:
CTuningParams()
{
@ -49,8 +52,6 @@ public:
#undef MACRO_TUNING_PARAM
}
static const char *ms_apNames[];
#define MACRO_TUNING_PARAM(Name, ScriptName, Value, Description) CTuneParam m_##Name;
#include "tuning.h"
#undef MACRO_TUNING_PARAM
@ -63,6 +64,8 @@ public:
bool Set(const char *pName, float Value);
bool Get(int Index, float *pValue) const;
bool Get(const char *pName, float *pValue) const;
static const char *Name(int Index) { return ms_apNames[Index]; }
int PossibleTunings(const char *pStr, IConsole::FPossibleCallback pfnCallback = IConsole::EmptyPossibleCommandCallback, void *pUser = nullptr);
};
inline void StrToInts(int *pInts, int Num, const char *pStr)

View file

@ -2550,17 +2550,34 @@ void CGameContext::ConTuneParam(IConsole::IResult *pResult, void *pUserData)
{
CGameContext *pSelf = (CGameContext *)pUserData;
const char *pParamName = pResult->GetString(0);
float NewValue = pResult->GetFloat(1);
if(pSelf->Tuning()->Set(pParamName, NewValue))
{
char aBuf[256];
if(pResult->NumArguments() == 2)
{
float NewValue = pResult->GetFloat(1);
if(pSelf->Tuning()->Set(pParamName, NewValue) && pSelf->Tuning()->Get(pParamName, &NewValue))
{
str_format(aBuf, sizeof(aBuf), "%s changed to %.2f", pParamName, NewValue);
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf);
pSelf->SendTuningParams(-1);
}
else
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", "No such tuning parameter");
{
str_format(aBuf, sizeof(aBuf), "No such tuning parameter: %s", pParamName);
}
}
else
{
float Value;
if(pSelf->Tuning()->Get(pParamName, &Value))
{
str_format(aBuf, sizeof(aBuf), "%s %.2f", pParamName, Value);
}
else
{
str_format(aBuf, sizeof(aBuf), "No such tuning parameter: %s", pParamName);
}
}
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf);
}
void CGameContext::ConToggleTuneParam(IConsole::IResult *pResult, void *pUserData)
@ -2569,17 +2586,19 @@ void CGameContext::ConToggleTuneParam(IConsole::IResult *pResult, void *pUserDat
const char *pParamName = pResult->GetString(0);
float OldValue;
char aBuf[256];
if(!pSelf->Tuning()->Get(pParamName, &OldValue))
{
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", "No such tuning parameter");
str_format(aBuf, sizeof(aBuf), "No such tuning parameter: %s", pParamName);
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf);
return;
}
float NewValue = fabs(OldValue - pResult->GetFloat(1)) < 0.0001f ? pResult->GetFloat(2) : pResult->GetFloat(1);
pSelf->Tuning()->Set(pParamName, NewValue);
pSelf->Tuning()->Get(pParamName, &NewValue);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "%s changed to %.2f", pParamName, NewValue);
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf);
pSelf->SendTuningParams(-1);
@ -2588,11 +2607,31 @@ void CGameContext::ConToggleTuneParam(IConsole::IResult *pResult, void *pUserDat
void CGameContext::ConTuneReset(IConsole::IResult *pResult, void *pUserData)
{
CGameContext *pSelf = (CGameContext *)pUserData;
if(pResult->NumArguments())
{
const char *pParamName = pResult->GetString(0);
float DefaultValue = 0.0f;
char aBuf[256];
CTuningParams TuningParams;
if(TuningParams.Get(pParamName, &DefaultValue) && pSelf->Tuning()->Set(pParamName, DefaultValue) && pSelf->Tuning()->Get(pParamName, &DefaultValue))
{
str_format(aBuf, sizeof(aBuf), "%s reset to %.2f", pParamName, DefaultValue);
pSelf->SendTuningParams(-1);
}
else
{
str_format(aBuf, sizeof(aBuf), "No such tuning parameter: %s", pParamName);
}
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf);
}
else
{
pSelf->ResetTuning();
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", "Tuning reset");
}
}
void CGameContext::ConTuneDump(IConsole::IResult *pResult, void *pUserData)
void CGameContext::ConTunes(IConsole::IResult *pResult, void *pUserData)
{
CGameContext *pSelf = (CGameContext *)pUserData;
char aBuf[256];
@ -2600,7 +2639,7 @@ void CGameContext::ConTuneDump(IConsole::IResult *pResult, void *pUserData)
{
float Value;
pSelf->Tuning()->Get(i, &Value);
str_format(aBuf, sizeof(aBuf), "%s %.2f", pSelf->Tuning()->ms_apNames[i], Value);
str_format(aBuf, sizeof(aBuf), "%s %.2f", CTuningParams::Name(i), Value);
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf);
}
}
@ -2613,16 +2652,18 @@ void CGameContext::ConTuneZone(IConsole::IResult *pResult, void *pUserData)
float NewValue = pResult->GetFloat(2);
if(List >= 0 && List < NUM_TUNEZONES)
{
if(pSelf->TuningList()[List].Set(pParamName, NewValue))
{
char aBuf[256];
if(pSelf->TuningList()[List].Set(pParamName, NewValue) && pSelf->TuningList()[List].Get(pParamName, &NewValue))
{
str_format(aBuf, sizeof(aBuf), "%s in zone %d changed to %.2f", pParamName, List, NewValue);
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf);
pSelf->SendTuningParams(-1, List);
}
else
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", "No such tuning parameter");
{
str_format(aBuf, sizeof(aBuf), "No such tuning parameter: %s", pParamName);
}
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf);
}
}
@ -2637,7 +2678,7 @@ void CGameContext::ConTuneDumpZone(IConsole::IResult *pResult, void *pUserData)
{
float Value;
pSelf->TuningList()[List].Get(i, &Value);
str_format(aBuf, sizeof(aBuf), "zone %d: %s %.2f", List, pSelf->TuningList()[List].ms_apNames[i], Value);
str_format(aBuf, sizeof(aBuf), "zone %d: %s %.2f", List, CTuningParams::Name(i), Value);
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf);
}
}
@ -3140,10 +3181,10 @@ void CGameContext::OnConsoleInit()
m_pEngine = Kernel()->RequestInterface<IEngine>();
m_pStorage = Kernel()->RequestInterface<IStorage>();
Console()->Register("tune", "s[tuning] i[value]", CFGFLAG_SERVER | CFGFLAG_GAME, ConTuneParam, this, "Tune variable to value");
Console()->Register("tune", "s[tuning] ?i[value]", CFGFLAG_SERVER | CFGFLAG_GAME, ConTuneParam, this, "Tune variable to value or show current value");
Console()->Register("toggle_tune", "s[tuning] i[value 1] i[value 2]", CFGFLAG_SERVER | CFGFLAG_GAME, ConToggleTuneParam, this, "Toggle tune variable");
Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "Reset tuning");
Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, "Dump tuning");
Console()->Register("tune_reset", "?s[tuning]", CFGFLAG_SERVER, ConTuneReset, this, "Reset all or one tuning variable to default");
Console()->Register("tunes", "", CFGFLAG_SERVER, ConTunes, this, "List all tuning variables and their values");
Console()->Register("tune_zone", "i[zone] s[tuning] i[value]", CFGFLAG_SERVER | CFGFLAG_GAME, ConTuneZone, this, "Tune in zone a variable to value");
Console()->Register("tune_zone_dump", "i[zone]", CFGFLAG_SERVER, ConTuneDumpZone, this, "Dump zone tuning in zone x");
Console()->Register("tune_zone_reset", "?i[zone]", CFGFLAG_SERVER, ConTuneResetZone, this, "reset zone tuning in zone x or in all zones");

View file

@ -90,7 +90,7 @@ class CGameContext : public IGameServer
static void ConTuneParam(IConsole::IResult *pResult, void *pUserData);
static void ConToggleTuneParam(IConsole::IResult *pResult, void *pUserData);
static void ConTuneReset(IConsole::IResult *pResult, void *pUserData);
static void ConTuneDump(IConsole::IResult *pResult, void *pUserData);
static void ConTunes(IConsole::IResult *pResult, void *pUserData);
static void ConTuneZone(IConsole::IResult *pResult, void *pUserData);
static void ConTuneDumpZone(IConsole::IResult *pResult, void *pUserData);
static void ConTuneResetZone(IConsole::IResult *pResult, void *pUserData);