mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-18 17:53:29 +00:00
Enable the sort-keys eslint rule (#1746)
## Description: Enable the `sort-keys` eslint rule. Fixes #1629 ## 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 - [ ] I have read and accepted the CLA agreement (only required once).
This commit is contained in:
@@ -50,6 +50,17 @@ export default [
|
|||||||
// Enable rules
|
// Enable rules
|
||||||
"@typescript-eslint/prefer-nullish-coalescing": "error",
|
"@typescript-eslint/prefer-nullish-coalescing": "error",
|
||||||
eqeqeq: "error",
|
eqeqeq: "error",
|
||||||
|
"sort-keys": "error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/*.config.{js,ts,jsx,tsx}",
|
||||||
|
"**/*.test.{js,ts,jsx,tsx}",
|
||||||
|
"src/client/**/*.{js,ts,jsx,tsx}",
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
"sort-keys": "off",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const RefreshResponseSchema = z.object({
|
|||||||
});
|
});
|
||||||
export type RefreshResponse = z.infer<typeof RefreshResponseSchema>;
|
export type RefreshResponse = z.infer<typeof RefreshResponseSchema>;
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
export const TokenPayloadSchema = z.object({
|
export const TokenPayloadSchema = z.object({
|
||||||
jti: z.string(),
|
jti: z.string(),
|
||||||
sub: z
|
sub: z
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import { RequiredPatternSchema } from "./Schemas";
|
|||||||
|
|
||||||
export const ProductSchema = z.object({
|
export const ProductSchema = z.object({
|
||||||
productId: z.string(),
|
productId: z.string(),
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
priceId: z.string(),
|
priceId: z.string(),
|
||||||
price: z.string(),
|
price: z.string(),
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
});
|
});
|
||||||
|
|
||||||
const PatternSchema = z.object({
|
const PatternSchema = z.object({
|
||||||
@@ -16,6 +18,7 @@ const PatternSchema = z.object({
|
|||||||
// Schema for resources/cosmetics/cosmetics.json
|
// Schema for resources/cosmetics/cosmetics.json
|
||||||
export const CosmeticsSchema = z.object({
|
export const CosmeticsSchema = z.object({
|
||||||
patterns: z.record(z.string(), PatternSchema),
|
patterns: z.record(z.string(), PatternSchema),
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
flag: z
|
flag: z
|
||||||
.object({
|
.object({
|
||||||
layers: z.record(
|
layers: z.record(
|
||||||
@@ -35,6 +38,7 @@ export const CosmeticsSchema = z.object({
|
|||||||
),
|
),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
});
|
});
|
||||||
export type Cosmetics = z.infer<typeof CosmeticsSchema>;
|
export type Cosmetics = z.infer<typeof CosmeticsSchema>;
|
||||||
export type Pattern = z.infer<typeof PatternSchema>;
|
export type Pattern = z.infer<typeof PatternSchema>;
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import { Cosmetics } from "./CosmeticSchemas";
|
|||||||
|
|
||||||
const ANIMATION_DURATIONS: Record<string, number> = {
|
const ANIMATION_DURATIONS: Record<string, number> = {
|
||||||
rainbow: 4000,
|
rainbow: 4000,
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
"bright-rainbow": 4000,
|
"bright-rainbow": 4000,
|
||||||
"copper-glow": 3000,
|
"copper-glow": 3000,
|
||||||
"silver-glow": 3000,
|
"silver-glow": 3000,
|
||||||
"gold-glow": 3000,
|
"gold-glow": 3000,
|
||||||
neon: 3000,
|
neon: 3000,
|
||||||
lava: 6000,
|
lava: 6000,
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
water: 6200,
|
water: 6200,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -28,6 +30,7 @@ export function renderPlayerFlag(
|
|||||||
const code = flag.slice("!".length);
|
const code = flag.slice("!".length);
|
||||||
const layers = code.split("_").map((segment) => {
|
const layers = code.split("_").map((segment) => {
|
||||||
const [layerKey, colorKey] = segment.split("-");
|
const [layerKey, colorKey] = segment.split("-");
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
return { layerKey, colorKey };
|
return { layerKey, colorKey };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -165,10 +165,10 @@ export class GameRunner {
|
|||||||
updates[GameUpdateType.Tile] = [];
|
updates[GameUpdateType.Tile] = [];
|
||||||
|
|
||||||
this.callBack({
|
this.callBack({
|
||||||
tick: this.game.ticks(),
|
|
||||||
packedTileUpdates: new BigUint64Array(packedTileUpdates),
|
packedTileUpdates: new BigUint64Array(packedTileUpdates),
|
||||||
updates: updates,
|
|
||||||
playerNameViewData: this.playerViewData,
|
playerNameViewData: this.playerViewData,
|
||||||
|
tick: this.game.ticks(),
|
||||||
|
updates: updates,
|
||||||
});
|
});
|
||||||
this.isExecuting = false;
|
this.isExecuting = false;
|
||||||
}
|
}
|
||||||
@@ -181,21 +181,21 @@ export class GameRunner {
|
|||||||
const player = this.game.player(playerID);
|
const player = this.game.player(playerID);
|
||||||
const tile = this.game.ref(x, y);
|
const tile = this.game.ref(x, y);
|
||||||
const actions = {
|
const actions = {
|
||||||
canAttack: player.canAttack(tile),
|
|
||||||
buildableUnits: player.buildableUnits(tile),
|
buildableUnits: player.buildableUnits(tile),
|
||||||
|
canAttack: player.canAttack(tile),
|
||||||
canSendEmojiAllPlayers: player.canSendEmoji(AllPlayers),
|
canSendEmojiAllPlayers: player.canSendEmoji(AllPlayers),
|
||||||
} as PlayerActions;
|
} as PlayerActions;
|
||||||
|
|
||||||
if (this.game.hasOwner(tile)) {
|
if (this.game.hasOwner(tile)) {
|
||||||
const other = this.game.owner(tile) as Player;
|
const other = this.game.owner(tile) as Player;
|
||||||
actions.interaction = {
|
actions.interaction = {
|
||||||
sharedBorder: player.sharesBorderWith(other),
|
|
||||||
canSendEmoji: player.canSendEmoji(other),
|
|
||||||
canTarget: player.canTarget(other),
|
|
||||||
canSendAllianceRequest: player.canSendAllianceRequest(other),
|
|
||||||
canBreakAlliance: player.isAlliedWith(other),
|
canBreakAlliance: player.isAlliedWith(other),
|
||||||
canDonate: player.canDonate(other),
|
canDonate: player.canDonate(other),
|
||||||
canEmbargo: !player.hasEmbargoAgainst(other),
|
canEmbargo: !player.hasEmbargoAgainst(other),
|
||||||
|
canSendAllianceRequest: player.canSendAllianceRequest(other),
|
||||||
|
canSendEmoji: player.canSendEmoji(other),
|
||||||
|
canTarget: player.canTarget(other),
|
||||||
|
sharedBorder: player.sharesBorderWith(other),
|
||||||
};
|
};
|
||||||
const alliance = player.allianceWith(other as Player);
|
const alliance = player.allianceWith(other as Player);
|
||||||
if (alliance) {
|
if (alliance) {
|
||||||
|
|||||||
+60
-60
@@ -144,17 +144,17 @@ const TeamCountConfigSchema = z.union([
|
|||||||
export type TeamCountConfig = z.infer<typeof TeamCountConfigSchema>;
|
export type TeamCountConfig = z.infer<typeof TeamCountConfigSchema>;
|
||||||
|
|
||||||
export const GameConfigSchema = z.object({
|
export const GameConfigSchema = z.object({
|
||||||
gameMap: z.enum(GameMapType),
|
|
||||||
difficulty: z.enum(Difficulty),
|
|
||||||
gameType: z.enum(GameType),
|
|
||||||
gameMode: z.enum(GameMode),
|
|
||||||
disableNPCs: z.boolean(),
|
|
||||||
bots: z.number().int().min(0).max(400),
|
bots: z.number().int().min(0).max(400),
|
||||||
|
difficulty: z.enum(Difficulty),
|
||||||
|
disableNPCs: z.boolean(),
|
||||||
|
disabledUnits: z.enum(UnitType).array().optional(),
|
||||||
|
gameMap: z.enum(GameMapType),
|
||||||
|
gameMode: z.enum(GameMode),
|
||||||
|
gameType: z.enum(GameType),
|
||||||
infiniteGold: z.boolean(),
|
infiniteGold: z.boolean(),
|
||||||
infiniteTroops: z.boolean(),
|
infiniteTroops: z.boolean(),
|
||||||
instantBuild: z.boolean(),
|
instantBuild: z.boolean(),
|
||||||
maxPlayers: z.number().optional(),
|
maxPlayers: z.number().optional(),
|
||||||
disabledUnits: z.enum(UnitType).array().optional(),
|
|
||||||
playerTeams: TeamCountConfigSchema.optional(),
|
playerTeams: TeamCountConfigSchema.optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -244,82 +244,82 @@ const BaseIntentSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const AllianceExtensionIntentSchema = BaseIntentSchema.extend({
|
export const AllianceExtensionIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("allianceExtension"),
|
|
||||||
recipient: ID,
|
recipient: ID,
|
||||||
|
type: z.literal("allianceExtension"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AttackIntentSchema = BaseIntentSchema.extend({
|
export const AttackIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("attack"),
|
|
||||||
targetID: ID.nullable(),
|
targetID: ID.nullable(),
|
||||||
troops: z.number().nonnegative().nullable(),
|
troops: z.number().nonnegative().nullable(),
|
||||||
|
type: z.literal("attack"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SpawnIntentSchema = BaseIntentSchema.extend({
|
export const SpawnIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("spawn"),
|
|
||||||
name: UsernameSchema,
|
|
||||||
flag: FlagSchema,
|
flag: FlagSchema,
|
||||||
|
name: UsernameSchema,
|
||||||
pattern: PatternSchema,
|
pattern: PatternSchema,
|
||||||
playerType: PlayerTypeSchema,
|
playerType: PlayerTypeSchema,
|
||||||
tile: z.number(),
|
tile: z.number(),
|
||||||
|
type: z.literal("spawn"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const BoatAttackIntentSchema = BaseIntentSchema.extend({
|
export const BoatAttackIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("boat"),
|
|
||||||
targetID: ID.nullable(),
|
|
||||||
troops: z.number().nonnegative(),
|
|
||||||
dst: z.number(),
|
dst: z.number(),
|
||||||
src: z.number().nullable(),
|
src: z.number().nullable(),
|
||||||
|
targetID: ID.nullable(),
|
||||||
|
troops: z.number().nonnegative(),
|
||||||
|
type: z.literal("boat"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AllianceRequestIntentSchema = BaseIntentSchema.extend({
|
export const AllianceRequestIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("allianceRequest"),
|
|
||||||
recipient: ID,
|
recipient: ID,
|
||||||
|
type: z.literal("allianceRequest"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AllianceRequestReplyIntentSchema = BaseIntentSchema.extend({
|
export const AllianceRequestReplyIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("allianceRequestReply"),
|
|
||||||
requestor: ID, // The one who made the original alliance request
|
|
||||||
accept: z.boolean(),
|
accept: z.boolean(),
|
||||||
|
requestor: ID, // The one who made the original alliance request
|
||||||
|
type: z.literal("allianceRequestReply"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const BreakAllianceIntentSchema = BaseIntentSchema.extend({
|
export const BreakAllianceIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("breakAlliance"),
|
|
||||||
recipient: ID,
|
recipient: ID,
|
||||||
|
type: z.literal("breakAlliance"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const TargetPlayerIntentSchema = BaseIntentSchema.extend({
|
export const TargetPlayerIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("targetPlayer"),
|
|
||||||
target: ID,
|
target: ID,
|
||||||
|
type: z.literal("targetPlayer"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const EmojiIntentSchema = BaseIntentSchema.extend({
|
export const EmojiIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("emoji"),
|
|
||||||
recipient: z.union([ID, z.literal(AllPlayers)]),
|
|
||||||
emoji: EmojiSchema,
|
emoji: EmojiSchema,
|
||||||
|
recipient: z.union([ID, z.literal(AllPlayers)]),
|
||||||
|
type: z.literal("emoji"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const EmbargoIntentSchema = BaseIntentSchema.extend({
|
export const EmbargoIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("embargo"),
|
|
||||||
targetID: ID,
|
|
||||||
action: z.union([z.literal("start"), z.literal("stop")]),
|
action: z.union([z.literal("start"), z.literal("stop")]),
|
||||||
|
targetID: ID,
|
||||||
|
type: z.literal("embargo"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DonateGoldIntentSchema = BaseIntentSchema.extend({
|
export const DonateGoldIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("donate_gold"),
|
|
||||||
recipient: ID,
|
|
||||||
gold: z.bigint().nullable(),
|
gold: z.bigint().nullable(),
|
||||||
|
recipient: ID,
|
||||||
|
type: z.literal("donate_gold"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DonateTroopIntentSchema = BaseIntentSchema.extend({
|
export const DonateTroopIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("donate_troops"),
|
|
||||||
recipient: ID,
|
recipient: ID,
|
||||||
troops: z.number().nullable(),
|
troops: z.number().nullable(),
|
||||||
|
type: z.literal("donate_troops"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const BuildUnitIntentSchema = BaseIntentSchema.extend({
|
export const BuildUnitIntentSchema = BaseIntentSchema.extend({
|
||||||
|
tile: z.number(),
|
||||||
type: z.literal("build_unit"),
|
type: z.literal("build_unit"),
|
||||||
unit: z.enum(UnitType),
|
unit: z.enum(UnitType),
|
||||||
tile: z.number(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const UpgradeStructureIntentSchema = BaseIntentSchema.extend({
|
export const UpgradeStructureIntentSchema = BaseIntentSchema.extend({
|
||||||
@@ -329,8 +329,8 @@ export const UpgradeStructureIntentSchema = BaseIntentSchema.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const CancelAttackIntentSchema = BaseIntentSchema.extend({
|
export const CancelAttackIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("cancel_attack"),
|
|
||||||
attackID: z.string(),
|
attackID: z.string(),
|
||||||
|
type: z.literal("cancel_attack"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CancelBoatIntentSchema = BaseIntentSchema.extend({
|
export const CancelBoatIntentSchema = BaseIntentSchema.extend({
|
||||||
@@ -339,9 +339,9 @@ export const CancelBoatIntentSchema = BaseIntentSchema.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const MoveWarshipIntentSchema = BaseIntentSchema.extend({
|
export const MoveWarshipIntentSchema = BaseIntentSchema.extend({
|
||||||
|
tile: z.number(),
|
||||||
type: z.literal("move_warship"),
|
type: z.literal("move_warship"),
|
||||||
unitId: z.number(),
|
unitId: z.number(),
|
||||||
tile: z.number(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DeleteUnitIntentSchema = BaseIntentSchema.extend({
|
export const DeleteUnitIntentSchema = BaseIntentSchema.extend({
|
||||||
@@ -350,20 +350,20 @@ export const DeleteUnitIntentSchema = BaseIntentSchema.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const QuickChatIntentSchema = BaseIntentSchema.extend({
|
export const QuickChatIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("quick_chat"),
|
|
||||||
recipient: ID,
|
|
||||||
quickChatKey: QuickChatKeySchema,
|
quickChatKey: QuickChatKeySchema,
|
||||||
|
recipient: ID,
|
||||||
target: ID.optional(),
|
target: ID.optional(),
|
||||||
|
type: z.literal("quick_chat"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const MarkDisconnectedIntentSchema = BaseIntentSchema.extend({
|
export const MarkDisconnectedIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("mark_disconnected"),
|
|
||||||
isDisconnected: z.boolean(),
|
isDisconnected: z.boolean(),
|
||||||
|
type: z.literal("mark_disconnected"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const KickPlayerIntentSchema = BaseIntentSchema.extend({
|
export const KickPlayerIntentSchema = BaseIntentSchema.extend({
|
||||||
type: z.literal("kick_player"),
|
|
||||||
target: ID,
|
target: ID,
|
||||||
|
type: z.literal("kick_player"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const IntentSchema = z.discriminatedUnion("type", [
|
const IntentSchema = z.discriminatedUnion("type", [
|
||||||
@@ -395,22 +395,22 @@ const IntentSchema = z.discriminatedUnion("type", [
|
|||||||
//
|
//
|
||||||
|
|
||||||
export const TurnSchema = z.object({
|
export const TurnSchema = z.object({
|
||||||
turnNumber: z.number(),
|
|
||||||
intents: IntentSchema.array(),
|
|
||||||
// The hash of the game state at the end of the turn.
|
// The hash of the game state at the end of the turn.
|
||||||
hash: z.number().nullable().optional(),
|
hash: z.number().nullable().optional(),
|
||||||
|
intents: IntentSchema.array(),
|
||||||
|
turnNumber: z.number(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const PlayerSchema = z.object({
|
export const PlayerSchema = z.object({
|
||||||
clientID: ID,
|
clientID: ID,
|
||||||
username: UsernameSchema,
|
|
||||||
flag: FlagSchema,
|
flag: FlagSchema,
|
||||||
pattern: PatternSchema,
|
pattern: PatternSchema,
|
||||||
|
username: UsernameSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GameStartInfoSchema = z.object({
|
export const GameStartInfoSchema = z.object({
|
||||||
gameID: ID,
|
|
||||||
config: GameConfigSchema,
|
config: GameConfigSchema,
|
||||||
|
gameID: ID,
|
||||||
players: PlayerSchema.array(),
|
players: PlayerSchema.array(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -427,8 +427,8 @@ export type Winner = z.infer<typeof WinnerSchema>;
|
|||||||
//
|
//
|
||||||
|
|
||||||
export const ServerTurnMessageSchema = z.object({
|
export const ServerTurnMessageSchema = z.object({
|
||||||
type: z.literal("turn"),
|
|
||||||
turn: TurnSchema,
|
turn: TurnSchema,
|
||||||
|
type: z.literal("turn"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ServerPingMessageSchema = z.object({
|
export const ServerPingMessageSchema = z.object({
|
||||||
@@ -436,30 +436,30 @@ export const ServerPingMessageSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const ServerPrestartMessageSchema = z.object({
|
export const ServerPrestartMessageSchema = z.object({
|
||||||
type: z.literal("prestart"),
|
|
||||||
gameMap: z.nativeEnum(GameMapType),
|
gameMap: z.nativeEnum(GameMapType),
|
||||||
|
type: z.literal("prestart"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ServerStartGameMessageSchema = z.object({
|
export const ServerStartGameMessageSchema = z.object({
|
||||||
type: z.literal("start"),
|
gameStartInfo: GameStartInfoSchema,
|
||||||
// Turns the client missed if they are late to the game.
|
// Turns the client missed if they are late to the game.
|
||||||
turns: TurnSchema.array(),
|
turns: TurnSchema.array(),
|
||||||
gameStartInfo: GameStartInfoSchema,
|
type: z.literal("start"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ServerDesyncSchema = z.object({
|
export const ServerDesyncSchema = z.object({
|
||||||
type: z.literal("desync"),
|
|
||||||
turn: z.number(),
|
|
||||||
correctHash: z.number().nullable(),
|
|
||||||
clientsWithCorrectHash: z.number(),
|
clientsWithCorrectHash: z.number(),
|
||||||
|
correctHash: z.number().nullable(),
|
||||||
totalActiveClients: z.number(),
|
totalActiveClients: z.number(),
|
||||||
|
turn: z.number(),
|
||||||
|
type: z.literal("desync"),
|
||||||
yourHash: z.number().optional(),
|
yourHash: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ServerErrorSchema = z.object({
|
export const ServerErrorSchema = z.object({
|
||||||
type: z.literal("error"),
|
|
||||||
error: z.string(),
|
error: z.string(),
|
||||||
message: z.string().optional(),
|
message: z.string().optional(),
|
||||||
|
type: z.literal("error"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ServerMessageSchema = z.discriminatedUnion("type", [
|
export const ServerMessageSchema = z.discriminatedUnion("type", [
|
||||||
@@ -476,21 +476,21 @@ export const ServerMessageSchema = z.discriminatedUnion("type", [
|
|||||||
//
|
//
|
||||||
|
|
||||||
export const ClientSendWinnerSchema = z.object({
|
export const ClientSendWinnerSchema = z.object({
|
||||||
|
allPlayersStats: AllPlayersStatsSchema,
|
||||||
type: z.literal("winner"),
|
type: z.literal("winner"),
|
||||||
winner: WinnerSchema,
|
winner: WinnerSchema,
|
||||||
allPlayersStats: AllPlayersStatsSchema,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ClientHashSchema = z.object({
|
export const ClientHashSchema = z.object({
|
||||||
type: z.literal("hash"),
|
|
||||||
hash: z.number(),
|
hash: z.number(),
|
||||||
turnNumber: z.number(),
|
turnNumber: z.number(),
|
||||||
|
type: z.literal("hash"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ClientLogMessageSchema = z.object({
|
export const ClientLogMessageSchema = z.object({
|
||||||
type: z.literal("log"),
|
|
||||||
severity: z.enum(LogSeverity),
|
|
||||||
log: ID,
|
log: ID,
|
||||||
|
severity: z.enum(LogSeverity),
|
||||||
|
type: z.literal("log"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ClientPingMessageSchema = z.object({
|
export const ClientPingMessageSchema = z.object({
|
||||||
@@ -498,20 +498,20 @@ export const ClientPingMessageSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const ClientIntentMessageSchema = z.object({
|
export const ClientIntentMessageSchema = z.object({
|
||||||
type: z.literal("intent"),
|
|
||||||
intent: IntentSchema,
|
intent: IntentSchema,
|
||||||
|
type: z.literal("intent"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// WARNING: never send this message to clients.
|
// WARNING: never send this message to clients.
|
||||||
export const ClientJoinMessageSchema = z.object({
|
export const ClientJoinMessageSchema = z.object({
|
||||||
type: z.literal("join"),
|
|
||||||
clientID: ID,
|
clientID: ID,
|
||||||
token: TokenSchema, // WARNING: PII
|
flag: FlagSchema,
|
||||||
gameID: ID,
|
gameID: ID,
|
||||||
lastTurn: z.number(), // The last turn the client saw.
|
lastTurn: z.number(), // The last turn the client saw.
|
||||||
username: UsernameSchema,
|
|
||||||
flag: FlagSchema,
|
|
||||||
pattern: PatternSchema,
|
pattern: PatternSchema,
|
||||||
|
token: TokenSchema, // WARNING: PII
|
||||||
|
type: z.literal("join"),
|
||||||
|
username: UsernameSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ClientMessageSchema = z.discriminatedUnion("type", [
|
export const ClientMessageSchema = z.discriminatedUnion("type", [
|
||||||
@@ -534,11 +534,11 @@ export const PlayerRecordSchema = PlayerSchema.extend({
|
|||||||
export type PlayerRecord = z.infer<typeof PlayerRecordSchema>;
|
export type PlayerRecord = z.infer<typeof PlayerRecordSchema>;
|
||||||
|
|
||||||
export const GameEndInfoSchema = GameStartInfoSchema.extend({
|
export const GameEndInfoSchema = GameStartInfoSchema.extend({
|
||||||
|
duration: z.number().nonnegative(),
|
||||||
|
end: z.number(),
|
||||||
|
num_turns: z.number(),
|
||||||
players: PlayerRecordSchema.array(),
|
players: PlayerRecordSchema.array(),
|
||||||
start: z.number(),
|
start: z.number(),
|
||||||
end: z.number(),
|
|
||||||
duration: z.number().nonnegative(),
|
|
||||||
num_turns: z.number(),
|
|
||||||
winner: WinnerSchema,
|
winner: WinnerSchema,
|
||||||
});
|
});
|
||||||
export type GameEndInfo = z.infer<typeof GameEndInfoSchema>;
|
export type GameEndInfo = z.infer<typeof GameEndInfoSchema>;
|
||||||
@@ -546,11 +546,11 @@ export type GameEndInfo = z.infer<typeof GameEndInfoSchema>;
|
|||||||
const GitCommitSchema = z.string().regex(/^[0-9a-fA-F]{40}$/);
|
const GitCommitSchema = z.string().regex(/^[0-9a-fA-F]{40}$/);
|
||||||
|
|
||||||
export const AnalyticsRecordSchema = z.object({
|
export const AnalyticsRecordSchema = z.object({
|
||||||
info: GameEndInfoSchema,
|
|
||||||
version: z.literal("v0.0.2"),
|
|
||||||
gitCommit: GitCommitSchema,
|
|
||||||
subdomain: z.string(),
|
|
||||||
domain: z.string(),
|
domain: z.string(),
|
||||||
|
gitCommit: GitCommitSchema,
|
||||||
|
info: GameEndInfoSchema,
|
||||||
|
subdomain: z.string(),
|
||||||
|
version: z.literal("v0.0.2"),
|
||||||
});
|
});
|
||||||
export type AnalyticsRecord = z.infer<typeof AnalyticsRecordSchema>;
|
export type AnalyticsRecord = z.infer<typeof AnalyticsRecordSchema>;
|
||||||
|
|
||||||
|
|||||||
+13
-12
@@ -88,6 +88,7 @@ export function calculateBoundingBox(
|
|||||||
maxY = Math.max(maxY, cell.y);
|
maxY = Math.max(maxY, cell.y);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
return { min: new Cell(minX, minY), max: new Cell(maxX, maxY) };
|
return { min: new Cell(minX, minY), max: new Cell(maxX, maxY) };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,10 +144,10 @@ export function sanitize(name: string): string {
|
|||||||
|
|
||||||
export function onlyImages(html: string) {
|
export function onlyImages(html: string) {
|
||||||
return DOMPurify.sanitize(html, {
|
return DOMPurify.sanitize(html, {
|
||||||
ALLOWED_TAGS: ["span", "img"],
|
|
||||||
ALLOWED_ATTR: ["src", "alt", "class", "style"],
|
|
||||||
ALLOWED_URI_REGEXP: /^https:\/\/cdn\.jsdelivr\.net\/gh\/twitter\/twemoji/,
|
|
||||||
ADD_ATTR: ["style"],
|
ADD_ATTR: ["style"],
|
||||||
|
ALLOWED_ATTR: ["src", "alt", "class", "style"],
|
||||||
|
ALLOWED_TAGS: ["span", "img"],
|
||||||
|
ALLOWED_URI_REGEXP: /^https:\/\/cdn\.jsdelivr\.net\/gh\/twitter\/twemoji/,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,21 +172,21 @@ export function createGameRecord(
|
|||||||
(t) => t.intents.length !== 0 || t.hash !== undefined,
|
(t) => t.intents.length !== 0 || t.hash !== undefined,
|
||||||
);
|
);
|
||||||
const record: GameRecord = {
|
const record: GameRecord = {
|
||||||
|
domain,
|
||||||
|
gitCommit,
|
||||||
info: {
|
info: {
|
||||||
gameID,
|
|
||||||
config,
|
config,
|
||||||
|
duration,
|
||||||
|
end,
|
||||||
|
gameID,
|
||||||
|
num_turns,
|
||||||
players,
|
players,
|
||||||
start,
|
start,
|
||||||
end,
|
|
||||||
duration,
|
|
||||||
num_turns,
|
|
||||||
winner,
|
winner,
|
||||||
},
|
},
|
||||||
version,
|
|
||||||
gitCommit,
|
|
||||||
subdomain,
|
subdomain,
|
||||||
domain,
|
|
||||||
turns,
|
turns,
|
||||||
|
version,
|
||||||
};
|
};
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
@@ -197,8 +198,8 @@ export function decompressGameRecord(gameRecord: GameRecord) {
|
|||||||
while (lastTurnNum < turn.turnNumber - 1) {
|
while (lastTurnNum < turn.turnNumber - 1) {
|
||||||
lastTurnNum++;
|
lastTurnNum++;
|
||||||
turns.push({
|
turns.push({
|
||||||
turnNumber: lastTurnNum,
|
|
||||||
intents: [],
|
intents: [],
|
||||||
|
turnNumber: lastTurnNum,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
turns.push(turn);
|
turns.push(turn);
|
||||||
@@ -207,8 +208,8 @@ export function decompressGameRecord(gameRecord: GameRecord) {
|
|||||||
const turnLength = turns.length;
|
const turnLength = turns.length;
|
||||||
for (let i = turnLength; i < gameRecord.info.num_turns; i++) {
|
for (let i = turnLength; i < gameRecord.info.num_turns; i++) {
|
||||||
turns.push({
|
turns.push({
|
||||||
turnNumber: i,
|
|
||||||
intents: [],
|
intents: [],
|
||||||
|
turnNumber: i,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
gameRecord.turns = turns;
|
gameRecord.turns = turns;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import lchPlugin from "colord/plugins/lch";
|
|||||||
extend([lchPlugin]);
|
extend([lchPlugin]);
|
||||||
extend([labPlugin]);
|
extend([labPlugin]);
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
export const red = colord({ h: 0, s: 82, l: 56 });
|
export const red = colord({ h: 0, s: 82, l: 56 });
|
||||||
export const blue = colord({ h: 224, s: 100, l: 58 });
|
export const blue = colord({ h: 224, s: 100, l: 58 });
|
||||||
export const teal = colord({ h: 172, s: 66, l: 50 });
|
export const teal = colord({ h: 172, s: 66, l: 50 });
|
||||||
|
|||||||
@@ -367,6 +367,7 @@ export class DefaultConfig implements Config {
|
|||||||
return 1_000_000;
|
return 1_000_000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
unitInfo(type: UnitType): UnitInfo {
|
unitInfo(type: UnitType): UnitInfo {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case UnitType.TransportShip:
|
case UnitType.TransportShip:
|
||||||
@@ -488,6 +489,7 @@ export class DefaultConfig implements Config {
|
|||||||
assertNever(type);
|
assertNever(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
|
|
||||||
private costWrapper(
|
private costWrapper(
|
||||||
type: UnitType,
|
type: UnitType,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export class PastelTheme implements Theme {
|
|||||||
private teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
|
private teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
|
||||||
private nationColorAllocator = new ColorAllocator(nationColors, nationColors);
|
private nationColorAllocator = new ColorAllocator(nationColors, nationColors);
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
private background = colord({ r: 60, g: 60, b: 60 });
|
private background = colord({ r: 60, g: 60, b: 60 });
|
||||||
private shore = colord({ r: 204, g: 203, b: 158 });
|
private shore = colord({ r: 204, g: 203, b: 158 });
|
||||||
private falloutColors = [
|
private falloutColors = [
|
||||||
@@ -34,6 +35,7 @@ export class PastelTheme implements Theme {
|
|||||||
private _enemyColor = colord({ r: 255, g: 0, b: 0 });
|
private _enemyColor = colord({ r: 255, g: 0, b: 0 });
|
||||||
|
|
||||||
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
|
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
|
|
||||||
teamColor(team: Team): Colord {
|
teamColor(team: Team): Colord {
|
||||||
return this.teamColorAllocator.assignTeamColor(team);
|
return this.teamColorAllocator.assignTeamColor(team);
|
||||||
@@ -59,20 +61,24 @@ export class PastelTheme implements Theme {
|
|||||||
|
|
||||||
specialBuildingColor(player: PlayerView): Colord {
|
specialBuildingColor(player: PlayerView): Colord {
|
||||||
const tc = this.territoryColor(player).rgba;
|
const tc = this.territoryColor(player).rgba;
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
return colord({
|
return colord({
|
||||||
r: Math.max(tc.r - 50, 0),
|
r: Math.max(tc.r - 50, 0),
|
||||||
g: Math.max(tc.g - 50, 0),
|
g: Math.max(tc.g - 50, 0),
|
||||||
b: Math.max(tc.b - 50, 0),
|
b: Math.max(tc.b - 50, 0),
|
||||||
});
|
});
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
}
|
}
|
||||||
|
|
||||||
railroadColor(player: PlayerView): Colord {
|
railroadColor(player: PlayerView): Colord {
|
||||||
const tc = this.territoryColor(player).rgba;
|
const tc = this.territoryColor(player).rgba;
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
const color = colord({
|
const color = colord({
|
||||||
r: Math.max(tc.r - 10, 0),
|
r: Math.max(tc.r - 10, 0),
|
||||||
g: Math.max(tc.g - 10, 0),
|
g: Math.max(tc.g - 10, 0),
|
||||||
b: Math.max(tc.b - 10, 0),
|
b: Math.max(tc.b - 10, 0),
|
||||||
});
|
});
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,16 +87,19 @@ export class PastelTheme implements Theme {
|
|||||||
return this.borderColorCache.get(player.id())!;
|
return this.borderColorCache.get(player.id())!;
|
||||||
}
|
}
|
||||||
const tc = this.territoryColor(player).rgba;
|
const tc = this.territoryColor(player).rgba;
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
const color = colord({
|
const color = colord({
|
||||||
r: Math.max(tc.r - 40, 0),
|
r: Math.max(tc.r - 40, 0),
|
||||||
g: Math.max(tc.g - 40, 0),
|
g: Math.max(tc.g - 40, 0),
|
||||||
b: Math.max(tc.b - 40, 0),
|
b: Math.max(tc.b - 40, 0),
|
||||||
});
|
});
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
|
|
||||||
this.borderColorCache.set(player.id(), color);
|
this.borderColorCache.set(player.id(), color);
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
defendedBorderColors(player: PlayerView): { light: Colord; dark: Colord } {
|
defendedBorderColors(player: PlayerView): { light: Colord; dark: Colord } {
|
||||||
return {
|
return {
|
||||||
light: this.territoryColor(player).darken(0.2),
|
light: this.territoryColor(player).darken(0.2),
|
||||||
@@ -140,6 +149,7 @@ export class PastelTheme implements Theme {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
|
|
||||||
backgroundColor(): Colord {
|
backgroundColor(): Colord {
|
||||||
return this.background;
|
return this.background;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export class PastelThemeDark implements Theme {
|
|||||||
private teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
|
private teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
|
||||||
private nationColorAllocator = new ColorAllocator(nationColors, nationColors);
|
private nationColorAllocator = new ColorAllocator(nationColors, nationColors);
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
private background = colord({ r: 0, g: 0, b: 0 });
|
private background = colord({ r: 0, g: 0, b: 0 });
|
||||||
private shore = colord({ r: 134, g: 133, b: 88 });
|
private shore = colord({ r: 134, g: 133, b: 88 });
|
||||||
private falloutColors = [
|
private falloutColors = [
|
||||||
@@ -34,6 +35,7 @@ export class PastelThemeDark implements Theme {
|
|||||||
private _enemyColor = colord({ r: 255, g: 0, b: 0 });
|
private _enemyColor = colord({ r: 255, g: 0, b: 0 });
|
||||||
|
|
||||||
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
|
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
|
|
||||||
teamColor(team: Team): Colord {
|
teamColor(team: Team): Colord {
|
||||||
return this.teamColorAllocator.assignTeamColor(team);
|
return this.teamColorAllocator.assignTeamColor(team);
|
||||||
@@ -57,6 +59,7 @@ export class PastelThemeDark implements Theme {
|
|||||||
return player.type() === PlayerType.Human ? "#ffffff" : "#e6e6e6";
|
return player.type() === PlayerType.Human ? "#ffffff" : "#e6e6e6";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
specialBuildingColor(player: PlayerView): Colord {
|
specialBuildingColor(player: PlayerView): Colord {
|
||||||
const tc = this.territoryColor(player).rgba;
|
const tc = this.territoryColor(player).rgba;
|
||||||
return colord({
|
return colord({
|
||||||
@@ -142,6 +145,7 @@ export class PastelThemeDark implements Theme {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
|
|
||||||
backgroundColor(): Colord {
|
backgroundColor(): Colord {
|
||||||
return this.background;
|
return this.background;
|
||||||
|
|||||||
@@ -184,9 +184,10 @@ export class NukeExecution implements Execution {
|
|||||||
this.mg.config().defaultNukeTargetableRange() ** 2;
|
this.mg.config().defaultNukeTargetableRange() ** 2;
|
||||||
const allTiles: TileRef[] = this.pathFinder.allTiles();
|
const allTiles: TileRef[] = this.pathFinder.allTiles();
|
||||||
for (const tile of allTiles) {
|
for (const tile of allTiles) {
|
||||||
|
const targetable = this.isTargetable(target, tile, targetRangeSquared);
|
||||||
trajectoryTiles.push({
|
trajectoryTiles.push({
|
||||||
|
targetable,
|
||||||
tile,
|
tile,
|
||||||
targetable: this.isTargetable(target, tile, targetRangeSquared),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export class RailroadExecution implements Execution {
|
|||||||
return this.active;
|
return this.active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
init(mg: Game, ticks: number): void {
|
init(mg: Game, ticks: number): void {
|
||||||
this.mg = mg;
|
this.mg = mg;
|
||||||
const tiles = this.railRoad.tiles;
|
const tiles = this.railRoad.tiles;
|
||||||
@@ -48,6 +49,7 @@ export class RailroadExecution implements Execution {
|
|||||||
: RailType.VERTICAL,
|
: RailType.VERTICAL,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
|
|
||||||
private computeExtremityDirection(tile: TileRef, next: TileRef): RailType {
|
private computeExtremityDirection(tile: TileRef, next: TileRef): RailType {
|
||||||
const x = this.mg.x(tile);
|
const x = this.mg.x(tile);
|
||||||
@@ -143,9 +145,9 @@ export class RailroadExecution implements Execution {
|
|||||||
}
|
}
|
||||||
if (updatedRailTiles) {
|
if (updatedRailTiles) {
|
||||||
this.mg.addUpdate({
|
this.mg.addUpdate({
|
||||||
type: GameUpdateType.RailroadEvent,
|
|
||||||
isActive: true,
|
isActive: true,
|
||||||
railTiles: updatedRailTiles,
|
railTiles: updatedRailTiles,
|
||||||
|
type: GameUpdateType.RailroadEvent,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ class SAMTargetingSystem {
|
|||||||
}
|
}
|
||||||
const interceptionTile = this.computeInterceptionTile(nuke.unit);
|
const interceptionTile = this.computeInterceptionTile(nuke.unit);
|
||||||
if (interceptionTile !== undefined) {
|
if (interceptionTile !== undefined) {
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
targets.push({ unit: nuke.unit, tile: interceptionTile });
|
targets.push({ unit: nuke.unit, tile: interceptionTile });
|
||||||
} else {
|
} else {
|
||||||
// Store unreachable nukes in order to prevent useless interception computation
|
// Store unreachable nukes in order to prevent useless interception computation
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ export class TradeShipExecution implements Execution {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.tradeShip = this.origOwner.buildUnit(UnitType.TradeShip, spawn, {
|
this.tradeShip = this.origOwner.buildUnit(UnitType.TradeShip, spawn, {
|
||||||
targetUnit: this._dstPort,
|
|
||||||
lastSetSafeFromPirates: ticks,
|
lastSetSafeFromPirates: ticks,
|
||||||
|
targetUnit: this._dstPort,
|
||||||
});
|
});
|
||||||
this.mg.stats().boatSendTrade(this.origOwner, this._dstPort.owner());
|
this.mg.stats().boatSendTrade(this.origOwner, this._dstPort.owner());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,8 +119,8 @@ export class TrainExecution implements Execution {
|
|||||||
for (let i = 0; i < this.numCars; i++) {
|
for (let i = 0; i < this.numCars; i++) {
|
||||||
this.cars.push(
|
this.cars.push(
|
||||||
this.player.buildUnit(UnitType.Train, tile, {
|
this.player.buildUnit(UnitType.Train, tile, {
|
||||||
trainType: TrainType.Carriage,
|
|
||||||
loaded: this.hasCargo,
|
loaded: this.hasCargo,
|
||||||
|
trainType: TrainType.Carriage,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export class WarshipExecution implements Execution {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
potentialTargets.push({ unit: unit, distSquared });
|
potentialTargets.push({ distSquared, unit });
|
||||||
}
|
}
|
||||||
|
|
||||||
return potentialTargets.sort((a, b) => {
|
return potentialTargets.sort((a, b) => {
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ export class AllianceRequestImpl implements AllianceRequest {
|
|||||||
|
|
||||||
toUpdate(): AllianceRequestUpdate {
|
toUpdate(): AllianceRequestUpdate {
|
||||||
return {
|
return {
|
||||||
type: GameUpdateType.AllianceRequest,
|
|
||||||
requestorID: this.requestor_.smallID(),
|
|
||||||
recipientID: this.recipient_.smallID(),
|
|
||||||
createdAt: this.tickCreated,
|
createdAt: this.tickCreated,
|
||||||
|
recipientID: this.recipient_.smallID(),
|
||||||
|
requestorID: this.requestor_.smallID(),
|
||||||
|
type: GameUpdateType.AllianceRequest,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,13 @@ export class BinaryLoaderGameMapLoader implements GameMapLoader {
|
|||||||
const fileName = key?.toLowerCase();
|
const fileName = key?.toLowerCase();
|
||||||
|
|
||||||
const mapData = {
|
const mapData = {
|
||||||
|
manifest: this.createLazyLoader(() =>
|
||||||
|
(
|
||||||
|
import(
|
||||||
|
`../../../resources/maps/${fileName}/manifest.json`
|
||||||
|
) as Promise<NationMapModule>
|
||||||
|
).then((m) => m.default),
|
||||||
|
),
|
||||||
mapBin: this.createLazyLoader(() =>
|
mapBin: this.createLazyLoader(() =>
|
||||||
(
|
(
|
||||||
import(
|
import(
|
||||||
@@ -51,13 +58,6 @@ export class BinaryLoaderGameMapLoader implements GameMapLoader {
|
|||||||
) as Promise<BinModule>
|
) as Promise<BinModule>
|
||||||
).then((m) => this.toUInt8Array(m.default)),
|
).then((m) => this.toUInt8Array(m.default)),
|
||||||
),
|
),
|
||||||
manifest: this.createLazyLoader(() =>
|
|
||||||
(
|
|
||||||
import(
|
|
||||||
`../../../resources/maps/${fileName}/manifest.json`
|
|
||||||
) as Promise<NationMapModule>
|
|
||||||
).then((m) => m.default),
|
|
||||||
),
|
|
||||||
webpPath: this.createLazyLoader(() =>
|
webpPath: this.createLazyLoader(() =>
|
||||||
(
|
(
|
||||||
import(
|
import(
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ export class FetchGameMapLoader implements GameMapLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mapData = {
|
const mapData = {
|
||||||
|
manifest: () => this.loadJsonFromUrl(this.url(fileName, "manifest.json")),
|
||||||
mapBin: () => this.loadBinaryFromUrl(this.url(fileName, "map.bin")),
|
mapBin: () => this.loadBinaryFromUrl(this.url(fileName, "map.bin")),
|
||||||
miniMapBin: () =>
|
miniMapBin: () =>
|
||||||
this.loadBinaryFromUrl(this.url(fileName, "mini_map.bin")),
|
this.loadBinaryFromUrl(this.url(fileName, "mini_map.bin")),
|
||||||
manifest: () => this.loadJsonFromUrl(this.url(fileName, "manifest.json")),
|
|
||||||
webpPath: async () => this.url(fileName, "thumbnail.webp"),
|
webpPath: async () => this.url(fileName, "thumbnail.webp"),
|
||||||
} satisfies MapData;
|
} satisfies MapData;
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ export const Duos = "Duos" as const;
|
|||||||
export const Trios = "Trios" as const;
|
export const Trios = "Trios" as const;
|
||||||
export const Quads = "Quads" as const;
|
export const Quads = "Quads" as const;
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
export const ColoredTeams: Record<string, Team> = {
|
export const ColoredTeams: Record<string, Team> = {
|
||||||
Red: "Red",
|
Red: "Red",
|
||||||
Blue: "Blue",
|
Blue: "Blue",
|
||||||
@@ -54,6 +55,7 @@ export const ColoredTeams: Record<string, Team> = {
|
|||||||
Green: "Green",
|
Green: "Green",
|
||||||
Bot: "Bot",
|
Bot: "Bot",
|
||||||
} as const;
|
} as const;
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
|
|
||||||
export enum GameMapType {
|
export enum GameMapType {
|
||||||
World = "World",
|
World = "World",
|
||||||
@@ -118,6 +120,7 @@ export const mapCategories: Record<string, GameMapType[]> = {
|
|||||||
GameMapType.Italia,
|
GameMapType.Italia,
|
||||||
GameMapType.Yenisei,
|
GameMapType.Yenisei,
|
||||||
],
|
],
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
fantasy: [
|
fantasy: [
|
||||||
GameMapType.Pangaea,
|
GameMapType.Pangaea,
|
||||||
GameMapType.Pluto,
|
GameMapType.Pluto,
|
||||||
|
|||||||
+27
-27
@@ -292,9 +292,9 @@ export class GameImpl implements Game {
|
|||||||
recipient.endTemporaryEmbargo(requestor.id());
|
recipient.endTemporaryEmbargo(requestor.id());
|
||||||
|
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.AllianceRequestReply,
|
|
||||||
request: request.toUpdate(),
|
|
||||||
accepted: true,
|
accepted: true,
|
||||||
|
request: request.toUpdate(),
|
||||||
|
type: GameUpdateType.AllianceRequestReply,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,9 +306,9 @@ export class GameImpl implements Game {
|
|||||||
request,
|
request,
|
||||||
);
|
);
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.AllianceRequestReply,
|
|
||||||
request: request.toUpdate(),
|
|
||||||
accepted: false,
|
accepted: false,
|
||||||
|
request: request.toUpdate(),
|
||||||
|
type: GameUpdateType.AllianceRequestReply,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,9 +358,9 @@ export class GameImpl implements Game {
|
|||||||
}
|
}
|
||||||
if (this.ticks() % 10 === 0) {
|
if (this.ticks() % 10 === 0) {
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.Hash,
|
|
||||||
tick: this.ticks(),
|
|
||||||
hash: this.hash(),
|
hash: this.hash(),
|
||||||
|
tick: this.ticks(),
|
||||||
|
type: GameUpdateType.Hash,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._ticks++;
|
this._ticks++;
|
||||||
@@ -573,9 +573,9 @@ export class GameImpl implements Game {
|
|||||||
|
|
||||||
target(targeter: Player, target: Player) {
|
target(targeter: Player, target: Player) {
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.TargetPlayer,
|
|
||||||
playerID: targeter.smallID(),
|
playerID: targeter.smallID(),
|
||||||
targetID: target.smallID(),
|
targetID: target.smallID(),
|
||||||
|
type: GameUpdateType.TargetPlayer,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,9 +604,9 @@ export class GameImpl implements Game {
|
|||||||
}
|
}
|
||||||
this.alliances_ = this.alliances_.filter((a) => a !== alliances[0]);
|
this.alliances_ = this.alliances_.filter((a) => a !== alliances[0]);
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.BrokeAlliance,
|
|
||||||
traitorID: breaker.smallID(),
|
|
||||||
betrayedID: other.smallID(),
|
betrayedID: other.smallID(),
|
||||||
|
traitorID: breaker.smallID(),
|
||||||
|
type: GameUpdateType.BrokeAlliance,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,24 +623,24 @@ export class GameImpl implements Game {
|
|||||||
}
|
}
|
||||||
this.alliances_ = this.alliances_.filter((a) => a !== alliances[0]);
|
this.alliances_ = this.alliances_.filter((a) => a !== alliances[0]);
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.AllianceExpired,
|
|
||||||
player1ID: alliance.requestor().smallID(),
|
player1ID: alliance.requestor().smallID(),
|
||||||
player2ID: alliance.recipient().smallID(),
|
player2ID: alliance.recipient().smallID(),
|
||||||
|
type: GameUpdateType.AllianceExpired,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEmojiUpdate(msg: EmojiMessage): void {
|
sendEmojiUpdate(msg: EmojiMessage): void {
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.Emoji,
|
|
||||||
emoji: msg,
|
emoji: msg,
|
||||||
|
type: GameUpdateType.Emoji,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setWinner(winner: Player | Team, allPlayersStats: AllPlayersStats): void {
|
setWinner(winner: Player | Team, allPlayersStats: AllPlayersStats): void {
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
|
allPlayersStats,
|
||||||
type: GameUpdateType.Win,
|
type: GameUpdateType.Win,
|
||||||
winner: this.makeWinner(winner),
|
winner: this.makeWinner(winner),
|
||||||
allPlayersStats,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,12 +683,12 @@ export class GameImpl implements Game {
|
|||||||
id = this.player(playerID).smallID();
|
id = this.player(playerID).smallID();
|
||||||
}
|
}
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.DisplayEvent,
|
goldAmount,
|
||||||
|
message,
|
||||||
messageType: type,
|
messageType: type,
|
||||||
message: message,
|
params,
|
||||||
playerID: id,
|
playerID: id,
|
||||||
goldAmount: goldAmount,
|
type: GameUpdateType.DisplayEvent,
|
||||||
params: params,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,13 +705,13 @@ export class GameImpl implements Game {
|
|||||||
id = this.player(playerID).smallID();
|
id = this.player(playerID).smallID();
|
||||||
}
|
}
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.DisplayChatEvent,
|
category,
|
||||||
key: message,
|
|
||||||
category: category,
|
|
||||||
target: target,
|
|
||||||
playerID: id,
|
|
||||||
isFrom,
|
isFrom,
|
||||||
recipient: recipient,
|
key: message,
|
||||||
|
playerID: id,
|
||||||
|
recipient,
|
||||||
|
target,
|
||||||
|
type: GameUpdateType.DisplayChatEvent,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,11 +724,11 @@ export class GameImpl implements Game {
|
|||||||
const id = this.player(playerID).smallID();
|
const id = this.player(playerID).smallID();
|
||||||
|
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.UnitIncoming,
|
message,
|
||||||
unitID: unitID,
|
|
||||||
message: message,
|
|
||||||
messageType: type,
|
messageType: type,
|
||||||
playerID: id,
|
playerID: id,
|
||||||
|
type: GameUpdateType.UnitIncoming,
|
||||||
|
unitID,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -889,10 +889,10 @@ export class GameImpl implements Game {
|
|||||||
conqueror.addGold(gold);
|
conqueror.addGold(gold);
|
||||||
conquered.removeGold(gold);
|
conquered.removeGold(gold);
|
||||||
this.addUpdate({
|
this.addUpdate({
|
||||||
type: GameUpdateType.ConquestEvent,
|
|
||||||
conquerorId: conqueror.id(),
|
|
||||||
conqueredId: conquered.id(),
|
conqueredId: conquered.id(),
|
||||||
|
conquerorId: conqueror.id(),
|
||||||
gold,
|
gold,
|
||||||
|
type: GameUpdateType.ConquestEvent,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Record stats
|
// Record stats
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ export class PlayerImpl implements Player {
|
|||||||
);
|
);
|
||||||
const stats = this.mg.stats().getPlayerStats(this);
|
const stats = this.mg.stats().getPlayerStats(this);
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
return {
|
return {
|
||||||
type: GameUpdateType.Player,
|
type: GameUpdateType.Player,
|
||||||
clientID: this.clientID(),
|
clientID: this.clientID(),
|
||||||
@@ -176,6 +177,7 @@ export class PlayerImpl implements Player {
|
|||||||
hasSpawned: this.hasSpawned(),
|
hasSpawned: this.hasSpawned(),
|
||||||
betrayals: stats?.betrayals,
|
betrayals: stats?.betrayals,
|
||||||
};
|
};
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
}
|
}
|
||||||
|
|
||||||
smallID(): number {
|
smallID(): number {
|
||||||
@@ -508,6 +510,7 @@ export class PlayerImpl implements Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
target(other: Player): void {
|
target(other: Player): void {
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
this.targets_.push({ tick: this.mg.ticks(), target: other });
|
this.targets_.push({ tick: this.mg.ticks(), target: other });
|
||||||
this.mg.target(this, other);
|
this.mg.target(this, other);
|
||||||
}
|
}
|
||||||
@@ -533,10 +536,10 @@ export class PlayerImpl implements Player {
|
|||||||
throw Error(`Cannot send emoji to oneself: ${this}`);
|
throw Error(`Cannot send emoji to oneself: ${this}`);
|
||||||
}
|
}
|
||||||
const msg: EmojiMessage = {
|
const msg: EmojiMessage = {
|
||||||
message: emoji,
|
|
||||||
senderID: this.smallID(),
|
|
||||||
recipientID: recipient === AllPlayers ? recipient : recipient.smallID(),
|
|
||||||
createdAt: this.mg.ticks(),
|
createdAt: this.mg.ticks(),
|
||||||
|
message: emoji,
|
||||||
|
recipientID: recipient === AllPlayers ? recipient : recipient.smallID(),
|
||||||
|
senderID: this.smallID(),
|
||||||
};
|
};
|
||||||
this.outgoingEmojis_.push(msg);
|
this.outgoingEmojis_.push(msg);
|
||||||
this.mg.sendEmojiUpdate(msg);
|
this.mg.sendEmojiUpdate(msg);
|
||||||
@@ -716,11 +719,11 @@ export class PlayerImpl implements Player {
|
|||||||
this._gold += toAdd;
|
this._gold += toAdd;
|
||||||
if (tile) {
|
if (tile) {
|
||||||
this.mg.addUpdate({
|
this.mg.addUpdate({
|
||||||
type: GameUpdateType.BonusEvent,
|
gold: Number(toAdd),
|
||||||
player: this.id(),
|
player: this.id(),
|
||||||
tile,
|
tile,
|
||||||
gold: Number(toAdd),
|
|
||||||
troops: 0,
|
troops: 0,
|
||||||
|
type: GameUpdateType.BonusEvent,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -837,12 +840,12 @@ export class PlayerImpl implements Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: u,
|
|
||||||
canBuild: this.mg.inSpawnPhase()
|
canBuild: this.mg.inSpawnPhase()
|
||||||
? false
|
? false
|
||||||
: this.canBuild(u, tile, validTiles),
|
: this.canBuild(u, tile, validTiles),
|
||||||
canUpgrade: canUpgrade,
|
canUpgrade: canUpgrade,
|
||||||
cost: this.mg.config().unitInfo(u).cost(this),
|
cost: this.mg.config().unitInfo(u).cost(this),
|
||||||
|
type: u,
|
||||||
} as BuildableUnit;
|
} as BuildableUnit;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1042,13 +1045,13 @@ export class PlayerImpl implements Player {
|
|||||||
|
|
||||||
public playerProfile(): PlayerProfile {
|
public playerProfile(): PlayerProfile {
|
||||||
const rel = {
|
const rel = {
|
||||||
|
alliances: this.alliances().map((a) => a.other(this).smallID()),
|
||||||
relations: Object.fromEntries(
|
relations: Object.fromEntries(
|
||||||
this.allRelationsSorted().map(({ player, relation }) => [
|
this.allRelationsSorted().map(({ player, relation }) => [
|
||||||
player.smallID(),
|
player.smallID(),
|
||||||
relation,
|
relation,
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
alliances: this.alliances().map((a) => a.other(this).smallID()),
|
|
||||||
};
|
};
|
||||||
return rel;
|
return rel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ export class RailNetworkImpl implements RailNetwork {
|
|||||||
|
|
||||||
const visited = new Set<TrainStation>();
|
const visited = new Set<TrainStation>();
|
||||||
const queue: Array<{ station: TrainStation; distance: number }> = [
|
const queue: Array<{ station: TrainStation; distance: number }> = [
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
{ station: start, distance: 0 },
|
{ station: start, distance: 0 },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -229,6 +230,7 @@ export class RailNetworkImpl implements RailNetwork {
|
|||||||
for (const neighbor of station.neighbors()) {
|
for (const neighbor of station.neighbors()) {
|
||||||
if (neighbor === dest) return distance + 1;
|
if (neighbor === dest) return distance + 1;
|
||||||
if (!visited.has(neighbor)) {
|
if (!visited.has(neighbor)) {
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
queue.push({ station: neighbor, distance: distance + 1 });
|
queue.push({ station: neighbor, distance: distance + 1 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ export class Railroad {
|
|||||||
|
|
||||||
delete(game: Game) {
|
delete(game: Game) {
|
||||||
const railTiles: RailTile[] = this.tiles.map((tile) => ({
|
const railTiles: RailTile[] = this.tiles.map((tile) => ({
|
||||||
tile,
|
|
||||||
railType: RailType.VERTICAL,
|
railType: RailType.VERTICAL,
|
||||||
|
tile,
|
||||||
}));
|
}));
|
||||||
game.addUpdate({
|
game.addUpdate({
|
||||||
type: GameUpdateType.RailroadEvent,
|
|
||||||
isActive: false,
|
isActive: false,
|
||||||
railTiles,
|
railTiles,
|
||||||
|
type: GameUpdateType.RailroadEvent,
|
||||||
});
|
});
|
||||||
this.from.getRailroads().delete(this);
|
this.from.getRailroads().delete(this);
|
||||||
this.to.getRailroads().delete(this);
|
this.to.getRailroads().delete(this);
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ export async function loadTerrainMap(
|
|||||||
await mapFiles.miniMapBin(),
|
await mapFiles.miniMapBin(),
|
||||||
);
|
);
|
||||||
const result = {
|
const result = {
|
||||||
manifest: await mapFiles.manifest(),
|
|
||||||
gameMap: gameMap,
|
gameMap: gameMap,
|
||||||
|
manifest: await mapFiles.manifest(),
|
||||||
miniGameMap: miniGameMap,
|
miniGameMap: miniGameMap,
|
||||||
};
|
};
|
||||||
loadedMaps.set(map, result);
|
loadedMaps.set(map, result);
|
||||||
|
|||||||
@@ -119,13 +119,13 @@ export class TrainStation {
|
|||||||
);
|
);
|
||||||
if (toRemove) {
|
if (toRemove) {
|
||||||
const railTiles: RailTile[] = toRemove.tiles.map((tile) => ({
|
const railTiles: RailTile[] = toRemove.tiles.map((tile) => ({
|
||||||
tile,
|
|
||||||
railType: RailType.VERTICAL,
|
railType: RailType.VERTICAL,
|
||||||
|
tile,
|
||||||
}));
|
}));
|
||||||
this.mg.addUpdate({
|
this.mg.addUpdate({
|
||||||
type: GameUpdateType.RailroadEvent,
|
|
||||||
isActive: false,
|
isActive: false,
|
||||||
railTiles,
|
railTiles,
|
||||||
|
type: GameUpdateType.RailroadEvent,
|
||||||
});
|
});
|
||||||
this.railroads.delete(toRemove);
|
this.railroads.delete(toRemove);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,10 +183,10 @@ export function candidateShoreTiles(
|
|||||||
|
|
||||||
let bestByManhattan: TileRef | null = null;
|
let bestByManhattan: TileRef | null = null;
|
||||||
const extremumTiles: Record<string, TileRef | null> = {
|
const extremumTiles: Record<string, TileRef | null> = {
|
||||||
minX: null,
|
|
||||||
minY: null,
|
|
||||||
maxX: null,
|
maxX: null,
|
||||||
maxY: null,
|
maxY: null,
|
||||||
|
minX: null,
|
||||||
|
minY: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const borderShoreTiles = Array.from(player.borderTiles()).filter((t) =>
|
const borderShoreTiles = Array.from(player.borderTiles()).filter((t) =>
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export class UnitGrid {
|
|||||||
gridY + Math.ceil((range - (cellSize - (y % cellSize))) / cellSize),
|
gridY + Math.ceil((range - (cellSize - (y % cellSize))) / cellSize),
|
||||||
);
|
);
|
||||||
|
|
||||||
return { startGridX, endGridX, startGridY, endGridY };
|
return { endGridX, endGridY, startGridX, startGridY };
|
||||||
}
|
}
|
||||||
|
|
||||||
private squaredDistanceFromTile(
|
private squaredDistanceFromTile(
|
||||||
@@ -154,6 +154,7 @@ export class UnitGrid {
|
|||||||
if (!unit.isActive()) continue;
|
if (!unit.isActive()) continue;
|
||||||
const distSquared = this.squaredDistanceFromTile(unit, tile);
|
const distSquared = this.squaredDistanceFromTile(unit, tile);
|
||||||
if (distSquared > rangeSquared) continue;
|
if (distSquared > rangeSquared) continue;
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
const value = { unit, distSquared };
|
const value = { unit, distSquared };
|
||||||
if (predicate !== undefined && !predicate(value)) continue;
|
if (predicate !== undefined && !predicate(value)) continue;
|
||||||
nearby.push(value);
|
nearby.push(value);
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ export class UnitImpl implements Unit {
|
|||||||
return this._id;
|
return this._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
toUpdate(): UnitUpdate {
|
toUpdate(): UnitUpdate {
|
||||||
return {
|
return {
|
||||||
type: GameUpdateType.Unit,
|
type: GameUpdateType.Unit,
|
||||||
@@ -139,6 +140,7 @@ export class UnitImpl implements Unit {
|
|||||||
loaded: this._loaded,
|
loaded: this._loaded,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
|
|
||||||
type(): UnitType {
|
type(): UnitType {
|
||||||
return this._type;
|
return this._type;
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ export class PathFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.game.manhattanDist(curr, dst) < dist) {
|
if (this.game.manhattanDist(curr, dst) < dist) {
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
return { type: PathFindResultType.Completed, node: curr };
|
return { type: PathFindResultType.Completed, node: curr };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +165,7 @@ export class PathFinder {
|
|||||||
if (tile === undefined) {
|
if (tile === undefined) {
|
||||||
throw new Error("missing tile");
|
throw new Error("missing tile");
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
return { type: PathFindResultType.NextTile, node: tile };
|
return { type: PathFindResultType.NextTile, node: tile };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export class SerialAStar<NodeType> implements AStar<NodeType> {
|
|||||||
this.fwdGScore.set(startPoint, 0);
|
this.fwdGScore.set(startPoint, 0);
|
||||||
this.fwdOpenSet.add({
|
this.fwdOpenSet.add({
|
||||||
tile: startPoint,
|
tile: startPoint,
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
fScore: this.heuristic(startPoint, dst),
|
fScore: this.heuristic(startPoint, dst),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -57,6 +58,7 @@ export class SerialAStar<NodeType> implements AStar<NodeType> {
|
|||||||
this.bwdGScore.set(dst, 0);
|
this.bwdGScore.set(dst, 0);
|
||||||
this.bwdOpenSet.add({
|
this.bwdOpenSet.add({
|
||||||
tile: dst,
|
tile: dst,
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
fScore: this.heuristic(dst, this.findClosestSource(dst)),
|
fScore: this.heuristic(dst, this.findClosestSource(dst)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -145,6 +147,7 @@ export class SerialAStar<NodeType> implements AStar<NodeType> {
|
|||||||
const fScore =
|
const fScore =
|
||||||
totalG +
|
totalG +
|
||||||
this.heuristic(neighbor, isForward ? this.dst : this.closestSource);
|
this.heuristic(neighbor, isForward ? this.dst : this.closestSource);
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
openSet.add({ tile: neighbor, fScore: fScore });
|
openSet.add({ tile: neighbor, fScore: fScore });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,33 +50,34 @@ export function validateUsername(username: string): {
|
|||||||
error?: string;
|
error?: string;
|
||||||
} {
|
} {
|
||||||
if (typeof username !== "string") {
|
if (typeof username !== "string") {
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
return { isValid: false, error: translateText("username.not_string") };
|
return { isValid: false, error: translateText("username.not_string") };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username.length < MIN_USERNAME_LENGTH) {
|
if (username.length < MIN_USERNAME_LENGTH) {
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
|
||||||
error: translateText("username.too_short", {
|
error: translateText("username.too_short", {
|
||||||
min: MIN_USERNAME_LENGTH,
|
min: MIN_USERNAME_LENGTH,
|
||||||
}),
|
}),
|
||||||
|
isValid: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username.length > MAX_USERNAME_LENGTH) {
|
if (username.length > MAX_USERNAME_LENGTH) {
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
|
||||||
error: translateText("username.too_long", {
|
error: translateText("username.too_long", {
|
||||||
max: MAX_USERNAME_LENGTH,
|
max: MAX_USERNAME_LENGTH,
|
||||||
}),
|
}),
|
||||||
|
isValid: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validPattern.test(username)) {
|
if (!validPattern.test(username)) {
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
|
||||||
error: translateText("username.invalid_chars", {
|
error: translateText("username.invalid_chars", {
|
||||||
max: MAX_USERNAME_LENGTH,
|
max: MAX_USERNAME_LENGTH,
|
||||||
}),
|
}),
|
||||||
|
isValid: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ function gameUpdate(gu: GameUpdateViewData | ErrorUpdate) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: "game_update",
|
|
||||||
gameUpdate: gu,
|
gameUpdate: gu,
|
||||||
|
type: "game_update",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,8 +48,8 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
|
|||||||
gameUpdate,
|
gameUpdate,
|
||||||
).then((gr) => {
|
).then((gr) => {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: "initialized",
|
|
||||||
id: message.id,
|
id: message.id,
|
||||||
|
type: "initialized",
|
||||||
} as InitializedMessage);
|
} as InitializedMessage);
|
||||||
return gr;
|
return gr;
|
||||||
});
|
});
|
||||||
@@ -85,9 +85,9 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
|
|||||||
message.y,
|
message.y,
|
||||||
);
|
);
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: "player_actions_result",
|
|
||||||
id: message.id,
|
id: message.id,
|
||||||
result: actions,
|
result: actions,
|
||||||
|
type: "player_actions_result",
|
||||||
} as PlayerActionsResultMessage);
|
} as PlayerActionsResultMessage);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to check borders:", error);
|
console.error("Failed to check borders:", error);
|
||||||
@@ -102,9 +102,9 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
|
|||||||
try {
|
try {
|
||||||
const profile = (await gameRunner).playerProfile(message.playerID);
|
const profile = (await gameRunner).playerProfile(message.playerID);
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: "player_profile_result",
|
|
||||||
id: message.id,
|
id: message.id,
|
||||||
result: profile,
|
result: profile,
|
||||||
|
type: "player_profile_result",
|
||||||
} as PlayerProfileResultMessage);
|
} as PlayerProfileResultMessage);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to check borders:", error);
|
console.error("Failed to check borders:", error);
|
||||||
@@ -121,9 +121,9 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
|
|||||||
message.playerID,
|
message.playerID,
|
||||||
);
|
);
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: "player_border_tiles_result",
|
|
||||||
id: message.id,
|
id: message.id,
|
||||||
result: borderTiles,
|
result: borderTiles,
|
||||||
|
type: "player_border_tiles_result",
|
||||||
} as PlayerBorderTilesResultMessage);
|
} as PlayerBorderTilesResultMessage);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to get border tiles:", error);
|
console.error("Failed to get border tiles:", error);
|
||||||
@@ -141,8 +141,8 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
|
|||||||
message.attackID,
|
message.attackID,
|
||||||
);
|
);
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: "attack_average_position_result",
|
|
||||||
id: message.id,
|
id: message.id,
|
||||||
|
type: "attack_average_position_result",
|
||||||
x: averagePosition ? averagePosition.x : null,
|
x: averagePosition ? averagePosition.x : null,
|
||||||
y: averagePosition ? averagePosition.y : null,
|
y: averagePosition ? averagePosition.y : null,
|
||||||
} as AttackAveragePositionResultMessage);
|
} as AttackAveragePositionResultMessage);
|
||||||
@@ -162,9 +162,9 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
|
|||||||
message.targetTile,
|
message.targetTile,
|
||||||
);
|
);
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: "transport_ship_spawn_result",
|
|
||||||
id: message.id,
|
id: message.id,
|
||||||
result: spawnTile,
|
result: spawnTile,
|
||||||
|
type: "transport_ship_spawn_result",
|
||||||
} as TransportShipSpawnResultMessage);
|
} as TransportShipSpawnResultMessage);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to spawn transport ship:", error);
|
console.error("Failed to spawn transport ship:", error);
|
||||||
|
|||||||
@@ -66,10 +66,10 @@ export class WorkerClient {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.worker.postMessage({
|
this.worker.postMessage({
|
||||||
type: "init",
|
|
||||||
id: messageId,
|
|
||||||
gameStartInfo: this.gameStartInfo,
|
|
||||||
clientID: this.clientID,
|
clientID: this.clientID,
|
||||||
|
gameStartInfo: this.gameStartInfo,
|
||||||
|
id: messageId,
|
||||||
|
type: "init",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add timeout for initialization
|
// Add timeout for initialization
|
||||||
@@ -95,8 +95,8 @@ export class WorkerClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.worker.postMessage({
|
this.worker.postMessage({
|
||||||
type: "turn",
|
|
||||||
turn,
|
turn,
|
||||||
|
type: "turn",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,9 +125,9 @@ export class WorkerClient {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.worker.postMessage({
|
this.worker.postMessage({
|
||||||
type: "player_profile",
|
|
||||||
id: messageId,
|
id: messageId,
|
||||||
playerID: playerID,
|
playerID,
|
||||||
|
type: "player_profile",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -151,9 +151,9 @@ export class WorkerClient {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.worker.postMessage({
|
this.worker.postMessage({
|
||||||
type: "player_border_tiles",
|
|
||||||
id: messageId,
|
id: messageId,
|
||||||
playerID: playerID,
|
playerID,
|
||||||
|
type: "player_border_tiles",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -181,9 +181,9 @@ export class WorkerClient {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.worker.postMessage({
|
this.worker.postMessage({
|
||||||
type: "player_actions",
|
|
||||||
id: messageId,
|
id: messageId,
|
||||||
playerID: playerID,
|
playerID,
|
||||||
|
type: "player_actions",
|
||||||
x: x,
|
x: x,
|
||||||
y: y,
|
y: y,
|
||||||
});
|
});
|
||||||
@@ -217,10 +217,10 @@ export class WorkerClient {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.worker.postMessage({
|
this.worker.postMessage({
|
||||||
type: "attack_average_position",
|
attackID,
|
||||||
id: messageId,
|
id: messageId,
|
||||||
playerID: playerID,
|
playerID,
|
||||||
attackID: attackID,
|
type: "attack_average_position",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -247,10 +247,10 @@ export class WorkerClient {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.worker.postMessage({
|
this.worker.postMessage({
|
||||||
type: "transport_ship_spawn",
|
|
||||||
id: messageId,
|
id: messageId,
|
||||||
playerID: playerID,
|
playerID,
|
||||||
targetTile: targetTile,
|
targetTile,
|
||||||
|
type: "transport_ship_spawn",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ const log = logger.child({ component: "Archive" });
|
|||||||
// R2 client configuration
|
// R2 client configuration
|
||||||
const r2 = new S3({
|
const r2 = new S3({
|
||||||
region: "auto", // R2 ignores region, but it's required by the SDK
|
region: "auto", // R2 ignores region, but it's required by the SDK
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
endpoint: config.r2Endpoint(),
|
endpoint: config.r2Endpoint(),
|
||||||
credentials: {
|
credentials: {
|
||||||
accessKeyId: config.r2AccessKey(),
|
accessKeyId: config.r2AccessKey(),
|
||||||
secretAccessKey: config.r2SecretKey(),
|
secretAccessKey: config.r2SecretKey(),
|
||||||
},
|
},
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
});
|
});
|
||||||
|
|
||||||
const bucket = config.r2Bucket();
|
const bucket = config.r2Bucket();
|
||||||
|
|||||||
@@ -60,12 +60,12 @@ export class Cloudflare {
|
|||||||
data?: any,
|
data?: any,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method,
|
body: data ? JSON.stringify(data) : undefined,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${this.apiToken}`,
|
Authorization: `Bearer ${this.apiToken}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: data ? JSON.stringify(data) : undefined,
|
method,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -178,7 +178,6 @@ export class Cloudflare {
|
|||||||
log.info(`Created credentials file at: ${this.credsPath}`);
|
log.info(`Created credentials file at: ${this.credsPath}`);
|
||||||
|
|
||||||
const tunnelConfig: CloudflaredConfig = {
|
const tunnelConfig: CloudflaredConfig = {
|
||||||
tunnel: tunnelId,
|
|
||||||
"credentials-file": this.credsPath,
|
"credentials-file": this.credsPath,
|
||||||
ingress: [
|
ingress: [
|
||||||
...Array.from(subdomainToService.entries()).map(
|
...Array.from(subdomainToService.entries()).map(
|
||||||
@@ -191,6 +190,7 @@ export class Cloudflare {
|
|||||||
service: "http_status:404",
|
service: "http_status:404",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
tunnel: tunnelId,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Write config file
|
// Write config file
|
||||||
@@ -210,11 +210,11 @@ export class Cloudflare {
|
|||||||
|
|
||||||
const recordId = existingRecords.result[0]?.id;
|
const recordId = existingRecords.result[0]?.id;
|
||||||
const dnsData = {
|
const dnsData = {
|
||||||
type: "CNAME",
|
|
||||||
name: subdomain,
|
|
||||||
content: `${tunnelId}.cfargotunnel.com`,
|
content: `${tunnelId}.cfargotunnel.com`,
|
||||||
ttl: 1,
|
name: subdomain,
|
||||||
proxied: true,
|
proxied: true,
|
||||||
|
ttl: 1,
|
||||||
|
type: "CNAME",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (recordId) {
|
if (recordId) {
|
||||||
@@ -240,12 +240,12 @@ export class Cloudflare {
|
|||||||
["tunnel", "--config", this.configPath, "--loglevel", "error", "run"],
|
["tunnel", "--config", this.configPath, "--loglevel", "error", "run"],
|
||||||
{
|
{
|
||||||
detached: true,
|
detached: true,
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
// Set this to bypass origin cert requirement for named tunnels
|
// Set this to bypass origin cert requirement for named tunnels
|
||||||
TUNNEL_ORIGIN_CERT: "/dev/null",
|
TUNNEL_ORIGIN_CERT: "/dev/null",
|
||||||
},
|
},
|
||||||
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -39,16 +39,16 @@ export class GameManager {
|
|||||||
Date.now(),
|
Date.now(),
|
||||||
this.config,
|
this.config,
|
||||||
{
|
{
|
||||||
gameMap: GameMapType.World,
|
bots: 400,
|
||||||
gameType: GameType.Private,
|
|
||||||
difficulty: Difficulty.Medium,
|
difficulty: Difficulty.Medium,
|
||||||
disableNPCs: false,
|
disableNPCs: false,
|
||||||
|
disabledUnits: [],
|
||||||
|
gameMap: GameMapType.World,
|
||||||
|
gameMode: GameMode.FFA,
|
||||||
|
gameType: GameType.Private,
|
||||||
infiniteGold: false,
|
infiniteGold: false,
|
||||||
infiniteTroops: false,
|
infiniteTroops: false,
|
||||||
instantBuild: false,
|
instantBuild: false,
|
||||||
gameMode: GameMode.FFA,
|
|
||||||
bots: 400,
|
|
||||||
disabledUnits: [],
|
|
||||||
...gameConfig,
|
...gameConfig,
|
||||||
},
|
},
|
||||||
creatorClientID,
|
creatorClientID,
|
||||||
|
|||||||
+26
-26
@@ -123,15 +123,15 @@ export class GameServer {
|
|||||||
// Log when lobby creator joins private game
|
// Log when lobby creator joins private game
|
||||||
if (client.clientID === this.LobbyCreatorID) {
|
if (client.clientID === this.LobbyCreatorID) {
|
||||||
this.log.info("Lobby creator joined", {
|
this.log.info("Lobby creator joined", {
|
||||||
gameID: this.id,
|
|
||||||
creatorID: this.LobbyCreatorID,
|
creatorID: this.LobbyCreatorID,
|
||||||
|
gameID: this.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.log.info("client (re)joining game", {
|
this.log.info("client (re)joining game", {
|
||||||
clientID: client.clientID,
|
clientID: client.clientID,
|
||||||
persistentID: client.persistentID,
|
|
||||||
clientIP: ipAnonymize(client.ip),
|
clientIP: ipAnonymize(client.ip),
|
||||||
isRejoin: lastTurn > 0,
|
isRejoin: lastTurn > 0,
|
||||||
|
persistentID: client.persistentID,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -210,9 +210,9 @@ export class GameServer {
|
|||||||
});
|
});
|
||||||
client.ws.send(
|
client.ws.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: "error",
|
|
||||||
error,
|
error,
|
||||||
message,
|
message,
|
||||||
|
type: "error",
|
||||||
} satisfies ServerErrorMessage),
|
} satisfies ServerErrorMessage),
|
||||||
);
|
);
|
||||||
client.ws.close(1002, "ClientMessageSchema");
|
client.ws.close(1002, "ClientMessageSchema");
|
||||||
@@ -244,8 +244,8 @@ export class GameServer {
|
|||||||
this.log.warn(`Only lobby creator can kick players`, {
|
this.log.warn(`Only lobby creator can kick players`, {
|
||||||
clientID: authenticatedClientID,
|
clientID: authenticatedClientID,
|
||||||
creatorID: this.LobbyCreatorID,
|
creatorID: this.LobbyCreatorID,
|
||||||
target: clientMsg.intent.target,
|
|
||||||
gameID: this.id,
|
gameID: this.id,
|
||||||
|
target: clientMsg.intent.target,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -261,9 +261,9 @@ export class GameServer {
|
|||||||
// Log and execute the kick
|
// Log and execute the kick
|
||||||
this.log.info(`Lobby creator initiated kick of player`, {
|
this.log.info(`Lobby creator initiated kick of player`, {
|
||||||
creatorID: authenticatedClientID,
|
creatorID: authenticatedClientID,
|
||||||
target: clientMsg.intent.target,
|
|
||||||
gameID: this.id,
|
gameID: this.id,
|
||||||
kickMethod: "websocket",
|
kickMethod: "websocket",
|
||||||
|
target: clientMsg.intent.target,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.kickClient(clientMsg.intent.target);
|
this.kickClient(clientMsg.intent.target);
|
||||||
@@ -358,8 +358,8 @@ export class GameServer {
|
|||||||
this._hasPrestarted = true;
|
this._hasPrestarted = true;
|
||||||
|
|
||||||
const prestartMsg = ServerPrestartMessageSchema.safeParse({
|
const prestartMsg = ServerPrestartMessageSchema.safeParse({
|
||||||
type: "prestart",
|
|
||||||
gameMap: this.gameConfig.gameMap,
|
gameMap: this.gameConfig.gameMap,
|
||||||
|
type: "prestart",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!prestartMsg.success) {
|
if (!prestartMsg.success) {
|
||||||
@@ -393,13 +393,13 @@ export class GameServer {
|
|||||||
this.lastPingUpdate = Date.now();
|
this.lastPingUpdate = Date.now();
|
||||||
|
|
||||||
const result = GameStartInfoSchema.safeParse({
|
const result = GameStartInfoSchema.safeParse({
|
||||||
gameID: this.id,
|
|
||||||
config: this.gameConfig,
|
config: this.gameConfig,
|
||||||
|
gameID: this.id,
|
||||||
players: this.activeClients.map((c) => ({
|
players: this.activeClients.map((c) => ({
|
||||||
username: c.username,
|
|
||||||
clientID: c.clientID,
|
clientID: c.clientID,
|
||||||
pattern: c.pattern,
|
|
||||||
flag: c.flag,
|
flag: c.flag,
|
||||||
|
pattern: c.pattern,
|
||||||
|
username: c.username,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
@@ -430,9 +430,9 @@ export class GameServer {
|
|||||||
try {
|
try {
|
||||||
ws.send(
|
ws.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: "start",
|
|
||||||
turns: this.turns.slice(lastTurn),
|
|
||||||
gameStartInfo: this.gameStartInfo,
|
gameStartInfo: this.gameStartInfo,
|
||||||
|
turns: this.turns.slice(lastTurn),
|
||||||
|
type: "start",
|
||||||
} satisfies ServerStartGameMessage),
|
} satisfies ServerStartGameMessage),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -447,8 +447,8 @@ export class GameServer {
|
|||||||
|
|
||||||
private endTurn() {
|
private endTurn() {
|
||||||
const pastTurn: Turn = {
|
const pastTurn: Turn = {
|
||||||
turnNumber: this.turns.length,
|
|
||||||
intents: this.intents,
|
intents: this.intents,
|
||||||
|
turnNumber: this.turns.length,
|
||||||
};
|
};
|
||||||
this.turns.push(pastTurn);
|
this.turns.push(pastTurn);
|
||||||
this.intents = [];
|
this.intents = [];
|
||||||
@@ -457,8 +457,8 @@ export class GameServer {
|
|||||||
this.checkDisconnectedStatus();
|
this.checkDisconnectedStatus();
|
||||||
|
|
||||||
const msg = JSON.stringify({
|
const msg = JSON.stringify({
|
||||||
type: "turn",
|
|
||||||
turn: pastTurn,
|
turn: pastTurn,
|
||||||
|
type: "turn",
|
||||||
} satisfies ServerTurnMessage);
|
} satisfies ServerTurnMessage);
|
||||||
this.activeClients.forEach((c) => {
|
this.activeClients.forEach((c) => {
|
||||||
c.ws.send(msg);
|
c.ws.send(msg);
|
||||||
@@ -510,9 +510,9 @@ export class GameServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.log.error("Error archiving game record details:", {
|
this.log.error("Error archiving game record details:", {
|
||||||
gameId: this.id,
|
|
||||||
errorType: typeof error,
|
|
||||||
error: errorDetails,
|
error: errorDetails,
|
||||||
|
errorType: typeof error,
|
||||||
|
gameId: this.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -587,12 +587,12 @@ export class GameServer {
|
|||||||
|
|
||||||
public gameInfo(): GameInfo {
|
public gameInfo(): GameInfo {
|
||||||
return {
|
return {
|
||||||
gameID: this.id,
|
|
||||||
clients: this.activeClients.map((c) => ({
|
clients: this.activeClients.map((c) => ({
|
||||||
username: c.username,
|
|
||||||
clientID: c.clientID,
|
clientID: c.clientID,
|
||||||
|
username: c.username,
|
||||||
})),
|
})),
|
||||||
gameConfig: this.gameConfig,
|
gameConfig: this.gameConfig,
|
||||||
|
gameID: this.id,
|
||||||
msUntilStart: this.isPublic()
|
msUntilStart: this.isPublic()
|
||||||
? this.createdAt + this.config.gameCreationRate()
|
? this.createdAt + this.config.gameCreationRate()
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -618,8 +618,8 @@ export class GameServer {
|
|||||||
});
|
});
|
||||||
client.ws.send(
|
client.ws.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: "error",
|
|
||||||
error: "Kicked from game (you may have been playing on another tab)",
|
error: "Kicked from game (you may have been playing on another tab)",
|
||||||
|
type: "error",
|
||||||
} satisfies ServerErrorMessage),
|
} satisfies ServerErrorMessage),
|
||||||
);
|
);
|
||||||
client.ws.close(1000, "Kicked from game");
|
client.ws.close(1000, "Kicked from game");
|
||||||
@@ -660,9 +660,9 @@ export class GameServer {
|
|||||||
private markClientDisconnected(clientID: string, isDisconnected: boolean) {
|
private markClientDisconnected(clientID: string, isDisconnected: boolean) {
|
||||||
this.clientsDisconnectedStatus.set(clientID, isDisconnected);
|
this.clientsDisconnectedStatus.set(clientID, isDisconnected);
|
||||||
this.addIntent({
|
this.addIntent({
|
||||||
type: "mark_disconnected",
|
clientID,
|
||||||
clientID: clientID,
|
|
||||||
isDisconnected: isDisconnected,
|
isDisconnected: isDisconnected,
|
||||||
|
type: "mark_disconnected",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,10 +681,10 @@ export class GameServer {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
clientID: player.clientID,
|
clientID: player.clientID,
|
||||||
username: player.username,
|
|
||||||
persistentID:
|
persistentID:
|
||||||
this.allClients.get(player.clientID)?.persistentID ?? "",
|
this.allClients.get(player.clientID)?.persistentID ?? "",
|
||||||
stats,
|
stats,
|
||||||
|
username: player.username,
|
||||||
} satisfies PlayerRecord;
|
} satisfies PlayerRecord;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -722,17 +722,17 @@ export class GameServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const serverDesync = ServerDesyncSchema.safeParse({
|
const serverDesync = ServerDesyncSchema.safeParse({
|
||||||
type: "desync",
|
|
||||||
turn: lastHashTurn,
|
|
||||||
correctHash: mostCommonHash,
|
|
||||||
clientsWithCorrectHash:
|
clientsWithCorrectHash:
|
||||||
this.activeClients.length - outOfSyncClients.length,
|
this.activeClients.length - outOfSyncClients.length,
|
||||||
|
correctHash: mostCommonHash,
|
||||||
totalActiveClients: this.activeClients.length,
|
totalActiveClients: this.activeClients.length,
|
||||||
|
turn: lastHashTurn,
|
||||||
|
type: "desync",
|
||||||
});
|
});
|
||||||
if (!serverDesync.success) {
|
if (!serverDesync.success) {
|
||||||
this.log.warn("failed to create desync message", {
|
this.log.warn("failed to create desync message", {
|
||||||
gameID: this.id,
|
|
||||||
error: serverDesync.error,
|
error: serverDesync.error,
|
||||||
|
gameID: this.id,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -745,8 +745,8 @@ export class GameServer {
|
|||||||
}
|
}
|
||||||
this.sentDesyncMessageClients.add(c.clientID);
|
this.sentDesyncMessageClients.add(c.clientID);
|
||||||
this.log.info("sending desync to client", {
|
this.log.info("sending desync to client", {
|
||||||
gameID: this.id,
|
|
||||||
clientID: c.clientID,
|
clientID: c.clientID,
|
||||||
|
gameID: this.id,
|
||||||
persistentID: c.persistentID,
|
persistentID: c.persistentID,
|
||||||
});
|
});
|
||||||
c.ws.send(desyncMsg);
|
c.ws.send(desyncMsg);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ if (config.otelEnabled()) {
|
|||||||
// Add OTLP exporter for logs
|
// Add OTLP exporter for logs
|
||||||
const logExporter = new OTLPLogExporter({
|
const logExporter = new OTLPLogExporter({
|
||||||
url: `${config.otelEndpoint()}/v1/logs`,
|
url: `${config.otelEndpoint()}/v1/logs`,
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
headers,
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -56,6 +57,7 @@ const addSeverityFormat = winston.format((info) => {
|
|||||||
// Define your base/parent logger
|
// Define your base/parent logger
|
||||||
const logger = winston.createLogger({
|
const logger = winston.createLogger({
|
||||||
level: "info",
|
level: "info",
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
format: winston.format.combine(
|
format: winston.format.combine(
|
||||||
winston.format.timestamp(),
|
winston.format.timestamp(),
|
||||||
addSeverityFormat(),
|
addSeverityFormat(),
|
||||||
@@ -65,6 +67,7 @@ const logger = winston.createLogger({
|
|||||||
service: "openfront",
|
service: "openfront",
|
||||||
environment: process.env.GAME_ENV ?? "prod",
|
environment: process.env.GAME_ENV ?? "prod",
|
||||||
},
|
},
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
transports: [
|
transports: [
|
||||||
new winston.transports.Console(),
|
new winston.transports.Console(),
|
||||||
new OpenTelemetryTransportV3(),
|
new OpenTelemetryTransportV3(),
|
||||||
|
|||||||
+29
-29
@@ -20,33 +20,33 @@ const config = getServerConfigFromServer();
|
|||||||
// How many times each map should appear in the playlist.
|
// How many times each map should appear in the playlist.
|
||||||
// Note: The Partial should eventually be removed for better type safety.
|
// Note: The Partial should eventually be removed for better type safety.
|
||||||
const frequency: Partial<Record<GameMapName, number>> = {
|
const frequency: Partial<Record<GameMapName, number>> = {
|
||||||
World: 3,
|
|
||||||
Europe: 2,
|
|
||||||
Africa: 2,
|
Africa: 2,
|
||||||
Baikal: 2,
|
|
||||||
Australia: 1,
|
|
||||||
NorthAmerica: 1,
|
|
||||||
Britannia: 1,
|
|
||||||
GatewayToTheAtlantic: 1,
|
|
||||||
Iceland: 1,
|
|
||||||
SouthAmerica: 1,
|
|
||||||
DeglaciatedAntarctica: 1,
|
|
||||||
EuropeClassic: 1,
|
|
||||||
Mena: 1,
|
|
||||||
Pangaea: 1,
|
|
||||||
Asia: 1,
|
Asia: 1,
|
||||||
|
Australia: 1,
|
||||||
|
Baikal: 2,
|
||||||
|
BetweenTwoSeas: 1,
|
||||||
|
BlackSea: 1,
|
||||||
|
Britannia: 1,
|
||||||
|
DeglaciatedAntarctica: 1,
|
||||||
|
EastAsia: 1,
|
||||||
|
Europe: 2,
|
||||||
|
EuropeClassic: 1,
|
||||||
|
FalklandIslands: 1,
|
||||||
|
FaroeIslands: 1,
|
||||||
|
GatewayToTheAtlantic: 1,
|
||||||
|
Halkidiki: 1,
|
||||||
|
Iceland: 1,
|
||||||
|
Italia: 1,
|
||||||
Mars: 1,
|
Mars: 1,
|
||||||
MarsRevised: 1,
|
MarsRevised: 1,
|
||||||
BetweenTwoSeas: 1,
|
Mena: 1,
|
||||||
EastAsia: 1,
|
NorthAmerica: 1,
|
||||||
BlackSea: 1,
|
Pangaea: 1,
|
||||||
FaroeIslands: 1,
|
|
||||||
FalklandIslands: 1,
|
|
||||||
Halkidiki: 1,
|
|
||||||
StraitOfGibraltar: 1,
|
|
||||||
Italia: 1,
|
|
||||||
Yenisei: 1,
|
|
||||||
Pluto: 1,
|
Pluto: 1,
|
||||||
|
SouthAmerica: 1,
|
||||||
|
StraitOfGibraltar: 1,
|
||||||
|
World: 3,
|
||||||
|
Yenisei: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
interface MapWithMode {
|
interface MapWithMode {
|
||||||
@@ -77,18 +77,18 @@ export class MapPlaylist {
|
|||||||
|
|
||||||
// Create the default public game config (from your GameManager)
|
// Create the default public game config (from your GameManager)
|
||||||
return {
|
return {
|
||||||
gameMap: map,
|
bots: 400,
|
||||||
maxPlayers: config.lobbyMaxPlayers(map, mode, playerTeams),
|
|
||||||
gameType: GameType.Public,
|
|
||||||
difficulty: Difficulty.Medium,
|
difficulty: Difficulty.Medium,
|
||||||
|
disableNPCs: mode === GameMode.Team,
|
||||||
|
disabledUnits: [],
|
||||||
|
gameMap: map,
|
||||||
|
gameMode: mode,
|
||||||
|
gameType: GameType.Public,
|
||||||
infiniteGold: false,
|
infiniteGold: false,
|
||||||
infiniteTroops: false,
|
infiniteTroops: false,
|
||||||
instantBuild: false,
|
instantBuild: false,
|
||||||
disableNPCs: mode === GameMode.Team,
|
maxPlayers: config.lobbyMaxPlayers(map, mode, playerTeams),
|
||||||
gameMode: mode,
|
|
||||||
playerTeams,
|
playerTeams,
|
||||||
bots: 400,
|
|
||||||
disabledUnits: [],
|
|
||||||
} satisfies GameConfig;
|
} satisfies GameConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ app.use(express.json());
|
|||||||
app.set("trust proxy", 3);
|
app.set("trust proxy", 3);
|
||||||
app.use(
|
app.use(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 1000, // 1 second
|
|
||||||
max: 20, // 20 requests per IP per second
|
max: 20, // 20 requests per IP per second
|
||||||
|
windowMs: 1000, // 1 second
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -180,10 +180,10 @@ app.post(
|
|||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`http://localhost:${config.workerPort(gameID)}/api/kick_player/${gameID}/${clientID}`,
|
`http://localhost:${config.workerPort(gameID)}/api/kick_player/${gameID}/${clientID}`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
|
||||||
headers: {
|
headers: {
|
||||||
[config.adminHeader()]: config.adminToken(),
|
[config.adminHeader()]: config.adminToken(),
|
||||||
},
|
},
|
||||||
|
method: "POST",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -232,10 +232,10 @@ async function fetchLobbies(): Promise<number> {
|
|||||||
.filter((result) => result !== null)
|
.filter((result) => result !== null)
|
||||||
.map((gi: GameInfo) => {
|
.map((gi: GameInfo) => {
|
||||||
return {
|
return {
|
||||||
gameID: gi.gameID,
|
|
||||||
numClients: gi?.clients?.length ?? 0,
|
|
||||||
gameConfig: gi.gameConfig,
|
gameConfig: gi.gameConfig,
|
||||||
|
gameID: gi.gameID,
|
||||||
msUntilStart: (gi.msUntilStart ?? Date.now()) - Date.now(),
|
msUntilStart: (gi.msUntilStart ?? Date.now()) - Date.now(),
|
||||||
|
numClients: gi?.clients?.length ?? 0,
|
||||||
} as GameInfo;
|
} as GameInfo;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -283,12 +283,12 @@ async function schedulePublicGame(playlist: MapPlaylist) {
|
|||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`http://localhost:${config.workerPort(gameID)}/api/create_game/${gameID}`,
|
`http://localhost:${config.workerPort(gameID)}/api/create_game/${gameID}`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
body: JSON.stringify(playlist.gameConfig()),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
[config.adminHeader()]: config.adminToken(),
|
[config.adminHeader()]: config.adminToken(),
|
||||||
},
|
},
|
||||||
body: JSON.stringify(playlist.gameConfig()),
|
method: "POST",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export function getOtelResource() {
|
|||||||
export function getPromLabels() {
|
export function getPromLabels() {
|
||||||
return {
|
return {
|
||||||
"service.instance.id": process.env.HOSTNAME,
|
"service.instance.id": process.env.HOSTNAME,
|
||||||
|
/* eslint-disable sort-keys */
|
||||||
"openfront.environment": config.env(),
|
"openfront.environment": config.env(),
|
||||||
"openfront.host": process.env.HOST,
|
"openfront.host": process.env.HOST,
|
||||||
"openfront.domain": process.env.DOMAIN,
|
"openfront.domain": process.env.DOMAIN,
|
||||||
@@ -25,5 +26,6 @@ export function getPromLabels() {
|
|||||||
"openfront.component": process.env.WORKER_ID
|
"openfront.component": process.env.WORKER_ID
|
||||||
? "Worker " + process.env.WORKER_ID
|
? "Worker " + process.env.WORKER_ID
|
||||||
: "Master",
|
: "Master",
|
||||||
|
/* eslint-enable sort-keys */
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ async function setupTunnels() {
|
|||||||
|
|
||||||
if (!(await cloudflare.configAlreadyExists())) {
|
if (!(await cloudflare.configAlreadyExists())) {
|
||||||
await cloudflare.createTunnel({
|
await cloudflare.createTunnel({
|
||||||
subdomain: config.subdomain(),
|
|
||||||
domain: config.domain(),
|
domain: config.domain(),
|
||||||
|
subdomain: config.subdomain(),
|
||||||
subdomainToService: domainToService,
|
subdomainToService: domainToService,
|
||||||
} as TunnelConfig);
|
} as TunnelConfig);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ export async function startWorker() {
|
|||||||
app.use(express.static(path.join(__dirname, "../../out")));
|
app.use(express.static(path.join(__dirname, "../../out")));
|
||||||
app.use(
|
app.use(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 1000, // 1 second
|
|
||||||
max: 20, // 20 requests per IP per second
|
max: 20, // 20 requests per IP per second
|
||||||
|
windowMs: 1000, // 1 second
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -232,9 +232,9 @@ export async function startWorker() {
|
|||||||
|
|
||||||
if (!gameRecord) {
|
if (!gameRecord) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
|
||||||
error: "Game not found",
|
error: "Game not found",
|
||||||
exists: false,
|
exists: false,
|
||||||
|
success: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,20 +246,20 @@ export async function startWorker() {
|
|||||||
`git commit mismatch for game ${req.params.id}, expected ${config.gitCommit()}, got ${gameRecord.gitCommit}`,
|
`git commit mismatch for game ${req.params.id}, expected ${config.gitCommit()}, got ${gameRecord.gitCommit}`,
|
||||||
);
|
);
|
||||||
return res.status(409).json({
|
return res.status(409).json({
|
||||||
success: false,
|
details: {
|
||||||
|
actualCommit: gameRecord.gitCommit,
|
||||||
|
expectedCommit: config.gitCommit(),
|
||||||
|
},
|
||||||
error: "Version mismatch",
|
error: "Version mismatch",
|
||||||
exists: true,
|
exists: true,
|
||||||
details: {
|
success: false,
|
||||||
expectedCommit: config.gitCommit(),
|
|
||||||
actualCommit: gameRecord.gitCommit,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
success: true,
|
|
||||||
exists: true,
|
exists: true,
|
||||||
gameRecord: gameRecord,
|
gameRecord: gameRecord,
|
||||||
|
success: true,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -324,8 +324,8 @@ export async function startWorker() {
|
|||||||
log.warn("Error parsing client message", error);
|
log.warn("Error parsing client message", error);
|
||||||
ws.send(
|
ws.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: "error",
|
|
||||||
error: error.toString(),
|
error: error.toString(),
|
||||||
|
type: "error",
|
||||||
} satisfies ServerErrorMessage),
|
} satisfies ServerErrorMessage),
|
||||||
);
|
);
|
||||||
ws.close(1002, "ClientJoinMessageSchema");
|
ws.close(1002, "ClientJoinMessageSchema");
|
||||||
|
|||||||
@@ -25,20 +25,20 @@ export function initWorkerMetrics(gameManager: GameManager): void {
|
|||||||
|
|
||||||
// Create metrics exporter
|
// Create metrics exporter
|
||||||
const metricExporter = new OTLPMetricExporter({
|
const metricExporter = new OTLPMetricExporter({
|
||||||
url: `${config.otelEndpoint()}/v1/metrics`,
|
|
||||||
headers,
|
headers,
|
||||||
|
url: `${config.otelEndpoint()}/v1/metrics`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configure the metric reader
|
// Configure the metric reader
|
||||||
const metricReader = new PeriodicExportingMetricReader({
|
const metricReader = new PeriodicExportingMetricReader({
|
||||||
exporter: metricExporter,
|
|
||||||
exportIntervalMillis: 15000, // Export metrics every 15 seconds
|
exportIntervalMillis: 15000, // Export metrics every 15 seconds
|
||||||
|
exporter: metricExporter,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a meter provider
|
// Create a meter provider
|
||||||
const meterProvider = new MeterProvider({
|
const meterProvider = new MeterProvider({
|
||||||
resource,
|
|
||||||
readers: [metricReader],
|
readers: [metricReader],
|
||||||
|
resource,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get meter for creating metrics
|
// Get meter for creating metrics
|
||||||
|
|||||||
+3
-2
@@ -21,6 +21,7 @@ export async function verifyClientToken(
|
|||||||
config: ServerConfig,
|
config: ServerConfig,
|
||||||
): Promise<TokenVerificationResult> {
|
): Promise<TokenVerificationResult> {
|
||||||
if (PersistentIdSchema.safeParse(token).success) {
|
if (PersistentIdSchema.safeParse(token).success) {
|
||||||
|
// eslint-disable-next-line sort-keys
|
||||||
return { persistentId: token, claims: null };
|
return { persistentId: token, claims: null };
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -29,8 +30,8 @@ export async function verifyClientToken(
|
|||||||
const key = await config.jwkPublicKey();
|
const key = await config.jwkPublicKey();
|
||||||
const { payload, protectedHeader } = await jwtVerify(token, key, {
|
const { payload, protectedHeader } = await jwtVerify(token, key, {
|
||||||
algorithms: ["EdDSA"],
|
algorithms: ["EdDSA"],
|
||||||
issuer,
|
|
||||||
audience,
|
audience,
|
||||||
|
issuer,
|
||||||
});
|
});
|
||||||
const result = TokenPayloadSchema.safeParse(payload);
|
const result = TokenPayloadSchema.safeParse(payload);
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
@@ -40,7 +41,7 @@ export async function verifyClientToken(
|
|||||||
}
|
}
|
||||||
const claims = result.data;
|
const claims = result.data;
|
||||||
const persistentId = claims.sub;
|
const persistentId = claims.sub;
|
||||||
return { persistentId, claims };
|
return { claims, persistentId };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -57,12 +57,12 @@ export async function setup(
|
|||||||
// Configure the game
|
// Configure the game
|
||||||
const serverConfig = new TestServerConfig();
|
const serverConfig = new TestServerConfig();
|
||||||
const gameConfig: GameConfig = {
|
const gameConfig: GameConfig = {
|
||||||
|
bots: 0,
|
||||||
|
difficulty: Difficulty.Medium,
|
||||||
|
disableNPCs: false,
|
||||||
gameMap: GameMapType.Asia,
|
gameMap: GameMapType.Asia,
|
||||||
gameMode: GameMode.FFA,
|
gameMode: GameMode.FFA,
|
||||||
gameType: GameType.Singleplayer,
|
gameType: GameType.Singleplayer,
|
||||||
difficulty: Difficulty.Medium,
|
|
||||||
disableNPCs: false,
|
|
||||||
bots: 0,
|
|
||||||
infiniteGold: false,
|
infiniteGold: false,
|
||||||
infiniteTroops: false,
|
infiniteTroops: false,
|
||||||
instantBuild: false,
|
instantBuild: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user