diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 48c1c30c8..071142470 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -603,17 +603,26 @@ 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] == ' ') - 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 @@ -621,20 +630,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 @@ -648,7 +656,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; } 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;