Improve cl_show_ids: support spectator menu, optimize, refactor

Also show client IDs in the spectator menu when `cl_show_ids` is enabled. Previously, this setting applied to scoreboard and chat, although the setting and config variable descriptions were only mentioning the scoreboard.

Extract client ID formatting in `CGameClient::FormatClientId` function to reduce duplicate code and ensure it is consistent.

Correctly indent client IDs also when largest client ID is 100 or larger. Fix scoreboard spectator client ID buffer not being large enough for IDs 100 and larger.

Make client ID formatting more efficient by avoiding `str_format` except for formatting the number itself and appending text directly to the text cursor when possible.
This commit is contained in:
Robert Müller 2024-08-08 21:57:33 +02:00
parent 26cccb7f51
commit 594968fe07
7 changed files with 65 additions and 30 deletions

View file

@ -577,7 +577,7 @@ MACRO_CONFIG_COL(ClMessageFriendColor, cl_message_friend_color, 65425, CFGFLAG_C
MACRO_CONFIG_INT(ConnTimeout, conn_timeout, 100, 5, 1000, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_SERVER, "Network timeout") MACRO_CONFIG_INT(ConnTimeout, conn_timeout, 100, 5, 1000, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_SERVER, "Network timeout")
MACRO_CONFIG_INT(ConnTimeoutProtection, conn_timeout_protection, 1000, 5, 10000, CFGFLAG_SERVER, "Network timeout protection") MACRO_CONFIG_INT(ConnTimeoutProtection, conn_timeout_protection, 1000, 5, 10000, CFGFLAG_SERVER, "Network timeout protection")
MACRO_CONFIG_INT(ClShowIds, cl_show_ids, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Whether to show client ids in scoreboard") MACRO_CONFIG_INT(ClShowIds, cl_show_ids, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Whether to show client IDs in scoreboard, chat and spectator menu")
MACRO_CONFIG_INT(ClScoreboardOnDeath, cl_scoreboard_on_death, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Whether to show scoreboard after death or not") MACRO_CONFIG_INT(ClScoreboardOnDeath, cl_scoreboard_on_death, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Whether to show scoreboard after death or not")
MACRO_CONFIG_INT(ClAutoRaceRecord, cl_auto_race_record, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Save the best demo of each race") MACRO_CONFIG_INT(ClAutoRaceRecord, cl_auto_race_record, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Save the best demo of each race")
MACRO_CONFIG_INT(ClReplays, cl_replays, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Enable/disable replays") MACRO_CONFIG_INT(ClReplays, cl_replays, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Enable/disable replays")

View file

@ -977,18 +977,12 @@ void CChat::OnPrepareLines(float y)
TextRender()->DeleteTextContainer(Line.m_TextContainerIndex); TextRender()->DeleteTextContainer(Line.m_TextContainerIndex);
Graphics()->DeleteQuadContainer(Line.m_QuadContainerIndex); Graphics()->DeleteQuadContainer(Line.m_QuadContainerIndex);
char aName[64 + 12] = ""; char aClientId[16] = "";
if(g_Config.m_ClShowIds && Line.m_ClientId >= 0 && Line.m_aName[0] != '\0') if(g_Config.m_ClShowIds && Line.m_ClientId >= 0 && Line.m_aName[0] != '\0')
{ {
if(Line.m_ClientId < 10) GameClient()->FormatClientId(Line.m_ClientId, aClientId, EClientIdFormat::INDENT_AUTO);
str_format(aName, sizeof(aName), "%d: ", Line.m_ClientId);
else
str_format(aName, sizeof(aName), "%d: ", Line.m_ClientId);
} }
str_append(aName, Line.m_aName);
char aCount[12]; char aCount[12];
if(Line.m_ClientId < 0) if(Line.m_ClientId < 0)
str_format(aCount, sizeof(aCount), "[%d] ", Line.m_TimesRepeated + 1); str_format(aCount, sizeof(aCount), "[%d] ", Line.m_TimesRepeated + 1);
@ -1029,7 +1023,8 @@ void CChat::OnPrepareLines(float y)
} }
} }
TextRender()->TextEx(&Cursor, aName); TextRender()->TextEx(&Cursor, aClientId);
TextRender()->TextEx(&Cursor, Line.m_aName);
if(Line.m_TimesRepeated > 0) if(Line.m_TimesRepeated > 0)
TextRender()->TextEx(&Cursor, aCount); TextRender()->TextEx(&Cursor, aCount);
@ -1099,7 +1094,8 @@ void CChat::OnPrepareLines(float y)
NameColor = ColorRGBA(0.8f, 0.8f, 0.8f, 1.f); NameColor = ColorRGBA(0.8f, 0.8f, 0.8f, 1.f);
TextRender()->TextColor(NameColor); TextRender()->TextColor(NameColor);
TextRender()->CreateOrAppendTextContainer(Line.m_TextContainerIndex, &Cursor, aName); TextRender()->CreateOrAppendTextContainer(Line.m_TextContainerIndex, &Cursor, aClientId);
TextRender()->CreateOrAppendTextContainer(Line.m_TextContainerIndex, &Cursor, Line.m_aName);
if(Line.m_TimesRepeated > 0) if(Line.m_TimesRepeated > 0)
{ {

View file

@ -2544,7 +2544,7 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView)
RightView.HSplitTop(MarginSmall, nullptr, &RightView); RightView.HSplitTop(MarginSmall, nullptr, &RightView);
// Switches of various DDRace HUD elements // Switches of various DDRace HUD elements
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowIds, Localize("Show client IDs in scoreboard"), &g_Config.m_ClShowIds, &RightView, LineSize); DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowIds, Localize("Show client IDs (scoreboard, chat, spectator)"), &g_Config.m_ClShowIds, &RightView, LineSize);
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowhudDDRace, Localize("Show DDRace HUD"), &g_Config.m_ClShowhudDDRace, &RightView, LineSize); DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowhudDDRace, Localize("Show DDRace HUD"), &g_Config.m_ClShowhudDDRace, &RightView, LineSize);
if(g_Config.m_ClShowhudDDRace) if(g_Config.m_ClShowhudDDRace)
{ {
@ -2750,18 +2750,12 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView)
LocalCursor.m_LineWidth = LineWidth; LocalCursor.m_LineWidth = LineWidth;
const auto &Line = s_vLines[LineIndex]; const auto &Line = s_vLines[LineIndex];
char aName[64 + 12] = ""; char aClientId[16] = "";
if(g_Config.m_ClShowIds && Line.m_ClientId >= 0 && Line.m_aName[0] != '\0') if(g_Config.m_ClShowIds && Line.m_ClientId >= 0 && Line.m_aName[0] != '\0')
{ {
if(Line.m_ClientId < 10) GameClient()->FormatClientId(Line.m_ClientId, aClientId, EClientIdFormat::INDENT_FORCE);
str_format(aName, sizeof(aName), "%d: ", Line.m_ClientId);
else
str_format(aName, sizeof(aName), "%d: ", Line.m_ClientId);
} }
str_append(aName, Line.m_aName);
char aCount[12]; char aCount[12];
if(Line.m_ClientId < 0) if(Line.m_ClientId < 0)
str_format(aCount, sizeof(aCount), "[%d] ", Line.m_TimesRepeated + 1); str_format(aCount, sizeof(aCount), "[%d] ", Line.m_TimesRepeated + 1);
@ -2793,7 +2787,8 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView)
if(Render) if(Render)
TextRender()->TextColor(NameColor); TextRender()->TextColor(NameColor);
TextRender()->TextEx(&LocalCursor, aName, -1); TextRender()->TextEx(&LocalCursor, aClientId);
TextRender()->TextEx(&LocalCursor, Line.m_aName);
if(Line.m_TimesRepeated > 0) if(Line.m_TimesRepeated > 0)
{ {

View file

@ -206,8 +206,8 @@ void CScoreboard::RenderSpectators(CUIRect Spectators)
if(g_Config.m_ClShowIds) if(g_Config.m_ClShowIds)
{ {
char aClientId[5]; char aClientId[16];
str_format(aClientId, sizeof(aClientId), "%d: ", pInfo->m_ClientId); GameClient()->FormatClientId(pInfo->m_ClientId, aClientId, EClientIdFormat::NO_INDENT);
TextRender()->TextEx(&Cursor, aClientId); TextRender()->TextEx(&Cursor, aClientId);
} }
TextRender()->TextEx(&Cursor, GameClient()->m_aClients[pInfo->m_ClientId].m_aName); TextRender()->TextEx(&Cursor, GameClient()->m_aClients[pInfo->m_ClientId].m_aName);
@ -512,13 +512,11 @@ void CScoreboard::RenderScoreboard(CUIRect Scoreboard, int Team, int CountStart,
} }
if(g_Config.m_ClShowIds) if(g_Config.m_ClShowIds)
{ {
str_format(aBuf, sizeof(aBuf), "%s%d: %s", pInfo->m_ClientId < 10 ? "" : "", pInfo->m_ClientId, ClientData.m_aName); char aClientId[16];
TextRender()->TextEx(&Cursor, aBuf); GameClient()->FormatClientId(pInfo->m_ClientId, aClientId, EClientIdFormat::INDENT_AUTO);
} TextRender()->TextEx(&Cursor, aClientId);
else
{
TextRender()->TextEx(&Cursor, ClientData.m_aName);
} }
TextRender()->TextEx(&Cursor, ClientData.m_aName);
// ready / watching // ready / watching
if(Client()->IsSixup() && Client()->m_TranslationContext.m_aClients[pInfo->m_ClientId].m_PlayerFlags7 & protocol7::PLAYERFLAG_READY) if(Client()->IsSixup() && Client()->m_TranslationContext.m_aClients[pInfo->m_ClientId].m_PlayerFlags7 & protocol7::PLAYERFLAG_READY)

View file

@ -477,7 +477,16 @@ void CSpectator::OnRender()
TextRender()->TextColor(1.0f, 1.0f, 1.0f, PlayerSelected ? 1.0f : 0.5f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, PlayerSelected ? 1.0f : 0.5f);
TeeAlpha = 1.0f; TeeAlpha = 1.0f;
} }
TextRender()->Text(Width / 2.0f + x + 50.0f, Height / 2.0f + y + BoxMove + (LineHeight - FontSize) / 2.f, FontSize, m_pClient->m_aClients[m_pClient->m_Snap.m_apInfoByDDTeamName[i]->m_ClientId].m_aName, 220.0f); CTextCursor NameCursor;
TextRender()->SetCursor(&NameCursor, Width / 2.0f + x + 50.0f, Height / 2.0f + y + BoxMove + (LineHeight - FontSize) / 2.f, FontSize, TEXTFLAG_RENDER | TEXTFLAG_ELLIPSIS_AT_END);
NameCursor.m_LineWidth = 180.0f;
if(g_Config.m_ClShowIds)
{
char aClientId[16];
GameClient()->FormatClientId(m_pClient->m_Snap.m_apInfoByDDTeamName[i]->m_ClientId, aClientId, EClientIdFormat::INDENT_AUTO);
TextRender()->TextEx(&NameCursor, aClientId);
}
TextRender()->TextEx(&NameCursor, m_pClient->m_aClients[m_pClient->m_Snap.m_apInfoByDDTeamName[i]->m_ClientId].m_aName);
if(GameClient()->m_MultiViewActivated) if(GameClient()->m_MultiViewActivated)
{ {

View file

@ -846,6 +846,32 @@ ColorRGBA CGameClient::GetDDTeamColor(int DDTeam, float Lightness) const
return color_cast<ColorRGBA>(ColorHSLA(Hue, 1.0f, Lightness)); return color_cast<ColorRGBA>(ColorHSLA(Hue, 1.0f, Lightness));
} }
void CGameClient::FormatClientId(int ClientId, char (&aClientId)[16], EClientIdFormat Format) const
{
if(Format == EClientIdFormat::NO_INDENT)
{
str_format(aClientId, sizeof(aClientId), "%d", ClientId);
}
else
{
const int HighestClientId = Format == EClientIdFormat::INDENT_AUTO ? m_Snap.m_HighestClientId : 64;
const char *pFigureSpace = "";
char aNumber[8];
str_format(aNumber, sizeof(aNumber), "%d", ClientId);
aClientId[0] = '\0';
if(ClientId < 100 && HighestClientId >= 100)
{
str_append(aClientId, pFigureSpace);
}
if(ClientId < 10 && HighestClientId >= 10)
{
str_append(aClientId, pFigureSpace);
}
str_append(aClientId, aNumber);
}
str_append(aClientId, ": ");
}
void CGameClient::OnRelease() void CGameClient::OnRelease()
{ {
// release all systems // release all systems
@ -1515,6 +1541,8 @@ void CGameClient::OnNewSnapshot()
} }
} }
m_Snap.m_HighestClientId = maximum(m_Snap.m_HighestClientId, pInfo->m_ClientId);
// calculate team-balance // calculate team-balance
if(pInfo->m_Team != TEAM_SPECTATORS) if(pInfo->m_Team != TEAM_SPECTATORS)
{ {

View file

@ -111,6 +111,13 @@ public:
const CNetObj_EntityEx *m_pDataEx; const CNetObj_EntityEx *m_pDataEx;
}; };
enum class EClientIdFormat
{
NO_INDENT,
INDENT_AUTO,
INDENT_FORCE, // for rendering settings preview
};
class CGameClient : public IGameClient class CGameClient : public IGameClient
{ {
public: public:
@ -315,6 +322,7 @@ public:
int m_LocalClientId; int m_LocalClientId;
int m_NumPlayers; int m_NumPlayers;
int m_aTeamSize[2]; int m_aTeamSize[2];
int m_HighestClientId;
// spectate data // spectate data
struct CSpectateInfo struct CSpectateInfo
@ -575,6 +583,7 @@ public:
bool PredictDummy() { return g_Config.m_ClPredictDummy && Client()->DummyConnected() && m_Snap.m_LocalClientId >= 0 && m_PredictedDummyId >= 0 && !m_aClients[m_PredictedDummyId].m_Paused; } bool PredictDummy() { return g_Config.m_ClPredictDummy && Client()->DummyConnected() && m_Snap.m_LocalClientId >= 0 && m_PredictedDummyId >= 0 && !m_aClients[m_PredictedDummyId].m_Paused; }
const CTuningParams *GetTuning(int i) { return &m_aTuningList[i]; } const CTuningParams *GetTuning(int i) { return &m_aTuningList[i]; }
ColorRGBA GetDDTeamColor(int DDTeam, float Lightness = 0.5f) const; ColorRGBA GetDDTeamColor(int DDTeam, float Lightness = 0.5f) const;
void FormatClientId(int ClientId, char (&aClientId)[16], EClientIdFormat Format) const;
CGameWorld m_GameWorld; CGameWorld m_GameWorld;
CGameWorld m_PredictedWorld; CGameWorld m_PredictedWorld;