fixed name leak

This commit is contained in:
Rj Manhas
2025-11-10 22:05:12 -07:00
parent 508849a8af
commit 735671cef6
13 changed files with 185 additions and 40 deletions
+16 -3
View File
@@ -577,10 +577,23 @@
"focus": "Focus",
"accept_alliance": "Accept",
"reject_alliance": "Reject",
"alliance_renewed": "Your alliance with {name} has been renewed",
"wants_to_renew_alliance": "{name} wants to renew your alliance",
"alliance_renewed": "Your alliance with {otherPlayerName} has been renewed",
"wants_to_renew_alliance": "{otherPlayerName} wants to renew your alliance",
"ignore": "Ignore",
"unit_voluntarily_deleted": "Unit voluntarily deleted"
"unit_voluntarily_deleted": "Unit voluntarily deleted",
"sent_troops_to_player": "Sent {troops} troops to {recipientPlayerName}",
"received_troops_from_player": "Received {troops} troops from {senderPlayerName}",
"sent_gold_to_player": "Sent {gold} gold to {recipientPlayerName}",
"received_gold_from_player": "Received {gold} gold from {senderPlayerName}",
"unit_captured_by_enemy": "Your {unitType} was captured by {captorPlayerName}",
"captured_enemy_unit": "Captured {unitType} from {previousOwnerPlayerName}",
"conquered_player": "Conquered {conqueredPlayerName} received {gold} gold",
"received_gold_from_trade": "Received {gold} gold from trade with {tradingPartnerPlayerName}",
"received_gold_from_captured_ship": "Received {gold} gold from ship captured from {originalOwnerPlayerName}",
"mirv_inbound": "{attackerPlayerName} - MIRV INBOUND",
"atom_bomb_inbound": "{attackerPlayerName} - atom bomb inbound",
"hydrogen_bomb_inbound": "{attackerPlayerName} - hydrogen bomb inbound",
"naval_invasion_inbound": "Naval invasion incoming from {attackerPlayerName}"
},
"unit_info_modal": {
"structure_info": "Structure Info",
+90 -13
View File
@@ -286,7 +286,7 @@ export class EventsDisplay extends LitElement implements Layer {
this.addEvent({
description: translateText("events_display.about_to_expire", {
name: other.name(),
name: other.displayName(),
}),
type: MessageType.RENEW_ALLIANCE,
duration: this.game.config().allianceExtensionPromptOffset() - 3 * 10, // 3 second buffer
@@ -299,7 +299,7 @@ export class EventsDisplay extends LitElement implements Layer {
},
{
text: translateText("events_display.renew_alliance", {
name: other.name(),
name: other.displayName(),
}),
className: "btn",
action: () =>
@@ -373,7 +373,43 @@ export class EventsDisplay extends LitElement implements Layer {
let description: string = event.message;
if (event.message.startsWith("events_display.")) {
description = translateText(event.message, event.params ?? {});
// Resolve player IDs in params to displayName() for hidden names support
// Pattern: Server sends params with keys ending in "PlayerID" (e.g., "recipientPlayerID")
// Client resolves these to displayName() and replaces "ID" with "Name" in the key
// (e.g., "recipientPlayerID" -> "recipientPlayerName") for translation
// This ensures hidden names work correctly and messages are translatable
const resolvedParams: Record<string, string | number> = {};
if (event.params) {
for (const [key, value] of Object.entries(event.params)) {
// If param ends with "PlayerID" or "PlayerId", resolve it to displayName
if (
(key.endsWith("PlayerID") ||
key.endsWith("PlayerId") ||
key.endsWith("playerID") ||
key.endsWith("playerId")) &&
(typeof value === "string" || typeof value === "number")
) {
try {
const player =
typeof value === "string"
? this.game.player(value)
: this.game.playerBySmallID(value);
if (player && player instanceof PlayerView) {
resolvedParams[key.replace(/ID$/i, "Name")] =
player.displayName();
} else {
resolvedParams[key] = value;
}
} catch (e) {
// If player not found, keep original value
resolvedParams[key] = value;
}
} else {
resolvedParams[key] = value;
}
}
}
description = translateText(event.message, resolvedParams);
}
this.addEvent({
@@ -445,7 +481,7 @@ export class EventsDisplay extends LitElement implements Layer {
this.addEvent({
description: translateText("events_display.request_alliance", {
name: requestor.name(),
name: requestor.displayName(),
}),
buttons: [
{
@@ -512,7 +548,7 @@ export class EventsDisplay extends LitElement implements Layer {
) as PlayerView;
this.addEvent({
description: translateText("events_display.alliance_request_status", {
name: recipient.name(),
name: recipient.displayName(),
status: update.accepted
? translateText("events_display.alliance_accepted")
: translateText("events_display.alliance_rejected"),
@@ -552,7 +588,7 @@ export class EventsDisplay extends LitElement implements Layer {
this.addEvent({
description: translateText("events_display.betrayal_description", {
name: betrayed.name(),
name: betrayed.displayName(),
malusPercent: malusPercent,
durationText: durationText,
}),
@@ -572,7 +608,7 @@ export class EventsDisplay extends LitElement implements Layer {
];
this.addEvent({
description: translateText("events_display.betrayed_you", {
name: traitor.name(),
name: traitor.displayName(),
}),
type: MessageType.ALLIANCE_BROKEN,
highlight: true,
@@ -599,7 +635,7 @@ export class EventsDisplay extends LitElement implements Layer {
this.addEvent({
description: translateText("events_display.alliance_expired", {
name: other.name(),
name: other.displayName(),
}),
type: MessageType.ALLIANCE_EXPIRED,
highlight: true,
@@ -617,8 +653,8 @@ export class EventsDisplay extends LitElement implements Layer {
this.addEvent({
description: translateText("events_display.attack_request", {
name: other.name(),
target: target.name(),
name: other.displayName(),
target: target.displayName(),
}),
type: MessageType.ATTACK_REQUEST,
highlight: true,
@@ -698,8 +734,49 @@ export class EventsDisplay extends LitElement implements Layer {
const unitView = this.game.unit(event.unitID);
let description: string = event.message;
// Resolve player IDs in params to displayName() for hidden names support
if (event.message.startsWith("events_display.")) {
const resolvedParams: Record<string, string | number> = {};
if (event.params) {
for (const [key, value] of Object.entries(event.params)) {
// If param ends with "PlayerID" or "PlayerId", resolve it to displayName
if (
(key.endsWith("PlayerID") ||
key.endsWith("PlayerId") ||
key.endsWith("playerID") ||
key.endsWith("playerId")) &&
(typeof value === "string" || typeof value === "number")
) {
try {
const player =
typeof value === "string"
? this.game.player(value)
: this.game.playerBySmallID(value);
if (player && player instanceof PlayerView) {
resolvedParams[key.replace(/ID$/i, "Name")] =
player.displayName();
} else {
resolvedParams[key] = value;
}
} catch (e) {
// If player not found, keep original value
resolvedParams[key] = value;
}
} else {
resolvedParams[key] = value;
}
}
}
description = translateText(event.message, resolvedParams);
// Add emojis for MIRV messages in code (not in translation)
if (event.message === "events_display.mirv_inbound") {
description = `⚠️⚠️⚠️ ${description} ⚠️⚠️⚠️`;
}
}
this.addEvent({
description: event.message,
description: description,
type: event.messageType,
unsafeDescription: false,
highlight: true,
@@ -747,7 +824,7 @@ export class EventsDisplay extends LitElement implements Layer {
${renderTroops(attack.troops)}
${(
this.game.playerBySmallID(attack.attackerID) as PlayerView
)?.name()}
)?.displayName()}
${attack.retreating
? `(${translateText("events_display.retreating")}...)`
: ""}
@@ -778,7 +855,7 @@ export class EventsDisplay extends LitElement implements Layer {
this.game.playerBySmallID(
attack.targetID,
) as PlayerView
)?.name()}
)?.displayName()}
`,
onClick: async () => this.attackWarningOnClick(attack),
className: "text-left text-blue-400",
+4 -2
View File
@@ -80,10 +80,12 @@ export class MirvExecution implements Execution {
this.mg.displayIncomingUnit(
this.nuke.id(),
// TODO TranslateText
`⚠️⚠️⚠️ ${this.player.name()} - MIRV INBOUND ⚠️⚠️⚠️`,
"events_display.mirv_inbound",
MessageType.MIRV_INBOUND,
this.targetPlayer.id(),
{
attackerPlayerID: this.player.id(),
},
);
}
+8 -4
View File
@@ -128,18 +128,22 @@ export class NukeExecution implements Execution {
} else if (this.nukeType === UnitType.AtomBomb) {
this.mg.displayIncomingUnit(
this.nuke.id(),
// TODO TranslateText
`${this.player.name()} - atom bomb inbound`,
"events_display.atom_bomb_inbound",
MessageType.NUKE_INBOUND,
target.id(),
{
attackerPlayerID: this.player.id(),
},
);
} else if (this.nukeType === UnitType.HydrogenBomb) {
this.mg.displayIncomingUnit(
this.nuke.id(),
// TODO TranslateText
`${this.player.name()} - hydrogen bomb inbound`,
"events_display.hydrogen_bomb_inbound",
MessageType.HYDROGEN_BOMB_INBOUND,
target.id(),
{
attackerPlayerID: this.player.id(),
},
);
}
+15 -3
View File
@@ -143,10 +143,14 @@ export class TradeShipExecution implements Execution {
if (this.wasCaptured) {
this.tradeShip!.owner().addGold(gold, this._dstPort.tile());
this.mg.displayMessage(
`Received ${renderNumber(gold)} gold from ship captured from ${this.origOwner.displayName()}`,
"events_display.received_gold_from_captured_ship",
MessageType.CAPTURED_ENEMY_UNIT,
this.tradeShip!.owner().id(),
gold,
{
gold: renderNumber(gold),
originalOwnerPlayerID: this.origOwner.id(),
},
);
// Record stats
this.mg
@@ -156,16 +160,24 @@ export class TradeShipExecution implements Execution {
this.srcPort.owner().addGold(gold);
this._dstPort.owner().addGold(gold, this._dstPort.tile());
this.mg.displayMessage(
`Received ${renderNumber(gold)} gold from trade with ${this.srcPort.owner().displayName()}`,
"events_display.received_gold_from_trade",
MessageType.RECEIVED_GOLD_FROM_TRADE,
this._dstPort.owner().id(),
gold,
{
gold: renderNumber(gold),
tradingPartnerPlayerID: this.srcPort.owner().id(),
},
);
this.mg.displayMessage(
`Received ${renderNumber(gold)} gold from trade with ${this._dstPort.owner().displayName()}`,
"events_display.received_gold_from_trade",
MessageType.RECEIVED_GOLD_FROM_TRADE,
this.srcPort.owner().id(),
gold,
{
gold: renderNumber(gold),
tradingPartnerPlayerID: this._dstPort.owner().id(),
},
);
// Record stats
this.mg
+4 -2
View File
@@ -143,10 +143,12 @@ export class TransportShipExecution implements Execution {
if (this.targetID && this.targetID !== mg.terraNullius().id()) {
mg.displayIncomingUnit(
this.boat.id(),
// TODO TranslateText
`Naval invasion incoming from ${this.attacker.displayName()}`,
"events_display.naval_invasion_inbound",
MessageType.NAVAL_INVASION_INBOUND,
this.targetID,
{
attackerPlayerID: this.attacker.id(),
},
);
}
@@ -50,14 +50,14 @@ export class AllianceExtensionExecution implements Execution {
MessageType.ALLIANCE_ACCEPTED,
this.from.id(),
undefined,
{ name: to.displayName() },
{ otherPlayerID: to.id() },
);
mg.displayMessage(
"events_display.alliance_renewed",
MessageType.ALLIANCE_ACCEPTED,
this.toID,
undefined,
{ name: this.from.displayName() },
{ otherPlayerID: this.from.id() },
);
} else if (alliance.onlyOneAgreedToExtend() && !wasOnlyOneAgreed) {
// Send message to the other player that someone wants to renew
@@ -67,7 +67,7 @@ export class AllianceExtensionExecution implements Execution {
MessageType.RENEW_ALLIANCE,
this.toID,
undefined,
{ name: this.from.displayName() },
{ otherPlayerID: this.from.id() },
);
}
}
+1
View File
@@ -723,6 +723,7 @@ export interface Game extends GameMap {
message: string,
type: MessageType,
playerID: PlayerID | null,
params?: Record<string, string | number>,
): void;
displayChat(
+7 -3
View File
@@ -738,6 +738,7 @@ export class GameImpl implements Game {
message: string,
type: MessageType,
playerID: PlayerID,
params?: Record<string, string | number>,
): void {
const id = this.player(playerID).smallID();
@@ -747,6 +748,7 @@ export class GameImpl implements Game {
message: message,
messageType: type,
playerID: id,
params: params,
});
}
@@ -897,12 +899,14 @@ export class GameImpl implements Game {
conquerPlayer(conqueror: Player, conquered: Player) {
const gold = conquered.gold();
this.displayMessage(
`Conquered ${conquered.displayName()} received ${renderNumber(
gold,
)} gold`,
"events_display.conquered_player",
MessageType.CONQUERED_PLAYER,
conqueror.id(),
gold,
{
conqueredPlayerID: conquered.id(),
gold: renderNumber(gold),
},
);
conqueror.addGold(gold);
conquered.removeGold(gold);
+1
View File
@@ -260,6 +260,7 @@ export interface UnitIncomingUpdate {
message: string;
messageType: MessageType;
playerID: number;
params?: Record<string, string | number>;
}
export interface EmbargoUpdate {
+23 -4
View File
@@ -651,14 +651,24 @@ export class PlayerImpl implements Player {
this.sentDonations.push(new Donation(recipient, this.mg.ticks()));
this.mg.displayMessage(
`Sent ${renderTroops(troops)} troops to ${recipient.name()}`,
"events_display.sent_troops_to_player",
MessageType.SENT_TROOPS_TO_PLAYER,
this.id(),
undefined,
{
troops: renderTroops(troops),
recipientPlayerID: recipient.id(),
},
);
this.mg.displayMessage(
`Received ${renderTroops(troops)} troops from ${this.name()}`,
"events_display.received_troops_from_player",
MessageType.RECEIVED_TROOPS_FROM_PLAYER,
recipient.id(),
undefined,
{
troops: renderTroops(troops),
senderPlayerID: this.id(),
},
);
return true;
}
@@ -671,15 +681,24 @@ export class PlayerImpl implements Player {
this.sentDonations.push(new Donation(recipient, this.mg.ticks()));
this.mg.displayMessage(
`Sent ${renderNumber(gold)} gold to ${recipient.name()}`,
"events_display.sent_gold_to_player",
MessageType.SENT_GOLD_TO_PLAYER,
this.id(),
undefined,
{
gold: renderNumber(gold),
recipientPlayerID: recipient.id(),
},
);
this.mg.displayMessage(
`Received ${renderNumber(gold)} gold from ${this.name()}`,
"events_display.received_gold_from_player",
MessageType.RECEIVED_GOLD_FROM_PLAYER,
recipient.id(),
gold,
{
gold: renderNumber(gold),
senderPlayerID: this.id(),
},
);
return true;
}
+12 -2
View File
@@ -202,14 +202,24 @@ export class UnitImpl implements Unit {
this._owner._units.push(this);
this.mg.addUpdate(this.toUpdate());
this.mg.displayMessage(
`Your ${this.type()} was captured by ${newOwner.displayName()}`,
"events_display.unit_captured_by_enemy",
MessageType.UNIT_CAPTURED_BY_ENEMY,
this._lastOwner.id(),
undefined,
{
unitType: this.type(),
captorPlayerID: newOwner.id(),
},
);
this.mg.displayMessage(
`Captured ${this.type()} from ${this._lastOwner.displayName()}`,
"events_display.captured_enemy_unit",
MessageType.CAPTURED_ENEMY_UNIT,
newOwner.id(),
undefined,
{
unitType: this.type(),
previousOwnerPlayerID: this._lastOwner.id(),
},
);
}
+1 -1
View File
@@ -152,7 +152,7 @@ describe("AllianceExtensionExecution", () => {
MessageType.RENEW_ALLIANCE,
player2.id(),
undefined,
{ name: player1.displayName() },
{ otherPlayerID: player1.id() },
);
expect(displayMessageSpy).toHaveBeenCalledTimes(1);