From b8fab0d3591dd1042bc2176340d9cb36f952c66e Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Fri, 24 Oct 2025 17:37:40 -0500 Subject: [PATCH] Expand Clan Name Possibilities (#2178) ## Description: **This PR expands clan name possibilities available to players.** **Suggested Label:** Feature **Suggested Milestone:** v26 or v27 The current clan name logic does not allow for all alphanumeric characters (e.g. 0-9). Instead it only allows for uppercase and lowercase letters (e.g. a-z and A-Z). This PR updates the logic to include 0-9 in the allowable character set. This is in line with how many other games utilize clan names. Secondarily, the requirement for the clan name to occur at the start of the player name has been relaxed. Now, the requirement is that the clan name is matched with `\[([a-zA-Z0-9]{2,5})\]`. The pre-requisites for clan regex matching have been updated so that both `[` and `]` must be *included* in the player name (whereas previously the `[` was required to appear at the start of the player name). This allows the clan name to occur anywhere in the player name. Finally, clan names (once matched by RegEx) are converted to Uppercase so that clan names such as `[un]`, `[UN]`, `[Un]`, and `[uN]` are all recognized as the *same* clan. As a result, all existing clan names remain valid, but new clan names are now possible. For example `[3M]MeanMrMustard` now has `3M` recognized as the clan name and `T[UN]able` now has `UN` recognized as the clan name. Test cases within the `tests/PlayerInfo.tests.ts` file have been updated accordingly to accurately represent the full alphanumeric character set. This addresses issue #2267. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: GlacialDrift (GlacialDrift_) --- src/core/game/Game.ts | 6 +- tests/PlayerInfo.test.ts | 122 +++++++++++++++++++++++++++++++++++---- 2 files changed, 114 insertions(+), 14 deletions(-) diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 6cd9d2d94..001f76e1f 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -408,11 +408,11 @@ export class PlayerInfo { public readonly nation?: Nation | null, ) { // Compute clan from name - if (!name.startsWith("[") || !name.includes("]")) { + if (!name.includes("[") || !name.includes("]")) { this.clan = null; } else { - const clanMatch = name.match(/^\[([a-zA-Z]{2,5})\]/); - this.clan = clanMatch ? clanMatch[1] : null; + const clanMatch = name.match(/\[([a-zA-Z0-9]{2,5})\]/); + this.clan = clanMatch ? clanMatch[1].toUpperCase() : null; } } } diff --git a/tests/PlayerInfo.test.ts b/tests/PlayerInfo.test.ts index 72ca1cceb..c1fa8b559 100644 --- a/tests/PlayerInfo.test.ts +++ b/tests/PlayerInfo.test.ts @@ -2,7 +2,7 @@ import { PlayerInfo, PlayerType } from "../src/core/game/Game"; describe("PlayerInfo", () => { describe("clan", () => { - test("should extract clan from name when format is [XX]Name", () => { + test("should extract clan from name when format contains [XX]", () => { const playerInfo = new PlayerInfo( "[CL]PlayerName", PlayerType.Human, @@ -12,7 +12,7 @@ describe("PlayerInfo", () => { expect(playerInfo.clan).toBe("CL"); }); - test("should extract clan from name when format is [XXX]Name", () => { + test("should extract clan from name when format contains [XXX]", () => { const playerInfo = new PlayerInfo( "[ABC]PlayerName", PlayerType.Human, @@ -22,7 +22,7 @@ describe("PlayerInfo", () => { expect(playerInfo.clan).toBe("ABC"); }); - test("should extract clan from name when format is [XXXX]Name", () => { + test("should extract clan from name when format contains [XXXX]", () => { const playerInfo = new PlayerInfo( "[ABCD]PlayerName", PlayerType.Human, @@ -32,7 +32,7 @@ describe("PlayerInfo", () => { expect(playerInfo.clan).toBe("ABCD"); }); - test("should extract clan from name when format is [XXXXX]Name", () => { + test("should extract clan from name when format contains [XXXXX]", () => { const playerInfo = new PlayerInfo( "[ABCDE]PlayerName", PlayerType.Human, @@ -42,27 +42,37 @@ describe("PlayerInfo", () => { expect(playerInfo.clan).toBe("ABCDE"); }); - test("should extract clan from name when format is [xxxxx]Name", () => { + test("should extract uppercase clan from name when format contains [xxxxx]", () => { const playerInfo = new PlayerInfo( "[abcde]PlayerName", PlayerType.Human, null, "player_id", ); - expect(playerInfo.clan).toBe("abcde"); + expect(playerInfo.clan).toBe("ABCDE"); }); - test("should extract clan from name when format is [XxXxX]Name", () => { + test("should extract uppercase clan from name when format contains [XxXxX]", () => { const playerInfo = new PlayerInfo( "[AbCdE]PlayerName", PlayerType.Human, null, "player_id", ); - expect(playerInfo.clan).toBe("AbCdE"); + expect(playerInfo.clan).toBe("ABCDE"); }); - test("should return null when name doesn't start with [", () => { + test("should extract uppercase clan from name when format contains [Xx#xX]", () => { + const playerInfo = new PlayerInfo( + "[Ab1cD]PlayerName", + PlayerType.Human, + null, + "player_id", + ); + expect(playerInfo.clan).toBe("AB1CD"); + }); + + test("should return null when name doesn't contain [", () => { const playerInfo = new PlayerInfo( "PlayerName", PlayerType.Human, @@ -82,7 +92,7 @@ describe("PlayerInfo", () => { expect(playerInfo.clan).toBeNull(); }); - test("should return null when clan tag is not 2-5 uppercase letters", () => { + test("should return null when clan tag is not 2-5 alphanumeric letters", () => { const playerInfo = new PlayerInfo( "[A]PlayerName", PlayerType.Human, @@ -94,7 +104,7 @@ describe("PlayerInfo", () => { test("should return null when clan tag contains non alphanumeric characters", () => { const playerInfo = new PlayerInfo( - "[A1c]PlayerName", + "[A?c]PlayerName", PlayerType.Human, null, "player_id", @@ -111,5 +121,95 @@ describe("PlayerInfo", () => { ); expect(playerInfo.clan).toBeNull(); }); + + test("should extract uppercase clan name from any location in the player name", () => { + const playerInfo = new PlayerInfo( + "Player[aa]Name", + PlayerType.Human, + null, + "player_id", + ); + expect(playerInfo.clan).toBe("AA"); + }); + + test("should extract only the first occurrence of a clan name match", () => { + const playerInfo = new PlayerInfo( + "[Ab1cD]Player[aa]Name", + PlayerType.Human, + null, + "player_id", + ); + expect(playerInfo.clan).toBe("AB1CD"); + }); + + test("should extract only the first occurrence of a valid clan name match and extract as uppercase", () => { + const playerInfo = new PlayerInfo( + "[Ab1cDEF]Player[aa]Name", + PlayerType.Human, + null, + "player_id", + ); + expect(playerInfo.clan).toBe("AA"); + }); + + test("should extract numeric-only clan names", () => { + const playerInfo = new PlayerInfo( + "[012]PlayerName", + PlayerType.Human, + null, + "player_id", + ); + expect(playerInfo.clan).toBe("012"); + }); + + test("should extract numeric-only clan names and only the first valid clan name", () => { + const playerInfo = new PlayerInfo( + "[012]Player[aa]Name", + PlayerType.Human, + null, + "player_id", + ); + expect(playerInfo.clan).toBe("012"); + }); + + test("should extract numeric-only clan names from anywhere within the name", () => { + const playerInfo = new PlayerInfo( + "Player[012]Name", + PlayerType.Human, + null, + "player_id", + ); + expect(playerInfo.clan).toBe("012"); + }); + + test("should extract numeric-only clan names from the end of the name", () => { + const playerInfo = new PlayerInfo( + "PlayerName[012]", + PlayerType.Human, + null, + "player_id", + ); + expect(playerInfo.clan).toBe("012"); + }); + + test("should extract uppercase alphanumeric clan names from anywhere within the name", () => { + const playerInfo = new PlayerInfo( + "Player[0a1B2]Name", + PlayerType.Human, + null, + "player_id", + ); + expect(playerInfo.clan).toBe("0A1B2"); + }); + + test("should extract uppercase alphanumeric clan names from the end of the name", () => { + const playerInfo = new PlayerInfo( + "PlayerName[0a1B2]", + PlayerType.Human, + null, + "player_id", + ); + expect(playerInfo.clan).toBe("0A1B2"); + }); }); });