Skip to content

Commit f8e9ff3

Browse files
committed
User name display improvements.
1. rename cl_add_index_to_name to rd_add_index_to_name 2. make the cvar working both server and client side 3. safely trim utf8 strings
1 parent 2e8880a commit f8e9ff3

5 files changed

Lines changed: 205 additions & 10 deletions

File tree

reactivedrop/content/traitors_challenge/resource/challenges/traitors.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"allowed_mode" "coop"
55
"convars" {
66
"rd_player_bots_allowed" "0"
7-
"cl_add_index_to_name" "1"
7+
"rd_add_index_to_name" "1"
88
"rd_draw_restricted_rectangles_coop" "1"
99

1010
"rd_auto_fast_restart" "1"

src/game/client/c_playerresource.cpp

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const float PLAYER_RESOURCE_THINK_INTERVAL = 0.2f;
2323
#define PLAYER_DEBUG_NAME "WWWWWWWWWWWWWWW"
2424

2525
ConVar cl_names_debug( "cl_names_debug", "0", FCVAR_DEVELOPMENTONLY );
26-
ConVar cl_add_index_to_name("cl_add_index_to_name", "0", FCVAR_REPLICATED);
26+
extern ConVar rd_add_index_to_name;
2727

2828
void RecvProxy_ChangedTeam( const CRecvProxyData *pData, void *pStruct, void *pOut )
2929
{
@@ -141,6 +141,61 @@ void C_PlayerResource::OnDataChanged(DataUpdateType_t updateType)
141141
}
142142
}
143143

144+
static inline bool _IsUtf8ContinuationByte(char b)
145+
{
146+
// check if the byte is a valid UTF-8 continuation byte (10xxxxxx)
147+
return (b & 0xC0) == 0x80;
148+
}
149+
150+
static int _Utf8CharLength(char first_byte)
151+
{
152+
if ((first_byte & 0x80) == 0x00) return 1; // 0xxxxxxx
153+
if ((first_byte & 0xE0) == 0xC0) return 2; // 110xxxxx
154+
if ((first_byte & 0xF0) == 0xE0) return 3; // 1110xxxx
155+
if ((first_byte & 0xF8) == 0xF0) return 4; // 11110xxx
156+
return 0; // Invalid UTF-8 start byte, return 0 to indicate an error
157+
}
158+
159+
static void _SafeUtf8Truncate(char* str, size_t max_size)
160+
{
161+
if (!str || max_size == 0) return;
162+
163+
const size_t MAX_CONTENT_SIZE = max_size - 1;
164+
str[MAX_CONTENT_SIZE] = '\0';
165+
166+
size_t len = strlen(str);
167+
if (len <= MAX_CONTENT_SIZE) return;
168+
169+
size_t truncate_pos = 0;
170+
while (truncate_pos < MAX_CONTENT_SIZE)
171+
{
172+
char byte = str[truncate_pos];
173+
int char_len = _Utf8CharLength(byte);
174+
175+
if (char_len == 0) {
176+
truncate_pos++;
177+
continue;
178+
}
179+
180+
size_t next_pos = truncate_pos + char_len;
181+
if (next_pos > len) break;
182+
183+
bool valid = true;
184+
for (int i = 1; i < char_len; ++i) {
185+
if (!_IsUtf8ContinuationByte(static_cast<uint8_t>(str[truncate_pos + i]))) {
186+
valid = false;
187+
break;
188+
}
189+
}
190+
191+
if (!valid || next_pos > MAX_CONTENT_SIZE)
192+
break;
193+
194+
truncate_pos = next_pos; // 移动到完整字符后
195+
}
196+
str[truncate_pos] = '\0'; // 安全截断
197+
}
198+
144199
void C_PlayerResource::UpdatePlayerName( int slot )
145200
{
146201
static char szNameTemp[MAX_PLAYERS + 1][MAX_PLAYER_NAME_LENGTH];
@@ -157,8 +212,9 @@ void C_PlayerResource::UpdatePlayerName( int slot )
157212
g_RDTextFiltering.FilterTextName( sPlayerInfo.name, g_RDTextFiltering.GetClientSteamID( slot ) );
158213
pchPlayerName = sPlayerInfo.name;
159214
V_snprintf(szNameTemp[slot], MAX_PLAYER_NAME_LENGTH - 1, "%d-%s", slot, pchPlayerName);
215+
_SafeUtf8Truncate(szNameTemp[slot], MAX_PLAYER_NAME_LENGTH);
160216
}
161-
if (cl_add_index_to_name.GetBool()) {
217+
if (rd_add_index_to_name.GetBool()) {
162218
if (!m_szName[slot] || Q_stricmp(m_szName[slot], szNameTemp[slot]))
163219
{
164220
m_szName[slot] = AllocPooledString(szNameTemp[slot]);

src/game/server/player.cpp

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ ConVar sv_noclipduringpause( "sv_noclipduringpause", "0", FCVAR_REPLICATED | FCV
109109
extern ConVar sv_maxunlag;
110110
extern ConVar sv_turbophysics;
111111
extern ConVar *sv_maxreplay;
112+
extern ConVar rd_add_index_to_name;
112113

113114
extern CServerGameDLL g_ServerGameDLL;
114115

@@ -8775,6 +8776,61 @@ const char *CBasePlayer::GetNetworkIDString()
87758776
//-----------------------------------------------------------------------------
87768777
// Assign the player a name
87778778
//-----------------------------------------------------------------------------
8779+
static inline bool _IsUtf8ContinuationByte(char b)
8780+
{
8781+
// check if the byte is a valid UTF-8 continuation byte (10xxxxxx)
8782+
return (b & 0xC0) == 0x80;
8783+
}
8784+
8785+
static int _Utf8CharLength(char first_byte)
8786+
{
8787+
if ((first_byte & 0x80) == 0x00) return 1; // 0xxxxxxx
8788+
if ((first_byte & 0xE0) == 0xC0) return 2; // 110xxxxx
8789+
if ((first_byte & 0xF0) == 0xE0) return 3; // 1110xxxx
8790+
if ((first_byte & 0xF8) == 0xF0) return 4; // 11110xxx
8791+
return 0; // Invalid UTF-8 start byte, return 0 to indicate an error
8792+
}
8793+
8794+
static void _SafeUtf8Truncate(char* str, size_t max_size)
8795+
{
8796+
if (!str || max_size == 0) return;
8797+
8798+
const size_t MAX_CONTENT_SIZE = max_size - 1;
8799+
str[MAX_CONTENT_SIZE] = '\0';
8800+
8801+
size_t len = strlen(str);
8802+
if (len <= MAX_CONTENT_SIZE) return;
8803+
8804+
size_t truncate_pos = 0;
8805+
while (truncate_pos < MAX_CONTENT_SIZE)
8806+
{
8807+
char byte = str[truncate_pos];
8808+
int char_len = _Utf8CharLength(byte);
8809+
8810+
if (char_len == 0) {
8811+
truncate_pos++;
8812+
continue;
8813+
}
8814+
8815+
size_t next_pos = truncate_pos + char_len;
8816+
if (next_pos > len) break;
8817+
8818+
bool valid = true;
8819+
for (int i = 1; i < char_len; ++i) {
8820+
if (!_IsUtf8ContinuationByte(static_cast<uint8_t>(str[truncate_pos + i]))) {
8821+
valid = false;
8822+
break;
8823+
}
8824+
}
8825+
8826+
if (!valid || next_pos > MAX_CONTENT_SIZE)
8827+
break;
8828+
8829+
truncate_pos = next_pos; // 移动到完整字符后
8830+
}
8831+
str[truncate_pos] = '\0'; // 安全截断
8832+
}
8833+
87788834
void CBasePlayer::SetPlayerName( const char *name )
87798835
{
87808836
Assert( name );
@@ -8783,7 +8839,18 @@ void CBasePlayer::SetPlayerName( const char *name )
87838839
{
87848840
Assert( strlen(name) > 0 );
87858841

8786-
Q_strncpy( m_szNetname, name, sizeof(m_szNetname) );
8842+
//set whole m_szNetname to 0
8843+
memset(m_szNetname, 0, sizeof(m_szNetname));
8844+
8845+
if(rd_add_index_to_name.GetBool())
8846+
{
8847+
int n = V_snprintf(m_szNetname, sizeof(m_szNetname) - 1, "%d-%s", ENTINDEX(edict()), name);
8848+
_SafeUtf8Truncate(m_szNetname, sizeof(m_szNetname));
8849+
}
8850+
else
8851+
{
8852+
Q_strncpy(m_szNetname, name, sizeof(m_szNetname));
8853+
}
87878854
}
87888855
}
87898856

src/game/server/swarm/asw_player.cpp

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ extern ConVar asw_default_campaign;
7777
extern ConVar rd_lock_onslaught;
7878
extern ConVar rd_lock_hardcoreff;
7979
extern ConVar rd_lock_challenge;
80+
extern ConVar rd_add_index_to_name;
8081

8182
ConVar rm_welcome_message("rm_welcome_message", "", FCVAR_NONE, "This message is displayed to a player after they join the game");
8283
ConVar rm_welcome_message_delay("rm_welcome_message_delay", "10", FCVAR_NONE, "The number of seconds the welcome message is delayed.", true, 0, true, 30);
@@ -2589,6 +2590,61 @@ void CASW_Player::LeaveMarines()
25892590
m_hInhabiting = NULL;
25902591
}
25912592

2593+
static inline bool _IsUtf8ContinuationByte(char b)
2594+
{
2595+
// check if the byte is a valid UTF-8 continuation byte (10xxxxxx)
2596+
return (b & 0xC0) == 0x80;
2597+
}
2598+
2599+
static int _Utf8CharLength(char first_byte)
2600+
{
2601+
if ((first_byte & 0x80) == 0x00) return 1; // 0xxxxxxx
2602+
if ((first_byte & 0xE0) == 0xC0) return 2; // 110xxxxx
2603+
if ((first_byte & 0xF0) == 0xE0) return 3; // 1110xxxx
2604+
if ((first_byte & 0xF8) == 0xF0) return 4; // 11110xxx
2605+
return 0; // Invalid UTF-8 start byte, return 0 to indicate an error
2606+
}
2607+
2608+
static void _SafeUtf8Truncate(char* str, size_t max_size)
2609+
{
2610+
if (!str || max_size == 0) return;
2611+
2612+
const size_t MAX_CONTENT_SIZE = max_size - 1;
2613+
str[MAX_CONTENT_SIZE] = '\0';
2614+
2615+
size_t len = strlen(str);
2616+
if (len <= MAX_CONTENT_SIZE) return;
2617+
2618+
size_t truncate_pos = 0;
2619+
while (truncate_pos < MAX_CONTENT_SIZE)
2620+
{
2621+
char byte = str[truncate_pos];
2622+
int char_len = _Utf8CharLength(byte);
2623+
2624+
if (char_len == 0) {
2625+
truncate_pos++;
2626+
continue;
2627+
}
2628+
2629+
size_t next_pos = truncate_pos + char_len;
2630+
if (next_pos > len) break;
2631+
2632+
bool valid = true;
2633+
for (int i = 1; i < char_len; ++i) {
2634+
if (!_IsUtf8ContinuationByte(static_cast<uint8_t>(str[truncate_pos + i]))) {
2635+
valid = false;
2636+
break;
2637+
}
2638+
}
2639+
2640+
if (!valid || next_pos > MAX_CONTENT_SIZE)
2641+
break;
2642+
2643+
truncate_pos = next_pos; // 移动到完整字符后
2644+
}
2645+
str[truncate_pos] = '\0'; // 安全截断
2646+
}
2647+
25922648
void CASW_Player::ChangeName( const char *pszNewName )
25932649
{
25942650
// make sure name is not too long
@@ -2597,18 +2653,33 @@ void CASW_Player::ChangeName( const char *pszNewName )
25972653

25982654
const char *pszOldName = GetPlayerName();
25992655

2656+
bool bShouldFireEvent = true;
2657+
if (rd_add_index_to_name.GetBool())
2658+
{
2659+
char tempName[MAX_PLAYER_NAME_LENGTH];
2660+
int n = V_snprintf(tempName, sizeof(tempName) - 1, "%d-%s", ENTINDEX(edict()), pszNewName);
2661+
_SafeUtf8Truncate(tempName, sizeof(tempName));
2662+
if (V_strncmp(pszOldName, tempName, sizeof(tempName)) == 0)
2663+
{
2664+
bShouldFireEvent = false;
2665+
}
2666+
}
2667+
26002668
//char text[256];
26012669
//Q_snprintf( text,sizeof(text), "%s changed name (CASW_Player::ChangeName) to %s\n", pszOldName, trimmedName );
26022670
//UTIL_ClientPrintAll( HUD_PRINTTALK, text );
26032671

26042672
// broadcast event
2605-
IGameEvent * event = gameeventmanager->CreateEvent( "player_changename" );
2606-
if ( event )
2673+
if (bShouldFireEvent)
26072674
{
2608-
event->SetInt( "userid", GetUserID() );
2609-
event->SetString( "oldname", pszOldName );
2610-
event->SetString( "newname", trimmedName );
2611-
gameeventmanager->FireEvent( event );
2675+
IGameEvent* event = gameeventmanager->CreateEvent("player_changename");
2676+
if (event)
2677+
{
2678+
event->SetInt("userid", GetUserID());
2679+
event->SetString("oldname", pszOldName);
2680+
event->SetString("newname", trimmedName);
2681+
gameeventmanager->FireEvent(event);
2682+
}
26122683
}
26132684

26142685
// change shared player name

src/game/shared/swarm/asw_player_shared.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ ConVar asw_rts_controls( "asw_rts_controls", "0", FCVAR_REPLICATED | FCVAR_CHEAT
102102
ConVar asw_controls( "asw_controls", "1", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEMO, "Default camera/control scheme mode", ASWControlsChanged );
103103
ConVar asw_controls_vehicle( "asw_controls_vehicle", "2", FCVAR_REPLICATED | FCVAR_CHEAT, "Default camera/control scheme mode when in a vehicle", ASWControlsChanged );
104104
ConVar asw_hl2_camera( "asw_hl2_camera", "0", FCVAR_REPLICATED | FCVAR_DONTRECORD | FCVAR_CHEAT );
105+
ConVar rd_add_index_to_name("rd_add_index_to_name", "0", FCVAR_REPLICATED);
105106
#ifdef CLIENT_DLL
106107
ConVar asw_controls_spectator_override( "asw_controls_spectator_override", "-1", FCVAR_DONTRECORD, "Force a value for asw_controls while spectating.", ASWControlsChanged );
107108
#endif

0 commit comments

Comments
 (0)