Remove SCUM sqlite user projections

This commit is contained in:
npc0-hue
2026-09-14 13:41:44 +08:00
parent b1f102bafb
commit ecdca8e28b
76 changed files with 2264 additions and 1558 deletions
+9 -1
View File
@@ -397,6 +397,12 @@ describe("PlatformApiClient AI providers", () => {
if (url.endsWith("/api/v1/server-instances/server-1/administrators/user-2") && init?.method === "DELETE") {
return jsonResponse({ ...server, adminUserIds: [] });
}
if (url.endsWith("/api/v1/server-instances/server-1/scum/users?limit=10&online=true")) {
return jsonResponse({ items: [{ id: "scum-user-1", steamId: "steam-1", online: true }], count: 1 });
}
if (url.endsWith("/api/v1/server-instances/server-1/scum/vehicle-locks?limit=10&gameVehicleId=veh-1")) {
return jsonResponse({ items: [{ id: "lock-1", gameVehicleId: "veh-1", scumUserId: "scum-user-1" }], count: 1 });
}
if (url.endsWith("/api/v1/server-instances/server-1/runtime/actions")) {
return jsonResponse(runtimeActions);
@@ -602,6 +608,8 @@ describe("PlatformApiClient AI providers", () => {
await expect(client.listServerAdministratorCandidates(server.id)).resolves.toMatchObject({ count: 1 });
await expect(client.addServerAdministrator(server.id, { userId: "user-2" })).resolves.toMatchObject({ adminUserIds: ["user-admin-1", "user-2"] });
await expect(client.removeServerAdministrator(server.id, "user-2")).resolves.toMatchObject({ adminUserIds: [] });
await expect(client.listScumUsers(server.id, { limit: 10, online: true })).resolves.toMatchObject({ items: [{ steamId: "steam-1" }], count: 1 });
await expect(client.listScumVehicleLocks(server.id, { limit: 10, gameVehicleId: "veh-1" })).resolves.toMatchObject({ items: [{ scumUserId: "scum-user-1" }], count: 1 });
const runtime = await client.getServerRuntimeActions(server.id);
expect(runtime.runStatus).toBe("online");
@@ -629,7 +637,7 @@ describe("PlatformApiClient AI providers", () => {
client.invokeAI({ requestId: "ai-1", serverInstanceId: server.id, purpose: "config.suggest", prompt: "Tune PVP safely", currentConfig: "server.name=Example Survival #1\n" })
).resolves.toMatchObject({ status: "ok", usage: { mocked: true }, configRecommendation: { diffSummary: "review required" } });
expect(fetchMock).toHaveBeenCalledTimes(47);
expect(fetchMock).toHaveBeenCalledTimes(49);
});
it("normalizes server file workspace null arrays from older platform responses", async () => {
+41
View File
@@ -87,6 +87,12 @@ import type {
ServerMemberListResponse,
ServerMemberRequest,
ServerMetricsListResponse,
ScumListRequest,
ScumUserListResponse,
ScumUserTrajectoryListResponse,
ScumVehicleLockListResponse,
ScumVehicleListResponse,
ScumVehicleTrajectoryListResponse,
SourceRCONCommandRequest,
SourceRCONCommandResponse,
MetricSampleListResponse,
@@ -511,6 +517,26 @@ export class PlatformApiClient {
return this.request<RemoteAdapterDeclarationListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/remote-adapters`);
}
async listScumUsers(serverInstanceId: string, request: ScumListRequest = {}): Promise<ScumUserListResponse> {
return this.request<ScumUserListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/scum/users${scumListQuery(request)}`);
}
async listScumUserTrajectories(serverInstanceId: string, request: ScumListRequest = {}): Promise<ScumUserTrajectoryListResponse> {
return this.request<ScumUserTrajectoryListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/scum/user-trajectories${scumListQuery(request)}`);
}
async listScumVehicles(serverInstanceId: string, request: ScumListRequest = {}): Promise<ScumVehicleListResponse> {
return this.request<ScumVehicleListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/scum/vehicles${scumListQuery(request)}`);
}
async listScumVehicleTrajectories(serverInstanceId: string, request: ScumListRequest = {}): Promise<ScumVehicleTrajectoryListResponse> {
return this.request<ScumVehicleTrajectoryListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/scum/vehicle-trajectories${scumListQuery(request)}`);
}
async listScumVehicleLocks(serverInstanceId: string, request: ScumListRequest = {}): Promise<ScumVehicleLockListResponse> {
return this.request<ScumVehicleLockListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/scum/vehicle-locks${scumListQuery(request)}`);
}
async listPluginData(serverInstanceId: string, collection: string, key?: string): Promise<{ items: Array<{ key: string; value: Record<string, unknown> }>; count: number }> {
const params = new URLSearchParams();
if (key) params.set("key", key);
@@ -931,6 +957,21 @@ function jobListQuery(serverInstanceId: string | undefined, filter: JobListFilte
return query ? `?${query}` : "";
}
function scumListQuery(request: ScumListRequest): string {
const params = new URLSearchParams();
if (request.limit !== undefined) params.set("limit", String(request.limit));
if (request.changedAfter) params.set("changedAfter", request.changedAfter);
if (request.after) params.set("after", request.after);
if (request.online !== undefined) params.set("online", String(request.online));
if (request.exists !== undefined) params.set("exists", String(request.exists));
if (request.steamId) params.set("steamId", request.steamId);
if (request.scumUserId) params.set("scumUserId", request.scumUserId);
if (request.gameVehicleId) params.set("gameVehicleId", request.gameVehicleId);
if (request.scumVehicleId) params.set("scumVehicleId", request.scumVehicleId);
const query = params.toString();
return query ? `?${query}` : "";
}
function serverFileListQuery(request: Partial<ServerFileListRequest>): string {
const params = new URLSearchParams();
if (request.directoryKey) params.set("directoryKey", request.directoryKey);
+5 -5
View File
@@ -83,14 +83,14 @@ const manifestDeclaration: GameClientBridgeManifestResponse = {
timeoutSeconds: 30,
maxPayloadBytes: 4096
}],
snapshots: [{ type: "scum.players", schemaVersion: "1", schemaRef: "schemas/bridge/snapshots/players.json", keepForSeconds: 3600, maxRecords: 24 }],
snapshots: [{ type: "game.players", schemaVersion: "1", schemaRef: "schemas/bridge/snapshots/players.json", keepForSeconds: 3600, maxRecords: 24 }],
queryTemplates: [{
key: "scum.player.search",
key: "game.player.search",
title: "Search players",
permission: "server.game-client.read",
engine: "sqlite",
transportKey: "scum-database",
targetKey: "scum-db",
transportKey: "sqlite-db",
targetKey: "game-db",
parameterSchemaRef: "schemas/bridge/queries/player-search.request.json",
resultSchemaRef: "schemas/bridge/queries/player-search.result.json",
maxRows: 50,
@@ -98,7 +98,7 @@ const manifestDeclaration: GameClientBridgeManifestResponse = {
}],
commandRetentionSeconds: 86400,
maxCommands: 1000,
pages: [{ pageKey: "operations", commandTypes: ["scum.diagnostic.ping"], snapshotTypes: ["scum.players"], queryTemplateKeys: ["scum.player.search"] }]
pages: [{ pageKey: "operations", commandTypes: ["scum.diagnostic.ping"], snapshotTypes: ["game.players"], queryTemplateKeys: ["game.player.search"] }]
};
const pluginBridgeProjection: Pick<GamePluginResponse, "gameClientBridge"> & Pick<MarketplacePluginResponse, "gameClientBridge"> = {
+126
View File
@@ -149,6 +149,132 @@ export interface GameClientBridgeCommandListResponse {
count: number;
}
export interface ScumListRequest {
limit?: number;
changedAfter?: string;
after?: string;
online?: boolean;
exists?: boolean;
steamId?: string;
scumUserId?: string;
gameVehicleId?: string;
scumVehicleId?: string;
}
export interface ScumUserResponse {
id: string;
serverInstanceId: string;
steamId: string;
gamePlayerId?: string;
displayName?: string;
squadId?: number;
registeredAt?: string;
lastLoginAt?: string;
lastSeenAt?: string;
lastActivityAt?: string;
lastLoginAgeSeconds?: number;
lastLoginIp?: string;
loginIp?: string;
online: boolean;
x?: number;
y?: number;
z?: number;
position?: { x: number; y: number; z: number };
bankBalance?: number;
normalBalance?: number;
goldBars?: number;
goldBalance?: number;
riddenVehicleId?: string;
gameVehicleId?: string;
source?: string;
createdAt?: string;
updatedAt?: string;
}
export interface ScumUserListResponse { items: ScumUserResponse[]; count: number; }
export interface ScumUserTrajectoryResponse {
id: string;
serverInstanceId: string;
scumUserId: string;
subjectType: "player";
subjectId: string;
steamId: string;
displayName?: string;
x: number;
y: number;
z: number;
riddenVehicleId?: string;
gameVehicleId?: string;
observedAt?: string;
sampledAt?: string;
source?: string;
createdAt?: string;
}
export interface ScumUserTrajectoryListResponse { items: ScumUserTrajectoryResponse[]; count: number; }
export interface ScumVehicleResponse {
id: string;
serverInstanceId: string;
vehicleId: string;
gameVehicleId: string;
entityId?: string;
vehicleClass?: string;
className?: string;
displayName?: string;
label?: string;
exists: boolean;
status?: string;
locked?: boolean;
x?: number;
y?: number;
z?: number;
position?: { x: number; y: number; z: number };
lastObservedAt?: string;
vehicleObservedAt?: string;
source?: string;
createdAt?: string;
updatedAt?: string;
}
export interface ScumVehicleListResponse { items: ScumVehicleResponse[]; count: number; }
export interface ScumVehicleTrajectoryResponse {
id: string;
serverInstanceId: string;
scumVehicleId: string;
subjectType: "vehicle";
subjectId: string;
vehicleId: string;
gameVehicleId: string;
vehicleClass?: string;
className?: string;
x: number;
y: number;
z: number;
observedAt?: string;
sampledAt?: string;
source?: string;
createdAt?: string;
}
export interface ScumVehicleTrajectoryListResponse { items: ScumVehicleTrajectoryResponse[]; count: number; }
export interface ScumVehicleLockResponse {
id: string;
serverInstanceId: string;
scumVehicleId: string;
gameVehicleId: string;
scumUserId: string;
steamId: string;
lockedAt?: string;
source?: string;
createdAt?: string;
}
export interface ScumVehicleLockListResponse { items: ScumVehicleLockResponse[]; count: number; }
export interface GameClientBridgeCommandFilterRequest {
profileKey?: string;
state?: GameClientBridgeCommandState;