Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ export class AppGameLaunchedEvent implements MixpanelEvent {
/** Fields on the app_game_exited event. */
export interface GameExitedProps {
game_id: number | null;
launch_method: GameLaunchMethod;
enabled_mod_count: number;
launch_session_id: string;
duration_ms: number;
// Process exit code, when Vortex launched the process itself; null for store launches.
Expand All @@ -137,7 +139,8 @@ export interface GameExitedProps {

/**
* Sent when a launched game/tool process exits. `duration_ms` is the time since its launch;
* `launch_session_id` matches the app_game_launched it pairs with.
* `launch_method`, `enabled_mod_count` and `launch_session_id` match the app_game_launched it
* pairs with (captured at launch, so they reflect state at launch time).
*/
export class AppGameExitedEvent implements MixpanelEvent {
readonly eventName = "app_game_exited";
Expand Down
6 changes: 4 additions & 2 deletions src/renderer/src/util/gameLaunchAnalytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,16 @@ describe("game launch analytics", () => {
const h = harness();
const info = makeStarterInfo({ exePath: "C:/g/exited.exe" });
emitGameLaunched(h.api, info);
const sessionId = h.events.find((e) => e.eventName === "app_game_launched")?.properties
.launch_session_id;
const launched = h.events.find((e) => e.eventName === "app_game_launched");
const sessionId = launched?.properties.launch_session_id;

emitExitsForStoppedTools(h.api, { [makeExeId(info.exePath)]: { started: 0 } }, {});

const exited = h.events.find((e) => e.eventName === "app_game_exited");
expect(exited).toBeDefined();
expect(exited?.properties.launch_session_id).toBe(sessionId);
expect(exited?.properties.launch_method).toBe("direct_exe");
expect(exited?.properties.enabled_mod_count).toBe(launched?.properties.enabled_mod_count);
expect(typeof exited?.properties.duration_ms).toBe("number");
expect(exited?.properties.exit_code).toBeNull();
});
Expand Down
14 changes: 11 additions & 3 deletions src/renderer/src/util/gameLaunchAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import type { IStarterInfo } from "./StarterInfo";
interface ILaunchRecord {
sessionId: string;
gameId: number | null;
launchMethod: GameLaunchMethod;
enabledModCount: number;
launchTime: number;
// Set from runExecutable's exit for Vortex-spawned launches; stays null for store launches.
exitCode: number | null;
Expand Down Expand Up @@ -65,6 +67,8 @@ export function emitExitsForStoppedTools(
"analytics-track-mixpanel-event",
new AppGameExitedEvent({
game_id: record.gameId,
launch_method: record.launchMethod,
enabled_mod_count: record.enabledModCount,
launch_session_id: record.sessionId,
duration_ms: Date.now() - record.launchTime,
exit_code: record.exitCode,
Expand Down Expand Up @@ -92,19 +96,23 @@ export function emitGameLaunched(api: IExtensionApi, info: IStarterInfo): void {
const state: IState = api.getState();
const gameId = numericNexusGameId(info.gameId);
const sessionId = shortid();
const method = launchMethod(info);
const profileId = lastActiveProfileForGame(state, info.gameId);
const enabledModCount = enabledModCountForProfile(state, profileId);
pendingLaunches.set(makeExeId(info.exePath), {
sessionId,
gameId,
launchMethod: method,
enabledModCount,
launchTime: Date.now(),
exitCode: null,
});
const profileId = lastActiveProfileForGame(state, info.gameId);
api.events.emit(
"analytics-track-mixpanel-event",
new AppGameLaunchedEvent({
game_id: gameId,
launch_method: launchMethod(info),
enabled_mod_count: enabledModCountForProfile(state, profileId),
launch_method: method,
enabled_mod_count: enabledModCount,
launch_session_id: sessionId,
}),
);
Expand Down