-
Notifications
You must be signed in to change notification settings - Fork 254
Ecosystem merge #2113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Ecosystem merge #2113
Changes from all commits
2d035c8
21646d0
4f31a9a
0b5d932
207c3d2
e661b0c
61f61c2
781013b
40e0721
380210f
16b23cd
01838e6
d8825c9
e31b49b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { defineBoot } from '#q-app/wrappers'; | ||
| import {updateEcosystemReactives} from "src/r2mm/ecosystem/EcosystemSchema"; | ||
| import FsProvider from "src/providers/generic/file/FsProvider"; | ||
| import {NodeFsImplementation} from "src/providers/node/fs/NodeFsImplementation"; | ||
|
|
||
| // @ts-ignore | ||
| export default defineBoot(async ({ app }) => { | ||
| FsProvider.provide(() => NodeFsImplementation); | ||
| await updateEcosystemReactives(); | ||
| // @ts-ignore | ||
| FsProvider.provide(() => undefined); | ||
|
Comment on lines
+8
to
+11
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't cause any issues because the intent is to set it when needed, then allow it to be re-set as part of the ordinary workflow. A separate PR addresses the concern and removes the need for the unset. |
||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| import bundledEcosystem from "../../assets/data/ecosystem.json"; | ||
| import {R2Modman, ThunderstoreEcosystem} from "../../assets/data/ecosystemTypes"; | ||
| import jsonSchema from "../../assets/data/ecosystemJsonSchema.json"; | ||
| import R2Error from "../../model/errors/R2Error"; | ||
| import Ajv from "ajv"; | ||
| import addFormats from "ajv-formats"; | ||
| import PathResolver from "../manager/PathResolver"; | ||
| import path from "../../providers/node/path/path"; | ||
| import FsProvider from "../../providers/generic/file/FsProvider"; | ||
| import VersionNumber from "../../model/VersionNumber"; | ||
| import ManagerInformation from "../../_managerinf/ManagerInformation"; | ||
| import {EcosystemModloaderPackages, EcosystemSupportedGames} from "../../model/schema/ThunderstoreSchema"; | ||
| import {updateModLoaderExports} from "../installing/profile_installers/ModLoaderVariantRecord"; | ||
| import LoggerProvider, {LogSeverity} from "../../providers/ror2/logging/LoggerProvider"; | ||
|
|
||
| export type VersionedThunderstoreEcosystem = ThunderstoreEcosystem & {version: string}; | ||
|
|
||
| async function getMergedEcosystemPath(): Promise<string> { | ||
| return path.join(PathResolver.ROOT, "latest-ecosystem-schema.json"); | ||
| } | ||
|
Comment on lines
+18
to
+20
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated PR description |
||
|
|
||
| export async function updateLatestEcosystemSchema(): Promise<void> { | ||
| const latestSchema = await fetchLatestSchema(); | ||
| await writeLatestEcosystemSchema(latestSchema); | ||
| await internalUpdateEcosystemReactives(latestSchema); | ||
| } | ||
|
|
||
| async function writeLatestEcosystemSchema(schema: ThunderstoreEcosystem): Promise<void> { | ||
| const asMergedSchema: VersionedThunderstoreEcosystem = { | ||
| ...schema, | ||
| version: ManagerInformation.VERSION.toString(), | ||
| }; | ||
| const writable = JSON.stringify(asMergedSchema); | ||
| return FsProvider.instance.writeFile(await getMergedEcosystemPath(), writable); | ||
| } | ||
|
|
||
| async function getLastSavedEcosystemSchema(): Promise<VersionedThunderstoreEcosystem> { | ||
| const contentBuffer = await FsProvider.instance.readFile(await getMergedEcosystemPath()); | ||
| const content = contentBuffer.toString("utf8"); | ||
| const parsedContent = JSON.parse(content); | ||
| await validateSchema(parsedContent); | ||
| return parsedContent; | ||
| } | ||
|
|
||
| async function validateSchema(schema: any): Promise<void> { | ||
| const ajv = new Ajv(); | ||
| addFormats(ajv); | ||
|
|
||
| const validate = ajv.compile(jsonSchema); | ||
| const isOk = validate(bundledEcosystem); | ||
|
|
||
| if (!isOk) { | ||
| throw new R2Error("Schema validation error", ajv.errorsText(validate.errors)); | ||
| } | ||
| } | ||
|
|
||
| async function loadBundledSchema(): Promise<ThunderstoreEcosystem> { | ||
| await validateSchema(bundledEcosystem); | ||
| return bundledEcosystem as ThunderstoreEcosystem; | ||
| } | ||
|
|
||
| async function fetchLatestSchema(): Promise<ThunderstoreEcosystem> { | ||
| // TODO - Implement fetching of latest resource | ||
| return { | ||
| schemaVersion: "", | ||
| communities: {}, | ||
| games: {}, | ||
| modloaderPackages: [], | ||
| packageInstallers: {}, | ||
| }; | ||
| } | ||
|
|
||
| async function resolveCachedEcosystemSchema(): Promise<VersionedThunderstoreEcosystem> { | ||
| const mergeFilePath = await getMergedEcosystemPath(); | ||
| const bundledSchema = async () => ({...(await loadBundledSchema()), version: ManagerInformation.VERSION.toString()}); | ||
| if (!(await FsProvider.instance.exists(mergeFilePath))) { | ||
| return bundledSchema(); | ||
| } | ||
| try { | ||
| let content = await getLastSavedEcosystemSchema(); | ||
| if (!new VersionNumber(content.version).isEqualTo(ManagerInformation.VERSION)) { | ||
| return bundledSchema(); | ||
| } | ||
| return content; | ||
| } catch (e) { | ||
|
Comment on lines
+79
to
+85
|
||
| const err = e as unknown as Error; | ||
| LoggerProvider.instance.Log( | ||
| LogSeverity.ERROR, | ||
| `Failed to load cached ecosystem schema, falling back to bundled schema\n${err.message}` | ||
| ); | ||
| return bundledSchema(); | ||
|
Comment on lines
+86
to
+91
|
||
| } | ||
| } | ||
|
|
||
| async function internalUpdateEcosystemReactives(schema: ThunderstoreEcosystem): Promise<void> { | ||
| const result: [string, R2Modman][] = [] | ||
| for (const [identifier, game] of Object.entries(schema.games)) { | ||
| if (game.r2modman == null) continue; | ||
| for (const entry of game.r2modman) { | ||
| result.push([identifier, entry]); | ||
| } | ||
| } | ||
| EcosystemSupportedGames.value = result; | ||
| EcosystemModloaderPackages.value = schema.modloaderPackages; | ||
| updateModLoaderExports(); | ||
| } | ||
|
|
||
| export async function updateEcosystemReactives() { | ||
| const mergedSchema = await resolveCachedEcosystemSchema(); | ||
| await internalUpdateEcosystemReactives(mergedSchema); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,10 @@ | ||
| import ModLoaderPackageMapping from '../../../model/installing/ModLoaderPackageMapping'; | ||
| import VersionNumber from '../../../model/VersionNumber'; | ||
| import { EcosystemSchema, PackageLoader } from '../../../model/schema/ThunderstoreSchema'; | ||
|
|
||
| /** | ||
| * A set of modloader packages read from the ecosystem schema. | ||
| */ | ||
| export const MODLOADER_PACKAGES = EcosystemSchema.modloaderPackages.map((x) => | ||
| new ModLoaderPackageMapping( | ||
| x.packageId, | ||
| x.rootFolder, | ||
| x.loader, | ||
| ), | ||
| ); | ||
| import { | ||
| EcosystemModloaderPackages, | ||
| EcosystemSupportedGames, | ||
| PackageLoader | ||
| } from '../../../model/schema/ThunderstoreSchema'; | ||
|
|
||
| type Modloaders = Record<string, ModLoaderPackageMapping[]>; | ||
|
|
||
|
|
@@ -28,16 +21,24 @@ const OVERRIDES: Modloaders = { | |
| ], | ||
| } | ||
|
|
||
| export const MOD_LOADER_VARIANTS: Modloaders = Object.fromEntries( | ||
| EcosystemSchema.supportedGames | ||
| .map(([_, game]) => [ | ||
| game.internalFolderName, | ||
| OVERRIDES[game.internalFolderName] || MODLOADER_PACKAGES | ||
| ]) | ||
| ); | ||
| export let MODLOADER_PACKAGES: ModLoaderPackageMapping[] = []; | ||
| export let MOD_LOADER_VARIANTS: Modloaders = {}; | ||
|
|
||
| export function updateModLoaderExports() { | ||
| MODLOADER_PACKAGES = EcosystemModloaderPackages.value.map((x) => | ||
| new ModLoaderPackageMapping(x.packageId, x.rootFolder, x.loader) | ||
| ); | ||
| MOD_LOADER_VARIANTS = Object.fromEntries( | ||
| EcosystemSupportedGames.value | ||
| .map(([_, game]) => [ | ||
| game.internalFolderName, | ||
| OVERRIDES[game.internalFolderName] || MODLOADER_PACKAGES | ||
| ]) | ||
| ); | ||
|
Comment on lines
+28
to
+37
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not an issue because as part of the Quasar boot files, we initialise this. It isn't until render + load of the GameSelectionScreen.vue that this is then applicable. |
||
| } | ||
|
|
||
| export const getModLoaderPackageNames = () => { | ||
| const deduplicated = new Set(EcosystemSchema.modloaderPackages.map((x) => x.packageId)); | ||
| const deduplicated = new Set(EcosystemModloaderPackages.value.map((x) => x.packageId)); | ||
| const names = Array.from(deduplicated); | ||
| names.sort(); | ||
| return names; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really awkward but I presume it's necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically no, because we bind to the same provider later anyway, it's more just to keep a separation of concerns so that providers are all bound in the place you'd expect.
On the flip side, this (#2116) removes the need entirely and we can have all providers set before this is even ran.