Game specialisation & delta versions (#323)

* feat: game specialisation, auto-guess extensions

* fix: enforce specialisation specific schema at API level

* fix: lint

* feat: partial work on depot endpoints

* feat: bump torrential

* feat: dummy version creation for depot uploads

* fix: lint

* fix: types

* fix: lint

* feat: depot version import

* fix: lint

* fix: remove any type

* fix: lint

* fix: push update interval

* fix: cpu usage calculation

* feat: delta version support

* feat: style tweaks for selectlaunch.vue

* fix: lint
This commit is contained in:
DecDuck
2026-01-23 05:04:38 +00:00
committed by GitHub
parent d8db5b5b85
commit 00adab21c2
46 changed files with 1164 additions and 347 deletions

View File

@@ -0,0 +1,17 @@
-- CreateEnum
CREATE TYPE "GameType" AS ENUM ('Game', 'Executor', 'Redist');
-- DropIndex
DROP INDEX "Game_mName_idx";
-- DropIndex
DROP INDEX "GameTag_name_idx";
-- AlterTable
ALTER TABLE "Game" ADD COLUMN "type" "GameType" NOT NULL DEFAULT 'Game';
-- CreateIndex
CREATE INDEX "Game_mName_idx" ON "Game" USING GIST ("mName" gist_trgm_ops(siglen=32));
-- CreateIndex
CREATE INDEX "GameTag_name_idx" ON "GameTag" USING GIST ("name" gist_trgm_ops(siglen=32));

View File

@@ -0,0 +1,64 @@
/*
Warnings:
- The primary key for the `GameVersion` table will be changed. If it partially fails, the table could be left without primary key constraint.
- You are about to drop the column `gameId` on the `LaunchConfiguration` table. All the data in the column will be lost.
- You are about to drop the column `gameId` on the `SetupConfiguration` table. All the data in the column will be lost.
*/
-- DropForeignKey
ALTER TABLE "LaunchConfiguration" DROP CONSTRAINT "LaunchConfiguration_executorId_fkey";
-- DropForeignKey
ALTER TABLE "LaunchConfiguration" DROP CONSTRAINT "LaunchConfiguration_gameId_versionId_fkey";
-- DropForeignKey
ALTER TABLE "SetupConfiguration" DROP CONSTRAINT "SetupConfiguration_gameId_versionId_fkey";
-- DropIndex
DROP INDEX "Game_mName_idx";
-- DropIndex
DROP INDEX "GameTag_name_idx";
-- AlterTable
ALTER TABLE "GameVersion" DROP CONSTRAINT "GameVersion_pkey",
ADD CONSTRAINT "GameVersion_pkey" PRIMARY KEY ("versionId");
-- AlterTable
ALTER TABLE "LaunchConfiguration" DROP COLUMN "gameId";
-- AlterTable
ALTER TABLE "SetupConfiguration" DROP COLUMN "gameId";
-- CreateTable
CREATE TABLE "_requiredContent" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_requiredContent_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateIndex
CREATE INDEX "_requiredContent_B_index" ON "_requiredContent"("B");
-- CreateIndex
CREATE INDEX "Game_mName_idx" ON "Game" USING GIST ("mName" gist_trgm_ops(siglen=32));
-- CreateIndex
CREATE INDEX "GameTag_name_idx" ON "GameTag" USING GIST ("name" gist_trgm_ops(siglen=32));
-- AddForeignKey
ALTER TABLE "SetupConfiguration" ADD CONSTRAINT "SetupConfiguration_versionId_fkey" FOREIGN KEY ("versionId") REFERENCES "GameVersion"("versionId") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LaunchConfiguration" ADD CONSTRAINT "LaunchConfiguration_executorId_fkey" FOREIGN KEY ("executorId") REFERENCES "LaunchConfiguration"("launchId") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LaunchConfiguration" ADD CONSTRAINT "LaunchConfiguration_versionId_fkey" FOREIGN KEY ("versionId") REFERENCES "GameVersion"("versionId") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_requiredContent" ADD CONSTRAINT "_requiredContent_A_fkey" FOREIGN KEY ("A") REFERENCES "GameVersion"("versionId") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_requiredContent" ADD CONSTRAINT "_requiredContent_B_fkey" FOREIGN KEY ("B") REFERENCES "GameVersion"("versionId") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,14 @@
-- DropIndex
DROP INDEX "Game_mName_idx";
-- DropIndex
DROP INDEX "GameTag_name_idx";
-- AlterTable
ALTER TABLE "LaunchConfiguration" ADD COLUMN "executorSuggestions" TEXT[];
-- CreateIndex
CREATE INDEX "Game_mName_idx" ON "Game" USING GIST ("mName" gist_trgm_ops(siglen=32));
-- CreateIndex
CREATE INDEX "GameTag_name_idx" ON "GameTag" USING GIST ("name" gist_trgm_ops(siglen=32));

View File

@@ -0,0 +1,27 @@
-- DropIndex
DROP INDEX "Game_mName_idx";
-- DropIndex
DROP INDEX "GameTag_name_idx";
-- AlterTable
ALTER TABLE "GameVersion" ALTER COLUMN "versionPath" DROP NOT NULL;
-- CreateTable
CREATE TABLE "UnimportedGameVersion" (
"id" TEXT NOT NULL,
"gameId" TEXT NOT NULL,
"versionName" TEXT NOT NULL,
"manifest" JSONB NOT NULL,
CONSTRAINT "UnimportedGameVersion_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "Game_mName_idx" ON "Game" USING GIST ("mName" gist_trgm_ops(siglen=32));
-- CreateIndex
CREATE INDEX "GameTag_name_idx" ON "GameTag" USING GIST ("name" gist_trgm_ops(siglen=32));
-- AddForeignKey
ALTER TABLE "UnimportedGameVersion" ADD CONSTRAINT "UnimportedGameVersion_gameId_fkey" FOREIGN KEY ("gameId") REFERENCES "Game"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,14 @@
-- DropIndex
DROP INDEX "Game_mName_idx";
-- DropIndex
DROP INDEX "GameTag_name_idx";
-- AlterTable
ALTER TABLE "UnimportedGameVersion" ADD COLUMN "fileList" TEXT[];
-- CreateIndex
CREATE INDEX "Game_mName_idx" ON "Game" USING GIST ("mName" gist_trgm_ops(siglen=32));
-- CreateIndex
CREATE INDEX "GameTag_name_idx" ON "GameTag" USING GIST ("name" gist_trgm_ops(siglen=32));

View File

@@ -0,0 +1,15 @@
-- DropIndex
DROP INDEX "Game_mName_idx";
-- DropIndex
DROP INDEX "GameTag_name_idx";
-- AlterTable
ALTER TABLE "GameVersion" ADD COLUMN "fileList" TEXT[],
ADD COLUMN "negativeFileList" TEXT[];
-- CreateIndex
CREATE INDEX "Game_mName_idx" ON "Game" USING GIST ("mName" gist_trgm_ops(siglen=32));
-- CreateIndex
CREATE INDEX "GameTag_name_idx" ON "GameTag" USING GIST ("name" gist_trgm_ops(siglen=32));

View File

@@ -8,6 +8,12 @@ enum MetadataSource {
OpenCritic
}
enum GameType {
Game
Executor
Redist
}
model Game {
id String @id @default(uuid())
@@ -15,6 +21,8 @@ model Game {
metadataId String
created DateTime @default(now())
type GameType @default(Game)
// Any field prefixed with m is filled in from metadata
// Acts as a cache so we can search and filter it
mName String // Name of game
@@ -47,8 +55,9 @@ model Game {
tags GameTag[]
playtime Playtime[]
developers Company[] @relation(name: "developers")
publishers Company[] @relation(name: "publishers")
developers Company[] @relation(name: "developers")
publishers Company[] @relation(name: "publishers")
unimportedGameVersions UnimportedGameVersion[]
@@unique([metadataSource, metadataId], name: "metadataKey")
@@unique([libraryId, libraryPath], name: "libraryKey")
@@ -82,14 +91,24 @@ model GameRating {
@@unique([metadataSource, metadataId], name: "metadataKey")
}
model UnimportedGameVersion {
id String @id @default(uuid())
gameId String
game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
versionName String
manifest Json
fileList String[]
}
// A particular set of files that relate to the version
model GameVersion {
gameId String
game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
versionId String @default(uuid())
versionId String @id @default(uuid())
displayName String?
versionPath String
versionPath String?
created DateTime @default(now())
@@ -98,12 +117,15 @@ model GameVersion {
onlySetup Boolean @default(false)
dropletManifest Json // Results from droplet
dropletManifest Json // Results from droplet
fileList String[] // List of all files, for delta updates
negativeFileList String[] // List of files to remove, for delta updates
versionIndex Int
delta Boolean @default(false)
@@id([gameId, versionId])
requiredContent GameVersion[] @relation(name: "requiredContent")
requiringContent GameVersion[] @relation(name: "requiredContent")
}
model SetupConfiguration {
@@ -113,9 +135,8 @@ model SetupConfiguration {
platform Platform
gameId String
versionId String
gameVersion GameVersion @relation(fields: [gameId, versionId], references: [gameId, versionId], onDelete: Cascade, onUpdate: Cascade)
gameVersion GameVersion @relation(fields: [versionId], references: [versionId], onDelete: Cascade, onUpdate: Cascade)
}
model LaunchConfiguration {
@@ -128,14 +149,14 @@ model LaunchConfiguration {
platform Platform
// For emulation targets
executorId String?
executor LaunchConfiguration? @relation(fields: [executorId], references: [launchId], name: "executor", onDelete: Cascade, onUpdate: Cascade)
executorId String?
executor LaunchConfiguration? @relation(fields: [executorId], references: [launchId], name: "executor")
executorSuggestions String[]
umuIdOverride String?
gameId String
versionId String
gameVersion GameVersion @relation(fields: [gameId, versionId], references: [gameId, versionId], onDelete: Cascade, onUpdate: Cascade)
gameVersion GameVersion @relation(fields: [versionId], references: [versionId], onDelete: Cascade, onUpdate: Cascade)
executions LaunchConfiguration[] @relation("executor")
}