ddnet/src/game/client/components/console.cpp

909 lines
27 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
2011-08-31 11:56:04 +00:00
#include <base/tl/sorted_array.h>
2008-08-30 21:09:13 +00:00
#include <math.h>
#include <limits.h>
2008-08-30 21:09:13 +00:00
2010-05-29 07:25:38 +00:00
#include <game/generated/client_data.h>
2008-01-16 22:14:06 +00:00
#include <base/system.h>
#include <engine/serverbrowser.h>
2010-05-29 07:25:38 +00:00
#include <engine/shared/ringbuffer.h>
#include <engine/shared/config.h>
#include <engine/graphics.h>
#include <engine/textrender.h>
#include <engine/storage.h>
2010-05-29 07:25:38 +00:00
#include <engine/keys.h>
#include <engine/console.h>
2008-01-16 22:14:06 +00:00
#include <cstring>
#include <cstdio>
2010-05-29 07:25:38 +00:00
#include <game/client/ui.h>
2008-01-16 22:14:06 +00:00
2010-05-29 07:25:38 +00:00
#include <game/version.h>
2010-05-29 07:25:38 +00:00
#include <game/client/lineinput.h>
#include <game/client/render.h>
2010-05-31 21:40:40 +00:00
#include <game/client/components/controls.h>
2010-05-29 07:25:38 +00:00
#include <game/client/components/menus.h>
2010-05-29 07:25:38 +00:00
#include "console.h"
CGameConsole::CInstance::CInstance(int Type)
{
2010-05-29 07:25:38 +00:00
m_pHistoryEntry = 0x0;
2010-05-29 07:25:38 +00:00
m_Type = Type;
if(Type == CGameConsole::CONSOLETYPE_LOCAL)
2010-05-29 07:25:38 +00:00
m_CompletionFlagmask = CFGFLAG_CLIENT;
else
2010-05-29 07:25:38 +00:00
m_CompletionFlagmask = CFGFLAG_SERVER;
2010-05-29 07:25:38 +00:00
m_aCompletionBuffer[0] = 0;
m_CompletionChosen = -1;
m_CompletionRenderOffset = 0.0f;
2014-10-06 21:36:39 +00:00
m_ReverseTAB = false;
m_aUser[0] = '\0';
m_UserGot = false;
m_UsernameReq = false;
m_IsCommand = false;
}
void CGameConsole::CInstance::Init(CGameConsole *pGameConsole)
{
m_pGameConsole = pGameConsole;
2018-11-18 06:25:15 +00:00
}
void CGameConsole::CInstance::ClearBacklog()
{
m_Backlog.Init();
m_BacklogActPage = 0;
}
void CGameConsole::CInstance::ClearHistory()
{
m_History.Init();
m_pHistoryEntry = 0;
}
2010-05-29 07:25:38 +00:00
void CGameConsole::CInstance::ExecuteLine(const char *pLine)
{
if(m_Type == CGameConsole::CONSOLETYPE_LOCAL)
m_pGameConsole->m_pConsole->ExecuteLine(pLine);
else
{
2010-05-29 07:25:38 +00:00
if(m_pGameConsole->Client()->RconAuthed())
m_pGameConsole->Client()->Rcon(pLine);
else
{
if(!m_UserGot && m_UsernameReq)
{
m_UserGot = true;
str_copy(m_aUser, pLine, sizeof m_aUser);
}
else
{
m_pGameConsole->Client()->RconAuth(m_aUser, pLine);
m_UserGot = false;
}
}
}
}
2010-05-29 07:25:38 +00:00
void CGameConsole::CInstance::PossibleCommandsCompleteCallback(const char *pStr, void *pUser)
{
2010-05-29 07:25:38 +00:00
CGameConsole::CInstance *pInstance = (CGameConsole::CInstance *)pUser;
if(pInstance->m_CompletionChosen == pInstance->m_CompletionEnumerationCount)
pInstance->m_Input.Set(pStr);
pInstance->m_CompletionEnumerationCount++;
}
2010-05-29 07:25:38 +00:00
void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
{
2010-05-29 07:25:38 +00:00
bool Handled = false;
if(m_pGameConsole->Input()->KeyIsPressed(KEY_LCTRL)) // jump to spaces and special ASCII characters
{
int SearchDirection = 0;
if(m_pGameConsole->Input()->KeyPress(KEY_LEFT))
SearchDirection = -1;
else if(m_pGameConsole->Input()->KeyPress(KEY_RIGHT) || m_pGameConsole->Input()->KeyPress(KEY_DELETE))
SearchDirection = 1;
if(SearchDirection != 0)
{
int OldOffset = m_Input.GetCursorOffset();
int FoundAt = SearchDirection > 0 ? m_Input.GetLength() - 1 : 0;
for(int i = m_Input.GetCursorOffset() + SearchDirection; SearchDirection > 0 ? i < m_Input.GetLength() - 1 : i > 0; i += SearchDirection)
{
int Next = i + SearchDirection;
if( (m_Input.GetString()[Next] == ' ') ||
(m_Input.GetString()[Next] >= 32 && m_Input.GetString()[Next] <= 47) ||
(m_Input.GetString()[Next] >= 58 && m_Input.GetString()[Next] <= 64) ||
(m_Input.GetString()[Next] >= 91 && m_Input.GetString()[Next] <= 96))
{
FoundAt = i;
if(SearchDirection < 0)
FoundAt++;
break;
}
}
if(m_pGameConsole->Input()->KeyPress(KEY_DELETE))
{
if(m_Input.GetCursorOffset() != m_Input.GetLength())
{
char aText[512];
aText[0] = '\0';
str_copy(aText, m_Input.GetString(), m_Input.GetCursorOffset() + 1);
if(FoundAt != m_Input.GetLength())
str_append(aText, m_Input.GetString() + FoundAt, sizeof(aText));
m_Input.Set(aText);
FoundAt = OldOffset;
}
}
m_Input.SetCursorOffset(FoundAt);
}
}
if(m_pGameConsole->Input()->KeyIsPressed(KEY_LCTRL) && m_pGameConsole->Input()->KeyPress(KEY_V))
{
const char *Text = m_pGameConsole->Input()->GetClipboardText();
if(Text)
{
char Line[256];
int i, Begin = 0;
for(i = 0; i < str_length(Text); i++)
{
if(Text[i] == '\n')
{
if(i == Begin)
{
Begin++;
continue;
}
2019-04-26 19:36:49 +00:00
int max = minimum(i - Begin + 1, (int)sizeof(Line));
str_copy(Line, Text + Begin, max);
Begin = i+1;
ExecuteLine(Line);
}
}
2019-04-26 19:36:49 +00:00
int max = minimum(i - Begin + 1, (int)sizeof(Line));
str_copy(Line, Text + Begin, max);
Begin = i+1;
m_Input.Add(Line);
}
}
else if(m_pGameConsole->Input()->KeyIsPressed(KEY_LCTRL) && m_pGameConsole->Input()->KeyPress(KEY_C))
{
m_pGameConsole->Input()->SetClipboardText(m_Input.GetString());
}
else if(m_pGameConsole->Input()->KeyIsPressed(KEY_LCTRL) && m_pGameConsole->Input()->KeyPress(KEY_A))
{
m_Input.SetCursorOffset(0);
}
else if(m_pGameConsole->Input()->KeyIsPressed(KEY_LCTRL) && m_pGameConsole->Input()->KeyPress(KEY_E))
{
m_Input.SetCursorOffset(m_Input.GetLength());
}
else if(m_pGameConsole->Input()->KeyIsPressed(KEY_LCTRL) && m_pGameConsole->Input()->KeyPress(KEY_U))
{
m_Input.DeleteUntilCursor();
}
2010-05-29 07:25:38 +00:00
if(Event.m_Flags&IInput::FLAG_PRESS)
{
2010-05-29 07:25:38 +00:00
if(Event.m_Key == KEY_RETURN || Event.m_Key == KEY_KP_ENTER)
{
if(m_Input.GetString()[0] || (m_UsernameReq && !m_pGameConsole->Client()->RconAuthed() && !m_UserGot))
{
if(m_Type == CONSOLETYPE_LOCAL || m_pGameConsole->Client()->RconAuthed())
{
char *pEntry = m_History.Allocate(m_Input.GetLength()+1);
mem_copy(pEntry, m_Input.GetString(), m_Input.GetLength()+1);
}
2010-05-29 07:25:38 +00:00
ExecuteLine(m_Input.GetString());
m_Input.Clear();
m_pHistoryEntry = 0x0;
}
2010-05-29 07:25:38 +00:00
Handled = true;
}
2010-05-29 07:25:38 +00:00
else if (Event.m_Key == KEY_UP)
{
2010-05-29 07:25:38 +00:00
if (m_pHistoryEntry)
{
2010-05-29 07:25:38 +00:00
char *pTest = m_History.Prev(m_pHistoryEntry);
2010-05-29 07:25:38 +00:00
if (pTest)
m_pHistoryEntry = pTest;
}
else
2010-05-29 07:25:38 +00:00
m_pHistoryEntry = m_History.Last();
2010-05-29 07:25:38 +00:00
if (m_pHistoryEntry)
2011-06-09 21:40:35 +00:00
m_Input.Set(m_pHistoryEntry);
2010-05-29 07:25:38 +00:00
Handled = true;
}
2010-05-29 07:25:38 +00:00
else if (Event.m_Key == KEY_DOWN)
{
2010-05-29 07:25:38 +00:00
if (m_pHistoryEntry)
m_pHistoryEntry = m_History.Next(m_pHistoryEntry);
2010-05-29 07:25:38 +00:00
if (m_pHistoryEntry)
2011-06-09 21:40:35 +00:00
m_Input.Set(m_pHistoryEntry);
else
2010-05-29 07:25:38 +00:00
m_Input.Clear();
Handled = true;
}
2010-05-29 07:25:38 +00:00
else if(Event.m_Key == KEY_TAB)
{
if(m_Type == CGameConsole::CONSOLETYPE_LOCAL || m_pGameConsole->Client()->RconAuthed())
{
2014-10-06 21:36:39 +00:00
if(m_ReverseTAB)
m_CompletionChosen--;
else
m_CompletionChosen++;
2010-05-29 07:25:38 +00:00
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);
2010-05-29 07:25:38 +00:00
// handle wrapping
2014-10-06 21:36:39 +00:00
if(m_CompletionEnumerationCount && (m_CompletionChosen >= m_CompletionEnumerationCount || m_CompletionChosen <0))
2010-05-29 07:25:38 +00:00
{
2014-10-06 21:36:39 +00:00
m_CompletionChosen= (m_CompletionChosen + m_CompletionEnumerationCount) % m_CompletionEnumerationCount;
2010-05-29 07:25:38 +00:00
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);
2010-05-29 07:25:38 +00:00
}
}
}
2010-05-29 07:25:38 +00:00
else if(Event.m_Key == KEY_PAGEUP)
{
++m_BacklogActPage;
}
else if(Event.m_Key == KEY_PAGEDOWN)
{
2010-05-29 07:25:38 +00:00
--m_BacklogActPage;
if(m_BacklogActPage < 0)
m_BacklogActPage = 0;
}
2019-04-07 21:28:30 +00:00
// in order not to conflict with CLineInput's handling of Home/End only
// react to it when the input is empty
else if(Event.m_Key == KEY_HOME && m_Input.GetString()[0] == '\0')
{
m_BacklogActPage = INT_MAX;
}
2019-04-07 21:28:30 +00:00
else if(Event.m_Key == KEY_END && m_Input.GetString()[0] == '\0')
{
m_BacklogActPage = 0;
}
2014-10-06 21:36:39 +00:00
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;
2010-05-29 07:25:38 +00:00
}
if(!Handled)
m_Input.ProcessInput(Event);
if(Event.m_Flags & (IInput::FLAG_PRESS|IInput::FLAG_TEXT))
2010-05-29 07:25:38 +00:00
{
2014-10-06 21:36:39 +00:00
if((Event.m_Key != KEY_TAB) && (Event.m_Key != KEY_LSHIFT))
2010-05-29 07:25:38 +00:00
{
m_CompletionChosen = -1;
str_copy(m_aCompletionBuffer, m_Input.GetString(), sizeof(m_aCompletionBuffer));
2016-05-22 19:31:35 +00:00
m_CompletionRenderOffset = 0.0f;
}
// find the current command
{
2010-05-29 07:25:38 +00:00
char aBuf[64] = {0};
const char *pSrc = GetString();
int i = 0;
for(; i < (int)sizeof(aBuf)-1 && *pSrc && *pSrc != ' '; i++, pSrc++)
2010-05-29 07:25:38 +00:00
aBuf[i] = *pSrc;
aBuf[i] = 0;
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)
{
m_IsCommand = true;
str_copy(m_aCommandName, pCommand->m_pName, IConsole::TEMPCMD_NAME_LENGTH);
str_copy(m_aCommandHelp, pCommand->m_pHelp, IConsole::TEMPCMD_HELP_LENGTH);
str_copy(m_aCommandParams, pCommand->m_pParams, IConsole::TEMPCMD_PARAMS_LENGTH);
}
else
m_IsCommand = false;
}
}
}
2014-12-20 12:37:11 +00:00
void CGameConsole::CInstance::PrintLine(const char *pLine, bool Highlighted)
{
2010-05-29 07:25:38 +00:00
int Len = str_length(pLine);
2010-05-29 07:25:38 +00:00
if (Len > 255)
Len = 255;
2010-10-16 08:32:56 +00:00
CBacklogEntry *pEntry = m_Backlog.Allocate(sizeof(CBacklogEntry)+Len);
pEntry->m_YOffset = -1.0f;
2014-12-20 12:37:11 +00:00
pEntry->m_Highlighted = Highlighted;
2010-10-16 08:32:56 +00:00
mem_copy(pEntry->m_aText, pLine, Len);
pEntry->m_aText[Len] = 0;
}
2010-05-29 07:25:38 +00:00
CGameConsole::CGameConsole()
: m_LocalConsole(CONSOLETYPE_LOCAL), m_RemoteConsole(CONSOLETYPE_REMOTE)
{
m_ConsoleType = CONSOLETYPE_LOCAL;
2010-05-29 07:25:38 +00:00
m_ConsoleState = CONSOLE_CLOSED;
m_StateChangeEnd = 0.0f;
m_StateChangeDuration = 0.1f;
}
2008-01-16 22:14:06 +00:00
2010-05-29 07:25:38 +00:00
float CGameConsole::TimeNow()
{
2010-05-29 07:25:38 +00:00
static long long s_TimeStart = time_get();
return float(time_get()-s_TimeStart)/float(time_freq());
}
2010-05-29 07:25:38 +00:00
CGameConsole::CInstance *CGameConsole::CurrentConsole()
2008-01-16 22:14:06 +00:00
{
if(m_ConsoleType == CONSOLETYPE_REMOTE)
return &m_RemoteConsole;
return &m_LocalConsole;
}
2008-01-16 22:14:06 +00:00
2010-05-29 07:25:38 +00:00
void CGameConsole::OnReset()
{
2008-01-16 22:14:06 +00:00
}
// only defined for 0<=t<=1
2010-05-29 07:25:38 +00:00
static float ConsoleScaleFunc(float t)
{
//return t;
return sinf(acosf(1.0f-t));
}
2010-05-29 07:25:38 +00:00
struct CRenderInfo
{
2010-05-29 07:25:38 +00:00
CGameConsole *m_pSelf;
CTextCursor m_Cursor;
const char *m_pCurrentCmd;
int m_WantedCompletion;
int m_EnumCount;
float m_Offset;
float m_Width;
};
2010-05-29 07:25:38 +00:00
void CGameConsole::PossibleCommandsRenderCallback(const char *pStr, void *pUser)
{
2010-05-29 07:25:38 +00:00
CRenderInfo *pInfo = static_cast<CRenderInfo *>(pUser);
2010-05-29 07:25:38 +00:00
if(pInfo->m_EnumCount == pInfo->m_WantedCompletion)
{
2010-05-29 07:25:38 +00:00
float tw = pInfo->m_pSelf->TextRender()->TextWidth(pInfo->m_Cursor.m_pFont, pInfo->m_Cursor.m_FontSize, pStr, -1);
pInfo->m_pSelf->Graphics()->TextureSet(-1);
pInfo->m_pSelf->Graphics()->QuadsBegin();
2018-04-15 17:35:56 +00:00
pInfo->m_pSelf->Graphics()->SetColor(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,0.85f);
pInfo->m_pSelf->RenderTools()->DrawRoundRect(pInfo->m_Cursor.m_X - 2.5f, pInfo->m_Cursor.m_Y - 4.f / 2.f, tw + 5.f, pInfo->m_Cursor.m_FontSize + 4.f, pInfo->m_Cursor.m_FontSize / 3.f);
2010-05-29 07:25:38 +00:00
pInfo->m_pSelf->Graphics()->QuadsEnd();
// scroll when out of sight
if(pInfo->m_Cursor.m_X < 3.0f)
pInfo->m_Offset = 0.0f;
else if(pInfo->m_Cursor.m_X+tw > pInfo->m_Width)
pInfo->m_Offset -= pInfo->m_Width/2;
2010-05-29 07:25:38 +00:00
pInfo->m_pSelf->TextRender()->TextColor(0.05f, 0.05f, 0.05f,1);
pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pStr, -1);
}
else
{
2010-05-29 07:25:38 +00:00
const char *pMatchStart = str_find_nocase(pStr, pInfo->m_pCurrentCmd);
2010-05-29 07:25:38 +00:00
if(pMatchStart)
{
2010-05-29 07:25:38 +00:00
pInfo->m_pSelf->TextRender()->TextColor(0.5f,0.5f,0.5f,1);
pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pStr, pMatchStart-pStr);
pInfo->m_pSelf->TextRender()->TextColor(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,1);
pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pMatchStart, str_length(pInfo->m_pCurrentCmd));
pInfo->m_pSelf->TextRender()->TextColor(0.5f,0.5f,0.5f,1);
pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pMatchStart+str_length(pInfo->m_pCurrentCmd), -1);
}
else
{
2010-05-29 07:25:38 +00:00
pInfo->m_pSelf->TextRender()->TextColor(0.75f,0.75f,0.75f,1);
pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pStr, -1);
}
}
2010-05-29 07:25:38 +00:00
pInfo->m_EnumCount++;
pInfo->m_Cursor.m_X += 7.0f;
}
2010-05-29 07:25:38 +00:00
void CGameConsole::OnRender()
{
CUIRect Screen = *UI()->Screen();
2010-05-29 07:25:38 +00:00
float ConsoleMaxHeight = Screen.h*3/5.0f;
float ConsoleHeight;
float Progress = (TimeNow()-(m_StateChangeEnd-m_StateChangeDuration))/m_StateChangeDuration;
2010-05-29 07:25:38 +00:00
if (Progress >= 1.0f)
{
2010-05-29 07:25:38 +00:00
if (m_ConsoleState == CONSOLE_CLOSING)
m_ConsoleState = CONSOLE_CLOSED;
else if (m_ConsoleState == CONSOLE_OPENING)
m_ConsoleState = CONSOLE_OPEN;
2010-05-29 07:25:38 +00:00
Progress = 1.0f;
}
2009-05-31 09:44:20 +00:00
2010-05-29 07:25:38 +00:00
if (m_ConsoleState == CONSOLE_OPEN && g_Config.m_ClEditor)
Toggle(CONSOLETYPE_LOCAL);
2010-05-29 07:25:38 +00:00
if (m_ConsoleState == CONSOLE_CLOSED)
return;
2010-05-29 07:25:38 +00:00
if (m_ConsoleState == CONSOLE_OPEN)
Input()->MouseModeAbsolute();
2010-05-29 07:25:38 +00:00
float ConsoleHeightScale;
2010-05-29 07:25:38 +00:00
if (m_ConsoleState == CONSOLE_OPENING)
ConsoleHeightScale = ConsoleScaleFunc(Progress);
else if (m_ConsoleState == CONSOLE_CLOSING)
ConsoleHeightScale = ConsoleScaleFunc(1.0f-Progress);
else //if (console_state == CONSOLE_OPEN)
2010-05-29 07:25:38 +00:00
ConsoleHeightScale = ConsoleScaleFunc(1.0f);
2010-05-29 07:25:38 +00:00
ConsoleHeight = ConsoleHeightScale*ConsoleMaxHeight;
2010-05-29 07:25:38 +00:00
Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h);
// do console shadow
2009-10-27 14:38:53 +00:00
Graphics()->TextureSet(-1);
Graphics()->QuadsBegin();
2010-05-29 07:25:38 +00:00
IGraphics::CColorVertex Array[4] = {
IGraphics::CColorVertex(0, 0,0,0, 0.5f),
IGraphics::CColorVertex(1, 0,0,0, 0.5f),
IGraphics::CColorVertex(2, 0,0,0, 0.0f),
2010-05-29 07:25:38 +00:00
IGraphics::CColorVertex(3, 0,0,0, 0.0f)};
Graphics()->SetColorVertex(Array, 4);
IGraphics::CQuadItem QuadItem(0, ConsoleHeight, Screen.w, 10.0f);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
// do background
2010-05-29 07:25:38 +00:00
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_CONSOLE_BG].m_Id);
Graphics()->QuadsBegin();
Graphics()->SetColor(0.2f, 0.2f, 0.2f,0.9f);
if(m_ConsoleType == CONSOLETYPE_REMOTE)
Graphics()->SetColor(0.4f, 0.2f, 0.2f,0.9f);
Graphics()->QuadsSetSubset(0,-ConsoleHeight*0.075f,Screen.w*0.075f*0.5f,0);
2010-05-29 07:25:38 +00:00
QuadItem = IGraphics::CQuadItem(0, 0, Screen.w, ConsoleHeight);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
// do small bar shadow
2009-10-27 14:38:53 +00:00
Graphics()->TextureSet(-1);
Graphics()->QuadsBegin();
2010-05-29 07:25:38 +00:00
Array[0] = IGraphics::CColorVertex(0, 0,0,0, 0.0f);
Array[1] = IGraphics::CColorVertex(1, 0,0,0, 0.0f);
Array[2] = IGraphics::CColorVertex(2, 0,0,0, 0.25f);
Array[3] = IGraphics::CColorVertex(3, 0,0,0, 0.25f);
Graphics()->SetColorVertex(Array, 4);
QuadItem = IGraphics::CQuadItem(0, ConsoleHeight-20, Screen.w, 10);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
// do the lower bar
2010-05-29 07:25:38 +00:00
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_CONSOLE_BAR].m_Id);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.9f);
Graphics()->QuadsSetSubset(0,0.1f,Screen.w*0.015f,1-0.1f);
2010-05-29 07:25:38 +00:00
QuadItem = IGraphics::CQuadItem(0,ConsoleHeight-10.0f,Screen.w,10.0f);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
ConsoleHeight -= 22.0f;
CInstance *pConsole = CurrentConsole();
{
2010-05-29 07:25:38 +00:00
float FontSize = 10.0f;
float RowHeight = FontSize*1.25f;
float x = 3;
float y = ConsoleHeight - RowHeight - 5.0f;
2010-05-29 07:25:38 +00:00
CRenderInfo 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;
2010-05-29 07:25:38 +00:00
Info.m_pCurrentCmd = pConsole->m_aCompletionBuffer;
TextRender()->SetCursor(&Info.m_Cursor, x+Info.m_Offset, y+RowHeight+2.0f, FontSize, TEXTFLAG_RENDER);
2010-05-29 07:25:38 +00:00
// render prompt
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, x, y, FontSize, TEXTFLAG_RENDER);
2010-05-29 07:25:38 +00:00
const char *pPrompt = "> ";
if(m_ConsoleType == CONSOLETYPE_REMOTE)
{
if(Client()->State() == IClient::STATE_LOADING || Client()->State() == IClient::STATE_ONLINE)
{
2010-05-29 07:25:38 +00:00
if(Client()->RconAuthed())
pPrompt = "rcon> ";
else
{
if(pConsole->m_UsernameReq)
{
if(!pConsole->m_UserGot)
pPrompt = "Enter Username> ";
else
pPrompt = "Enter Password> ";
}
else
2017-03-06 19:11:23 +00:00
pPrompt = "Enter Password> ";
}
}
else
2010-05-29 07:25:38 +00:00
pPrompt = "NOT CONNECTED> ";
}
2010-05-29 07:25:38 +00:00
TextRender()->TextEx(&Cursor, pPrompt, -1);
x = Cursor.m_X;
2010-05-29 07:25:38 +00:00
//console text editing
bool Editing = false;
int EditingCursor = Input()->GetEditingCursor();
if (Input()->GetIMEState())
{
if(str_length(Input()->GetIMECandidate()))
{
pConsole->m_Input.Editing(Input()->GetIMECandidate(), EditingCursor);
Editing = true;
}
}
2010-05-29 07:25:38 +00:00
//hide rcon password
char aInputString[512];
str_copy(aInputString, pConsole->m_Input.GetString(Editing), sizeof(aInputString));
if(m_ConsoleType == CONSOLETYPE_REMOTE && Client()->State() == IClient::STATE_ONLINE && !Client()->RconAuthed() && (pConsole->m_UserGot || !pConsole->m_UsernameReq))
{
for(int i = 0; i < pConsole->m_Input.GetLength(Editing); ++i)
2010-05-29 07:25:38 +00:00
aInputString[i] = '*';
}
// render console input (wrap line)
TextRender()->SetCursor(&Cursor, x, y, FontSize, 0);
Cursor.m_LineWidth = Screen.w - 10.0f - x;
TextRender()->TextEx(&Cursor, aInputString, pConsole->m_Input.GetCursorOffset(Editing));
TextRender()->TextEx(&Cursor, aInputString+pConsole->m_Input.GetCursorOffset(Editing), -1);
int Lines = Cursor.m_LineCount;
2015-07-09 00:08:14 +00:00
y -= (Lines - 1) * FontSize;
TextRender()->SetCursor(&Cursor, x, y, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = Screen.w - 10.0f - x;
2015-07-09 00:08:14 +00:00
TextRender()->TextEx(&Cursor, aInputString, pConsole->m_Input.GetCursorOffset(Editing));
static float MarkerOffset = TextRender()->TextWidth(0, FontSize, "|", -1)/3;
2010-05-29 07:25:38 +00:00
CTextCursor Marker = Cursor;
Marker.m_X -= MarkerOffset;
Marker.m_LineWidth = -1;
2010-05-29 07:25:38 +00:00
TextRender()->TextEx(&Marker, "|", -1);
TextRender()->TextEx(&Cursor, aInputString+pConsole->m_Input.GetCursorOffset(Editing), -1);
2010-05-29 07:25:38 +00:00
// render possible commands
if(m_ConsoleType == CONSOLETYPE_LOCAL || Client()->RconAuthed())
2010-05-29 07:25:38 +00:00
{
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);
pConsole->m_CompletionRenderOffset = Info.m_Offset;
2010-05-29 07:25:38 +00:00
if(Info.m_EnumCount <= 0)
{
if(pConsole->m_IsCommand)
2010-05-29 07:25:38 +00:00
{
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_aCommandHelp);
2010-05-29 07:25:38 +00:00
TextRender()->TextEx(&Info.m_Cursor, aBuf, -1);
TextRender()->TextColor(0.75f, 0.75f, 0.75f, 1);
str_format(aBuf, sizeof(aBuf), "Usage: %s %s", pConsole->m_aCommandName, pConsole->m_aCommandParams);
2010-05-29 07:25:38 +00:00
TextRender()->TextEx(&Info.m_Cursor, aBuf, -1);
}
}
}
}
2014-12-20 12:37:11 +00:00
2019-05-10 21:34:21 +00:00
ColorRGBA rgb = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageHighlightColor));
2010-05-29 07:25:38 +00:00
// render log (actual page, wrap lines)
2010-10-16 08:32:56 +00:00
CInstance::CBacklogEntry *pEntry = pConsole->m_Backlog.Last();
float OffsetY = 0.0f;
float LineOffset = 1.0f;
2014-12-20 12:37:11 +00:00
2010-10-16 08:32:56 +00:00
for(int Page = 0; Page <= pConsole->m_BacklogActPage; ++Page, OffsetY = 0.0f)
{
while(pEntry)
2010-05-29 07:25:38 +00:00
{
2014-12-20 12:37:11 +00:00
if(pEntry->m_Highlighted)
2019-04-26 22:34:20 +00:00
TextRender()->TextColor(rgb);
else
TextRender()->TextColor(1,1,1,1);
2014-12-20 12:37:11 +00:00
2010-10-16 08:32:56 +00:00
// get y offset (calculate it if we haven't yet)
if(pEntry->m_YOffset < 0.0f)
{
TextRender()->SetCursor(&Cursor, 0.0f, 0.0f, FontSize, 0);
Cursor.m_LineWidth = Screen.w-10;
TextRender()->TextEx(&Cursor, pEntry->m_aText, -1);
pEntry->m_YOffset = Cursor.m_Y+Cursor.m_FontSize+LineOffset;
}
OffsetY += pEntry->m_YOffset;
// next page when lines reach the top
if(y-OffsetY <= RowHeight)
break;
2010-05-29 07:25:38 +00:00
// just render output from actual backlog page (render bottom up)
if(Page == pConsole->m_BacklogActPage)
{
2010-10-16 08:32:56 +00:00
TextRender()->SetCursor(&Cursor, 0.0f, y-OffsetY, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = Screen.w-10.0f;
TextRender()->TextEx(&Cursor, pEntry->m_aText, -1);
2010-05-29 07:25:38 +00:00
}
pEntry = pConsole->m_Backlog.Prev(pEntry);
2015-01-27 00:13:27 +00:00
// reset color
TextRender()->TextColor(1,1,1,1);
2010-05-29 07:25:38 +00:00
}
2010-05-29 07:25:38 +00:00
// actual backlog page number is too high, render last available page (current checked one, render top down)
if(!pEntry && Page < pConsole->m_BacklogActPage)
{
pConsole->m_BacklogActPage = Page;
pEntry = pConsole->m_Backlog.First();
2010-10-16 08:32:56 +00:00
while(OffsetY > 0.0f && pEntry)
2010-05-29 07:25:38 +00:00
{
2010-10-16 08:32:56 +00:00
TextRender()->SetCursor(&Cursor, 0.0f, y-OffsetY, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = Screen.w-10.0f;
TextRender()->TextEx(&Cursor, pEntry->m_aText, -1);
OffsetY -= pEntry->m_YOffset;
2010-05-29 07:25:38 +00:00
pEntry = pConsole->m_Backlog.Next(pEntry);
}
break;
}
}
// render page
char aBuf[128];
2015-05-19 15:47:51 +00:00
TextRender()->TextColor(1,1,1,1);
str_format(aBuf, sizeof(aBuf), Localize("-Page %d-"), pConsole->m_BacklogActPage+1);
2018-04-04 00:40:30 +00:00
TextRender()->Text(0, 10.0f, FontSize / 2.f, FontSize, aBuf, -1);
// render version
str_format(aBuf, sizeof(aBuf), "v%s", GAME_VERSION);
float Width = TextRender()->TextWidth(0, FontSize, aBuf, -1);
2018-04-04 00:40:30 +00:00
TextRender()->Text(0, Screen.w-Width-10.0f, FontSize / 2.f, FontSize, aBuf, -1);
}
2008-03-23 09:22:15 +00:00
}
2010-05-29 07:25:38 +00:00
void CGameConsole::OnMessage(int MsgType, void *pRawMsg)
{
}
2010-05-29 07:25:38 +00:00
bool CGameConsole::OnInput(IInput::CEvent Event)
{
// accept input when opening, but not at first frame to discard the input that caused the console to open
if(m_ConsoleState != CONSOLE_OPEN && (m_ConsoleState != CONSOLE_OPENING || m_StateChangeEnd == TimeNow()+m_StateChangeDuration))
2008-08-27 16:23:15 +00:00
return false;
2015-08-24 20:46:28 +00:00
if((Event.m_Key >= KEY_F1 && Event.m_Key <= KEY_F12) || (Event.m_Key >= KEY_F13 && Event.m_Key <= KEY_F24))
2008-08-27 16:23:15 +00:00
return false;
2010-05-29 07:25:38 +00:00
if(Event.m_Key == KEY_ESCAPE && (Event.m_Flags&IInput::FLAG_PRESS))
Toggle(m_ConsoleType);
2008-08-27 16:23:15 +00:00
else
2010-05-29 07:25:38 +00:00
CurrentConsole()->OnInput(Event);
2008-08-27 16:23:15 +00:00
return true;
}
2010-05-29 07:25:38 +00:00
void CGameConsole::Toggle(int Type)
2008-08-27 16:23:15 +00:00
{
2010-05-29 07:25:38 +00:00
if(m_ConsoleType != Type && (m_ConsoleState == CONSOLE_OPEN || m_ConsoleState == CONSOLE_OPENING))
2008-08-27 16:23:15 +00:00
{
// don't toggle console, just switch what console to use
}
else
{
2010-05-29 07:25:38 +00:00
if (m_ConsoleState == CONSOLE_CLOSED || m_ConsoleState == CONSOLE_OPEN)
2008-08-27 16:23:15 +00:00
{
2010-05-29 07:25:38 +00:00
m_StateChangeEnd = TimeNow()+m_StateChangeDuration;
2008-08-27 16:23:15 +00:00
}
else
{
2010-05-29 07:25:38 +00:00
float Progress = m_StateChangeEnd-TimeNow();
float ReversedProgress = m_StateChangeDuration-Progress;
2008-08-27 16:23:15 +00:00
2010-05-29 07:25:38 +00:00
m_StateChangeEnd = TimeNow()+ReversedProgress;
2008-08-27 16:23:15 +00:00
}
2010-05-29 07:25:38 +00:00
if (m_ConsoleState == CONSOLE_CLOSED || m_ConsoleState == CONSOLE_CLOSING)
2008-10-20 18:12:15 +00:00
{
/*Input()->MouseModeAbsolute();
m_pClient->m_pMenus->UseMouseButtons(false);*/
2010-05-29 07:25:38 +00:00
m_ConsoleState = CONSOLE_OPENING;
/*// reset controls
m_pClient->m_pControls->OnReset();*/
Input()->SetIMEState(true);
2008-10-20 18:12:15 +00:00
}
2008-08-27 16:23:15 +00:00
else
2008-10-20 18:12:15 +00:00
{
2010-05-29 07:25:38 +00:00
Input()->MouseModeRelative();
m_pClient->m_pMenus->UseMouseButtons(true);
m_pClient->OnRelease();
2010-05-29 07:25:38 +00:00
m_ConsoleState = CONSOLE_CLOSING;
Input()->SetIMEState(false);
2008-10-20 18:12:15 +00:00
}
2008-08-27 16:23:15 +00:00
}
2010-05-29 07:25:38 +00:00
m_ConsoleType = Type;
2008-08-27 16:23:15 +00:00
}
void CGameConsole::Dump(int Type)
{
CInstance *pConsole = Type == CONSOLETYPE_REMOTE ? &m_RemoteConsole : &m_LocalConsole;
char aFilename[128];
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)
{
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));
2011-12-29 22:36:53 +00:00
io_write_newline(io);
}
io_close(io);
}
}
void CGameConsole::ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData)
2008-08-27 16:23:15 +00:00
{
((CGameConsole *)pUserData)->Toggle(CONSOLETYPE_LOCAL);
2008-08-27 16:23:15 +00:00
}
void CGameConsole::ConToggleRemoteConsole(IConsole::IResult *pResult, void *pUserData)
2008-08-27 16:23:15 +00:00
{
((CGameConsole *)pUserData)->Toggle(CONSOLETYPE_REMOTE);
2008-08-27 16:23:15 +00:00
}
void CGameConsole::ConClearLocalConsole(IConsole::IResult *pResult, void *pUserData)
{
((CGameConsole *)pUserData)->m_LocalConsole.ClearBacklog();
}
void CGameConsole::ConClearRemoteConsole(IConsole::IResult *pResult, void *pUserData)
{
((CGameConsole *)pUserData)->m_RemoteConsole.ClearBacklog();
}
void CGameConsole::ConDumpLocalConsole(IConsole::IResult *pResult, void *pUserData)
{
((CGameConsole *)pUserData)->Dump(CONSOLETYPE_LOCAL);
}
void CGameConsole::ConDumpRemoteConsole(IConsole::IResult *pResult, void *pUserData)
{
((CGameConsole *)pUserData)->Dump(CONSOLETYPE_REMOTE);
}
2014-12-20 12:37:11 +00:00
void CGameConsole::ClientConsolePrintCallback(const char *pStr, void *pUserData, bool Highlighted)
2008-08-27 16:23:15 +00:00
{
2014-12-20 12:37:11 +00:00
((CGameConsole *)pUserData)->m_LocalConsole.PrintLine(pStr, Highlighted);
2008-08-27 16:23:15 +00:00
}
void CGameConsole::ConConsolePageUp(IConsole::IResult *pResult, void *pUserData)
{
CInstance *pConsole = ((CGameConsole *)pUserData)->CurrentConsole();
pConsole->m_BacklogActPage++;
}
void CGameConsole::ConConsolePageDown(IConsole::IResult *pResult, void *pUserData)
{
CInstance *pConsole = ((CGameConsole *)pUserData)->CurrentConsole();
--pConsole->m_BacklogActPage;
if(pConsole->m_BacklogActPage < 0)
pConsole->m_BacklogActPage = 0;
}
2011-07-30 11:40:01 +00:00
void CGameConsole::ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments() == 1)
{
CGameConsole *pThis = static_cast<CGameConsole *>(pUserData);
pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0));
}
}
void CGameConsole::RequireUsername(bool UsernameReq)
{
if((m_RemoteConsole.m_UsernameReq = UsernameReq))
{
m_RemoteConsole.m_aUser[0] = '\0';
m_RemoteConsole.m_UserGot = false;
}
}
2010-05-29 07:25:38 +00:00
void CGameConsole::PrintLine(int Type, const char *pLine)
2008-08-30 08:01:29 +00:00
{
if(Type == CONSOLETYPE_LOCAL)
2010-05-29 07:25:38 +00:00
m_LocalConsole.PrintLine(pLine);
else if(Type == CONSOLETYPE_REMOTE)
2010-05-29 07:25:38 +00:00
m_RemoteConsole.PrintLine(pLine);
2008-08-30 08:01:29 +00:00
}
2010-05-29 07:25:38 +00:00
void CGameConsole::OnConsoleInit()
2008-08-27 16:23:15 +00:00
{
// init console instances
m_LocalConsole.Init(this);
m_RemoteConsole.Init(this);
2010-05-29 07:25:38 +00:00
m_pConsole = Kernel()->RequestInterface<IConsole>();
2008-08-27 16:23:15 +00:00
//
2011-07-30 11:40:01 +00:00
m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, ClientConsolePrintCallback, this);
2010-05-29 07:25:38 +00:00
Console()->Register("toggle_local_console", "", CFGFLAG_CLIENT, ConToggleLocalConsole, this, "Toggle local console");
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");
2011-07-30 11:40:01 +00:00
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");
2011-07-30 11:40:01 +00:00
Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this);
}
void CGameConsole::OnStateChange(int NewState, int OldState)
{
2017-03-23 06:16:03 +00:00
if(OldState == IClient::STATE_ONLINE && NewState < IClient::STATE_LOADING)
{
2017-03-23 06:16:03 +00:00
m_RemoteConsole.m_UserGot = false;
2017-10-16 18:01:33 +00:00
m_RemoteConsole.m_aUser[0] = '\0';
m_RemoteConsole.m_Input.Clear();
m_RemoteConsole.m_UsernameReq = false;
}
2008-01-16 22:14:06 +00:00
}