From 29813acb5a634f7763741053c9e80d3bab9883b4 Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Tue, 10 Jun 2025 20:00:41 +0200 Subject: [PATCH] feat: add metadata-related commands --- apps/oneclient/frontend/src/bindings.gen.ts | 99 --- apps/oneclient/frontend/src/routeTree.gen.ts | 147 ---- apps/onelauncher/frontend/src/bindings.gen.ts | 114 --- .../onelauncher/frontend/src/routeTree.gen.ts | 669 ------------------ packages/core/examples/metadata.rs | 8 +- packages/core/src/api/game/metadata.rs | 24 +- packages/core/src/api/tauri/commands.rs | 37 +- packages/core/src/store/metadata.rs | 49 +- packages/entity/src/domain/loader.rs | 10 + 9 files changed, 104 insertions(+), 1053 deletions(-) delete mode 100644 apps/oneclient/frontend/src/bindings.gen.ts delete mode 100644 apps/oneclient/frontend/src/routeTree.gen.ts delete mode 100644 apps/onelauncher/frontend/src/bindings.gen.ts delete mode 100644 apps/onelauncher/frontend/src/routeTree.gen.ts diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts deleted file mode 100644 index 273d8182..00000000 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ /dev/null @@ -1,99 +0,0 @@ -// This file has been generated by Specta. DO NOT EDIT. - -import type { InferCommandOutput } from 'taurpc'; -import { createTauRPCProxy as createProxy } from 'taurpc'; - -type TAURI_CHANNEL = (response: T) => void; - -export type ClusterError = { type: 'InvalidVersion'; data: string } | { type: 'MissingJavaVersion'; data: string } | { type: 'ClusterDownloading'; data: string } | { type: 'ClusterAlreadyRunning'; data: string }; - -export type ClusterStage = 'notready' | 'downloading' | 'repairing' | 'ready'; - -export interface CreateCluster { name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; icon: Icon | null } - -export type CryptoError = { type: 'InvalidHash'; data: string } | { type: 'InvalidAlgorithm'; data: string }; - -export type DaoError = { type: 'NotFound'; data: string } | { type: 'AlreadyExists'; data: string } | { type: 'InvalidValue'; data: string }; - -export interface DirectoryError { type: 'BaseDir'; data: string } - -export type DiscordError = { type: 'MissingClientId'; data: string } | { type: 'ConnectError'; data: string }; - -export type GameLoader = 'vanilla' | 'forge' | 'neoforge' | 'quilt' | 'fabric' | 'legacyfabric'; - -export type IOError = { type: 'InvalidAbsolutePath'; data: string } | { type: 'IOErrorWrapper'; data: string } | { type: 'IOError'; data: string } | { type: 'DeserializeError'; data: string } | { type: 'AsyncZipError'; data: string }; - -export type Icon = string; - -export type IncompatiblePackageType = { type: 'McVersion'; data: string } | { type: 'Loader'; data: string }; - -export interface IngressError { type: 'NotFound'; data: string } - -export interface IngressPayload { id: string; message: string; ingress_type: IngressType; percent: number | null; total: number } - -export type IngressType = { Download: { file_name: string } } | 'JavaPrepare' | 'JavaCheck' | 'JavaLocate' | 'MinecraftDownload' | { PrepareCluster: { cluster_name: string } }; - -export type JavaError = { type: 'ParseVersion'; data: string } | { type: 'Execute'; data: string } | { type: 'MissingJava'; data: string }; - -export type LauncherError = { type: 'DirError'; data: DirectoryError } | { type: 'IOError'; data: IOError } | { type: 'IngressError'; data: IngressError } | { type: 'JavaError'; data: JavaError } | { type: 'CryptoError'; data: CryptoError } | { type: 'DiscordError'; data: DiscordError } | { type: 'MetadataError'; data: MetadataError } | { type: 'ClusterError'; data: ClusterError } | { type: 'MinecraftAuthError'; data: MinecraftAuthError } | { type: 'ProcessError'; data: ProcessError } | { type: 'PackageError'; data: PackageError } | { type: 'DaoError'; data: DaoError } | { type: 'SerdeError'; data: string } | { type: 'AnyhowError'; data: string } | { type: 'DbError'; data: string } | { type: 'ReqwestError'; data: string } | { type: 'InterpulseError'; data: string } | { type: 'RegexError'; data: string } | { type: 'SemaphoreError'; data: string } | { type: 'UrlError'; data: string } | { type: 'TauriError'; data: string }; - -export type MessageLevel = 'Info' | 'Warn' | 'Error'; - -export interface MessagePayload { level: MessageLevel; message: string } - -export type MetadataError = { type: 'FetchError'; data: string } | { type: 'NotModdedManifest'; data: string } | { type: 'NotVanillaManifest'; data: string } | { type: 'ParseError'; data: string } | { type: 'NoMatchingLoader'; data: string } | { type: 'NoMatchingVersion'; data: string }; - -export type MinecraftAuthError = { type: 'PublicKeyReading'; data: string } | { type: 'PKCS8Error'; data: string } | { type: 'SerializeError'; data: string } | { type: 'DeserializeError'; data: string } | { type: 'RequestError'; data: string } | { type: 'SigningError'; data: string } | { type: 'HashError'; data: string } | { type: 'SessionIdError'; data: string }; - -/** - * A structure of all needed Minecraft credentials for logging in and account management. - */ -export interface MinecraftCredentials { -/** - * The uuid of the credentials. - */ - id: string; - /** - * The username of the Minecraft account. - */ - username: string; - /** - * The access token as a String. - */ - access_token: string; - /** - * The refresh token as a string for [`MinecraftState#refresh`]. - */ - refresh_token: string; - /** - * The time that the access token expires as a [`DateTime`]. - */ - expires: string; - -} - -export interface Model { id: bigint; folder_name: string; stage: ClusterStage; created_at: string; group_id: bigint | null; name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; last_played: string | null; overall_played: bigint | null; icon_url: Icon | null; setting_profile_name: string | null; linked_pack_id: string | null; linked_pack_version: string | null } - -export type PackageError = { type: 'NoPrimaryFile'; data: string } | { type: 'IsModPack'; data: string } | { type: 'Incompatible'; data: IncompatiblePackageType }; - -export interface Process { pid: number; started_at: string; cluster_id: bigint; post_hook: string | null; account_id: string } - -export type ProcessError = { type: 'HookUnsuccessful'; data: string } | { type: 'NoPid'; data: string }; - -export type ProcessPayload = { type: 'Starting'; command: string } | { type: 'Started'; process: Process } | { type: 'Stopped'; pid: number; exit_code: number } | { type: 'Output'; pid: number; output: string }; - -export interface Resolution { width: number; height: number } - -export interface SettingProfileModel { name: string; java_id: bigint | null; res: Resolution | null; force_fullscreen: boolean | null; mem_max: number | null; launch_args: string | null; launch_env: string | null; hook_pre: string | null; hook_wrapper: string | null; hook_post: string | null; os_extra: SettingsOsExtra | null } - -export interface SettingsOsExtra { enable_gamemode: boolean | null } - -const ARGS_MAP = { events: '{"message":["event"],"ingress":["event"],"process":["event"]}', core: '{"getUser":["uuid"],"getDefaultUser":["fallback"],"createCluster":["options"],"getClusters":[],"launchCluster":["id","uuid"],"setDefaultUser":["uuid"],"getGlobalProfile":[],"getProfileOrDefault":["name"],"getUsers":[],"openMsaLogin":[],"removeUser":["uuid"],"removeCluster":["id"],"getClusterById":["id"]}', oneclient: '{"return_error":[],"open_dev_tools":[]}' }; -export interface Router { - oneclient: { return_error: () => Promise; open_dev_tools: () => Promise }; - core: { getClusters: () => Promise>; getClusterById: (id: bigint) => Promise; removeCluster: (id: bigint) => Promise; createCluster: (options: CreateCluster) => Promise; launchCluster: (id: bigint, uuid: string | null) => Promise; getProfileOrDefault: (name: string | null) => Promise; getGlobalProfile: () => Promise; getUsers: () => Promise>; getUser: (uuid: string) => Promise; removeUser: (uuid: string) => Promise; getDefaultUser: (fallback: boolean | null) => Promise; setDefaultUser: (uuid: string | null) => Promise; openMsaLogin: () => Promise }; - events: { ingress: (event: IngressPayload) => Promise; message: (event: MessagePayload) => Promise; process: (event: ProcessPayload) => Promise }; -} - -export type { InferCommandOutput }; -export const createTauRPCProxy = () => createProxy(ARGS_MAP); diff --git a/apps/oneclient/frontend/src/routeTree.gen.ts b/apps/oneclient/frontend/src/routeTree.gen.ts deleted file mode 100644 index 9a5d0a35..00000000 --- a/apps/oneclient/frontend/src/routeTree.gen.ts +++ /dev/null @@ -1,147 +0,0 @@ -/* eslint-disable */ - -// @ts-nocheck - -// noinspection JSUnusedGlobalSymbols - -// This file was automatically generated by TanStack Router. -// You should NOT make any changes in this file as it will be overwritten. -// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. - -// Import Routes - -import { Route as rootRoute } from './routes/__root' -import { Route as TestRouteImport } from './routes/test/route' -import { Route as IndexImport } from './routes/index' -import { Route as TestAppImport } from './routes/test/app' - -// Create/Update Routes - -const TestRouteRoute = TestRouteImport.update({ - id: '/test', - path: '/test', - getParentRoute: () => rootRoute, -} as any) - -const IndexRoute = IndexImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRoute, -} as any) - -const TestAppRoute = TestAppImport.update({ - id: '/app', - path: '/app', - getParentRoute: () => TestRouteRoute, -} as any) - -// Populate the FileRoutesByPath interface - -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexImport - parentRoute: typeof rootRoute - } - '/test': { - id: '/test' - path: '/test' - fullPath: '/test' - preLoaderRoute: typeof TestRouteImport - parentRoute: typeof rootRoute - } - '/test/app': { - id: '/test/app' - path: '/app' - fullPath: '/test/app' - preLoaderRoute: typeof TestAppImport - parentRoute: typeof TestRouteImport - } - } -} - -// Create and export the route tree - -interface TestRouteRouteChildren { - TestAppRoute: typeof TestAppRoute -} - -const TestRouteRouteChildren: TestRouteRouteChildren = { - TestAppRoute: TestAppRoute, -} - -const TestRouteRouteWithChildren = TestRouteRoute._addFileChildren( - TestRouteRouteChildren, -) - -export interface FileRoutesByFullPath { - '/': typeof IndexRoute - '/test': typeof TestRouteRouteWithChildren - '/test/app': typeof TestAppRoute -} - -export interface FileRoutesByTo { - '/': typeof IndexRoute - '/test': typeof TestRouteRouteWithChildren - '/test/app': typeof TestAppRoute -} - -export interface FileRoutesById { - __root__: typeof rootRoute - '/': typeof IndexRoute - '/test': typeof TestRouteRouteWithChildren - '/test/app': typeof TestAppRoute -} - -export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/test' | '/test/app' - fileRoutesByTo: FileRoutesByTo - to: '/' | '/test' | '/test/app' - id: '__root__' | '/' | '/test' | '/test/app' - fileRoutesById: FileRoutesById -} - -export interface RootRouteChildren { - IndexRoute: typeof IndexRoute - TestRouteRoute: typeof TestRouteRouteWithChildren -} - -const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, - TestRouteRoute: TestRouteRouteWithChildren, -} - -export const routeTree = rootRoute - ._addFileChildren(rootRouteChildren) - ._addFileTypes() - -/* ROUTE_MANIFEST_START -{ - "routes": { - "__root__": { - "filePath": "__root.tsx", - "children": [ - "/", - "/test" - ] - }, - "/": { - "filePath": "index.tsx" - }, - "/test": { - "filePath": "test/route.tsx", - "children": [ - "/test/app" - ] - }, - "/test/app": { - "filePath": "test/app.tsx", - "parent": "/test" - } - } -} -ROUTE_MANIFEST_END */ diff --git a/apps/onelauncher/frontend/src/bindings.gen.ts b/apps/onelauncher/frontend/src/bindings.gen.ts deleted file mode 100644 index 6da474ae..00000000 --- a/apps/onelauncher/frontend/src/bindings.gen.ts +++ /dev/null @@ -1,114 +0,0 @@ -// This file has been generated by Specta. DO NOT EDIT. - -import type { InferCommandOutput } from 'taurpc'; -import { createTauRPCProxy as createProxy } from 'taurpc'; - -type TAURI_CHANNEL = (response: T) => void; - -export type ClusterError = { InvalidVersion: string } | 'MissingJavaVersion' | 'ClusterDownloading' | 'ClusterAlreadyRunning'; - -export type ClusterStage = 'notready' | 'downloading' | 'repairing' | 'ready'; - -export interface CreateCluster { name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; icon: Icon | null } - -export type CryptoError = { InvalidHash: { algorithm: HashAlgorithm; expected: string; actual: string } } | 'InvalidAlgorithm'; - -export type DaoError = 'NotFound' | 'AlreadyExists' | { InvalidValue: [string, string] }; - -export type DirectoryError = 'BaseDir'; - -export type DiscordError = 'MissingClientId' | 'ConnectError'; - -export type GameLoader = 'vanilla' | 'forge' | 'neoforge' | 'quilt' | 'fabric' | 'legacyfabric'; - -export type HashAlgorithm = 'Sha1' | 'Sha256' | 'Md5'; - -/** - * A wrapper around generic and unhelpful [`std::io::Error`] messages. - */ -export type IOError = { InvalidAbsolutePath: string } | { IOErrorWrapper: { path: string } } | 'IOError' | 'DeserializeError' | 'AsyncZipError'; - -export type Icon = string; - -export type IncompatiblePackageType = 'McVersion' | 'Loader'; - -export type IngressError = 'NotFound'; - -export interface IngressPayload { id: string; message: string; ingress_type: IngressType; percent: number | null; total: number } - -export type IngressType = { Download: { file_name: string } } | 'JavaPrepare' | 'JavaCheck' | 'JavaLocate' | 'MinecraftDownload' | { PrepareCluster: { cluster_name: string } }; - -export type JavaError = { ParseVersion: [string] } | 'Execute' | 'MissingJava'; - -export type LauncherError = { type: 'DirError'; data: DirectoryError } | { type: 'IOError'; data: IOError } | { type: 'IngressError'; data: IngressError } | { type: 'JavaError'; data: JavaError } | { type: 'CryptoError'; data: CryptoError } | { type: 'DiscordError'; data: DiscordError } | { type: 'MetadataError'; data: MetadataError } | { type: 'ClusterError'; data: ClusterError } | { type: 'MinecraftAuthError'; data: MinecraftAuthError } | { type: 'ProcessError'; data: ProcessError } | { type: 'PackageError'; data: PackageError } | { type: 'DaoError'; data: DaoError } | { type: 'SerdeError' } | { type: 'AnyhowError' } | { type: 'DbError' } | { type: 'ReqwestError' } | { type: 'InterpulseError' } | { type: 'RegexError' } | { type: 'SemaphoreError' } | { type: 'UrlError' } | { type: 'TauriError' }; - -export type LauncherEvent = { Ingress: IngressPayload } | { Message: MessagePayload } | { Process: ProcessPayload }; - -export type MessageLevel = 'Info' | 'Warn' | 'Error'; - -export interface MessagePayload { level: MessageLevel; message: string } - -export type MetadataError = 'FetchError' | { NotModdedManifest: GameLoader } | { NotVanillaManifest: GameLoader } | 'ParseError' | 'NoMatchingLoader' | 'NoMatchingVersion'; - -/** - * Wrapper around all `Error`s that can occur during the Microsoft authentication process. - */ -export type MinecraftAuthError = 'PublicKeyReading' | 'PKCS8Error' | { SerializeError: { step: MinecraftAuthStep } } | { DeserializeError: { step: MinecraftAuthStep; raw: string } } | { RequestError: { step: MinecraftAuthStep } } | { SigningError: { step: MinecraftAuthStep } } | 'HashError' | 'SessionIdError'; - -/** - * An ordered list of all MSA authentication steps. - */ -export type MinecraftAuthStep = 'DeviceToken' | 'SisuAuthenicate' | 'OAuthToken' | 'RefreshOAuthToken' | 'SisuAuthorize' | 'XstsAuthorize' | 'MinecraftToken' | 'MinecraftEntitlements' | 'MinecraftProfile'; - -/** - * A structure of all needed Minecraft credentials for logging in and account management. - */ -export interface MinecraftCredentials { -/** - * The uuid of the credentials. - */ - id: string; - /** - * The username of the Minecraft account. - */ - username: string; - /** - * The access token as a String. - */ - access_token: string; - /** - * The refresh token as a string for [`MinecraftState#refresh`]. - */ - refresh_token: string; - /** - * The time that the access token expires as a [`DateTime`]. - */ - expires: string; - -} - -export interface Model { id: bigint; folder_name: string; stage: ClusterStage; created_at: string; group_id: bigint | null; name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; last_played: string | null; overall_played: bigint | null; icon_url: Icon | null; setting_profile_name: string | null; linked_pack_id: string | null; linked_pack_version: string | null } - -export type PackageError = 'NoPrimaryFile' | 'IsModPack' | { Incompatible: IncompatiblePackageType }; - -export interface Process { pid: number; started_at: string; cluster_id: bigint; post_hook: string | null; account_id: string } - -export type ProcessError = { HookUnsuccessful: number } | 'NoPid'; - -export type ProcessPayload = { Starting: { command: string } } | { Started: { process: Process } } | { Stopped: { pid: number; exit_code: number } } | { Output: { pid: number; output: string } }; - -export interface Resolution { width: number; height: number } - -export interface SettingProfileModel { name: string; java_id: bigint | null; res: Resolution | null; force_fullscreen: boolean | null; mem_max: number | null; launch_args: string | null; launch_env: string | null; hook_pre: string | null; hook_wrapper: string | null; hook_post: string | null; os_extra: SettingsOsExtra | null } - -export interface SettingsOsExtra { enable_gamemode: boolean | null } - -const ARGS_MAP = { 'core': '{"getGlobalProfile":[],"getUsers":[],"removeUser":["uuid"],"getDefaultUser":["fallback"],"getUser":["uuid"],"getClusterById":["id"],"setDefaultUser":["uuid"],"openMsaLogin":[],"removeCluster":["id"],"getProfileOrDefault":["name"],"getClusters":[],"createCluster":["options"]}', 'onelauncher': '{"open_dev_tools":[],"return_error":[]}', '': '{"send_event":["event"]}' }; -export interface Router { - 'onelauncher': { return_error: () => Promise; open_dev_tools: () => Promise }; - '': { send_event: (event: LauncherEvent) => Promise }; - 'core': { getClusters: () => Promise>; getClusterById: (id: bigint) => Promise; removeCluster: (id: bigint) => Promise; createCluster: (options: CreateCluster) => Promise; getProfileOrDefault: (name: string | null) => Promise; getGlobalProfile: () => Promise; getUsers: () => Promise>; getUser: (uuid: string) => Promise; removeUser: (uuid: string) => Promise; getDefaultUser: (fallback: boolean | null) => Promise; setDefaultUser: (uuid: string | null) => Promise; openMsaLogin: () => Promise }; -} - -export type { InferCommandOutput }; -export const createTauRPCProxy = () => createProxy(ARGS_MAP); diff --git a/apps/onelauncher/frontend/src/routeTree.gen.ts b/apps/onelauncher/frontend/src/routeTree.gen.ts deleted file mode 100644 index 1ba59c25..00000000 --- a/apps/onelauncher/frontend/src/routeTree.gen.ts +++ /dev/null @@ -1,669 +0,0 @@ -/* eslint-disable */ - -// @ts-nocheck - -// noinspection JSUnusedGlobalSymbols - -// This file was automatically generated by TanStack Router. -// You should NOT make any changes in this file as it will be overwritten. -// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. - -// Import Routes - -import { Route as rootRoute } from './routes/__root' -import { Route as OnboardingRouteImport } from './routes/onboarding/route' -import { Route as AppRouteImport } from './routes/app/route' -import { Route as IndexImport } from './routes/index' -import { Route as OnboardingIndexImport } from './routes/onboarding/index' -import { Route as AppIndexImport } from './routes/app/index' -import { Route as OnboardingSummaryImport } from './routes/onboarding/summary' -import { Route as OnboardingLoginImport } from './routes/onboarding/login' -import { Route as OnboardingLanguageImport } from './routes/onboarding/language' -import { Route as OnboardingImportImport } from './routes/onboarding/import' -import { Route as OnboardingCompleteImport } from './routes/onboarding/complete' -import { Route as AppSettingsRouteImport } from './routes/app/settings/route' -import { Route as AppClusterRouteImport } from './routes/app/cluster/route' -import { Route as AppSettingsIndexImport } from './routes/app/settings/index' -import { Route as AppClusterIndexImport } from './routes/app/cluster/index' -import { Route as AppBrowserIndexImport } from './routes/app/browser/index' -import { Route as AppSettingsMinecraftImport } from './routes/app/settings/minecraft' -import { Route as AppSettingsAppearanceImport } from './routes/app/settings/appearance' -import { Route as AppSettingsAccountsImport } from './routes/app/settings/accounts' -import { Route as AppClusterWorldsImport } from './routes/app/cluster/worlds' -import { Route as AppClusterSettingsImport } from './routes/app/cluster/settings' -import { Route as AppClusterScreenshotsImport } from './routes/app/cluster/screenshots' - -// Create/Update Routes - -const OnboardingRouteRoute = OnboardingRouteImport.update({ - id: '/onboarding', - path: '/onboarding', - getParentRoute: () => rootRoute, -} as any) - -const AppRouteRoute = AppRouteImport.update({ - id: '/app', - path: '/app', - getParentRoute: () => rootRoute, -} as any) - -const IndexRoute = IndexImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRoute, -} as any) - -const OnboardingIndexRoute = OnboardingIndexImport.update({ - id: '/', - path: '/', - getParentRoute: () => OnboardingRouteRoute, -} as any) - -const AppIndexRoute = AppIndexImport.update({ - id: '/', - path: '/', - getParentRoute: () => AppRouteRoute, -} as any) - -const OnboardingSummaryRoute = OnboardingSummaryImport.update({ - id: '/summary', - path: '/summary', - getParentRoute: () => OnboardingRouteRoute, -} as any) - -const OnboardingLoginRoute = OnboardingLoginImport.update({ - id: '/login', - path: '/login', - getParentRoute: () => OnboardingRouteRoute, -} as any) - -const OnboardingLanguageRoute = OnboardingLanguageImport.update({ - id: '/language', - path: '/language', - getParentRoute: () => OnboardingRouteRoute, -} as any) - -const OnboardingImportRoute = OnboardingImportImport.update({ - id: '/import', - path: '/import', - getParentRoute: () => OnboardingRouteRoute, -} as any) - -const OnboardingCompleteRoute = OnboardingCompleteImport.update({ - id: '/complete', - path: '/complete', - getParentRoute: () => OnboardingRouteRoute, -} as any) - -const AppSettingsRouteRoute = AppSettingsRouteImport.update({ - id: '/settings', - path: '/settings', - getParentRoute: () => AppRouteRoute, -} as any) - -const AppClusterRouteRoute = AppClusterRouteImport.update({ - id: '/cluster', - path: '/cluster', - getParentRoute: () => AppRouteRoute, -} as any) - -const AppSettingsIndexRoute = AppSettingsIndexImport.update({ - id: '/', - path: '/', - getParentRoute: () => AppSettingsRouteRoute, -} as any) - -const AppClusterIndexRoute = AppClusterIndexImport.update({ - id: '/', - path: '/', - getParentRoute: () => AppClusterRouteRoute, -} as any) - -const AppBrowserIndexRoute = AppBrowserIndexImport.update({ - id: '/browser/', - path: '/browser/', - getParentRoute: () => AppRouteRoute, -} as any) - -const AppSettingsMinecraftRoute = AppSettingsMinecraftImport.update({ - id: '/minecraft', - path: '/minecraft', - getParentRoute: () => AppSettingsRouteRoute, -} as any) - -const AppSettingsAppearanceRoute = AppSettingsAppearanceImport.update({ - id: '/appearance', - path: '/appearance', - getParentRoute: () => AppSettingsRouteRoute, -} as any) - -const AppSettingsAccountsRoute = AppSettingsAccountsImport.update({ - id: '/accounts', - path: '/accounts', - getParentRoute: () => AppSettingsRouteRoute, -} as any) - -const AppClusterWorldsRoute = AppClusterWorldsImport.update({ - id: '/worlds', - path: '/worlds', - getParentRoute: () => AppClusterRouteRoute, -} as any) - -const AppClusterSettingsRoute = AppClusterSettingsImport.update({ - id: '/settings', - path: '/settings', - getParentRoute: () => AppClusterRouteRoute, -} as any) - -const AppClusterScreenshotsRoute = AppClusterScreenshotsImport.update({ - id: '/screenshots', - path: '/screenshots', - getParentRoute: () => AppClusterRouteRoute, -} as any) - -// Populate the FileRoutesByPath interface - -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexImport - parentRoute: typeof rootRoute - } - '/app': { - id: '/app' - path: '/app' - fullPath: '/app' - preLoaderRoute: typeof AppRouteImport - parentRoute: typeof rootRoute - } - '/onboarding': { - id: '/onboarding' - path: '/onboarding' - fullPath: '/onboarding' - preLoaderRoute: typeof OnboardingRouteImport - parentRoute: typeof rootRoute - } - '/app/cluster': { - id: '/app/cluster' - path: '/cluster' - fullPath: '/app/cluster' - preLoaderRoute: typeof AppClusterRouteImport - parentRoute: typeof AppRouteImport - } - '/app/settings': { - id: '/app/settings' - path: '/settings' - fullPath: '/app/settings' - preLoaderRoute: typeof AppSettingsRouteImport - parentRoute: typeof AppRouteImport - } - '/onboarding/complete': { - id: '/onboarding/complete' - path: '/complete' - fullPath: '/onboarding/complete' - preLoaderRoute: typeof OnboardingCompleteImport - parentRoute: typeof OnboardingRouteImport - } - '/onboarding/import': { - id: '/onboarding/import' - path: '/import' - fullPath: '/onboarding/import' - preLoaderRoute: typeof OnboardingImportImport - parentRoute: typeof OnboardingRouteImport - } - '/onboarding/language': { - id: '/onboarding/language' - path: '/language' - fullPath: '/onboarding/language' - preLoaderRoute: typeof OnboardingLanguageImport - parentRoute: typeof OnboardingRouteImport - } - '/onboarding/login': { - id: '/onboarding/login' - path: '/login' - fullPath: '/onboarding/login' - preLoaderRoute: typeof OnboardingLoginImport - parentRoute: typeof OnboardingRouteImport - } - '/onboarding/summary': { - id: '/onboarding/summary' - path: '/summary' - fullPath: '/onboarding/summary' - preLoaderRoute: typeof OnboardingSummaryImport - parentRoute: typeof OnboardingRouteImport - } - '/app/': { - id: '/app/' - path: '/' - fullPath: '/app/' - preLoaderRoute: typeof AppIndexImport - parentRoute: typeof AppRouteImport - } - '/onboarding/': { - id: '/onboarding/' - path: '/' - fullPath: '/onboarding/' - preLoaderRoute: typeof OnboardingIndexImport - parentRoute: typeof OnboardingRouteImport - } - '/app/cluster/screenshots': { - id: '/app/cluster/screenshots' - path: '/screenshots' - fullPath: '/app/cluster/screenshots' - preLoaderRoute: typeof AppClusterScreenshotsImport - parentRoute: typeof AppClusterRouteImport - } - '/app/cluster/settings': { - id: '/app/cluster/settings' - path: '/settings' - fullPath: '/app/cluster/settings' - preLoaderRoute: typeof AppClusterSettingsImport - parentRoute: typeof AppClusterRouteImport - } - '/app/cluster/worlds': { - id: '/app/cluster/worlds' - path: '/worlds' - fullPath: '/app/cluster/worlds' - preLoaderRoute: typeof AppClusterWorldsImport - parentRoute: typeof AppClusterRouteImport - } - '/app/settings/accounts': { - id: '/app/settings/accounts' - path: '/accounts' - fullPath: '/app/settings/accounts' - preLoaderRoute: typeof AppSettingsAccountsImport - parentRoute: typeof AppSettingsRouteImport - } - '/app/settings/appearance': { - id: '/app/settings/appearance' - path: '/appearance' - fullPath: '/app/settings/appearance' - preLoaderRoute: typeof AppSettingsAppearanceImport - parentRoute: typeof AppSettingsRouteImport - } - '/app/settings/minecraft': { - id: '/app/settings/minecraft' - path: '/minecraft' - fullPath: '/app/settings/minecraft' - preLoaderRoute: typeof AppSettingsMinecraftImport - parentRoute: typeof AppSettingsRouteImport - } - '/app/browser/': { - id: '/app/browser/' - path: '/browser' - fullPath: '/app/browser' - preLoaderRoute: typeof AppBrowserIndexImport - parentRoute: typeof AppRouteImport - } - '/app/cluster/': { - id: '/app/cluster/' - path: '/' - fullPath: '/app/cluster/' - preLoaderRoute: typeof AppClusterIndexImport - parentRoute: typeof AppClusterRouteImport - } - '/app/settings/': { - id: '/app/settings/' - path: '/' - fullPath: '/app/settings/' - preLoaderRoute: typeof AppSettingsIndexImport - parentRoute: typeof AppSettingsRouteImport - } - } -} - -// Create and export the route tree - -interface AppClusterRouteRouteChildren { - AppClusterScreenshotsRoute: typeof AppClusterScreenshotsRoute - AppClusterSettingsRoute: typeof AppClusterSettingsRoute - AppClusterWorldsRoute: typeof AppClusterWorldsRoute - AppClusterIndexRoute: typeof AppClusterIndexRoute -} - -const AppClusterRouteRouteChildren: AppClusterRouteRouteChildren = { - AppClusterScreenshotsRoute: AppClusterScreenshotsRoute, - AppClusterSettingsRoute: AppClusterSettingsRoute, - AppClusterWorldsRoute: AppClusterWorldsRoute, - AppClusterIndexRoute: AppClusterIndexRoute, -} - -const AppClusterRouteRouteWithChildren = AppClusterRouteRoute._addFileChildren( - AppClusterRouteRouteChildren, -) - -interface AppSettingsRouteRouteChildren { - AppSettingsAccountsRoute: typeof AppSettingsAccountsRoute - AppSettingsAppearanceRoute: typeof AppSettingsAppearanceRoute - AppSettingsMinecraftRoute: typeof AppSettingsMinecraftRoute - AppSettingsIndexRoute: typeof AppSettingsIndexRoute -} - -const AppSettingsRouteRouteChildren: AppSettingsRouteRouteChildren = { - AppSettingsAccountsRoute: AppSettingsAccountsRoute, - AppSettingsAppearanceRoute: AppSettingsAppearanceRoute, - AppSettingsMinecraftRoute: AppSettingsMinecraftRoute, - AppSettingsIndexRoute: AppSettingsIndexRoute, -} - -const AppSettingsRouteRouteWithChildren = - AppSettingsRouteRoute._addFileChildren(AppSettingsRouteRouteChildren) - -interface AppRouteRouteChildren { - AppClusterRouteRoute: typeof AppClusterRouteRouteWithChildren - AppSettingsRouteRoute: typeof AppSettingsRouteRouteWithChildren - AppIndexRoute: typeof AppIndexRoute - AppBrowserIndexRoute: typeof AppBrowserIndexRoute -} - -const AppRouteRouteChildren: AppRouteRouteChildren = { - AppClusterRouteRoute: AppClusterRouteRouteWithChildren, - AppSettingsRouteRoute: AppSettingsRouteRouteWithChildren, - AppIndexRoute: AppIndexRoute, - AppBrowserIndexRoute: AppBrowserIndexRoute, -} - -const AppRouteRouteWithChildren = AppRouteRoute._addFileChildren( - AppRouteRouteChildren, -) - -interface OnboardingRouteRouteChildren { - OnboardingCompleteRoute: typeof OnboardingCompleteRoute - OnboardingImportRoute: typeof OnboardingImportRoute - OnboardingLanguageRoute: typeof OnboardingLanguageRoute - OnboardingLoginRoute: typeof OnboardingLoginRoute - OnboardingSummaryRoute: typeof OnboardingSummaryRoute - OnboardingIndexRoute: typeof OnboardingIndexRoute -} - -const OnboardingRouteRouteChildren: OnboardingRouteRouteChildren = { - OnboardingCompleteRoute: OnboardingCompleteRoute, - OnboardingImportRoute: OnboardingImportRoute, - OnboardingLanguageRoute: OnboardingLanguageRoute, - OnboardingLoginRoute: OnboardingLoginRoute, - OnboardingSummaryRoute: OnboardingSummaryRoute, - OnboardingIndexRoute: OnboardingIndexRoute, -} - -const OnboardingRouteRouteWithChildren = OnboardingRouteRoute._addFileChildren( - OnboardingRouteRouteChildren, -) - -export interface FileRoutesByFullPath { - '/': typeof IndexRoute - '/app': typeof AppRouteRouteWithChildren - '/onboarding': typeof OnboardingRouteRouteWithChildren - '/app/cluster': typeof AppClusterRouteRouteWithChildren - '/app/settings': typeof AppSettingsRouteRouteWithChildren - '/onboarding/complete': typeof OnboardingCompleteRoute - '/onboarding/import': typeof OnboardingImportRoute - '/onboarding/language': typeof OnboardingLanguageRoute - '/onboarding/login': typeof OnboardingLoginRoute - '/onboarding/summary': typeof OnboardingSummaryRoute - '/app/': typeof AppIndexRoute - '/onboarding/': typeof OnboardingIndexRoute - '/app/cluster/screenshots': typeof AppClusterScreenshotsRoute - '/app/cluster/settings': typeof AppClusterSettingsRoute - '/app/cluster/worlds': typeof AppClusterWorldsRoute - '/app/settings/accounts': typeof AppSettingsAccountsRoute - '/app/settings/appearance': typeof AppSettingsAppearanceRoute - '/app/settings/minecraft': typeof AppSettingsMinecraftRoute - '/app/browser': typeof AppBrowserIndexRoute - '/app/cluster/': typeof AppClusterIndexRoute - '/app/settings/': typeof AppSettingsIndexRoute -} - -export interface FileRoutesByTo { - '/': typeof IndexRoute - '/onboarding/complete': typeof OnboardingCompleteRoute - '/onboarding/import': typeof OnboardingImportRoute - '/onboarding/language': typeof OnboardingLanguageRoute - '/onboarding/login': typeof OnboardingLoginRoute - '/onboarding/summary': typeof OnboardingSummaryRoute - '/app': typeof AppIndexRoute - '/onboarding': typeof OnboardingIndexRoute - '/app/cluster/screenshots': typeof AppClusterScreenshotsRoute - '/app/cluster/settings': typeof AppClusterSettingsRoute - '/app/cluster/worlds': typeof AppClusterWorldsRoute - '/app/settings/accounts': typeof AppSettingsAccountsRoute - '/app/settings/appearance': typeof AppSettingsAppearanceRoute - '/app/settings/minecraft': typeof AppSettingsMinecraftRoute - '/app/browser': typeof AppBrowserIndexRoute - '/app/cluster': typeof AppClusterIndexRoute - '/app/settings': typeof AppSettingsIndexRoute -} - -export interface FileRoutesById { - __root__: typeof rootRoute - '/': typeof IndexRoute - '/app': typeof AppRouteRouteWithChildren - '/onboarding': typeof OnboardingRouteRouteWithChildren - '/app/cluster': typeof AppClusterRouteRouteWithChildren - '/app/settings': typeof AppSettingsRouteRouteWithChildren - '/onboarding/complete': typeof OnboardingCompleteRoute - '/onboarding/import': typeof OnboardingImportRoute - '/onboarding/language': typeof OnboardingLanguageRoute - '/onboarding/login': typeof OnboardingLoginRoute - '/onboarding/summary': typeof OnboardingSummaryRoute - '/app/': typeof AppIndexRoute - '/onboarding/': typeof OnboardingIndexRoute - '/app/cluster/screenshots': typeof AppClusterScreenshotsRoute - '/app/cluster/settings': typeof AppClusterSettingsRoute - '/app/cluster/worlds': typeof AppClusterWorldsRoute - '/app/settings/accounts': typeof AppSettingsAccountsRoute - '/app/settings/appearance': typeof AppSettingsAppearanceRoute - '/app/settings/minecraft': typeof AppSettingsMinecraftRoute - '/app/browser/': typeof AppBrowserIndexRoute - '/app/cluster/': typeof AppClusterIndexRoute - '/app/settings/': typeof AppSettingsIndexRoute -} - -export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: - | '/' - | '/app' - | '/onboarding' - | '/app/cluster' - | '/app/settings' - | '/onboarding/complete' - | '/onboarding/import' - | '/onboarding/language' - | '/onboarding/login' - | '/onboarding/summary' - | '/app/' - | '/onboarding/' - | '/app/cluster/screenshots' - | '/app/cluster/settings' - | '/app/cluster/worlds' - | '/app/settings/accounts' - | '/app/settings/appearance' - | '/app/settings/minecraft' - | '/app/browser' - | '/app/cluster/' - | '/app/settings/' - fileRoutesByTo: FileRoutesByTo - to: - | '/' - | '/onboarding/complete' - | '/onboarding/import' - | '/onboarding/language' - | '/onboarding/login' - | '/onboarding/summary' - | '/app' - | '/onboarding' - | '/app/cluster/screenshots' - | '/app/cluster/settings' - | '/app/cluster/worlds' - | '/app/settings/accounts' - | '/app/settings/appearance' - | '/app/settings/minecraft' - | '/app/browser' - | '/app/cluster' - | '/app/settings' - id: - | '__root__' - | '/' - | '/app' - | '/onboarding' - | '/app/cluster' - | '/app/settings' - | '/onboarding/complete' - | '/onboarding/import' - | '/onboarding/language' - | '/onboarding/login' - | '/onboarding/summary' - | '/app/' - | '/onboarding/' - | '/app/cluster/screenshots' - | '/app/cluster/settings' - | '/app/cluster/worlds' - | '/app/settings/accounts' - | '/app/settings/appearance' - | '/app/settings/minecraft' - | '/app/browser/' - | '/app/cluster/' - | '/app/settings/' - fileRoutesById: FileRoutesById -} - -export interface RootRouteChildren { - IndexRoute: typeof IndexRoute - AppRouteRoute: typeof AppRouteRouteWithChildren - OnboardingRouteRoute: typeof OnboardingRouteRouteWithChildren -} - -const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, - AppRouteRoute: AppRouteRouteWithChildren, - OnboardingRouteRoute: OnboardingRouteRouteWithChildren, -} - -export const routeTree = rootRoute - ._addFileChildren(rootRouteChildren) - ._addFileTypes() - -/* ROUTE_MANIFEST_START -{ - "routes": { - "__root__": { - "filePath": "__root.tsx", - "children": [ - "/", - "/app", - "/onboarding" - ] - }, - "/": { - "filePath": "index.tsx" - }, - "/app": { - "filePath": "app/route.tsx", - "children": [ - "/app/cluster", - "/app/settings", - "/app/", - "/app/browser/" - ] - }, - "/onboarding": { - "filePath": "onboarding/route.tsx", - "children": [ - "/onboarding/complete", - "/onboarding/import", - "/onboarding/language", - "/onboarding/login", - "/onboarding/summary", - "/onboarding/" - ] - }, - "/app/cluster": { - "filePath": "app/cluster/route.tsx", - "parent": "/app", - "children": [ - "/app/cluster/screenshots", - "/app/cluster/settings", - "/app/cluster/worlds", - "/app/cluster/" - ] - }, - "/app/settings": { - "filePath": "app/settings/route.tsx", - "parent": "/app", - "children": [ - "/app/settings/accounts", - "/app/settings/appearance", - "/app/settings/minecraft", - "/app/settings/" - ] - }, - "/onboarding/complete": { - "filePath": "onboarding/complete.tsx", - "parent": "/onboarding" - }, - "/onboarding/import": { - "filePath": "onboarding/import.tsx", - "parent": "/onboarding" - }, - "/onboarding/language": { - "filePath": "onboarding/language.tsx", - "parent": "/onboarding" - }, - "/onboarding/login": { - "filePath": "onboarding/login.tsx", - "parent": "/onboarding" - }, - "/onboarding/summary": { - "filePath": "onboarding/summary.tsx", - "parent": "/onboarding" - }, - "/app/": { - "filePath": "app/index.tsx", - "parent": "/app" - }, - "/onboarding/": { - "filePath": "onboarding/index.tsx", - "parent": "/onboarding" - }, - "/app/cluster/screenshots": { - "filePath": "app/cluster/screenshots.tsx", - "parent": "/app/cluster" - }, - "/app/cluster/settings": { - "filePath": "app/cluster/settings.tsx", - "parent": "/app/cluster" - }, - "/app/cluster/worlds": { - "filePath": "app/cluster/worlds.tsx", - "parent": "/app/cluster" - }, - "/app/settings/accounts": { - "filePath": "app/settings/accounts.tsx", - "parent": "/app/settings" - }, - "/app/settings/appearance": { - "filePath": "app/settings/appearance.tsx", - "parent": "/app/settings" - }, - "/app/settings/minecraft": { - "filePath": "app/settings/minecraft.tsx", - "parent": "/app/settings" - }, - "/app/browser/": { - "filePath": "app/browser/index.tsx", - "parent": "/app" - }, - "/app/cluster/": { - "filePath": "app/cluster/index.tsx", - "parent": "/app/cluster" - }, - "/app/settings/": { - "filePath": "app/settings/index.tsx", - "parent": "/app/settings" - } - } -} -ROUTE_MANIFEST_END */ diff --git a/packages/core/examples/metadata.rs b/packages/core/examples/metadata.rs index e6bd8f49..47fbd5f7 100644 --- a/packages/core/examples/metadata.rs +++ b/packages/core/examples/metadata.rs @@ -10,11 +10,11 @@ async fn main() -> LauncherResult<()> { println!("Initialized: {}", metadata.initialized()); println!("Minecraft: {:?}", metadata.get_vanilla_or_fetch().await.is_ok()); - println!("Forge: {:?}", metadata.get_modded_or_fetch(GameLoader::Forge).await.is_ok()); + println!("Forge: {:?}", metadata.get_modded_or_fetch(&GameLoader::Forge).await.is_ok()); println!("Initialized: {}", metadata.initialized()); - println!("NeoForge: {:?}", metadata.get_modded_or_fetch(GameLoader::NeoForge).await.is_ok()); - println!("Fabric: {:?}", metadata.get_modded_or_fetch(GameLoader::Fabric).await.is_ok()); - println!("Quilt: {:?}", metadata.get_modded_or_fetch(GameLoader::Quilt).await.is_ok()); + println!("NeoForge: {:?}", metadata.get_modded_or_fetch(&GameLoader::NeoForge).await.is_ok()); + println!("Fabric: {:?}", metadata.get_modded_or_fetch(&GameLoader::Fabric).await.is_ok()); + println!("Quilt: {:?}", metadata.get_modded_or_fetch(&GameLoader::Quilt).await.is_ok()); Ok(()) } \ No newline at end of file diff --git a/packages/core/src/api/game/metadata.rs b/packages/core/src/api/game/metadata.rs index 23e38502..bf3792d8 100644 --- a/packages/core/src/api/game/metadata.rs +++ b/packages/core/src/api/game/metadata.rs @@ -386,7 +386,7 @@ pub async fn download_libraries( Ok(failed) } -// MARK: Loader Version +// MARK: Get Loader /// Gets the loader version for a given Minecraft version and loader /// If `loader_version` is `None`, it will return the latest stable version else /// it will return the specified version if found @@ -402,7 +402,7 @@ pub async fn get_loader_version( let state = State::get().await?; let metadata = state.metadata.read().await; - let manifest = metadata.get_modded(loader)?; + let manifest = metadata.get_modded(&loader)?; let Some(loaders) = manifest.game_versions.iter().find(|it| { it.id @@ -422,3 +422,23 @@ pub async fn get_loader_version( Ok(Some(loader_version)) } + +// MARK: Get Versions +/// Gets the list of Minecraft versions from the metadata store +#[tracing::instrument] +pub async fn get_game_versions() -> LauncherResult> { + let state = State::get().await?; + let metadata = state.metadata.read().await; + + let manifest = metadata.get_vanilla()?; + Ok(manifest.versions.clone()) +} + +/// Gets the list of modded loaders and their versions from the metadata store +#[tracing::instrument] +pub async fn get_loaders_for_version(mc_version: &str) -> LauncherResult> { + let state = State::get().await?; + let mut metadata = state.metadata.write().await; + + Ok(metadata.get_loaders_for_version(mc_version).await?) +} diff --git a/packages/core/src/api/tauri/commands.rs b/packages/core/src/api/tauri/commands.rs index 770563e6..05455a7e 100644 --- a/packages/core/src/api/tauri/commands.rs +++ b/packages/core/src/api/tauri/commands.rs @@ -1,3 +1,4 @@ +use interpulse::api::minecraft::Version; use onelauncher_entity::{icon::Icon, loader::GameLoader}; use tauri::{AppHandle, Runtime}; @@ -32,6 +33,13 @@ pub trait TauriLauncherApi { #[taurpc(alias = "getGlobalProfile")] async fn get_global_profile() -> SettingsProfile; + // Game Metadata + #[taurpc(alias = "getGameVersions")] + async fn get_game_versions() -> LauncherResult>; + + #[taurpc(alias = "getLoadersForVersion")] + async fn get_loaders_for_version(mc_version: String) -> LauncherResult>; + // Users #[taurpc(alias = "getUsers")] @@ -70,6 +78,8 @@ pub struct TauriLauncherApiImpl; #[taurpc::resolvers] impl TauriLauncherApi for TauriLauncherApiImpl { + + // Clusters async fn get_clusters(self) -> LauncherResult> { api::cluster::dao::get_all_clusters().await } @@ -85,13 +95,6 @@ impl TauriLauncherApi for TauriLauncherApiImpl { async fn create_cluster(self, options: CreateCluster) -> LauncherResult { let cluster = api::cluster::create_cluster(&options.name, &options.mc_version, options.mc_loader, options.mc_loader_version.as_deref(), options.icon).await?; - // if api::setting_profiles::dao::get_profile_by_name(&options.name).await?.is_none() { - // api::setting_profiles::create_profile(&options.name, async |mut profile| { - // profile.mem_max = sea_orm::ActiveValue::Set(Some(2048)); - // Ok(profile) - // }).await?; - // } - Ok(cluster) } @@ -114,14 +117,28 @@ impl TauriLauncherApi for TauriLauncherApiImpl { Ok(()) } - async fn get_profile_or_default(self, name: Option) -> LauncherResult { - api::setting_profiles::dao::get_profile_or_default(name.as_ref()).await - } + // Setting Profiles async fn get_global_profile(self) -> SettingsProfile { api::setting_profiles::get_global_profile().await } + async fn get_profile_or_default(self, name: Option) -> LauncherResult { + api::setting_profiles::dao::get_profile_or_default(name.as_ref()).await + } + + + // Game Metadata + async fn get_loaders_for_version(self, mc_version: String) -> LauncherResult> { + api::game::metadata::get_loaders_for_version(&mc_version).await + } + + async fn get_game_versions(self) -> LauncherResult> { + api::game::metadata::get_game_versions().await + } + + + // Users async fn get_users(self) -> LauncherResult> { api::credentials::get_users().await } diff --git a/packages/core/src/store/metadata.rs b/packages/core/src/store/metadata.rs index 6de36ecd..e05b6e92 100644 --- a/packages/core/src/store/metadata.rs +++ b/packages/core/src/store/metadata.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::str::FromStr; use interpulse::api::minecraft::VersionManifest as VanillaManifest; @@ -8,7 +9,7 @@ use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use crate::constants; -use crate::error::LauncherResult; +use crate::error::{LauncherError, LauncherResult}; use crate::utils::http::fetch_json; use crate::utils::io; @@ -19,6 +20,7 @@ use super::Dirs; pub struct Metadata { initialized: bool, inner: MetadataInner, + version_loader_cache: HashMap> } #[derive(Debug, Default, Serialize, Deserialize)] @@ -74,9 +76,9 @@ impl Metadata { } #[tracing::instrument(skip(self))] - pub async fn get_modded_or_fetch(&mut self, loader: GameLoader) -> LauncherResult<&ModdedManifest> { + pub async fn get_modded_or_fetch(&mut self, loader: &GameLoader) -> LauncherResult<&ModdedManifest> { if !loader.is_modded() { - return Err(MetadataError::NotModdedManifest(loader).into()); + return Err(MetadataError::NotModdedManifest(*loader).into()); } if !self.initialized() { @@ -90,9 +92,9 @@ impl Metadata { self.inner.minecraft.as_ref().ok_or_else(|| MetadataError::FetchError.into()) } - pub fn get_modded(&self, loader: GameLoader) -> LauncherResult<&ModdedManifest> { + pub fn get_modded(&self, loader: &GameLoader) -> LauncherResult<&ModdedManifest> { if !loader.is_modded() { - return Err(MetadataError::NotModdedManifest(loader).into()); + return Err(MetadataError::NotModdedManifest(*loader).into()); } match loader { @@ -219,17 +221,48 @@ impl Metadata { (quilt, true) }; } + + /// Returns a list of loaders that are compatible with the given Minecraft version. + /// + /// WARNING: Can be heavy in certain cases! + pub async fn get_loaders_for_version(&mut self, mc_version: &str) -> LauncherResult> { + if !self.initialized() { + self.initialize().await?; + } + + if let Some(hit) = self.version_loader_cache.get(mc_version) { + return Ok(hit.clone()); + } + + let mut loaders = Vec::::new(); + for loader in GameLoader::modded_loaders() { + let manifest = match self.get_modded(loader) { + Ok(manifest) => manifest, + Err(LauncherError::MetadataError(MetadataError::NotModdedManifest(_))) => continue, + Err(e) => return Err(e), + }; + + let found = manifest.game_versions.iter().find(|it| it.id == mc_version); + if found.is_some() { + loaders.push(*loader); + } + } + + self.version_loader_cache.insert(mc_version.to_owned(), loaders.clone()); + + Ok(loaders) + } } -pub async fn fetch_vanilla_manifest(loader: GameLoader) -> LauncherResult { +async fn fetch_vanilla_manifest(loader: GameLoader) -> LauncherResult { fetch_manifest(loader).await } -pub async fn fetch_modded_manifest(loader: GameLoader) -> LauncherResult { +async fn fetch_modded_manifest(loader: GameLoader) -> LauncherResult { fetch_manifest(loader).await } -pub async fn fetch_manifest(loader: GameLoader) -> LauncherResult { +async fn fetch_manifest(loader: GameLoader) -> LauncherResult { fetch_json::( Method::GET, format!( diff --git a/packages/entity/src/domain/loader.rs b/packages/entity/src/domain/loader.rs index 14dc3322..fa4dca20 100644 --- a/packages/entity/src/domain/loader.rs +++ b/packages/entity/src/domain/loader.rs @@ -55,6 +55,16 @@ impl GameLoader { } } + pub const fn modded_loaders() -> &'static [Self] { + &[ + Self::Forge, + Self::NeoForge, + Self::Quilt, + Self::Fabric, + Self::LegacyFabric, + ] + } + } impl TryFrom for GameLoader {