Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/game/client/hud_basechat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,8 @@ CBaseHudChatInputLine::CBaseHudChatInputLine( CBaseHudChat *parent, char const *
m_pPrompt = new vgui::Label( this, "ChatInputPrompt", L"Enter text:" );
m_pInput = new CBaseHudChatEntry( this, "ChatInput", parent );
m_pInput->SetMaximumCharCount( 127 );
// Send converts text to utf-8
m_pInput->m_iMaxByteCount = 127;
}

void CBaseHudChatInputLine::ApplySchemeSettings(vgui::IScheme *pScheme)
Expand Down Expand Up @@ -2519,3 +2521,59 @@ void CBaseHudChat::FireGameEvent( IGameEvent *event )
ChatPrintf( player->entindex(), CHAT_FILTER_NONE, "(SourceTV) %s", event->GetString( "text" ) );
}
}

// Prevent player from inserting text over utf-8 byte limit
void CBaseHudChatEntry::InsertChar(wchar_t ch)
{
if ( m_iMaxByteCount == -1 )
{
BaseClass::InsertChar(ch);
return;
}

// single utf-16 char converted to utf-8 is 3 byte long in worst case
const int iBufLen = BaseClass::GetTextLength() * 3;

// Shortcut: fitting max byte count even in worst case
if ( iBufLen + 4 <= m_iMaxByteCount )
{
BaseClass::InsertChar(ch);
return;
}

// Count bytes of converted utf-8 str
m_szCharBuf.EnsureCapacity( iBufLen );
BaseClass::GetText( m_szCharBuf.Base(), m_szCharBuf.Count() );
const int iCurrentByteLen = Q_strlen( m_szCharBuf.Base() );

// Shortcut: average case
if ( iCurrentByteLen + 4 <= m_iMaxByteCount )
{
BaseClass::InsertChar(ch);
return;
}

// Edge case: check if current wchar_t will cause Send to truncate message
int iCharByteLen; // utf-8
if ( ch < 0x80 ) iCharByteLen = 1;
else if ( ch < 0x800 ) iCharByteLen = 2;
else if ( ch >= 0xD800 && ch <= 0xDBFF ) iCharByteLen = 4; // high surrogate, assume pair
else if ( ch >= 0xDC00 && ch <= 0xDFFF ) // low surrogate
{
// Check if previous wchar was a high surrogate
wchar_t wszHigh[2];
const int iLast = BaseClass::GetTextLength() - 1;
BaseClass::GetTextRange( wszHigh, iLast, 1 );
if ( wszHigh[0] >= 0xD800 && wszHigh[0] <= 0xDBFF ) { BaseClass::InsertChar(ch); return; }
else return; // it was not a hight surrogate, reject
}
else iCharByteLen = 3;

if ( iCurrentByteLen + iCharByteLen <= m_iMaxByteCount )
{
BaseClass::InsertChar(ch);
return;
}

return; // do not insert anything
}
5 changes: 5 additions & 0 deletions src/game/client/hud_basechat.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ class CBaseHudChatEntry : public vgui::TextEntry
{
typedef vgui::TextEntry BaseClass;
public:
int m_iMaxByteCount = -1; // utf-8 string length

CBaseHudChatEntry( vgui::Panel *parent, char const *panelName, CBaseHudChat *pChat )
: BaseClass( parent, panelName )
{
Expand All @@ -340,6 +342,8 @@ class CBaseHudChatEntry : public vgui::TextEntry
SetPaintBorderEnabled( false );
}

virtual void InsertChar(wchar_t ch);

virtual void OnKeyCodeTyped(vgui::KeyCode code)
{
if ( code == KEY_ENTER || code == KEY_PAD_ENTER || code == KEY_ESCAPE )
Expand Down Expand Up @@ -371,6 +375,7 @@ class CBaseHudChatEntry : public vgui::TextEntry

private:
CBaseHudChat *m_pHudChat;
CUtlMemory<char> m_szCharBuf{ 0, 64 }; // tmp buffer
};

//-----------------------------------------------------------------------------
Expand Down
7 changes: 6 additions & 1 deletion src/game/server/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ char * CheckChatText( CBasePlayer *pPlayer, char *text )

// cut off after P_MAX_LEN chars
if ( length > P_MAX_LEN )
p[P_MAX_LEN] = 0;
{
// don't split utf-8 code point
size_t i = P_MAX_LEN;
while( i > 0 && ( static_cast<uint8_t>(p[i]) & 0b1100'0000 ) == 0b1000'0000 ) --i;
p[i] = '\0';
}

GameRules()->CheckChatText( pPlayer, p );

Expand Down
Loading