mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-30 19:12:20 +00:00
sanitize profane usernames
This commit is contained in:
@@ -22,14 +22,13 @@ import { NameViewData } from "./game/Game";
|
||||
import { GameUpdateType } from "./game/GameUpdates";
|
||||
import { createGame } from "./game/GameImpl";
|
||||
import { loadTerrainMap as loadGameMap } from "./game/TerrainMapLoader";
|
||||
import { GameConfig, Turn } from "./Schemas";
|
||||
import { ClientID, GameConfig, Turn } from "./Schemas";
|
||||
import { GameUpdateViewData } from "./game/GameUpdates";
|
||||
import { andFN, manhattanDistFN, TileRef } from "./game/GameMap";
|
||||
import { targetTransportTile } from "./Util";
|
||||
|
||||
export async function createGameRunner(
|
||||
gameID: string,
|
||||
gameConfig: GameConfig,
|
||||
clientID: ClientID,
|
||||
callBack: (gu: GameUpdateViewData) => void
|
||||
): Promise<GameRunner> {
|
||||
const config = getConfig(gameConfig);
|
||||
@@ -40,7 +39,7 @@ export async function createGameRunner(
|
||||
gameMap.nationMap,
|
||||
config
|
||||
);
|
||||
const gr = new GameRunner(game as Game, new Executor(game, gameID), callBack);
|
||||
const gr = new GameRunner(game as Game, new Executor(game, gameID, clientID), callBack);
|
||||
gr.init();
|
||||
return gr;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import {
|
||||
AttackIntent,
|
||||
BoatAttackIntentSchema,
|
||||
ClientID,
|
||||
GameID,
|
||||
Intent,
|
||||
Turn,
|
||||
@@ -22,29 +23,26 @@ import { BotSpawner } from "./BotSpawner";
|
||||
import { TransportShipExecution } from "./TransportShipExecution";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { FakeHumanExecution } from "./FakeHumanExecution";
|
||||
import { generateID, processName, sanitize, simpleHash } from "../Util";
|
||||
import { sanitize, simpleHash } from "../Util";
|
||||
import { AllianceRequestExecution } from "./alliance/AllianceRequestExecution";
|
||||
import { AllianceRequestReplyExecution } from "./alliance/AllianceRequestReplyExecution";
|
||||
import { BreakAllianceExecution } from "./alliance/BreakAllianceExecution";
|
||||
import { TargetPlayerExecution } from "./TargetPlayerExecution";
|
||||
import { EmojiExecution } from "./EmojiExecution";
|
||||
import { DonateExecution } from "./DonateExecution";
|
||||
import { NukeExecution } from "./NukeExecution";
|
||||
import { SetTargetTroopRatioExecution } from "./SetTargetTroopRatioExecution";
|
||||
import { WarshipExecution } from "./WarshipExecution";
|
||||
import { PortExecution } from "./PortExecution";
|
||||
import { MissileSiloExecution } from "./MissileSiloExecution";
|
||||
import { DefensePostExecution } from "./DefensePostExecution";
|
||||
import { CityExecution } from "./CityExecution";
|
||||
import { TileRef } from "../game/GameMap";
|
||||
import { MirvExecution } from "./MIRVExecution";
|
||||
import { ConstructionExecution } from "./ConstructionExecution";
|
||||
import { fixProfaneUsername, isProfaneUsername } from "../validations/username";
|
||||
|
||||
export class Executor {
|
||||
// private random = new PseudoRandom(999)
|
||||
private random: PseudoRandom = null;
|
||||
|
||||
constructor(private mg: Game, private gameID: GameID) {
|
||||
constructor(
|
||||
private mg: Game,
|
||||
private gameID: GameID,
|
||||
private clientID: ClientID
|
||||
) {
|
||||
// Add one to avoid id collisions with bots.
|
||||
this.random = new PseudoRandom(simpleHash(gameID) + 1);
|
||||
}
|
||||
@@ -66,7 +64,10 @@ export class Executor {
|
||||
case "spawn":
|
||||
return new SpawnExecution(
|
||||
new PlayerInfo(
|
||||
sanitize(intent.name),
|
||||
// Players see their original name, others see a sanitized version
|
||||
intent.clientID == this.clientID
|
||||
? sanitize(intent.name)
|
||||
: fixProfaneUsername(sanitize(intent.name)),
|
||||
intent.playerType,
|
||||
intent.clientID,
|
||||
intent.playerID
|
||||
|
||||
@@ -1,8 +1,41 @@
|
||||
import {
|
||||
RegExpMatcher,
|
||||
englishDataset,
|
||||
englishRecommendedTransformers,
|
||||
} from "obscenity";
|
||||
import { simpleHash } from "../Util";
|
||||
|
||||
const matcher = new RegExpMatcher({
|
||||
...englishDataset.build(),
|
||||
...englishRecommendedTransformers,
|
||||
});
|
||||
|
||||
export const MIN_USERNAME_LENGTH = 3;
|
||||
export const MAX_USERNAME_LENGTH = 20;
|
||||
|
||||
const validPattern = /^[a-zA-Z0-9_ ]+$/;
|
||||
|
||||
const shadowNames = [
|
||||
"NicePeopleOnly",
|
||||
"BeKindPlz",
|
||||
"LearningManners",
|
||||
"StayClassy",
|
||||
"BeNicer",
|
||||
"NeedHugs",
|
||||
"MakeFriends",
|
||||
];
|
||||
|
||||
export function fixProfaneUsername(username: string): string {
|
||||
if (isProfaneUsername(username)) {
|
||||
return shadowNames[simpleHash(username) % shadowNames.length];
|
||||
}
|
||||
return username;
|
||||
}
|
||||
|
||||
export function isProfaneUsername(username: string): boolean {
|
||||
return matcher.hasMatch(username);
|
||||
}
|
||||
|
||||
export function validateUsername(username: string): {
|
||||
isValid: boolean;
|
||||
error?: string;
|
||||
|
||||
@@ -27,14 +27,15 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
|
||||
|
||||
switch (message.type) {
|
||||
case "heartbeat":
|
||||
(await gameRunner).executeNextTick()
|
||||
(await gameRunner).executeNextTick();
|
||||
break;
|
||||
case "init":
|
||||
try {
|
||||
gameRunner = createGameRunner(
|
||||
message.gameID,
|
||||
message.gameConfig,
|
||||
gameUpdate,
|
||||
message.clientID,
|
||||
gameUpdate
|
||||
).then((gr) => {
|
||||
sendMessage({
|
||||
type: "initialized",
|
||||
@@ -71,7 +72,7 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
|
||||
const actions = (await gameRunner).playerActions(
|
||||
message.playerID,
|
||||
message.x,
|
||||
message.y,
|
||||
message.y
|
||||
);
|
||||
sendMessage({
|
||||
type: "player_actions_result",
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
PlayerProfile,
|
||||
} from "../game/Game";
|
||||
import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates";
|
||||
import { GameConfig, GameID, Turn } from "../Schemas";
|
||||
import { ClientID, GameConfig, GameID, Turn } from "../Schemas";
|
||||
import { generateID } from "../Util";
|
||||
import { WorkerMessage } from "./WorkerMessages";
|
||||
|
||||
@@ -17,7 +17,11 @@ export class WorkerClient {
|
||||
update: GameUpdateViewData | ErrorUpdate
|
||||
) => void;
|
||||
|
||||
constructor(private gameID: GameID, private gameConfig: GameConfig) {
|
||||
constructor(
|
||||
private gameID: GameID,
|
||||
private gameConfig: GameConfig,
|
||||
private clientID: ClientID
|
||||
) {
|
||||
this.worker = new Worker(new URL("./Worker.worker.ts", import.meta.url));
|
||||
this.messageHandlers = new Map();
|
||||
|
||||
@@ -65,6 +69,7 @@ export class WorkerClient {
|
||||
id: messageId,
|
||||
gameID: this.gameID,
|
||||
gameConfig: this.gameConfig,
|
||||
clientID: this.clientID,
|
||||
});
|
||||
|
||||
// Add timeout for initialization
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GameUpdateViewData } from "../game/GameUpdates";
|
||||
import { GameConfig, GameID, Turn } from "../Schemas";
|
||||
import { ClientID, GameConfig, GameID, Turn } from "../Schemas";
|
||||
import { PlayerActions, PlayerID, PlayerProfile } from "../game/Game";
|
||||
|
||||
export type WorkerMessageType =
|
||||
@@ -28,6 +28,7 @@ export interface InitMessage extends BaseWorkerMessage {
|
||||
type: "init";
|
||||
gameID: GameID;
|
||||
gameConfig: GameConfig;
|
||||
clientID: ClientID;
|
||||
}
|
||||
|
||||
export interface TurnMessage extends BaseWorkerMessage {
|
||||
|
||||
Reference in New Issue
Block a user