mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-14 16:51:15 +10:00
feat: rearchitecture of database schemas, migration reset, and #180
This commit is contained in:
@ -15,12 +15,13 @@ class DownloadContextManager {
|
||||
}
|
||||
> = new Map();
|
||||
|
||||
async createContext(game: string, versionName: string) {
|
||||
const version = await prisma.gameVersion.findUnique({
|
||||
async createContext(game: string, versionPath: string) {
|
||||
const version = await prisma.version.findFirst({
|
||||
where: {
|
||||
gameId_versionName: {
|
||||
gameId: game,
|
||||
versionName,
|
||||
gameId: game,
|
||||
versionPath,
|
||||
gameVersion: {
|
||||
isNot: null,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
@ -38,9 +39,9 @@ class DownloadContextManager {
|
||||
this.contexts.set(contextId, {
|
||||
timeout: new Date(),
|
||||
manifest: JSON.parse(version.dropletManifest as string) as DropManifest,
|
||||
versionName,
|
||||
libraryId: version.game.libraryId!,
|
||||
libraryPath: version.game.libraryPath,
|
||||
versionName: versionPath,
|
||||
libraryId: version.game!.libraryId!,
|
||||
libraryPath: version.game!.libraryPath,
|
||||
});
|
||||
|
||||
return contextId;
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { GameVersionModel } from "~/prisma/client/models";
|
||||
import prisma from "../db/database";
|
||||
|
||||
export type DropChunk = {
|
||||
@ -14,11 +13,11 @@ export type DropManifest = {
|
||||
|
||||
export type DropManifestMetadata = {
|
||||
manifest: DropManifest;
|
||||
versionName: string;
|
||||
versionId: string;
|
||||
};
|
||||
|
||||
export type DropGeneratedManifest = DropManifest & {
|
||||
[key: string]: { versionName: string };
|
||||
[key: string]: { versionId: string };
|
||||
};
|
||||
|
||||
class ManifestGenerator {
|
||||
@ -31,7 +30,7 @@ class ManifestGenerator {
|
||||
Object.entries(rootManifest.manifest).map(([key, value]) => {
|
||||
return [
|
||||
key,
|
||||
Object.assign({}, value, { versionName: rootManifest.versionName }),
|
||||
Object.assign({}, value, { versionId: rootManifest.versionId }),
|
||||
];
|
||||
}),
|
||||
);
|
||||
@ -44,7 +43,7 @@ class ManifestGenerator {
|
||||
for (const [filename, chunk] of Object.entries(version.manifest)) {
|
||||
if (manifest[filename]) continue;
|
||||
manifest[filename] = Object.assign({}, chunk, {
|
||||
versionName: version.versionName,
|
||||
versionId: version.versionId,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -53,45 +52,50 @@ class ManifestGenerator {
|
||||
}
|
||||
|
||||
// Local function because eventual caching
|
||||
async generateManifest(gameId: string, versionName: string) {
|
||||
const versions: GameVersionModel[] = [];
|
||||
async generateManifest(versionId: string) {
|
||||
const versions = [];
|
||||
|
||||
const baseVersion = await prisma.gameVersion.findUnique({
|
||||
const baseVersion = await prisma.version.findUnique({
|
||||
where: {
|
||||
gameId_versionName: {
|
||||
gameId: gameId,
|
||||
versionName: versionName,
|
||||
},
|
||||
versionId,
|
||||
},
|
||||
include: {
|
||||
gameVersion: true,
|
||||
},
|
||||
});
|
||||
if (!baseVersion) return undefined;
|
||||
versions.push(baseVersion);
|
||||
|
||||
// Collect other versions if this is a delta
|
||||
if (baseVersion.delta) {
|
||||
if (baseVersion.gameVersion?.delta) {
|
||||
// Start at the same index minus one, and keep grabbing them
|
||||
// until we run out or we hit something that isn't a delta
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
for (let i = baseVersion.versionIndex - 1; true; i--) {
|
||||
const currentVersion = await prisma.gameVersion.findFirst({
|
||||
for (let i = baseVersion.gameVersion.versionIndex - 1; true; i--) {
|
||||
const currentVersion = await prisma.version.findFirst({
|
||||
where: {
|
||||
gameId: gameId,
|
||||
versionIndex: i,
|
||||
gameId: baseVersion.gameId,
|
||||
platform: baseVersion.platform,
|
||||
gameVersion: {
|
||||
versionIndex: i,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
gameVersion: true,
|
||||
},
|
||||
});
|
||||
if (!currentVersion) return undefined;
|
||||
versions.push(currentVersion);
|
||||
if (!currentVersion.delta) break;
|
||||
if (!currentVersion.gameVersion?.delta) break;
|
||||
}
|
||||
}
|
||||
const leastToMost = versions.reverse();
|
||||
const metadata: DropManifestMetadata[] = leastToMost.map((e) => {
|
||||
versions.reverse();
|
||||
const metadata: DropManifestMetadata[] = versions.map((version) => {
|
||||
return {
|
||||
manifest: JSON.parse(
|
||||
e.dropletManifest?.toString() ?? "{}",
|
||||
version.dropletManifest?.toString() ?? "{}",
|
||||
) as DropManifest,
|
||||
versionName: e.versionName,
|
||||
versionId: version.versionId,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -14,6 +14,8 @@ import notificationSystem from "../notifications";
|
||||
import { GameNotFoundError, type LibraryProvider } from "./provider";
|
||||
import { logger } from "../logging";
|
||||
import { createHash } from "node:crypto";
|
||||
import type { ImportVersion } from "~/server/api/v1/admin/import/version/index.post";
|
||||
import type { GameVersionLaunchCreateManyGameVersionInputEnvelope } from "~/prisma/client/models";
|
||||
|
||||
export function createGameImportTaskId(libraryId: string, libraryPath: string) {
|
||||
return createHash("md5")
|
||||
@ -247,21 +249,10 @@ class LibraryManager {
|
||||
|
||||
async importVersion(
|
||||
gameId: string,
|
||||
versionName: string,
|
||||
metadata: {
|
||||
platform: string;
|
||||
onlySetup: boolean;
|
||||
|
||||
setup: string;
|
||||
setupArgs: string;
|
||||
launch: string;
|
||||
launchArgs: string;
|
||||
delta: boolean;
|
||||
|
||||
umuId: string;
|
||||
},
|
||||
versionPath: string,
|
||||
metadata: typeof ImportVersion.infer,
|
||||
) {
|
||||
const taskId = createVersionImportTaskId(gameId, versionName);
|
||||
const taskId = createVersionImportTaskId(gameId, versionPath);
|
||||
|
||||
const platform = parsePlatform(metadata.platform);
|
||||
if (!platform) return undefined;
|
||||
@ -278,14 +269,14 @@ class LibraryManager {
|
||||
taskHandler.create({
|
||||
id: taskId,
|
||||
taskGroup: "import:game",
|
||||
name: `Importing version ${versionName} for ${game.mName}`,
|
||||
name: `Importing version "${metadata.name}" (${versionPath}) for ${game.mName}`,
|
||||
acls: ["system:import:version:read"],
|
||||
async run({ progress, logger }) {
|
||||
// First, create the manifest via droplet.
|
||||
// This takes up 90% of our progress, so we wrap it in a *0.9
|
||||
const manifest = await library.generateDropletManifest(
|
||||
game.libraryPath,
|
||||
versionName,
|
||||
versionPath,
|
||||
(err, value) => {
|
||||
if (err) throw err;
|
||||
progress(value * 0.9);
|
||||
@ -299,34 +290,52 @@ class LibraryManager {
|
||||
logger.info("Created manifest successfully!");
|
||||
|
||||
const currentIndex = await prisma.gameVersion.count({
|
||||
where: { gameId: gameId },
|
||||
where: { version: { gameId: gameId } },
|
||||
});
|
||||
|
||||
// Then, create the database object
|
||||
await prisma.gameVersion.create({
|
||||
await prisma.version.create({
|
||||
data: {
|
||||
gameId: gameId,
|
||||
versionName: versionName,
|
||||
gameId,
|
||||
versionPath: versionPath,
|
||||
versionName: metadata.name ?? versionPath,
|
||||
dropletManifest: manifest,
|
||||
versionIndex: currentIndex,
|
||||
delta: metadata.delta,
|
||||
umuIdOverride: metadata.umuId,
|
||||
platform: platform,
|
||||
|
||||
onlySetup: metadata.onlySetup,
|
||||
setupCommand: metadata.setup,
|
||||
setupArgs: metadata.setupArgs.split(" "),
|
||||
launchCommand: metadata.launch,
|
||||
launchArgs: metadata.launchArgs.split(" "),
|
||||
gameVersion: {
|
||||
create: {
|
||||
versionIndex: currentIndex,
|
||||
delta: metadata.delta,
|
||||
umuIdOverride: metadata.umuId,
|
||||
|
||||
onlySetup: metadata.onlySetup,
|
||||
setup: metadata.setup,
|
||||
setupArgs: metadata.setupArgs,
|
||||
|
||||
launches: {
|
||||
createMany: {
|
||||
data: metadata.launches.map(
|
||||
(v) =>
|
||||
({
|
||||
name: v.name,
|
||||
description: v.description,
|
||||
launchCommand: v.launchCommand,
|
||||
launchArgs: v.launchArgs,
|
||||
}) satisfies GameVersionLaunchCreateManyGameVersionInputEnvelope["data"],
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
logger.info("Successfully created version!");
|
||||
|
||||
notificationSystem.systemPush({
|
||||
nonce: `version-create-${gameId}-${versionName}`,
|
||||
title: `'${game.mName}' ('${versionName}') finished importing.`,
|
||||
description: `Drop finished importing version ${versionName} for ${game.mName}.`,
|
||||
nonce: `version-create-${gameId}-${versionPath}`,
|
||||
title: `'${game.mName}' ('${versionPath}') finished importing.`,
|
||||
description: `Drop finished importing version ${versionPath} for ${game.mName}.`,
|
||||
actions: [`View|/admin/library/${gameId}`],
|
||||
acls: ["system:import:version:read"],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user