Turnstile: require token before joining a multiplayer game (#2572)

When user tries to join either a public or private multiplayer game, the
turnstile callback is triggered, and the turnstile token is passed to
the server when joining a game.

## 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:

evan
This commit is contained in:
Evan
2025-12-08 16:16:31 -08:00
committed by GitHub
parent 075c232d8a
commit 3314ca16ce
16 changed files with 219 additions and 29 deletions
+1
View File
@@ -534,6 +534,7 @@ export const ClientJoinMessageSchema = z.object({
username: UsernameSchema,
// Server replaces the refs with the actual cosmetic data.
cosmetics: PlayerCosmeticRefsSchema.optional(),
turnstileToken: z.string().nullable(),
});
export const ClientRejoinMessageSchema = z.object({
+2
View File
@@ -27,6 +27,8 @@ export enum GameEnv {
}
export interface ServerConfig {
turnstileSiteKey(): string;
turnstileSecretKey(): string;
turnIntervalMs(): number;
gameCreationRate(): number;
lobbyMaxPlayers(
+4
View File
@@ -80,6 +80,10 @@ const numPlayersConfig = {
} as const satisfies Record<GameMapType, [number, number, number]>;
export abstract class DefaultServerConfig implements ServerConfig {
turnstileSecretKey(): string {
return process.env.TURNSTILE_SECRET_KEY ?? "";
}
abstract turnstileSiteKey(): string;
allowedFlares(): string[] | undefined {
return;
}
+8 -28
View File
@@ -1,10 +1,17 @@
import { UnitInfo, UnitType } from "../game/Game";
import { UserSettings } from "../game/UserSettings";
import { GameConfig } from "../Schemas";
import { GameEnv, ServerConfig } from "./Config";
import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig";
export class DevServerConfig extends DefaultServerConfig {
turnstileSiteKey(): string {
return "1x00000000000000000000AA";
}
turnstileSecretKey(): string {
return "1x0000000000000000000000000000000AA";
}
adminToken(): string {
return "WARNING_DEV_ADMIN_KEY_DO_NOT_USE_IN_PRODUCTION";
}
@@ -57,31 +64,4 @@ export class DevConfig extends DefaultConfig {
) {
super(sc, gc, us, isReplay);
}
unitInfo(type: UnitType): UnitInfo {
const info = super.unitInfo(type);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const oldCost = info.cost;
// info.cost = (p: Player) => oldCost(p) / 1000000000;
return info;
}
// tradeShipSpawnRate(): number {
// return 10;
// }
// percentageTilesOwnedToWin(): number {
// return 1
// }
// boatMaxDistance(): number {
// return 5000
// }
// numBots(): number {
// return 0;
// }
// spawnNPCs(): boolean {
// return false;
// }
}
+3
View File
@@ -8,6 +8,9 @@ export const preprodConfig = new (class extends DefaultServerConfig {
numWorkers(): number {
return 2;
}
turnstileSiteKey(): string {
return "0x4AAAAAAB7QetxHwRCKw-aP";
}
jwtAudience(): string {
return "openfront.dev";
}
+3
View File
@@ -11,4 +11,7 @@ export const prodConfig = new (class extends DefaultServerConfig {
jwtAudience(): string {
return "openfront.io";
}
turnstileSiteKey(): string {
return "0x4AAAAAACFLkaecN39lS8sk";
}
})();