功能修改
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import type { GameClientBridgeJsonObject, GameClientBridgeSnapshotResponse } from "../api/types";
|
||||
import type {
|
||||
ScumCompanionHealthView,
|
||||
ScumFlagView,
|
||||
ScumOperationsSnapshotView,
|
||||
ScumPlayerView,
|
||||
ScumSessionView,
|
||||
ScumSnapshotCollection,
|
||||
ScumSquadView,
|
||||
ScumVehicleView
|
||||
} from "../contracts/scumOperations";
|
||||
import { safeDiagnosticText } from "../utils/safeDiagnosticText";
|
||||
|
||||
const maxVisibleItems = 50;
|
||||
|
||||
export function projectScumOperationsSnapshots(snapshots: GameClientBridgeSnapshotResponse[]): ScumOperationsSnapshotView {
|
||||
return {
|
||||
health: projectHealth(latestPayload(snapshots, "companion.health")),
|
||||
sessions: projectCollection(latestSnapshot(snapshots, "online.sessions"), "sessions", projectSession),
|
||||
players: projectCollection(latestSnapshot(snapshots, "players"), "players", projectPlayer),
|
||||
squads: projectCollection(latestSnapshot(snapshots, "squads"), "squads", projectSquad),
|
||||
vehicles: projectCollection(latestSnapshot(snapshots, "vehicles"), "vehicles", projectVehicle),
|
||||
flags: projectCollection(latestSnapshot(snapshots, "flags"), "flags", projectFlag)
|
||||
};
|
||||
}
|
||||
|
||||
function latestSnapshot(snapshots: GameClientBridgeSnapshotResponse[], type: string): GameClientBridgeSnapshotResponse | undefined {
|
||||
return snapshots
|
||||
.filter((snapshot) => snapshot.type === type)
|
||||
.sort((left, right) => right.observedAt.localeCompare(left.observedAt) || right.sequence - left.sequence)[0];
|
||||
}
|
||||
|
||||
function latestPayload(snapshots: GameClientBridgeSnapshotResponse[], type: string): GameClientBridgeJsonObject | undefined {
|
||||
return latestSnapshot(snapshots, type)?.payload;
|
||||
}
|
||||
|
||||
function projectHealth(payload: GameClientBridgeJsonObject | undefined): ScumCompanionHealthView | undefined {
|
||||
if (!payload) return undefined;
|
||||
const status = readText(payload.status, 20);
|
||||
return {
|
||||
status: status === "online" || status === "degraded" || status === "offline" ? status : "unknown",
|
||||
version: readOptionalText(payload.version, 40),
|
||||
observedAt: readOptionalText(payload.observedAt, 64),
|
||||
latencyMs: readOptionalNumber(payload.latencyMs, 0, 30000),
|
||||
capabilities: readArray(payload.capabilities).map((value) => readText(value, 80)).filter(Boolean).slice(0, 16)
|
||||
};
|
||||
}
|
||||
|
||||
function projectCollection<T>(
|
||||
snapshot: GameClientBridgeSnapshotResponse | undefined,
|
||||
key: string,
|
||||
project: (value: unknown) => T | null
|
||||
): ScumSnapshotCollection<T> {
|
||||
const values = snapshot ? readArray(snapshot.payload[key]) : [];
|
||||
return {
|
||||
observedAt: snapshot?.observedAt,
|
||||
total: values.length,
|
||||
items: values.slice(0, maxVisibleItems).map(project).filter((value): value is T => value !== null)
|
||||
};
|
||||
}
|
||||
|
||||
function projectSession(value: unknown): ScumSessionView | null {
|
||||
const record = readObject(value);
|
||||
const sessionId = readText(record?.sessionId, 120);
|
||||
const playerName = readText(record?.playerName, 80);
|
||||
return sessionId && playerName ? { sessionId, playerName, startedAt: readOptionalText(record?.startedAt, 64) } : null;
|
||||
}
|
||||
|
||||
function projectPlayer(value: unknown): ScumPlayerView | null {
|
||||
const record = readObject(value);
|
||||
const playerId = readText(record?.playerId, 96);
|
||||
const playerName = readText(record?.playerName, 80);
|
||||
const status = readText(record?.status, 20);
|
||||
return playerId && playerName && status ? {
|
||||
playerId,
|
||||
playerName,
|
||||
status,
|
||||
squadId: readOptionalText(record?.squadId, 96),
|
||||
pingMs: readOptionalNumber(record?.pingMs, 0, 10000),
|
||||
lastSeenAt: readOptionalText(record?.lastSeenAt, 64)
|
||||
} : null;
|
||||
}
|
||||
|
||||
function projectSquad(value: unknown): ScumSquadView | null {
|
||||
const record = readObject(value);
|
||||
const squadId = readText(record?.squadId, 96);
|
||||
const name = readText(record?.name, 80);
|
||||
const memberCount = readOptionalNumber(record?.memberCount, 0, 64);
|
||||
return squadId && name && memberCount !== undefined ? {
|
||||
squadId,
|
||||
name,
|
||||
memberCount,
|
||||
leaderPlayerId: readOptionalText(record?.leaderPlayerId, 96),
|
||||
lastActiveAt: readOptionalText(record?.lastActiveAt, 64)
|
||||
} : null;
|
||||
}
|
||||
|
||||
function projectVehicle(value: unknown): ScumVehicleView | null {
|
||||
const record = readObject(value);
|
||||
const vehicleId = readText(record?.vehicleId, 96);
|
||||
const vehicleType = readText(record?.vehicleType, 80);
|
||||
const status = readText(record?.status, 20);
|
||||
return vehicleId && vehicleType && status ? {
|
||||
vehicleId,
|
||||
vehicleType,
|
||||
status,
|
||||
ownerPlayerId: readOptionalText(record?.ownerPlayerId, 96),
|
||||
squadId: readOptionalText(record?.squadId, 96),
|
||||
fuelPercent: readOptionalNumber(record?.fuelPercent, 0, 100),
|
||||
healthPercent: readOptionalNumber(record?.healthPercent, 0, 100),
|
||||
lastSeenAt: readOptionalText(record?.lastSeenAt, 64)
|
||||
} : null;
|
||||
}
|
||||
|
||||
function projectFlag(value: unknown): ScumFlagView | null {
|
||||
const record = readObject(value);
|
||||
const flagId = readText(record?.flagId, 96);
|
||||
const status = readText(record?.status, 20);
|
||||
return flagId && status ? {
|
||||
flagId,
|
||||
status,
|
||||
ownerPlayerId: readOptionalText(record?.ownerPlayerId, 96),
|
||||
squadId: readOptionalText(record?.squadId, 96),
|
||||
radiusMeters: readOptionalNumber(record?.radiusMeters, 0, 5000),
|
||||
lastUpdatedAt: readOptionalText(record?.lastUpdatedAt, 64)
|
||||
} : null;
|
||||
}
|
||||
|
||||
function readObject(value: unknown): Record<string, unknown> | undefined {
|
||||
return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;
|
||||
}
|
||||
|
||||
function readArray(value: unknown): unknown[] {
|
||||
return Array.isArray(value) ? value : [];
|
||||
}
|
||||
|
||||
function readText(value: unknown, maxLength: number): string {
|
||||
if (typeof value !== "string") return "";
|
||||
return (safeDiagnosticText(value.slice(0, maxLength), "") ?? "").trim();
|
||||
}
|
||||
|
||||
function readOptionalText(value: unknown, maxLength: number): string | undefined {
|
||||
return readText(value, maxLength) || undefined;
|
||||
}
|
||||
|
||||
function readOptionalNumber(value: unknown, minimum: number, maximum: number): number | undefined {
|
||||
return typeof value === "number" && Number.isFinite(value) && value >= minimum && value <= maximum ? value : undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user