diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index fb531f94e..8118a1dec 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -390,6 +390,13 @@ export class PlayerImpl implements Player { if (other === this) { return false; } + if (this.isDisconnected() || other.isDisconnected()) { + // Disconnected players are marked as not-friendly even if they are allies, + // so we need to return early if either player is disconnected. + // Otherise we could end up sending an alliance request to someone + // we are already allied with. + return false; + } if (this.isFriendly(other) || !this.isAlive()) { return false; } diff --git a/src/server/Privilege.ts b/src/server/Privilege.ts index 402b4c3c7..fece2849e 100644 --- a/src/server/Privilege.ts +++ b/src/server/Privilege.ts @@ -1,6 +1,7 @@ import { Cosmetics } from "../core/CosmeticSchemas"; import { decodePatternData } from "../core/PatternDecoder"; import { + FlagSchema, PlayerColor, PlayerCosmeticRefs, PlayerCosmetics, @@ -42,10 +43,14 @@ export class PrivilegeCheckerImpl implements PrivilegeChecker { } } if (refs.flag) { - cosmetics.flag = cosmetics.flag = refs.flag.replace( - /[^a-z0-9-_ ()]/gi, - "", - ); + const result = FlagSchema.safeParse(refs.flag); + if (!result.success) { + return { + type: "forbidden", + reason: "invalid flag: " + result.error.message, + }; + } + cosmetics.flag = result.data; } return { type: "allowed", cosmetics };