mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-26 09:24:36 +00:00
Combine analytics and game types (#839)
## Description: Combine analytics and game types. Simplify and remove redundant player information. - Remove ip address. - Add playerID. - Combine redundant player tables. - Move game metadata in to GameEndInfo type, an extension of GameStartInfo ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors --------- Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
This commit is contained in:
+18
-16
@@ -1,6 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import quickChatData from "../../resources/QuickChat.json" with { type: "json" };
|
||||
import { PlayerStatsSchema } from "./ArchiveSchemas";
|
||||
import {
|
||||
AllPlayers,
|
||||
Difficulty,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
PlayerType,
|
||||
UnitType,
|
||||
} from "./game/Game";
|
||||
import { PlayerStatsSchema } from "./StatsSchemas";
|
||||
import { flattenedEmojiTable } from "./Util";
|
||||
|
||||
export type GameID = string;
|
||||
@@ -88,9 +88,6 @@ export type ClientJoinMessage = z.infer<typeof ClientJoinMessageSchema>;
|
||||
export type ClientLogMessage = z.infer<typeof ClientLogMessageSchema>;
|
||||
export type ClientHashMessage = z.infer<typeof ClientHashSchema>;
|
||||
|
||||
export type PlayerRecord = z.infer<typeof PlayerRecordSchema>;
|
||||
export type GameRecord = z.infer<typeof GameRecordSchema>;
|
||||
|
||||
export type AllPlayersStats = z.infer<typeof AllPlayersStatsSchema>;
|
||||
export type Player = z.infer<typeof PlayerSchema>;
|
||||
export type GameStartInfo = z.infer<typeof GameStartInfoSchema>;
|
||||
@@ -442,26 +439,31 @@ export const ClientMessageSchema = z.union([
|
||||
ClientHashSchema,
|
||||
]);
|
||||
|
||||
export const PlayerRecordSchema = z.object({
|
||||
clientID: ID,
|
||||
username: SafeString,
|
||||
ip: SafeString.nullable(), // WARNING: PII
|
||||
export const PlayerRecordSchema = PlayerSchema.extend({
|
||||
persistentID: PersistentIdSchema, // WARNING: PII
|
||||
stats: PlayerStatsSchema,
|
||||
});
|
||||
export type PlayerRecord = z.infer<typeof PlayerRecordSchema>;
|
||||
|
||||
export const GameRecordSchema = z.object({
|
||||
id: ID,
|
||||
gameStartInfo: GameStartInfoSchema,
|
||||
export const GameEndInfoSchema = GameStartInfoSchema.extend({
|
||||
players: z.array(PlayerRecordSchema),
|
||||
startTimestampMS: z.number(),
|
||||
endTimestampMS: z.number(),
|
||||
durationSeconds: z.number(),
|
||||
date: SafeString,
|
||||
start: z.number(),
|
||||
end: z.number(),
|
||||
duration: z.number().nonnegative(),
|
||||
num_turns: z.number(),
|
||||
turns: z.array(TurnSchema),
|
||||
winner: z.union([ID, SafeString]).nullable().optional(),
|
||||
winnerType: z.enum(["player", "team"]).nullable().optional(),
|
||||
});
|
||||
export type GameEndInfo = z.infer<typeof GameEndInfoSchema>;
|
||||
|
||||
export const AnalyticsRecordSchema = z.object({
|
||||
info: GameEndInfoSchema,
|
||||
version: z.literal("v0.0.2"),
|
||||
gitCommit: z.string(),
|
||||
});
|
||||
export type AnalyticsRecord = z.infer<typeof AnalyticsRecordSchema>;
|
||||
|
||||
export const GameRecordSchema = AnalyticsRecordSchema.extend({
|
||||
turns: z.array(TurnSchema),
|
||||
});
|
||||
export type GameRecord = z.infer<typeof GameRecordSchema>;
|
||||
|
||||
+25
-39
@@ -5,9 +5,9 @@ import { Cell, Team, Unit } from "./game/Game";
|
||||
import { GameMap, TileRef } from "./game/GameMap";
|
||||
import {
|
||||
ClientID,
|
||||
GameConfig,
|
||||
GameID,
|
||||
GameRecord,
|
||||
GameStartInfo,
|
||||
PlayerRecord,
|
||||
Turn,
|
||||
} from "./Schemas";
|
||||
@@ -184,53 +184,39 @@ export function onlyImages(html: string) {
|
||||
}
|
||||
|
||||
export function createGameRecord(
|
||||
id: GameID,
|
||||
gameStartInfo: GameStartInfo,
|
||||
gameID: GameID,
|
||||
config: GameConfig,
|
||||
// username does not need to be set.
|
||||
players: PlayerRecord[],
|
||||
turns: Turn[],
|
||||
startTimestampMS: number,
|
||||
endTimestampMS: number,
|
||||
allTurns: Turn[],
|
||||
start: number,
|
||||
end: number,
|
||||
winner: ClientID | Team | null,
|
||||
winnerType: "player" | "team" | null,
|
||||
): GameRecord {
|
||||
const durationSeconds = Math.floor(
|
||||
(endTimestampMS - startTimestampMS) / 1000,
|
||||
);
|
||||
const date = new Date().toISOString().split("T")[0];
|
||||
const duration = Math.floor((end - start) / 1000);
|
||||
const version = "v0.0.2";
|
||||
const gitCommit = "";
|
||||
const num_turns = allTurns.length;
|
||||
const turns = allTurns.filter(
|
||||
(t) => t.intents.length !== 0 || t.hash !== undefined,
|
||||
);
|
||||
const record: GameRecord = {
|
||||
gitCommit,
|
||||
id,
|
||||
gameStartInfo,
|
||||
players,
|
||||
startTimestampMS,
|
||||
endTimestampMS,
|
||||
durationSeconds,
|
||||
date,
|
||||
num_turns: 0,
|
||||
turns: [],
|
||||
info: {
|
||||
gameID,
|
||||
config,
|
||||
players,
|
||||
start,
|
||||
end,
|
||||
duration,
|
||||
num_turns,
|
||||
winner,
|
||||
winnerType,
|
||||
},
|
||||
version,
|
||||
winner,
|
||||
winnerType,
|
||||
gitCommit,
|
||||
turns,
|
||||
};
|
||||
|
||||
for (const turn of turns) {
|
||||
if (turn.intents.length !== 0 || turn.hash !== undefined) {
|
||||
record.turns.push(turn);
|
||||
for (const intent of turn.intents) {
|
||||
if (intent.type === "spawn") {
|
||||
for (const playerRecord of players) {
|
||||
if (playerRecord.clientID === intent.clientID) {
|
||||
playerRecord.username = intent.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
record.num_turns = turns.length;
|
||||
return record;
|
||||
}
|
||||
|
||||
@@ -249,7 +235,7 @@ export function decompressGameRecord(gameRecord: GameRecord) {
|
||||
lastTurnNum = turn.turnNumber;
|
||||
}
|
||||
const turnLength = turns.length;
|
||||
for (let i = turnLength; i < gameRecord.num_turns; i++) {
|
||||
for (let i = turnLength; i < gameRecord.info.num_turns; i++) {
|
||||
turns.push({
|
||||
turnNumber: i,
|
||||
intents: [],
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { NukeType } from "../ArchiveSchemas";
|
||||
import { consolex } from "../Consolex";
|
||||
import {
|
||||
Execution,
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
import { TileRef } from "../game/GameMap";
|
||||
import { ParabolaPathFinder } from "../pathfinding/PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { NukeType } from "../StatsSchemas";
|
||||
|
||||
export class NukeExecution implements Execution {
|
||||
private active = true;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { NukeType } from "../ArchiveSchemas";
|
||||
import {
|
||||
Execution,
|
||||
Game,
|
||||
@@ -10,6 +9,7 @@ import {
|
||||
import { TileRef } from "../game/GameMap";
|
||||
import { AirPathFinder } from "../pathfinding/PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { NukeType } from "../StatsSchemas";
|
||||
|
||||
export class SAMMissileExecution implements Execution {
|
||||
private active = true;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NukeType, OtherUnitType, PlayerStats } from "../ArchiveSchemas";
|
||||
import { AllPlayersStats } from "../Schemas";
|
||||
import { NukeType, OtherUnitType, PlayerStats } from "../StatsSchemas";
|
||||
import { Player, TerraNullius } from "./Game";
|
||||
|
||||
export interface Stats {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AllPlayersStats } from "../Schemas";
|
||||
import {
|
||||
ATTACK_INDEX_CANCEL,
|
||||
ATTACK_INDEX_RECV,
|
||||
@@ -23,8 +24,7 @@ import {
|
||||
PlayerStats,
|
||||
unitTypeToBombUnit,
|
||||
unitTypeToOtherUnit,
|
||||
} from "../ArchiveSchemas";
|
||||
import { AllPlayersStats } from "../Schemas";
|
||||
} from "../StatsSchemas";
|
||||
import { Player, TerraNullius } from "./Game";
|
||||
import { Stats } from "./Stats";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user