Add per-recipient cooldown to QuickChatExecution (#4012)

`QuickChatExecution` had no cooldown, allowing a player to spam
quick-chat intents and flood a recipient's chat UI. This could bury
incoming alliance request notifications, preventing them from being seen
or accepted.

This fix mirrors the existing emoji cooldown pattern:

- Added `quickChatCooldown()` to `Config` (default: 30 ticks / 3 seconds)
- Added `canSendQuickChat(recipient)` and `recordQuickChat(recipient)`
to `Player` / `PlayerImpl`, tracking outgoing chats per recipient
- `QuickChatExecution.tick()` now checks `canSendQuickChat` before
displaying and records before the display calls (so the cooldown is
always written even if display throws)
This commit is contained in:
Josh Harris
2026-05-31 15:20:46 +01:00
committed by Josh Harris
parent 712b2bc473
commit 413efed895
5 changed files with 113 additions and 0 deletions
+3
View File
@@ -513,6 +513,9 @@ export class Config {
emojiMessageCooldown(): Tick {
return 5 * 10;
}
quickChatCooldown(): Tick {
return 3 * 10;
}
targetDuration(): Tick {
return 10 * 10;
}
+7
View File
@@ -27,8 +27,15 @@ export class QuickChatExecution implements Execution {
}
tick(ticks: number): void {
if (!this.sender.canSendQuickChat(this.recipient)) {
this.active = false;
return;
}
const message = this.getMessageFromKey(this.quickChatKey);
this.sender.recordQuickChat(this.recipient);
this.mg.displayChat(
message[1],
message[0],
+2
View File
@@ -808,6 +808,8 @@ export interface Player {
canSendEmoji(recipient: Player | typeof AllPlayers): boolean;
outgoingEmojis(): EmojiMessage[];
sendEmoji(recipient: Player | typeof AllPlayers, emoji: string): void;
canSendQuickChat(recipient: Player): boolean;
recordQuickChat(recipient: Player): void;
// Donation
canDonateGold(recipient: Player): boolean;
+16
View File
@@ -89,6 +89,7 @@ export class PlayerImpl implements Player {
private targets_: Target[] = [];
private outgoingEmojis_: EmojiMessage[] = [];
private outgoingQuickChats_ = new Map<number, Tick>();
private sentDonations: Donation[] = [];
@@ -759,6 +760,21 @@ export class PlayerImpl implements Player {
return true;
}
canSendQuickChat(recipient: Player): boolean {
if (recipient === this) {
return false;
}
const lastSentAt = this.outgoingQuickChats_.get(recipient.smallID());
return (
lastSentAt === undefined ||
this.mg.ticks() - lastSentAt >= this.mg.config().quickChatCooldown()
);
}
recordQuickChat(recipient: Player): void {
this.outgoingQuickChats_.set(recipient.smallID(), this.mg.ticks());
}
canDonateGold(recipient: Player): boolean {
if (
!this.isAlive() ||