# HG changeset patch # User Adam Kaminski # Date 1620589774 14400 # Sun May 09 15:49:34 2021 -0400 # Node ID 6908d6ca91b651c5a3eb436ca8b350339d6b36e9 # Parent 22d0c7789c034fb49a8a1ed98f41bdca71f7946e Color codes are now omitted from the length of the player's name, allowing for more complex color schemes without reaching the limit. There's still a technical limitation so that names aren't too long when sent across the network. diff -r 22d0c7789c03 -r 6908d6ca91b6 docs/zandronum-history.txt --- a/docs/zandronum-history.txt Fri May 07 01:39:31 2021 -0400 +++ b/docs/zandronum-history.txt Sun May 09 15:49:34 2021 -0400 @@ -68,6 +68,7 @@ ! - The Player_SetTeam line special now accepts a second parameter to enable/disable the broadcast message. By default, the message is still printed. [Kaminsky] ! - ZIP/PK3 files containing duplicate lumps will now print warning messages in the console when they're found, and a fatal error will occur if any of these duplicate lumps need to be authenticated on the server. [Kaminsky] ! - The "map" and "changemap" votes now show the full name of the level in addition to the lump name. [Kaminsky] +! - Color codes are now omitted from the length of the player's name, allowing for more complex color schemes without reaching the limit. There's still a technical limitation so that names aren't too long when sent across the network. [Kaminsky] 3.0.1 diff -r 22d0c7789c03 -r 6908d6ca91b6 src/botcommands.cpp --- a/src/botcommands.cpp Fri May 07 01:39:31 2021 -0400 +++ b/src/botcommands.cpp Sun May 09 15:49:34 2021 -0400 @@ -296,8 +296,8 @@ static bool g_bReturnBool = false; static char g_szReturnString[BOTCMD_RETURNSTRING_SIZE] = { 0 }; static char g_szLastChatString[256] = { 0 }; -static char g_szLastChatPlayer[MAXPLAYERNAME+1] = { 0 }; -static char g_szLastJoinedPlayer[MAXPLAYERNAME+1] = { 0 }; +static FString g_szLastChatPlayer; +static FString g_szLastJoinedPlayer; static FRandom g_RandomBotCmdSeed( "RandomBotCmdSeed" ); static FRandom g_RandomBotChatSeed( "RandomBotChatSeed" ); @@ -612,16 +612,14 @@ // void BOTCMD_SetLastChatPlayer( const char *pszString ) { - strncpy( g_szLastChatPlayer, pszString, MAXPLAYERNAME ); - g_szLastChatPlayer[MAXPLAYERNAME] = 0; + g_szLastChatPlayer = pszString; } //***************************************************************************** // void BOTCMD_SetLastJoinedPlayer( const char *pszString ) { - strncpy( g_szLastJoinedPlayer, pszString, MAXPLAYERNAME ); - g_szLastJoinedPlayer[MAXPLAYERNAME] = 0; + g_szLastJoinedPlayer = pszString; } //***************************************************************************** @@ -746,10 +744,10 @@ pszInString += strlen( "player_random" ); } - else if (( strnicmp( pszInString + 1, "player_lastchat", strlen( "player_lastchat" )) == 0 ) && ( g_szLastChatPlayer[0] != 0 )) + else if (( strnicmp( pszInString + 1, "player_lastchat", strlen( "player_lastchat" )) == 0 ) && ( g_szLastChatPlayer.Len( ) > 0 )) { - sprintf( pszOutString, "%s", g_szLastChatPlayer ); - pszOutString += strlen( g_szLastChatPlayer ); + sprintf( pszOutString, "%s", g_szLastChatPlayer.GetChars( )); + pszOutString += g_szLastChatPlayer.Len( ); pszInString += strlen( "player_lastchat" ); } @@ -2270,8 +2268,8 @@ // static void botcmd_GetLastChatPlayer( CSkullBot *pBot ) { - if ( strlen( g_szLastChatPlayer ) < BOTCMD_RETURNSTRING_SIZE ) - sprintf( g_szReturnString, "%s", g_szLastChatPlayer ); + if ( g_szLastChatPlayer.Len( ) < BOTCMD_RETURNSTRING_SIZE ) + sprintf( g_szReturnString, "%s", g_szLastChatPlayer.GetChars( )); else g_szReturnString[0] = 0; } @@ -2518,8 +2516,8 @@ // static void botcmd_GetLastJoinedPlayer( CSkullBot *pBot ) { - if ( strlen( g_szLastJoinedPlayer ) < BOTCMD_RETURNSTRING_SIZE ) - sprintf( g_szReturnString, "%s", g_szLastJoinedPlayer ); + if ( g_szLastJoinedPlayer.Len( ) < BOTCMD_RETURNSTRING_SIZE ) + sprintf( g_szReturnString, "%s", g_szLastJoinedPlayer.GetChars( )); else g_szReturnString[0] = 0; } diff -r 22d0c7789c03 -r 6908d6ca91b6 src/cl_main.cpp --- a/src/cl_main.cpp Fri May 07 01:39:31 2021 -0400 +++ b/src/cl_main.cpp Sun May 09 15:49:34 2021 -0400 @@ -4014,8 +4014,8 @@ // Player's name. if ( name == NAME_Name ) { - if ( value.Len() > MAXPLAYERNAME ) - value = value.Left( MAXPLAYERNAME ); + if ( value.Len() > MAXPLAYERNAMEBUFFER ) + value.Truncate( MAXPLAYERNAMEBUFFER ); player->userinfo.NameChanged ( value ); } // Other info. diff -r 22d0c7789c03 -r 6908d6ca91b6 src/d_netinfo.cpp --- a/src/d_netinfo.cpp Fri May 07 01:39:31 2021 -0400 +++ b/src/d_netinfo.cpp Sun May 09 15:49:34 2021 -0400 @@ -736,9 +736,9 @@ void userinfo_t::NameChanged(const char *name) { FString cleanedName = name; - // [BB] Don't allow the CVAR to be longer than MAXPLAYERNAME, userinfo_t::netname - // can't be longer anyway. Note: The length limit needs to be applied in colorized form. - cleanedName = cleanedName.Left(MAXPLAYERNAME); + // [AK] Ideally, we don't want the CVar to be longer than MAXPLAYERNAMEBUFER, especially + // if sent across the network. Note: the length limit needs to be applied in colorized form. + cleanedName = cleanedName.Left( MAXPLAYERNAMEBUFFER ); V_CleanPlayerName ( cleanedName ); *static_cast((*this)[NAME_Name]) = cleanedName; } @@ -855,9 +855,16 @@ // To clean the name, we first convert the color codes, clean the name and // then restore the color codes again. V_ColorizeString ( cleanedName ); - // [BB] Don't allow the CVAR to be longer than MAXPLAYERNAME, userinfo_t::netname - // can't be longer anyway. Note: The length limit needs to be applied in colorized form. - cleanedName = cleanedName.Left(MAXPLAYERNAME); + // [AK] Ideally, we don't want the CVar to be longer than MAXPLAYERNAMEBUFER, especially + // if sent across the network. Note: the length limit needs to be applied in colorized form. + if ( cleanedName.Len() > MAXPLAYERNAMEBUFFER ) + { + // [AK] Print a warning message to the user. + Printf( PRINT_MEDIUM, "Your name is %d characters long and can't be sent in networked games! " + "The maximum length is %d characters.\n", cleanedName.Len(), MAXPLAYERNAMEBUFFER ); + cleanedName.Truncate( MAXPLAYERNAMEBUFFER ); + } + V_CleanPlayerName ( cleanedName ); V_UnColorizeString ( cleanedName ); // [BB] The name needed to be cleaned. Update the CVAR name with the cleaned @@ -1441,7 +1448,7 @@ void ReadCompatibleUserInfo(FArchive &arc, userinfo_t &info) { - char netname[MAXPLAYERNAME + 1]; + char netname[MAXPLAYERNAMEBUFFER + 1]; BYTE team; int aimdist, color, colorset, skin, gender; bool neverswitch; diff -r 22d0c7789c03 -r 6908d6ca91b6 src/d_player.h --- a/src/d_player.h Fri May 07 01:39:31 2021 -0400 +++ b/src/d_player.h Sun May 09 15:49:34 2021 -0400 @@ -281,8 +281,9 @@ // [BC] Allow longer names since we can now colorize them and stuff. -// [BB] "+3" so that playernames can always be terminated by "\\c-" -#define MAXPLAYERNAME 31+3 +#define MAXPLAYERNAME 32 +// [AK] The maximum length of the buffer used to send a player's name over the network. +#define MAXPLAYERNAMEBUFFER 96 // [GRB] Custom player classes enum diff -r 22d0c7789c03 -r 6908d6ca91b6 src/menu/menu.h --- a/src/menu/menu.h Fri May 07 01:39:31 2021 -0400 +++ b/src/menu/menu.h Sun May 09 15:49:34 2021 -0400 @@ -431,8 +431,9 @@ FFont *mFont; EColorRange mFontColor; int mFrameSize; - char mPlayerName[MAXPLAYERNAME+1]; - char mEditName[MAXPLAYERNAME+2]; + // [AK] Increased the length of the name boxes to MAXPLAYERNAMEBUFFER. + char mPlayerName[MAXPLAYERNAMEBUFFER+1]; + char mEditName[MAXPLAYERNAMEBUFFER+2]; bool mEntering; void DrawBorder (int x, int y, int len); diff -r 22d0c7789c03 -r 6908d6ca91b6 src/menu/playermenu.cpp --- a/src/menu/playermenu.cpp Fri May 07 01:39:31 2021 -0400 +++ b/src/menu/playermenu.cpp Sun May 09 15:49:34 2021 -0400 @@ -93,8 +93,9 @@ { if (i == 0) { - strncpy(mPlayerName, s, MAXPLAYERNAME); - mPlayerName[MAXPLAYERNAME] = 0; + // [AK] Increased the length of the name boxes to MAXPLAYERNAMEBUFFER. + strncpy(mPlayerName, s, MAXPLAYERNAMEBUFFER); + mPlayerName[MAXPLAYERNAMEBUFFER] = 0; return true; } return false; @@ -176,6 +177,58 @@ else { size_t l = strlen(mEditName); + + // [AK] Since color codes aren't included in the length of the name, it's possible + // that the user may enter a string longer than the width of the namebox. If this + // happens, we'll need to strip any excess characters so the string doesn't go + // past the boundary of the namebox. + FString tempString = mEditName; + V_RemoveColorCodes(tempString); + + // [AK] If there's an excessive amount of characters, we'll need to shorten it down. + int excess = static_cast(tempString.Len()) - MAXPLAYERNAME; + if ( excess > 0 ) + { + FString tempColorString = mEditName; + FString lastColorCode; + int characters = 0; + + for (size_t i = 0; i < tempColorString.Len(); i++) + { + // [AK] We want to remember the color code that's used from where the string + // will be stripped so characters still keep the color they're supposed to have. + if (tempColorString[i] == TEXTCOLOR_ESCAPE) + { + size_t j = i; + + // [AK] Also check for color codes defined in-between square brackets. + if ((++i < tempColorString.Len()) && (tempColorString[i] == '[')) + { + do + { + i++; + } + while ((i < tempColorString.Len()) && (tempColorString[i] != ']')); + } + + lastColorCode = tempColorString.Mid(j, 1 + (i - j)); + } + // [AK] Count the number of actual characters used in the name + else if (++characters == excess) + { + tempColorString = tempColorString.Right(tempColorString.Len() - i); + break; + } + } + + // [AK] Insert the last used color code at the front of the now shortened string. + tempColorString.Insert(0, lastColorCode.GetChars()); + tempColorString += SmallFont->GetCursor(); + + screen->DrawText (SmallFont, CR_UNTRANSLATED, x + mFrameSize, mYpos, tempColorString.GetChars(), DTA_Clean, true, TAG_DONE); + return; + } + mEditName[l] = SmallFont->GetCursor(); mEditName[l+1] = 0; @@ -200,7 +253,8 @@ strcpy(mEditName, mPlayerName); mEntering = true; // [TP] Pass allowcolors=true to support colors in player names. - DMenu *input = new DTextEnterMenu(DMenu::CurrentMenu, mEditName, MAXPLAYERNAME, 2, fromcontroller, true); + // [AK] Increased the length of the name boxes to MAXPLAYERNAMEBUFFER. + DMenu *input = new DTextEnterMenu(DMenu::CurrentMenu, mEditName, MAXPLAYERNAMEBUFFER, 2, fromcontroller, true); M_ActivateMenu(input); return true; } @@ -869,9 +923,10 @@ void DPlayerMenu::PlayerNameChanged(FListMenuItem *li) { - char pp[MAXPLAYERNAME+1]; + // [AK] Increased the length of the name boxes to MAXPLAYERNAMEBUFFER. + char pp[MAXPLAYERNAMEBUFFER+1]; const char *p; - if (li->GetString(0, pp, MAXPLAYERNAME)) + if (li->GetString(0, pp, MAXPLAYERNAMEBUFFER)) { FString command("name \""); diff -r 22d0c7789c03 -r 6908d6ca91b6 src/p_interaction.cpp --- a/src/p_interaction.cpp Fri May 07 01:39:31 2021 -0400 +++ b/src/p_interaction.cpp Sun May 09 15:49:34 2021 -0400 @@ -215,10 +215,6 @@ char gendermessage[1024]; bool friendly; int gender; - // We enough characters for the player's name, the terminating zero, and 4 characters - // to strip the color codes (I actually believe it's 3, but let's play it safe). - char szAttacker[MAXPLAYERNAME+1+4]; - char szVictim[MAXPLAYERNAME+1+4]; // No obituaries for non-players, voodoo dolls or when not wanted // [AK] Added a check if the player was forced as a dead spectator through ACS. @@ -366,20 +362,8 @@ message = GStrings("OB_DEFAULT"); } - // [BC] Stop the color codes after we display a name in the obituary string. - if ( attacker && attacker->player ) - sprintf( szAttacker, "%s", attacker->player->userinfo.GetName() ); - else - szAttacker[0] = 0; - - if ( self && self->player ) - sprintf( szVictim, "%s", self->player->userinfo.GetName() ); - else - szVictim[0] = 0; - SexMessage (message, gendermessage, gender, - szVictim[0] ? szVictim : self->player->userinfo.GetName(), - szAttacker[0] ? szAttacker : attacker->player->userinfo.GetName()); + self->player->userinfo.GetName(), attacker->player->userinfo.GetName()); // [AK] Format our message so color codes can appear. V_ColorizeString( gendermessage ); diff -r 22d0c7789c03 -r 6908d6ca91b6 src/scoreboard.cpp --- a/src/scoreboard.cpp Fri May 07 01:39:31 2021 -0400 +++ b/src/scoreboard.cpp Sun May 09 15:49:34 2021 -0400 @@ -645,9 +645,9 @@ void SCOREBOARD_RenderStats_Holders( void ) { ULONG ulYPos; - char szString[160]; + FString message; + FString playerName; char szPatchName[9]; - char szName[MAXPLAYERNAME+1]; // Draw the carrier information for ONE object (POS, TERM, OFCTF). if ( oneflagctf || terminator || possession || teampossession) @@ -658,41 +658,41 @@ sprintf( szPatchName, "HELLSTON" ); if ( g_pPossessionArtifactCarrier ) { - sprintf( szName, "%s", g_pPossessionArtifactCarrier->userinfo.GetName() ); + playerName.Format( "%s", g_pPossessionArtifactCarrier->userinfo.GetName( )); if ( teampossession ) { - V_RemoveColorCodes( szName ); + V_RemoveColorCodes( playerName ); if ( TEAM_CheckIfValid ( g_pPossessionArtifactCarrier->ulTeam ) ) - sprintf( szString, "\\c%c%s :", V_GetColorChar( TEAM_GetTextColor( g_pPossessionArtifactCarrier->ulTeam )), szName ); + message.Format( "\\c%c%s :", V_GetColorChar( TEAM_GetTextColor( g_pPossessionArtifactCarrier->ulTeam )), playerName.GetChars( )); } else - sprintf( szString, "%s \\cG:", szName ); + message.Format( "%s " TEXTCOLOR_RED ":", playerName.GetChars( )); } else - sprintf( szString, "\\cC- \\cG:" ); + message.Format( TEXTCOLOR_GRAY "- " TEXTCOLOR_RED ":" ); } else if ( terminator ) { sprintf( szPatchName, "TERMINAT" ); - sprintf( szString, "\\cC%s \\cG:", g_pTerminatorArtifactCarrier ? g_pTerminatorArtifactCarrier->userinfo.GetName() : "-" ); + message.Format( TEXTCOLOR_GRAY "%s " TEXTCOLOR_RED ":", g_pTerminatorArtifactCarrier ? g_pTerminatorArtifactCarrier->userinfo.GetName() : "-" ); } else if ( oneflagctf ) { sprintf( szPatchName, "STFLA3" ); if ( g_pWhiteCarrier ) { - sprintf( szName, "%s", g_pWhiteCarrier->userinfo.GetName() ); - V_RemoveColorCodes( szName ); + playerName.Format( "%s", g_pWhiteCarrier->userinfo.GetName( )); + V_RemoveColorCodes( playerName ); if ( TEAM_CheckIfValid ( g_pWhiteCarrier->ulTeam ) ) - sprintf( szString, "\\cC%s \\cC:", szName ); + message.Format( TEXTCOLOR_GRAY "%s " TEXTCOLOR_GRAY ":", playerName.GetChars( )); } else - sprintf( szString, "\\cC%s \\cC:", TEAM_GetReturnTicks( teams.Size( ) ) ? "?" : "-" ); + message.Format( TEXTCOLOR_GRAY "%s " TEXTCOLOR_GRAY ":", TEAM_GetReturnTicks( teams.Size( )) ? "?" : "-" ); } // Now, draw it. ulYPos = ST_Y - g_ulTextHeight + 1; - V_ColorizeString( szString ); + V_ColorizeString( message ); if ( g_bScale ) { screen->DrawTexture( TexMan[szPatchName], @@ -703,9 +703,9 @@ TAG_DONE ); screen->DrawText( SmallFont, CR_GRAY, - ( con_virtualwidth ) - ( TexMan[szPatchName]->GetWidth( )) - ( SmallFont->StringWidth( szString )), + ( con_virtualwidth ) - ( TexMan[szPatchName]->GetWidth( )) - ( SmallFont->StringWidth( message.GetChars( ))), (LONG)( ulYPos * g_rYScale ), - szString, + message.GetChars( ), DTA_VirtualWidth, g_ValWidth.Int, DTA_VirtualHeight, g_ValHeight.Int, TAG_DONE ); @@ -718,9 +718,9 @@ TAG_DONE ); screen->DrawText( SmallFont, CR_GRAY, - ( SCREENWIDTH ) - ( TexMan[szPatchName]->GetWidth( )) - ( SmallFont->StringWidth( szString )), + ( SCREENWIDTH ) - ( TexMan[szPatchName]->GetWidth( )) - ( SmallFont->StringWidth( message.GetChars( ))), (LONG)ulYPos, - szString, + message.GetChars( ), TAG_DONE ); } } @@ -736,7 +736,9 @@ continue; sprintf( szPatchName, "%s", TEAM_GetSmallHUDIcon( i )); - + message.Format( TEXTCOLOR_GRAY "%s \\c%c:", TEAM_GetCarrier( i ) ? TEAM_GetCarrier( i )->userinfo.GetName( ) : TEAM_GetReturnTicks( i ) ? "?" : "-", V_GetColorChar( TEAM_GetTextColor( i ))); + V_ColorizeString( message ); + if ( g_bScale ) { screen->DrawTexture( TexMan[szPatchName], @@ -746,13 +748,10 @@ DTA_VirtualHeight, g_ValHeight.Int, TAG_DONE ); - sprintf( szString, "\\cC%s \\c%c:", TEAM_GetCarrier( i ) ? TEAM_GetCarrier( i )->userinfo.GetName() : TEAM_GetReturnTicks( i ) ? "?" : "-", V_GetColorChar( TEAM_GetTextColor( i ))); - V_ColorizeString( szString ); - screen->DrawText( SmallFont, CR_GRAY, - ( g_ValWidth.Int ) - ( TexMan[szPatchName]->GetWidth( )) - ( SmallFont->StringWidth( szString )), + ( g_ValWidth.Int ) - ( TexMan[szPatchName]->GetWidth( )) - ( SmallFont->StringWidth( message.GetChars( ))), (LONG)( ulYPos * g_rYScale ), - szString, + message.GetChars( ), DTA_VirtualWidth, g_ValWidth.Int, DTA_VirtualHeight, g_ValHeight.Int, TAG_DONE ); @@ -764,13 +763,10 @@ ulYPos, TAG_DONE ); - sprintf( szString, "\\cC%s \\c%c:", TEAM_GetCarrier( i ) ? TEAM_GetCarrier( i )->userinfo.GetName() : TEAM_GetReturnTicks( i ) ? "?" : "-", V_GetColorChar( TEAM_GetTextColor( i ))); - V_ColorizeString( szString ); - screen->DrawText( SmallFont, CR_GRAY, - ( SCREENWIDTH ) - ( TexMan[szPatchName]->GetWidth( )) - ( SmallFont->StringWidth( szString )), + ( SCREENWIDTH ) - ( TexMan[szPatchName]->GetWidth( )) - ( SmallFont->StringWidth( message.GetChars( ))), ulYPos, - szString, + message.GetChars( ), TAG_DONE ); } diff -r 22d0c7789c03 -r 6908d6ca91b6 src/sv_main.cpp --- a/src/sv_main.cpp Fri May 07 01:39:31 2021 -0400 +++ b/src/sv_main.cpp Sun May 09 15:49:34 2021 -0400 @@ -1944,7 +1944,6 @@ { player_t *pPlayer; FString szSkin; - char szOldPlayerName[MAXPLAYERNAME+1]; ULONG ulUserInfoInstance; // [BB] We may only kick the player after completely parsing the current message, // otherwise we'll get network parsing errors. @@ -2019,10 +2018,10 @@ if ( name == NAME_Name ) { - sprintf( szOldPlayerName, "%s", pPlayer->userinfo.GetName() ); - - if ( value.Len() > MAXPLAYERNAME ) - value.Truncate(MAXPLAYERNAME); + FString oldPlayerName = pPlayer->userinfo.GetName(); + + if ( value.Len() > MAXPLAYERNAMEBUFFER ) + value.Truncate( MAXPLAYERNAMEBUFFER ); FString nameStringCopy = value; @@ -2067,20 +2066,17 @@ if ( g_aClients[g_lCurrentClient].State == CLS_SPAWNED ) { - char szPlayerNameNoColor[MAXPLAYERNAME+1]; - char szOldPlayerNameNoColor[MAXPLAYERNAME+1]; - - sprintf( szPlayerNameNoColor, "%s", pPlayer->userinfo.GetName() ); - sprintf( szOldPlayerNameNoColor, "%s", szOldPlayerName ); - - V_ColorizeString( szPlayerNameNoColor ); - V_ColorizeString( szOldPlayerNameNoColor ); - V_RemoveColorCodes( szPlayerNameNoColor ); - V_RemoveColorCodes( szOldPlayerNameNoColor ); - - if ( stricmp( szPlayerNameNoColor, szOldPlayerNameNoColor ) != 0 ) + FString playerNameNoColor = pPlayer->userinfo.GetName(); + FString oldPlayerNameNoColor = oldPlayerName.GetChars(); + + V_ColorizeString( playerNameNoColor ); + V_RemoveColorCodes( playerNameNoColor ); + V_ColorizeString( oldPlayerNameNoColor ); + V_RemoveColorCodes( oldPlayerNameNoColor ); + + if ( playerNameNoColor.CompareNoCase( oldPlayerNameNoColor ) != 0 ) { - SERVER_Printf( "%s is now known as %s\n", szOldPlayerName, pPlayer->userinfo.GetName() ); + SERVER_Printf( "%s is now known as %s\n", oldPlayerName.GetChars(), pPlayer->userinfo.GetName() ); // [RC] Update clients using the RCON utility. SERVER_RCON_UpdateInfo( SVRCU_PLAYERDATA ); @@ -2924,7 +2920,7 @@ Info.lPointCount = players[ulClient].lPointCount; Info.lWinCount = players[ulClient].ulWins; Info.ulTime = players[ulClient].ulTime; // [RC] Save time - sprintf( Info.szName, "%s", players[ulClient].userinfo.GetName() ); + Info.Name = players[ulClient].userinfo.GetName(); SERVER_SAVE_SaveInfo( &Info ); } @@ -2940,7 +2936,7 @@ pInfo->lFragCount = 0; pInfo->lPointCount = 0; pInfo->lWinCount = 0; - pInfo->szName[0] = 0; + pInfo->Name = ""; } } diff -r 22d0c7789c03 -r 6908d6ca91b6 src/sv_save.cpp --- a/src/sv_save.cpp Fri May 07 01:39:31 2021 -0400 +++ b/src/sv_save.cpp Sun May 09 15:49:34 2021 -0400 @@ -77,17 +77,16 @@ PLAYERSAVEDINFO_t *SERVER_SAVE_GetSavedInfo( const char *pszPlayerName, NETADDRESS_s Address ) { ULONG ulIdx; - char szPlayerName[128]; + FString name = pszPlayerName; - sprintf( szPlayerName, "%s", pszPlayerName ); - V_RemoveColorCodes( szPlayerName ); + V_RemoveColorCodes( name ); for ( ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) { if ( g_SavedPlayerInfo[ulIdx].bInitialized == false ) continue; - if (( stricmp( szPlayerName, g_SavedPlayerInfo[ulIdx].szName ) == 0 ) && + if (( g_SavedPlayerInfo[ulIdx].Name.CompareNoCase( name ) == 0 ) && Address.Compare( g_SavedPlayerInfo[ulIdx].Address )) { return ( &g_SavedPlayerInfo[ulIdx] ); @@ -110,7 +109,7 @@ g_SavedPlayerInfo[ulIdx].lFragCount = 0; g_SavedPlayerInfo[ulIdx].lPointCount = 0; g_SavedPlayerInfo[ulIdx].lWinCount = 0; - g_SavedPlayerInfo[ulIdx].szName[0] = 0; + g_SavedPlayerInfo[ulIdx].Name = ""; g_SavedPlayerInfo[ulIdx].ulTime = 0; } } @@ -120,17 +119,16 @@ void SERVER_SAVE_SaveInfo( PLAYERSAVEDINFO_t *pInfo ) { ULONG ulIdx; - char szPlayerName[128]; + FString name = pInfo->Name.GetChars(); - sprintf( szPlayerName, "%s", pInfo->szName ); - V_RemoveColorCodes( szPlayerName ); + V_RemoveColorCodes( name ); for ( ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) { if ( g_SavedPlayerInfo[ulIdx].bInitialized ) { // If this slot matches the player we're trying to save, just update it. - if (( stricmp( szPlayerName, g_SavedPlayerInfo[ulIdx].szName ) == 0 ) && + if (( g_SavedPlayerInfo[ulIdx].Name.CompareNoCase( name ) == 0 ) && pInfo->Address.Compare( g_SavedPlayerInfo[ulIdx].Address )) { server_save_UpdateSlotWithInfo( ulIdx, pInfo ); @@ -159,7 +157,7 @@ g_SavedPlayerInfo[ulSlot].lPointCount = pInfo->lPointCount; g_SavedPlayerInfo[ulSlot].lWinCount = pInfo->lWinCount; g_SavedPlayerInfo[ulSlot].ulTime = pInfo->ulTime; - sprintf( g_SavedPlayerInfo[ulSlot].szName, "%s", pInfo->szName ); + g_SavedPlayerInfo[ulSlot].Name = pInfo->Name.GetChars(); - V_RemoveColorCodes( g_SavedPlayerInfo[ulSlot].szName ); + V_RemoveColorCodes( g_SavedPlayerInfo[ulSlot].Name ); } diff -r 22d0c7789c03 -r 6908d6ca91b6 src/sv_save.h --- a/src/sv_save.h Fri May 07 01:39:31 2021 -0400 +++ b/src/sv_save.h Sun May 09 15:49:34 2021 -0400 @@ -59,7 +59,7 @@ struct PLAYERSAVEDINFO_t { // Name of the player. - char szName[MAXPLAYERNAME+1]; + FString Name; // Address of the player whose information is being saved. NETADDRESS_s Address; diff -r 22d0c7789c03 -r 6908d6ca91b6 src/v_text.cpp --- a/src/v_text.cpp Fri May 07 01:39:31 2021 -0400 +++ b/src/v_text.cpp Sun May 09 15:49:34 2021 -0400 @@ -532,117 +532,30 @@ } } -// [RC] Returns whether this character is allowed in names. -bool v_IsCharAcceptableInNames ( char c ) -{ - // Allow color codes. - if ( c == 28 ) - return true; - - // No undisplayable system ascii. - if ( c <= 31 ) - return false; - - // Percent is forbiddon in net names, because it can be abused for string exploits. - if ( c == '%' ) - return false; - - // Ampersands aren't very distinguishable in Heretic - if ( c == '&' ) - return false; - - // No escape codes (\c is handled differently). - if ( c == 92 ) - return false; - - // Tilde is invisible. - if ( c == 96 ) - return false; - - // Ascii above 123 is invisible or hard to type. - if ( c >= 123 ) - return false; - - return true; -} - -// [RC] Returns whether this character is invisible. -bool v_IsCharacterWhitespace ( char c ) +void V_CleanPlayerName( FString &String ) { - // System ascii < 32 is invisible. - if ( c <= 31 ) - return true; - - // Text colorization. - if ( c == TEXTCOLOR_ESCAPE ) - return true; - - // Space. - if ( c == 32 ) - return true; - - // Delete. - if ( c == 127 ) - return true; - - // Ascii 255. - if ( static_cast( c ) == 255 ) //[BL] 255 is supposedly out of range for a char - return true; - - return false; -} - - -// [RC] Conforms names to meet standards. -void V_CleanPlayerName( char *pszString ) -{ - char *pszStart; - char *p; - char c; - ULONG ulStringLength; - ULONG ulTotalLength; - ULONG ulNonWhitespace; - char szColorlessName[256]; - - ulStringLength = static_cast(strlen( pszString )); - ulTotalLength = 0; - ulNonWhitespace = 0; - - // Start at the beginning of the string. - p = pszString; - pszStart = pszString; + ULONG ulLegalCharacters = 0; + ULONG ulNonWhitespaces = 0; // The name must be longer than three characters. - if ( ulStringLength < 3 ) + if ( String.Len( ) < 3 ) { - strcpy( pszString, "Player" ); + String = "Player"; return; } // Go through and remove the illegal characters. - while ( (c = *p++) ) + for ( int i = 0; i < static_cast( String.Len( )); i++ ) { - if ( !v_IsCharAcceptableInNames(c) ) - { - ULONG ulPos; - ulStringLength = static_cast(strlen( pszString )); - - // Shift the rest of the string back one. - for ( ulPos = 0; ulPos < ulStringLength; ulPos++ ) - pszString[ulPos] = pszString[ulPos + 1]; - - // Don't skip a character. - p--; - } - else - { - pszString++; - ulTotalLength++; - } + // [AK] Remove undisplayable ASCII characters that are invisible or hard to type. + if ((( String[i] <= 31 ) && ( String[i] != TEXTCOLOR_ESCAPE )) || ( String[i] >= 123 )) + String.StripChars( String[i--] ); } - // Cut the string at its new end. - *pszString = 0; + // [AK] Remove other forbidden characters like percentages, ampersands, escape codes, + // and tildas that are invisible, indistinguishable, or otherwise exploitable. + static const char forbiddenChars[] = { 37, 38, 92, 96, 0 }; + String.StripChars( forbiddenChars ); // [BB] Remove any trailing incomplete escaped color codes. Since we just removed // quite a bit from the string, it's possible that those are there now. @@ -652,60 +565,56 @@ // unescaped color codes, so I have to uncolorize, clean and colorize the name here. // Not so efficient, but V_CleanPlayerName is called seldom enough so that this // doesn't matter. - FString tempString = pszStart; - V_UnColorizeString ( tempString ); - V_RemoveTrailingCrapFromFString ( tempString ); + V_UnColorizeString( String ); + V_RemoveTrailingCrapFromFString( String ); + + V_ColorizeString ( String ); + + // [AK] We want to make sure the player's name isn't too short or too long so we'll + // need to count how many characters are used in the actual name. + for ( int i = 0; i < static_cast( String.Len( )); i++ ) + { + // [AK] Skip color codes, also taking into account those that use square brackets. + if ( String[i] == TEXTCOLOR_ESCAPE ) + { + if (( ++i < static_cast( String.Len( ))) && ( String[i] == '[' )) + { + do + { + i++; + } + while (( i < static_cast(String.Len( ))) && ( String[i] != ']' )); + } + } + // [AK] If the name exceeds the limit then truncate the string. + else if ( ++ulLegalCharacters > MAXPLAYERNAME ) + { + String.Truncate( i ); + break; + } + // [AK] Count how many characters in the name aren't whitespaces. + else if ( String[i] != 32 ) + { + ulNonWhitespaces++; + } + } + + // Check the length again, as characters were removed. + if ( ulNonWhitespaces < 3 ) + { + String = "Player"; + return; + } + // [BB] If the name uses color codes, make sure that it is terminated with "\\c-". // V_RemoveTrailingCrapFromFString removes all trailing color codes including "\\c-". // This is necessary to catch incomplete color codes before the trailing "\\c-". // Hence, we have to re-add "\\c-" here. - if ( ( tempString.IndexOf ( "\\c" ) != -1 ) ) + if (( String.Len( ) > 0 ) && ( String.IndexOf( TEXTCOLOR_ESCAPE ) != -1 )) { - // [BB] In the uncolorized string, color codes need one additional char, take this - // into account when checking whether the name is too long. - FString tempColorizedString = tempString.GetChars(); - V_ColorizeString ( tempColorizedString ); - const unsigned int numColorCodes = tempString.Len() - tempColorizedString.Len(); - - if ( tempString.Len() > MAXPLAYERNAME - 3 + numColorCodes ) - { - tempString = tempString.Left ( MAXPLAYERNAME - 3 + numColorCodes ); - V_RemoveTrailingCrapFromFString ( tempString ); - } - tempString += "\\c-"; + if ( String.IndexOf( TEXTCOLOR_NORMAL ) != String.Len( ) - 2 ) + String += TEXTCOLOR_NORMAL; } - V_ColorizeString ( tempString ); - sprintf ( pszStart, "%s", tempString.GetChars() ); - - // Determine the name's actual length. - strncpy( szColorlessName, pszStart, 256 ); - V_RemoveColorCodes( szColorlessName ); - - p = szColorlessName; - ulNonWhitespace = 0; - while ( (c = *p++) ) - { - if ( !v_IsCharacterWhitespace(c) ) - ulNonWhitespace++; - } - - // Check the length again, as characters were removed. - if ( ulNonWhitespace < 3 ) - strcpy( pszStart, "Player" ); -} - -// [BB] Version of V_CleanPlayerName that accepts a FString as argument. -void V_CleanPlayerName( FString &String ) -{ - const int length = (int) String.Len(); - // [BB] V_CleanPlayerName possibly appends "\\c-", hence we need to reserve more memory than just "length + terminating 0". - // [EP] Don't forget it might return "Player" in case the name results being too short. - char *tempCharArray = new char[MAX(7, length+4)]; - strncpy( tempCharArray, String.GetChars(), length ); - tempCharArray[length] = 0; - V_CleanPlayerName( tempCharArray ); - String = tempCharArray; - delete[] tempCharArray; } // [RC] Converts COL_ numbers to their \c counterparts. @@ -744,7 +653,7 @@ while ( ulStringLength > 0 ) { // [BB] Remove trailing whitespace. - if ( v_IsCharacterWhitespace ( pszString[ulStringLength-1] ) ) + if ( ( pszString[ulStringLength-1] == TEXTCOLOR_ESCAPE ) || ( pszString[ulStringLength-1] == 32 ) ) { pszString[ulStringLength-1] = 0; ulStringLength--; diff -r 22d0c7789c03 -r 6908d6ca91b6 src/v_text.h --- a/src/v_text.h Fri May 07 01:39:31 2021 -0400 +++ b/src/v_text.h Sun May 09 15:49:34 2021 -0400 @@ -94,9 +94,6 @@ void V_RemoveInvalidColorCodes( FString &String ); // [RC] Functions related to user name cleaning. -bool v_IsCharAcceptableInNames ( char c ); -bool v_IsCharacterWhitespace ( char c ); -void V_CleanPlayerName( char *pszString ); void V_CleanPlayerName( FString &String ); FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *str); diff -r 22d0c7789c03 -r 6908d6ca91b6 src/win32/serverconsole/serverconsole.cpp --- a/src/win32/serverconsole/serverconsole.cpp Fri May 07 01:39:31 2021 -0400 +++ b/src/win32/serverconsole/serverconsole.cpp Sun May 09 15:49:34 2021 -0400 @@ -1853,7 +1853,7 @@ void SERVERCONSOLE_ReListPlayers( void ) { LVITEM Item; - char szString[MAXPLAYERNAME+1]; + FString playerName; LONG lIndex; LONG lIdx; @@ -1876,9 +1876,9 @@ if ( playeringame[lIdx] == false ) continue; - sprintf( szString, "%s", players[lIdx].userinfo.GetName() ); - V_RemoveColorCodes( szString ); - Item.pszText = szString; + playerName = players[lIdx].userinfo.GetName(); + V_RemoveColorCodes( playerName ); + Item.pszText = const_cast( playerName.GetChars()); lIndex = SendDlgItemMessage( g_hDlg, IDC_PLAYERLIST, LVM_INSERTITEM, 0, (LPARAM)&Item ); if ( lIndex == -1 ) @@ -1896,7 +1896,7 @@ void SERVERCONSOLE_UpdatePlayerInfo( LONG lPlayer, ULONG ulUpdateFlags ) { LVITEM Item; - char szString[MAXPLAYERNAME+1]; + FString message; LONG lIndex = -1; LONG lIdx; @@ -1918,9 +1918,9 @@ if ( ulUpdateFlags & UDF_NAME ) { Item.iSubItem = COLUMN_NAME; - sprintf( szString, "%s", players[lPlayer].userinfo.GetName() ); - Item.pszText = szString; - V_RemoveColorCodes( szString ); + message = players[lPlayer].userinfo.GetName(); + V_RemoveColorCodes( message ); + Item.pszText = const_cast( message.GetChars()); SendDlgItemMessage( g_hDlg, IDC_PLAYERLIST, LVM_SETITEM, lIndex, (LPARAM)&Item ); } @@ -1929,25 +1929,25 @@ { Item.iSubItem = COLUMN_FRAGS; if ( PLAYER_IsTrueSpectator( &players[lPlayer] )) - sprintf( szString, "Spectating" ); + message = "Spectating"; else if (( GAMEMODE_GetCurrentFlags( ) & GMF_PLAYERSONTEAMS ) && ( !players[lPlayer].bOnTeam )) - sprintf( szString, "No team" ); + message = "No team"; else if ( lastmanstanding || teamlms ) { if ( players[lPlayer].health <=0 ) - sprintf( szString, "Dead" ); + message = "Dead"; else if ( lastmanstanding ) - sprintf( szString, "%ld", players[lPlayer].ulWins ); + message.Format( "%ld", players[lPlayer].ulWins ); else - sprintf( szString, "%d", players[lPlayer].fragcount ); + message.Format( "%d", players[lPlayer].fragcount ); } else if (( GAMEMODE_GetCurrentFlags( ) & GMF_PLAYERSEARNPOINTS ) || (( GAMEMODE_GetCurrentFlags( ) & GMF_PLAYERSEARNKILLS ) && ( zadmflags & ZADF_AWARD_DAMAGE_INSTEAD_KILLS ))) - sprintf( szString, "%ld", players[lPlayer].lPointCount ); + message.Format( "%ld", players[lPlayer].lPointCount ); else if ( GAMEMODE_GetCurrentFlags( ) & GMF_PLAYERSEARNFRAGS ) - sprintf( szString, "%d", players[lPlayer].fragcount ); + message.Format( "%d", players[lPlayer].fragcount ); else - sprintf( szString, "%d", players[lPlayer].killcount ); - Item.pszText = szString; + message.Format( "%d", players[lPlayer].killcount ); + Item.pszText = const_cast( message.GetChars()); SendDlgItemMessage( g_hDlg, IDC_PLAYERLIST, LVM_SETITEM, lIndex, (LPARAM)&Item ); } @@ -1955,10 +1955,10 @@ { Item.iSubItem = COLUMN_PING; if ( players[lPlayer].bIsBot ) - sprintf( szString, "Bot" ); + message = "Bot"; else - sprintf( szString, "%ld", players[lPlayer].ulPing ); - Item.pszText = szString; + message.Format( "%ld", players[lPlayer].ulPing ); + Item.pszText = const_cast( message.GetChars()); SendDlgItemMessage( g_hDlg, IDC_PLAYERLIST, LVM_SETITEM, lIndex, (LPARAM)&Item ); } @@ -1966,8 +1966,8 @@ if ( ulUpdateFlags & UDF_TIME ) { Item.iSubItem = COLUMN_TIME; - sprintf( szString, "%ld", ( players[lPlayer].ulTime / ( TICRATE * 60 ))); - Item.pszText = szString; + message.Format( "%ld", ( players[lPlayer].ulTime / ( TICRATE * 60 ))); + Item.pszText = const_cast( message.GetChars()); SendDlgItemMessage( g_hDlg, IDC_PLAYERLIST, LVM_SETITEM, lIndex, (LPARAM)&Item ); }