From 1ef30637724a487974af0b9f8e0ec04541d86ce7 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 18 Mar 2024 19:17:34 +0000 Subject: [PATCH 1/3] EnsurePlayerNameIsGood: Only evaluate strlen once Also has a slightly better chance of conflict avoidance for its absolute worst case scenario --- src/d_netcmd.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f5d5fd5b8..472e0cdd6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -602,11 +602,11 @@ static boolean AllowedPlayerNameChar(char ch) boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) { - INT32 ix; + size_t ix, len = strlen(name); - if (strlen(name) == 0 || strlen(name) > MAXPLAYERNAME) + if (len == 0 || len > MAXPLAYERNAME) return false; // Empty or too long. - if (name[0] == ' ' || name[strlen(name)-1] == ' ') + if (name[0] == ' ' || name[len-1] == ' ') return false; // Starts or ends with a space. if (isdigit(name[0])) return false; // Starts with a digit. @@ -620,20 +620,19 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) // Also, anything over 0x80 is disallowed too, since compilers love to // differ on whether they're printable characters or not. - for (ix = 0; name[ix] != '\0'; ix++) + for (ix = 0; ix < len; ix++) if (!AllowedPlayerNameChar(name[ix])) return false; // Check if a player is currently using the name, case-insensitively. for (ix = 0; ix < MAXPLAYERS; ix++) { - if (ix != playernum && playeringame[ix] + if (ix != (size_t)playernum && playeringame[ix] && strcasecmp(name, player_names[ix]) == 0) { // We shouldn't kick people out just because // they joined the game with the same name // as someone else -- modify the name instead. - size_t len = strlen(name); // Recursion! // Slowly strip characters off the end of the @@ -647,7 +646,7 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) else if (len == 1) // Agh! { // Last ditch effort... - sprintf(name, "%d", M_RandomKey(10)); + sprintf(name, "%d", 'A' + M_RandomKey(26)); if (!EnsurePlayerNameIsGood (name, playernum)) return false; } From a4cec98c26f3d4d05b949f36aee3cb636219efcc Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 18 Mar 2024 19:23:55 +0000 Subject: [PATCH 2/3] K_SetNameForBot Func specifically for setting name for CPU players. Lets anything go in offline mode, as per previous behaviour. In online mode, attempts to append a slot-specific char before running EnsurePlayerNameIsGood (which removes characters at the end if a pre-existing match is found). --- src/k_bot.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++- src/k_bot.h | 18 ++++++++++++++++ src/k_grandprix.c | 23 ++++++++++++++++++-- 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 0fc2436a2..b4ee19963 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -46,6 +46,59 @@ extern "C" consvar_t cv_forcebots; +/*-------------------------------------------------- + void K_SetNameForBot(UINT8 playerNum, UINT8 skinnum) + + See header file for description. +--------------------------------------------------*/ +void K_SetNameForBot(UINT8 newplayernum, const char *realname) +{ + UINT8 ix = MAXPLAYERS; + + // These names are generally sourced from skins. + I_Assert(MAXPLAYERNAME >= SKINNAMESIZE+2); + + if (netgame == true) + { + // Check if a player is currently using the name, case-insensitively. + // We only do this if online, because it doesn't matter if there are multiple Eggrobo *off*line. + // See also EnsurePlayerNameIsGood + for (ix = 0; ix < MAXPLAYERS; ix++) + { + if (ix == newplayernum) + continue; + if (playeringame[ix] == false) + continue; + if (strcasecmp(realname, player_names[ix]) != 0) + continue; + + break; + } + } + + if (ix == MAXPLAYERS) + { + // No conflict detected! + sprintf(player_names[newplayernum], "%s", realname); + return; + } + + // Ok, now we append on the end for duplicates... + char namebuffer[MAXPLAYERNAME+1]; + sprintf(namebuffer, "%s %c", realname, 'A'+newplayernum); + + // ...and use the actual function, to handle more devious duplication. + if (!EnsurePlayerNameIsGood(namebuffer, newplayernum)) + { + // we can't bail from adding the bot... + // this hopefully uncontroversial pick is all we CAN do + sprintf(namebuffer, "Bot %u", newplayernum+1); + } + + // And finally write. + sprintf(player_names[newplayernum], "%s", namebuffer); +} + /*-------------------------------------------------- void K_SetBot(UINT8 playerNum, UINT8 skinnum, UINT8 difficulty, botStyle_e style) @@ -117,7 +170,7 @@ void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e st } } players[newplayernum].skincolor = color; - sprintf(player_names[newplayernum], "%s", realname); + K_SetNameForBot(newplayernum, realname); SetPlayerSkinByNum(newplayernum, skinnum); diff --git a/src/k_bot.h b/src/k_bot.h index 86e985475..e133d7f8a 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -197,6 +197,24 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p); // NOT AVAILABLE FOR LUA +/*-------------------------------------------------- + void K_SetNameForBot(UINT8 newplayernum, const char *realname) + + Sets a bot's name. + by K_AddBot, and indirectly by K_AddBotFromServer by sending + a packet. + + Input Arguments:- + newplayernum - Player slot number to set name for. + realname - Proposed name for bot. + + Return:- + None +--------------------------------------------------*/ + +void K_SetNameForBot(UINT8 newplayernum, const char *realname); + + /*-------------------------------------------------- void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e style); diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 92bd7b41b..b6f6cb82c 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -745,6 +745,25 @@ void K_RetireBots(void) newDifficulty = 1; } + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *bot = NULL; + + if (!playeringame[i] || !players[i].bot || players[i].spectator) + { + continue; + } + + bot = &players[i]; + + if (bot->pflags & PF_NOCONTEST) + { + // HACK!!!!! two days to end of cleanup period :) + // we do this so that any bot that's been removed doesn't count for K_SetNameForBot conflicts + player_names[i][0] = '0'; + } + } + for (i = 0; i < MAXPLAYERS; i++) { player_t *bot = NULL; @@ -772,9 +791,9 @@ void K_RetireBots(void) bot->botvars.difficulty = newDifficulty; bot->botvars.diffincrease = 0; - SetPlayerSkinByNum(bot - players, skinnum); + SetPlayerSkinByNum(i, skinnum); bot->skincolor = skins[skinnum].prefcolor; - sprintf(player_names[bot - players], "%s", skins[skinnum].realname); + K_SetNameForBot(i, skins[skinnum].realname); bot->score = 0; bot->pflags &= ~PF_NOCONTEST; From a161df579f0e3f52f6d10b3320ab8f3b64fd436d Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 18 Mar 2024 19:24:42 +0000 Subject: [PATCH 3/3] EnsurePlayerNameIsGood: Remove trailing whitespace, instead of just failing on it Interesting error with its recursive duplication resolving exposed by the previous commit! --- src/d_netcmd.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 472e0cdd6..8c2c571c9 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -606,13 +606,22 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) if (len == 0 || len > MAXPLAYERNAME) return false; // Empty or too long. - if (name[0] == ' ' || name[len-1] == ' ') - return false; // Starts or ends with a space. + if (name[0] == ' ') + return false; // Starts with a space. if (isdigit(name[0])) return false; // Starts with a digit. if (name[0] == '@' || name[0] == '~') return false; // Starts with an admin symbol. + // Clean up trailing whitespace. + while (len && name[len-1] == ' ') + { + name[len-1] = '\0'; + len--; + } + if (len == 0) + return false; + // Check if it contains a non-printing character. // Note: ANSI C isprint() considers space a printing character. // Also don't allow semicolons, since they are used as