Track real player sessions and tower builds

This commit is contained in:
Emporex committed 2026-10-11 22:02:51 +02:00
1 parent 8777f28595
commit 1294000348
8 files changed
+213 -8

No files matched your search

+33
View File
@@ -231,6 +231,36 @@ assert.strictEqual(privateStats.platforms.android, 1);
assert.strictEqual(privateStats.versions["0.1.0-alpha.2"], 2);
assert.strictEqual(privateStats.daily_active[context.utcDay(Date.now())], 2);
assert.throws(() => context.rpcPrivateStats({ userId: "pilot-a" }, logger, metricNk, "{}"), /server-to-server/);
const metricSource = "guest_0123456789abcdef0123456789abcdef";
assert.deepStrictEqual(JSON.parse(context.rpcRecordPlayerEvent({ userId: "pilot-metric-a" }, logger, metricNk,
JSON.stringify({ event: "play_start", source: metricSource, sequence: 1, at: Date.now() }))), { accepted: true, duplicate: false });
assert.deepStrictEqual(JSON.parse(context.rpcRecordPlayerEvent({ userId: "pilot-metric-a" }, logger, metricNk,
JSON.stringify({ event: "tower_build", tower_type: 1, source: metricSource, sequence: 2, at: Date.now() }))), { accepted: true, duplicate: false });
assert.deepStrictEqual(JSON.parse(context.rpcRecordPlayerEvent({ userId: "pilot-metric-a" }, logger, metricNk,
JSON.stringify({ event: "tower_build", tower_type: 1, source: metricSource, sequence: 2, at: Date.now() }))), { accepted: true, duplicate: true });
assert.strictEqual(JSON.parse(context.rpcRecordPlayerEvent({ userId: "pilot-metric-a" }, logger, metricNk,
JSON.stringify({ event: "tower_build", tower_type: 7, source: metricSource, sequence: 3 }))).accepted, false, "Admin towers must not enter real tower statistics.");
context.rpcRecordActivity({ userId: "pilot-metric-a" }, logger, metricNk, JSON.stringify({ platform: "Windows", version: "0.1.0-alpha.16" }));
const metricActivity = metricStorage.get("player_activity:pilot-metric-a:summary").value;
assert.strictEqual(metricActivity.tower_builds[1], 1, "Repeated telemetry must not double-count towers.");
assert.ok(metricActivity.last_played > 0, "Game sessions should track when the player last played.");
assert.strictEqual(context.isInternalTestUsername("godot_a_2323883"), true);
assert.strictEqual(context.isInternalTestUsername("social_b_2289087"), true);
assert.strictEqual(context.isInternalTestUsername("Emporex"), false);
const directoryNk = {
sqlQuery(sql) {
if (sql.includes("count(*) AS total")) return [{ total: 7, active_today: 3 }];
if (sql.includes("WHERE id=$1::uuid")) return [{ id: "11111111-1111-1111-1111-111111111111", username: "pilot_real", display_name: "Real Pilot", registered_at: 1000 }];
return [{ id: "11111111-1111-1111-1111-111111111111", username: "pilot_real", display_name: "Real Pilot", registered_at: 1000, last_seen: 2000, last_played: 3000, tower_build_total: 2 }];
},
storageRead() { return [{ value: { tower_builds: [0, 2, 0, 0, 0, 0, 0], tower_build_total: 2, last_seen: 2000, last_played: 3000 } }]; }
};
assert.strictEqual(JSON.parse(context.rpcListRegisteredPlayers({}, logger, directoryNk, "{}")).total, 7);
assert.strictEqual(JSON.parse(context.rpcGetRegisteredPlayer({}, logger, directoryNk,
JSON.stringify({ user_id: "11111111-1111-1111-1111-111111111111" }))).player.tower_builds[1], 2);
assert.throws(() => context.rpcListRegisteredPlayers({ userId: "pilot-a" }, logger, directoryNk, "{}"), /server-to-server/);
nk.storageRead = metricNk.storageRead.bind(metricNk);
nk.storageWrite = metricNk.storageWrite.bind(metricNk);
const promoStorage = new Map();
let promoVersion = 0;
@@ -376,6 +406,8 @@ state = context.matchLoop({}, logger, nk, dispatcher, 5, state, [
assert.strictEqual(state.towers.length, 2);
assert.strictEqual(state.credits[0], 310);
assert.strictEqual(state.credits[1], 250);
assert.strictEqual(metricStorage.get("player_activity:pilot-a:summary").value.tower_builds[0], 1, "Accepted online tower builds should be counted on the server.");
assert.strictEqual(metricStorage.get("player_activity:pilot-b:summary").value.tower_builds[1], 1);
state = context.matchLoop({}, logger, nk, dispatcher, 51, state,
[message(presenceA, 10, { tower_type: 7, x: 2200, y: 600 }),
@@ -402,6 +434,7 @@ assert.strictEqual(state.credits[0], 310, "Authorized test energy removal should
state = context.matchLoop({}, logger, nk, dispatcher, 56, state,
[message(presenceA, 10, { tower_type: 7, x: 2200, y: 600 })]).state;
assert.ok(state.towers.some(tower => tower.type === 7 && tower.owner === 0), "Authorized player may build the admin turret anywhere on the co-op map.");
assert.strictEqual(metricStorage.get("player_activity:pilot-a:summary").value.tower_build_total, 1, "Admin towers are excluded from player statistics.");
const adminCombatState = {
balance: state.balance,
towers: [{ id: 99, type: 7, x: 2200, y: 600, owner: 0, level: 1, cooldown: 0, angle: 0 }],