mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-06-22 04:11:32 +10:00
API optimisations (#343)
* feat: api optimisation * feat: emulator rename
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="executor"
|
||||
v-if="emulator"
|
||||
class="flex space-x-4 rounded-md bg-zinc-900/50 px-6 outline -outline-offset-1 outline-white/10 w-fit text-xs font-bold text-zinc-100"
|
||||
>
|
||||
<div class="inline-flex gap-x-2 items-center">
|
||||
<img :src="useObject(executor.gameIcon)" class="size-6" />
|
||||
<span>{{ executor.gameName }}</span>
|
||||
<img :src="useObject(emulator.gameIcon)" class="size-6" />
|
||||
<span>{{ emulator.gameName }}</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<svg
|
||||
@@ -17,7 +17,7 @@
|
||||
>
|
||||
<path d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
|
||||
</svg>
|
||||
<span class="ml-4">{{ executor.versionName }}</span>
|
||||
<span class="ml-4">{{ emulator.versionName }}</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<svg
|
||||
@@ -29,13 +29,13 @@
|
||||
>
|
||||
<path d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
|
||||
</svg>
|
||||
<span class="ml-4 truncate">{{ executor.launchName }}</span>
|
||||
<span class="ml-4 truncate">{{ emulator.launchName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ExecutorLaunchObject } from "~/composables/frontend";
|
||||
import type { EmulatorLaunchObject } from "~/composables/frontend";
|
||||
|
||||
defineProps<{ executor: ExecutorLaunchObject }>();
|
||||
defineProps<{ emulator: EmulatorLaunchObject }>();
|
||||
</script>
|
||||
@@ -23,16 +23,16 @@
|
||||
>
|
||||
<p>{{ props.config.command }}</p>
|
||||
</div>
|
||||
<ExecutorWidget
|
||||
v-if="!isSetup(props.config) && props.config.executor"
|
||||
:executor="{
|
||||
<EmulatorWidget
|
||||
v-if="!isSetup(props.config) && props.config.emulator"
|
||||
:emulator="{
|
||||
launchId: props.config.launchId,
|
||||
gameName: props.config.executor.gameVersion.game.mName,
|
||||
gameIcon: props.config.executor.gameVersion.game.mIconObjectId,
|
||||
versionName: (props.config.executor.gameVersion.displayName ??
|
||||
props.config.executor.gameVersion.versionPath)!,
|
||||
launchName: props.config.executor.name,
|
||||
platform: props.config.executor.platform,
|
||||
gameName: props.config.emulator.gameVersion.game.mName,
|
||||
gameIcon: props.config.emulator.gameVersion.game.mIconObjectId,
|
||||
versionName: (props.config.emulator.gameVersion.displayName ??
|
||||
props.config.emulator.gameVersion.versionPath)!,
|
||||
launchName: props.config.emulator.name,
|
||||
platform: props.config.emulator.platform,
|
||||
}"
|
||||
/>
|
||||
</li>
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
class="size-5"
|
||||
/>
|
||||
<img
|
||||
v-if="guess.type === 'executor'"
|
||||
v-if="guess.type === 'emulator'"
|
||||
:src="useObject(guess.icon)"
|
||||
class="size-5"
|
||||
/>
|
||||
@@ -142,7 +142,7 @@
|
||||
</Combobox>
|
||||
</div>
|
||||
<div
|
||||
v-if="props.type && props.type === 'Executor'"
|
||||
v-if="props.type && props.type === 'Emulator'"
|
||||
class="ml-1 mt-2 rounded-lg bg-blue-900/10 p-1 outline outline-blue-900"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
@@ -155,16 +155,16 @@
|
||||
<div class="ml-2 inline-flex items-center">
|
||||
<p class="text-sm text-blue-200">
|
||||
<i18n-t
|
||||
keypath="library.admin.launchRow.executorHint"
|
||||
keypath="library.admin.launchRow.emulatorHint"
|
||||
tag="span"
|
||||
scope="global"
|
||||
>
|
||||
<template #executor>
|
||||
<template #rom>
|
||||
<span
|
||||
class="font-mono bg-zinc-950 text-zinc-100 py-1 px-0.5 rounded-xl"
|
||||
>{{
|
||||
// eslint-disable-next-line @intlify/vue-i18n/no-raw-text
|
||||
"{executor}"
|
||||
"{rom}"
|
||||
}}</span
|
||||
>
|
||||
</template>
|
||||
@@ -181,32 +181,32 @@
|
||||
>
|
||||
{{ $t("library.admin.import.version.platform") }}
|
||||
</SelectorPlatform>
|
||||
<div v-if="props.type && props.type === 'Game' && props.allowExecutor">
|
||||
<div v-if="props.type && props.type === 'Game' && props.allowEmulator">
|
||||
<h1 class="block text-sm font-medium leading-6 text-zinc-100">
|
||||
{{ $t("library.admin.launchRow.executorTitle") }}
|
||||
{{ $t("library.admin.launchRow.emulatorTitle") }}
|
||||
</h1>
|
||||
<div class="relative mt-2 space-x-1 inline-flex items-center w-full">
|
||||
<ExecutorWidget v-if="executor" :executor="executor" />
|
||||
<EmulatorWidget v-if="emulator" :emulator="emulator" />
|
||||
<div
|
||||
v-else
|
||||
class="font-bold uppercase font-display text-zinc-500 text-sm"
|
||||
>
|
||||
{{ $t("library.admin.launchRow.noExecutorSelected") }}
|
||||
{{ $t("library.admin.launchRow.noEmulatorSelected") }}
|
||||
</div>
|
||||
<div class="grow" />
|
||||
<LoadingButton :loading="false" @click="selectLaunchOpen = true">{{
|
||||
$t("library.admin.launchRow.executorSelect")
|
||||
$t("library.admin.launchRow.emulatorSelect")
|
||||
}}</LoadingButton>
|
||||
<button
|
||||
:disabled="!executor"
|
||||
:disabled="!emulator"
|
||||
class="transition rounded p-2 bg-zinc-900/30 group hover:enabled:bg-red-600/10 text-zinc-400 hover:enabled:text-red-600 disabled:bg-zinc-900/80 disabled:text-zinc-700"
|
||||
@click="() => (executor = undefined)"
|
||||
@click="() => (emulator = undefined)"
|
||||
>
|
||||
<TrashIcon class="transition size-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="props.type && props.type === 'Executor'">
|
||||
<div v-if="props.type && props.type === 'Emulator'">
|
||||
<p class="block text-sm font-medium leading-6 text-zinc-100">
|
||||
{{ $t("library.admin.launchRow.autosuggestHint") }}
|
||||
</p>
|
||||
@@ -219,7 +219,7 @@
|
||||
v-model="selectLaunchOpen"
|
||||
class="-mt-2"
|
||||
:filter-platform="launchConfiguration.platform"
|
||||
@select="(v) => (executor = v)"
|
||||
@select="(v) => (emulator = v)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -234,7 +234,7 @@ import {
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import { InformationCircleIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
||||
import type { ExecutorLaunchObject } from "~/composables/frontend";
|
||||
import type { EmulatorLaunchObject } from "~/composables/frontend";
|
||||
import type { GameType, Platform } from "~/prisma/client/enums";
|
||||
|
||||
import type { ImportVersion } from "~/server/api/v1/admin/import/version/index.post";
|
||||
@@ -247,17 +247,17 @@ const launchConfiguration = defineModel<
|
||||
name?: string;
|
||||
}
|
||||
>({ required: true });
|
||||
const _executorMetadata = ref<ExecutorLaunchObject | undefined>(undefined);
|
||||
const executor = computed({
|
||||
const _emulatorMetadata = ref<EmulatorLaunchObject | undefined>(undefined);
|
||||
const emulator = computed({
|
||||
get() {
|
||||
return _executorMetadata.value;
|
||||
return _emulatorMetadata.value;
|
||||
},
|
||||
set(v) {
|
||||
_executorMetadata.value = v;
|
||||
_emulatorMetadata.value = v;
|
||||
if (v) {
|
||||
launchConfiguration.value.executorId = v.launchId;
|
||||
launchConfiguration.value.emulatorId = v.launchId;
|
||||
} else {
|
||||
launchConfiguration.value.executorId = undefined;
|
||||
launchConfiguration.value.emulatorId = undefined;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -265,9 +265,9 @@ const executor = computed({
|
||||
function updatePlatform(v: Platform | undefined) {
|
||||
if (!v) return;
|
||||
launchConfiguration.value.platform = v;
|
||||
if (executor.value) {
|
||||
if (executor.value.platform !== v) {
|
||||
executor.value = undefined;
|
||||
if (emulator.value) {
|
||||
if (emulator.value.platform !== v) {
|
||||
emulator.value = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,11 +275,11 @@ function updatePlatform(v: Platform | undefined) {
|
||||
const props = defineProps<{
|
||||
versionGuesses: Array<VersionGuess> | undefined;
|
||||
needsName: boolean;
|
||||
allowExecutor?: boolean;
|
||||
allowEmulator?: boolean;
|
||||
type?: GameType;
|
||||
}>();
|
||||
|
||||
if (props.type && props.type === "Executor")
|
||||
if (props.type && props.type === "Emulator")
|
||||
launchConfiguration.value.suggestions ??= [];
|
||||
|
||||
const selectLaunchOpen = ref(false);
|
||||
@@ -299,15 +299,15 @@ function updateLaunchCommand(command: string) {
|
||||
if (autosetGuess) {
|
||||
if (autosetGuess.type === "platform") {
|
||||
launchConfiguration.value.platform = autosetGuess.platform;
|
||||
} else if (autosetGuess.type === "executor") {
|
||||
executor.value = {
|
||||
launchId: autosetGuess.executorId,
|
||||
} else if (autosetGuess.type === "emulator") {
|
||||
emulator.value = {
|
||||
launchId: autosetGuess.emulatorId,
|
||||
gameName: autosetGuess.gameName,
|
||||
gameIcon: autosetGuess.icon,
|
||||
versionName: autosetGuess.launchName,
|
||||
launchName: autosetGuess.launchName,
|
||||
platform: autosetGuess.platform,
|
||||
} satisfies ExecutorLaunchObject;
|
||||
} satisfies EmulatorLaunchObject;
|
||||
launchConfiguration.value.platform = autosetGuess.platform;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { XCircleIcon } from "@heroicons/vue/24/outline";
|
||||
import type { ExecutorLaunchObject } from "~/composables/frontend";
|
||||
import type { EmulatorLaunchObject } from "~/composables/frontend";
|
||||
import type { Platform } from "~/prisma/client/enums";
|
||||
import type { GameMetadataSearchResult } from "~/server/internal/metadata/types";
|
||||
|
||||
@@ -174,12 +174,12 @@ const versions = ref<
|
||||
>(undefined);
|
||||
|
||||
const emit = defineEmits<{
|
||||
select: [data: ExecutorLaunchObject];
|
||||
select: [data: EmulatorLaunchObject];
|
||||
}>();
|
||||
|
||||
async function search(query: string) {
|
||||
return await $dropFetch("/api/v1/admin/search/game", {
|
||||
query: { q: query, type: "Executor" },
|
||||
query: { q: query, type: "Emulator" },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -12,7 +12,7 @@ declare module "@vue/runtime-core" {
|
||||
interface ComponentCustomOptions extends _ComponentCustomOptions {}
|
||||
}
|
||||
|
||||
export interface ExecutorLaunchObject {
|
||||
export interface EmulatorLaunchObject {
|
||||
launchId: string;
|
||||
gameName: string;
|
||||
gameIcon: string;
|
||||
|
||||
@@ -436,16 +436,16 @@
|
||||
"launchRow": {
|
||||
"autosuggestHint": "Auto-suggest extensions",
|
||||
"currentDirHint": "The installation directory is set as the current directory when launching. It is not prepended to your command.",
|
||||
"executorHint": "{executor} is replaced with the game's launch command for emulators.",
|
||||
"executorSelect": "Select new executor",
|
||||
"executorTitle": "Executor",
|
||||
"noExecutorSelected": "No executor selected"
|
||||
"emulatorHint": "{rom} is replaced with the game's launch command for emulators.",
|
||||
"emulatorSelect": "Select new emulator",
|
||||
"emulatorTitle": "Emulator",
|
||||
"noEmulatorSelected": "No emulator selected"
|
||||
},
|
||||
"launchSelector": {
|
||||
"description": "Select a launch option as an executor for your new launch option.",
|
||||
"description": "Select a launch option as an emulator for your new launch option.",
|
||||
"noVersions": "No versions imported.",
|
||||
"platformFilterHint": "Only showing launches for:",
|
||||
"search": "Search for an executor",
|
||||
"search": "Search for an emulator",
|
||||
"selectCommand": "Select a launch command",
|
||||
"selectVersions": "Select a version",
|
||||
"title": "Select a launch option"
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
"prisma": "6.11.1",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"semver": "^7.7.1",
|
||||
"shescape": "^2.1.8",
|
||||
"stream-mime-type": "^2.0.0",
|
||||
"turndown": "^7.2.0",
|
||||
"unstorage": "^1.15.0",
|
||||
|
||||
@@ -75,7 +75,6 @@
|
||||
|
||||
<div v-if="versionGuesses" class="flex flex-col gap-4">
|
||||
<!-- setup executable -->
|
||||
|
||||
<div class="bg-zinc-800 p-4 rounded-xl relative flex flex-col gap-y-2">
|
||||
<div>
|
||||
<label class="block text-sm font-medium leading-6 text-zinc-100">{{
|
||||
@@ -155,7 +154,7 @@
|
||||
</Switch>
|
||||
</SwitchGroup>
|
||||
<div
|
||||
v-if="type === GameType.Redist"
|
||||
v-if="type === GameType.Dependency"
|
||||
class="absolute inset-0 bg-zinc-900/50"
|
||||
/>
|
||||
</div>
|
||||
@@ -215,7 +214,7 @@
|
||||
v-model="versionSettings.launches[launchIdx]"
|
||||
:version-guesses="versionGuesses"
|
||||
:needs-name="true"
|
||||
:allow-executor="true"
|
||||
:allow-emulator="true"
|
||||
:type="type"
|
||||
/>
|
||||
</DisclosurePanel>
|
||||
@@ -361,7 +360,7 @@ const currentlySelectedVersion = ref(-1);
|
||||
const versionSettings = ref<Omit<typeof ImportVersion.infer, "version" | "id">>(
|
||||
{
|
||||
delta: false,
|
||||
onlySetup: type === GameType.Redist,
|
||||
onlySetup: type === GameType.Dependency,
|
||||
launches: [],
|
||||
setups: [],
|
||||
requiredContent: [],
|
||||
|
||||
@@ -369,15 +369,15 @@ const importModes: {
|
||||
title: "Game",
|
||||
description: "Games are shown in store, and are discoverable.",
|
||||
},
|
||||
Executor: {
|
||||
title: "Executor",
|
||||
Emulator: {
|
||||
title: "Emulator",
|
||||
description:
|
||||
"Executors are used to launch games. Mainly emulators or wrappers.",
|
||||
"Emulators are used to launch other games, wrapping them in another executable.",
|
||||
},
|
||||
Redist: {
|
||||
title: "Redistributable",
|
||||
Dependency: {
|
||||
title: "Dependency",
|
||||
description:
|
||||
"Additional content that must be downloaded and installed before running the game.",
|
||||
"Dependencies are setup-only files that get installed before the game is launched.",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -93,12 +93,12 @@
|
||||
{{ $t("store.size") }}
|
||||
</td>
|
||||
<td
|
||||
v-if="size.versions.length > 0"
|
||||
v-if="sizes.length > 0"
|
||||
class="whitespace-nowrap inline-flex gap-x-4 px-3 py-4 text-sm text-zinc-400"
|
||||
>
|
||||
<ul class="flex flex-col">
|
||||
<ol
|
||||
v-for="version in size.versions"
|
||||
v-for="version in sizes"
|
||||
:key="version.versionId"
|
||||
class="inline-flex items-center gap-x-1"
|
||||
>
|
||||
@@ -268,21 +268,14 @@ const gameId = route.params.id.toString();
|
||||
|
||||
const user = useUser();
|
||||
|
||||
const { game, rating, size } = await $dropFetch(`/api/v1/games/${gameId}`);
|
||||
const { game, rating, sizes, platforms } = await $dropFetch(
|
||||
`/api/v1/games/${gameId}`,
|
||||
);
|
||||
|
||||
const isClient = isClientRequest();
|
||||
|
||||
const descriptionHTML = micromark(game.mDescription);
|
||||
|
||||
const platforms = game.versions
|
||||
.map((e) => [
|
||||
...e.launches.map((v) => v.platform),
|
||||
...e.setups.map((v) => v.platform),
|
||||
])
|
||||
.flat()
|
||||
.flat()
|
||||
.filter((e, i, u) => u.indexOf(e) === i);
|
||||
|
||||
// const rating = Math.round(game.mReviewRating * 5);
|
||||
const averageRating = Math.round((rating._avg.mReviewRating ?? 0) * 5);
|
||||
const ratingArray = Array(5)
|
||||
|
||||
Generated
+26
@@ -125,6 +125,9 @@ importers:
|
||||
semver:
|
||||
specifier: ^7.7.1
|
||||
version: 7.7.2
|
||||
shescape:
|
||||
specifier: ^2.1.8
|
||||
version: 2.1.8
|
||||
stream-mime-type:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
@@ -532,6 +535,10 @@ packages:
|
||||
'@emnapi/wasi-threads@1.1.0':
|
||||
resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
|
||||
|
||||
'@ericcornelissen/lregexp@1.0.7':
|
||||
resolution: {integrity: sha512-1FFCOKq49eWRCinu3VuTfV1jv0kgeSET89OHDAUDaASzvIpwmh2/nFUayoEXB65mPT3atgECjlu3KVTIQU7jhw==}
|
||||
engines: {node: ^12.20.0 || ^14.13.0 || ^15 || ^16 || ^17 || ^18 || ^19 || ^20 || ^21 || ^22 || ^23 || ^24 || ^25}
|
||||
|
||||
'@es-joy/jsdoccomment@0.52.0':
|
||||
resolution: {integrity: sha512-BXuN7BII+8AyNtn57euU2Yxo9yA/KUDNzrpXyi3pfqKmBhhysR6ZWOebFh3vyPoqA3/j1SOvGgucElMGwlXing==}
|
||||
engines: {node: '>=20.11.0'}
|
||||
@@ -4435,6 +4442,10 @@ packages:
|
||||
resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
is-supported-regexp-flag@2.0.0:
|
||||
resolution: {integrity: sha512-8i4+OYUjdUaJ88KAs1WojIThDFjIpeYNrSlYy1g/At2p9YjQ7HEmB1yn60un0jRFjM3TQbKPMAluTPEPncZfqA==}
|
||||
engines: {node: '>=12.20'}
|
||||
|
||||
is-url-superb@4.0.0:
|
||||
resolution: {integrity: sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -5941,6 +5952,10 @@ packages:
|
||||
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
shescape@2.1.8:
|
||||
resolution: {integrity: sha512-owfw+5BB1A51KyNiCuUOyDSh1JOw+DrxgX0Uac7eORpF6YBEoLjbDlHn6tHQudnyi3pt3HeHl9jJ30YPmtEMHQ==}
|
||||
engines: {node: ^14.18.0 || ^16.13.0 || ^18 || ^19 || ^20 || ^22 || ^24 || ^25}
|
||||
|
||||
side-channel-list@1.0.0:
|
||||
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -7324,6 +7339,10 @@ snapshots:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@ericcornelissen/lregexp@1.0.7':
|
||||
dependencies:
|
||||
is-supported-regexp-flag: 2.0.0
|
||||
|
||||
'@es-joy/jsdoccomment@0.52.0':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@@ -11564,6 +11583,8 @@ snapshots:
|
||||
|
||||
is-stream@4.0.1: {}
|
||||
|
||||
is-supported-regexp-flag@2.0.0: {}
|
||||
|
||||
is-url-superb@4.0.0: {}
|
||||
|
||||
is-url@1.2.4: {}
|
||||
@@ -13484,6 +13505,11 @@ snapshots:
|
||||
|
||||
shell-quote@1.8.3: {}
|
||||
|
||||
shescape@2.1.8:
|
||||
dependencies:
|
||||
'@ericcornelissen/lregexp': 1.0.7
|
||||
which: 5.0.0
|
||||
|
||||
side-channel-list@1.0.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The values [Executor,Redist] on the enum `GameType` will be removed. If these variants are still used in the database, this will fail.
|
||||
|
||||
*/
|
||||
-- AlterEnum
|
||||
BEGIN;
|
||||
CREATE TYPE "GameType_new" AS ENUM ('Game', 'Emulator', 'Dependency');
|
||||
ALTER TABLE "Game" ALTER COLUMN "type" DROP DEFAULT;
|
||||
ALTER TABLE "Game" ALTER COLUMN "type" TYPE "GameType_new" USING ("type"::text::"GameType_new");
|
||||
ALTER TYPE "GameType" RENAME TO "GameType_old";
|
||||
ALTER TYPE "GameType_new" RENAME TO "GameType";
|
||||
DROP TYPE "GameType_old";
|
||||
ALTER TABLE "Game" ALTER COLUMN "type" SET DEFAULT 'Game';
|
||||
COMMIT;
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "Game_mName_idx";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "GameTag_name_idx";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LaunchConfiguration" ADD COLUMN "umuStoreOverride" 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));
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `executorId` on the `LaunchConfiguration` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `executorSuggestions` on the `LaunchConfiguration` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "LaunchConfiguration" DROP CONSTRAINT "LaunchConfiguration_executorId_fkey";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "Game_mName_idx";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "GameTag_name_idx";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LaunchConfiguration" DROP COLUMN "executorId",
|
||||
DROP COLUMN "executorSuggestions",
|
||||
ADD COLUMN "emulatorId" TEXT,
|
||||
ADD COLUMN "emulatorSuggestions" 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));
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "LaunchConfiguration" ADD CONSTRAINT "LaunchConfiguration_emulatorId_fkey" FOREIGN KEY ("emulatorId") REFERENCES "LaunchConfiguration"("launchId") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -10,8 +10,8 @@ enum MetadataSource {
|
||||
|
||||
enum GameType {
|
||||
Game
|
||||
Executor
|
||||
Redist
|
||||
Emulator
|
||||
Dependency
|
||||
}
|
||||
|
||||
model Game {
|
||||
@@ -149,16 +149,17 @@ model LaunchConfiguration {
|
||||
platform Platform
|
||||
|
||||
// For emulation targets
|
||||
executorId String?
|
||||
executor LaunchConfiguration? @relation(fields: [executorId], references: [launchId], name: "executor")
|
||||
executorSuggestions String[]
|
||||
emulatorId String?
|
||||
emulator LaunchConfiguration? @relation(fields: [emulatorId], references: [launchId], name: "emulator")
|
||||
emulatorSuggestions String[]
|
||||
|
||||
umuIdOverride String?
|
||||
umuIdOverride String?
|
||||
umuStoreOverride String?
|
||||
|
||||
versionId String
|
||||
gameVersion GameVersion @relation(fields: [versionId], references: [versionId], onDelete: Cascade, onUpdate: Cascade)
|
||||
|
||||
executions LaunchConfiguration[] @relation("executor")
|
||||
emulations LaunchConfiguration[] @relation("emulator")
|
||||
}
|
||||
|
||||
// A save slot for a game
|
||||
|
||||
@@ -20,7 +20,7 @@ export type AdminFetchGameType = Prisma.GameGetPayload<{
|
||||
setups: true;
|
||||
launches: {
|
||||
include: {
|
||||
executor: {
|
||||
emulator: {
|
||||
include: {
|
||||
gameVersion: {
|
||||
select: {
|
||||
@@ -38,7 +38,7 @@ export type AdminFetchGameType = Prisma.GameGetPayload<{
|
||||
};
|
||||
};
|
||||
};
|
||||
executions: {
|
||||
emulations: {
|
||||
select: {
|
||||
launchId: true;
|
||||
};
|
||||
@@ -77,7 +77,7 @@ export default defineEventHandler<
|
||||
setups: true,
|
||||
launches: {
|
||||
include: {
|
||||
executor: {
|
||||
emulator: {
|
||||
include: {
|
||||
gameVersion: {
|
||||
select: {
|
||||
@@ -95,7 +95,7 @@ export default defineEventHandler<
|
||||
},
|
||||
},
|
||||
},
|
||||
executions: {
|
||||
emulations: {
|
||||
select: {
|
||||
launchId: true,
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ export const ImportVersion = type({
|
||||
name: "string",
|
||||
launch: "string",
|
||||
umuId: "string?",
|
||||
executorId: "string?",
|
||||
emulatorId: "string?",
|
||||
suggestions: "string[]?",
|
||||
}).array(),
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export default defineClientEventHandler(async (h3) => {
|
||||
include: {
|
||||
launches: {
|
||||
include: {
|
||||
executor: {
|
||||
emulator: {
|
||||
include: {
|
||||
gameVersion: {
|
||||
select: {
|
||||
@@ -46,11 +46,11 @@ export default defineClientEventHandler(async (h3) => {
|
||||
...gameVersion,
|
||||
launches: gameVersion.launches.map((launch) => ({
|
||||
...launch,
|
||||
executor: launch.executor
|
||||
emulator: launch.emulator
|
||||
? {
|
||||
...launch.executor,
|
||||
...launch.emulator,
|
||||
gameVersion: undefined,
|
||||
gameId: launch.executor.gameVersion.game.id,
|
||||
gameId: launch.emulator.gameVersion.game.id,
|
||||
}
|
||||
: undefined,
|
||||
})),
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { GameVersionSize } from "~/server/internal/gamesize";
|
||||
import gameSizeManager from "~/server/internal/gamesize";
|
||||
|
||||
type VersionDownloadOption = {
|
||||
gameId: string;
|
||||
versionId: string;
|
||||
displayName?: string | undefined;
|
||||
versionPath?: string | undefined;
|
||||
@@ -43,7 +44,7 @@ export default defineClientEventHandler(async (h3) => {
|
||||
launches: {
|
||||
select: {
|
||||
platform: true,
|
||||
executor: {
|
||||
emulator: {
|
||||
select: {
|
||||
gameVersion: {
|
||||
select: {
|
||||
@@ -78,18 +79,16 @@ export default defineClientEventHandler(async (h3) => {
|
||||
if (!platformOptions.has(launch.platform))
|
||||
platformOptions.set(launch.platform, []);
|
||||
|
||||
if ("executor" in launch && launch.executor) {
|
||||
if ("emulator" in launch && launch.emulator) {
|
||||
const old = platformOptions.get(launch.platform)!;
|
||||
const gv = launch.emulator.gameVersion;
|
||||
old.push({
|
||||
gameId: launch.executor.gameVersion.game.id,
|
||||
versionId: launch.executor.gameVersion.versionId,
|
||||
name: launch.executor.gameVersion.game.mName,
|
||||
iconObjectId: launch.executor.gameVersion.game.mIconObjectId,
|
||||
shortDescription:
|
||||
launch.executor.gameVersion.game.mShortDescription,
|
||||
size: (await gameSizeManager.getVersionSize(
|
||||
launch.executor.gameVersion.versionId,
|
||||
))!,
|
||||
gameId: gv.game.id,
|
||||
versionId: gv.versionId,
|
||||
name: gv.game.mName,
|
||||
iconObjectId: gv.game.mIconObjectId,
|
||||
shortDescription: gv.game.mShortDescription,
|
||||
size: (await gameSizeManager.getVersionSize(gv.versionId))!,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -101,6 +100,7 @@ export default defineClientEventHandler(async (h3) => {
|
||||
.map(
|
||||
([platform, requiredContent]) =>
|
||||
({
|
||||
gameId: v.gameId,
|
||||
versionId: v.versionId,
|
||||
displayName: v.displayName || undefined,
|
||||
versionPath: v.versionPath || undefined,
|
||||
|
||||
@@ -21,6 +21,12 @@ export default defineEventHandler(async (h3) => {
|
||||
launches: true,
|
||||
setups: true,
|
||||
},
|
||||
omit: {
|
||||
dropletManifest: true,
|
||||
},
|
||||
orderBy: {
|
||||
versionIndex: "desc",
|
||||
},
|
||||
},
|
||||
publishers: {
|
||||
select: {
|
||||
@@ -57,7 +63,30 @@ export default defineEventHandler(async (h3) => {
|
||||
},
|
||||
});
|
||||
|
||||
const size = (await gameSizeManager.getGameBreakdown(gameId))!;
|
||||
const sizes = await Promise.all(
|
||||
game.versions!.map(
|
||||
async (v) => (await gameSizeManager.getVersionSize(v.versionId))!,
|
||||
),
|
||||
);
|
||||
|
||||
return { game, rating, size };
|
||||
const platforms = new Set(
|
||||
game
|
||||
.versions!.map((v) => [
|
||||
...v.setups.map((v) => v.platform),
|
||||
...v.launches.map((v) => v.platform),
|
||||
])
|
||||
.flat(),
|
||||
);
|
||||
|
||||
const gameV: Omit<typeof game, "versions"> = game;
|
||||
|
||||
// @ts-expect-error value exists at runtime
|
||||
delete gameV.versions;
|
||||
|
||||
return {
|
||||
game: gameV,
|
||||
rating,
|
||||
sizes,
|
||||
platforms: platforms.values().toArray(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ import gameSizeManager from "~/server/internal/gamesize";
|
||||
import type { ImportVersion } from "~/server/api/v1/admin/import/version/index.post";
|
||||
import { GameType, type Platform } from "~/prisma/client/enums";
|
||||
import { castManifest } from "./manifest/utils";
|
||||
import { Shescape } from "shescape";
|
||||
|
||||
export function createGameImportTaskId(libraryId: string, libraryPath: string) {
|
||||
return createHash("md5")
|
||||
@@ -35,9 +36,9 @@ export function createVersionImportTaskKey(
|
||||
.digest("hex");
|
||||
}
|
||||
|
||||
export interface ExecutorVersionGuess {
|
||||
type: "executor";
|
||||
executorId: string;
|
||||
export interface EmulatorVersionGuess {
|
||||
type: "emulator";
|
||||
emulatorId: string;
|
||||
icon: string;
|
||||
gameName: string;
|
||||
versionName: string;
|
||||
@@ -51,7 +52,7 @@ export interface PlatformVersionGuess {
|
||||
export type VersionGuess = {
|
||||
filename: string;
|
||||
match: number;
|
||||
} & (PlatformVersionGuess | ExecutorVersionGuess);
|
||||
} & (PlatformVersionGuess | EmulatorVersionGuess);
|
||||
|
||||
export interface UnimportedVersionInformation {
|
||||
type: "local" | "depot";
|
||||
@@ -61,6 +62,7 @@ export interface UnimportedVersionInformation {
|
||||
|
||||
class LibraryManager {
|
||||
private libraries: Map<string, LibraryProvider<unknown>> = new Map();
|
||||
private shescape = new Shescape({});
|
||||
|
||||
addLibrary(library: LibraryProvider<unknown>) {
|
||||
this.libraries.set(library.id(), library);
|
||||
@@ -253,19 +255,19 @@ class LibraryManager {
|
||||
],
|
||||
};
|
||||
|
||||
const executorSuggestions = await prisma.launchConfiguration.findMany({
|
||||
const emulators = await prisma.launchConfiguration.findMany({
|
||||
where: {
|
||||
executorSuggestions: {
|
||||
emulatorSuggestions: {
|
||||
isEmpty: false,
|
||||
},
|
||||
gameVersion: {
|
||||
game: {
|
||||
type: GameType.Executor,
|
||||
type: GameType.Emulator,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
executorSuggestions: true,
|
||||
emulatorSuggestions: true,
|
||||
gameVersion: {
|
||||
select: {
|
||||
game: {
|
||||
@@ -318,28 +320,28 @@ class LibraryManager {
|
||||
const fuzzyValue = fuzzy(basename, game.mName);
|
||||
options.push({
|
||||
type: "platform",
|
||||
filename: filename.replaceAll(" ", "\\ "),
|
||||
filename: this.shescape.escape(filename),
|
||||
platform: platform as Platform,
|
||||
match: fuzzyValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const executorSuggestion of executorSuggestions) {
|
||||
for (const suggestion of executorSuggestion.executorSuggestions) {
|
||||
for (const emulator of emulators) {
|
||||
for (const suggestion of emulator.emulatorSuggestions) {
|
||||
if (suggestion != ext) continue;
|
||||
const fuzzyValue = fuzzy(basename, game.mName);
|
||||
options.push({
|
||||
type: "executor",
|
||||
filename: filename.replaceAll(" ", "\\ "),
|
||||
type: "emulator",
|
||||
filename: this.shescape.escape(filename),
|
||||
match: fuzzyValue,
|
||||
executorId: executorSuggestion.launchId,
|
||||
emulatorId: emulator.launchId,
|
||||
|
||||
icon: executorSuggestion.gameVersion.game.mIconObjectId,
|
||||
gameName: executorSuggestion.gameVersion.game.mName,
|
||||
versionName: (executorSuggestion.gameVersion.displayName ??
|
||||
executorSuggestion.gameVersion.versionPath)!,
|
||||
launchName: executorSuggestion.name,
|
||||
platform: executorSuggestion.platform,
|
||||
icon: emulator.gameVersion.game.mIconObjectId,
|
||||
gameName: emulator.gameVersion.game.mName,
|
||||
versionName: (emulator.gameVersion.displayName ??
|
||||
emulator.gameVersion.versionPath)!,
|
||||
launchName: emulator.name,
|
||||
platform: emulator.platform,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -382,10 +384,10 @@ class LibraryManager {
|
||||
});
|
||||
if (!game || !game.libraryId) return undefined;
|
||||
|
||||
if (game.type === GameType.Redist && !metadata.onlySetup)
|
||||
if (game.type === GameType.Dependency && !metadata.onlySetup)
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: "Redistributables can only be in setup-only mode.",
|
||||
message: "Dependencies can only be in setup-only mode.",
|
||||
});
|
||||
|
||||
const library = this.libraries.get(game.libraryId);
|
||||
@@ -474,13 +476,13 @@ class LibraryManager {
|
||||
name: v.name,
|
||||
command: v.launch,
|
||||
platform: v.platform,
|
||||
...(v.executorId && game.type === "Game"
|
||||
...(v.emulatorId && game.type === "Game"
|
||||
? {
|
||||
executorId: v.executorId,
|
||||
emulatorId: v.emulatorId,
|
||||
}
|
||||
: undefined),
|
||||
executorSuggestions:
|
||||
game.type === "Executor" ? (v.suggestions ?? []) : [],
|
||||
emulatorSuggestions:
|
||||
game.type === "Emulator" ? (v.suggestions ?? []) : [],
|
||||
})),
|
||||
}
|
||||
: { data: [] },
|
||||
@@ -549,7 +551,7 @@ class LibraryManager {
|
||||
where: {
|
||||
launches: {
|
||||
some: {
|
||||
executor: {
|
||||
emulator: {
|
||||
gameVersion: {
|
||||
gameId,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user