UNPKG

@bscotch/stitch-launcher

Version:

Manage GameMaker IDE and runtime installations for fast switching between versions.

205 lines 8.07 kB
import { __decorate, __metadata } from "tslib"; import { gameMakerReleaseWithNotesSchema, releasesUrl, runtimeFeedUrls, } from '@bscotch/gamemaker-releases'; import { pathy } from '@bscotch/pathy'; import { MaxAge } from '@bscotch/utility/browser'; import { z } from 'zod'; import { gameMakerUserDataSchema, } from './GameMakerLauncher.types.js'; import { GameMakerUser } from './GameMakerUser.js'; import { cleanVersionString, downloadIfCacheExpired, listGameMakerDataDirs, listInstalledIdes, listRuntimeFeedsConfigPaths, stitchConfigDir, } from './utility.js'; export class GameMakerComponent { info; constructor(info) { this.info = info; } get version() { return this.info.version; } get channel() { return this.info.channel; } get executablePath() { return this.info.executablePath; } get directory() { return this.info.directory; } get publishedAt() { return this.info.publishedAt; } get usersDirectory() { return pathy(GameMakerComponent.userDirectories[this.channel || 'beta']); } async activeUser() { const userInfoFilePath = this.usersDirectory.join('um.json'); return new GameMakerUser(await userInfoFilePath.read({ schema: gameMakerUserDataSchema, fallback: {}, })); } async activeUserDirectory() { const userInfo = await this.activeUser(); return this.usersDirectory.join(userInfo.directoryBasename); } /** * List paths to important files and directories. */ static async listWellKnownPaths(options) { const known = []; const [dataDirs, ideExes] = await Promise.all([ listGameMakerDataDirs(), listInstalledIdes(options?.programFiles), ]); for (const dir of dataDirs) { known.push({ id: 'gameMakerDataDir', path: dir.absolute, name: 'IDE: Data', description: 'The directory in which all GameMaker data is stored.', }); known.push({ id: 'runtimeFeedsConfigFile', path: dir.join('runtime_feeds.json').absolute, name: 'Runtime: Feeds', description: 'The list of Runtime Feed URLs used by the IDE to find and display allowed runtimes.', }); known.push({ id: 'activeRuntimeConfigFile', path: dir.join('runtime.json').absolute, name: 'Runtime: Active', description: 'The active runtime to be used by the IDE.', }); known.push({ id: 'defaultMacrosFile', path: dir.join('default_macros.json').absolute, name: 'IDE: Defaults', description: `IDE configuration defaults, including URLs for IDE feeds.`, }); known.push({ id: 'runtimesCacheDir', path: dir.join('Cache/runtimes').absolute, name: 'Runtime: Downloads', description: `The local cache for downloaded GameMaker Runtimes.`, }); known.push({ id: 'uiLogFile', path: dir.join('ui.log').absolute, name: 'IDE: UI Logs', description: `Logs created by the GameMaker IDE during its most recent run.`, }); } for (const exe of ideExes) { known.push({ id: 'gameMakerIdeDir', path: exe.up().absolute, name: 'IDE: Installation Directory', description: 'Where the GameMaker installer installs the IDE.', }); known.push({ id: 'gameMakerIdeExe', path: exe.absolute, name: 'IDE: Executable', description: 'The GameMaker IDE executable.', }); known.push({ id: 'initialDefaultMacrosFile', path: exe.up().join('defaults/default_macros.json').absolute, name: 'IDE: Initial Defaults', description: `IDE configuration defaults, which can be overridden in the program data's default_macros.json file.`, }); } const userDirs = GameMakerComponent.userDirectories; for (const [, dir] of Object.entries(userDirs)) { const path = pathy(dir); if (!(await path.exists())) { continue; } known.push({ id: 'gameMakerUserDir', path: path.absolute, name: `User: Directory`, description: `Where GameMaker stores login and related data.`, }); known.push({ id: 'activeUserFile', path: path.join('um.json').absolute, name: 'User: Active', description: `Information about the most recently active user.`, }); } return known; } static get releasesCachePath() { return GameMakerComponent.cacheDir .join(`releases-summary.json`) .withValidator(z.array(gameMakerReleaseWithNotesSchema)); } static async listReleases(options) { await downloadIfCacheExpired(releasesUrl, GameMakerComponent.releasesCachePath, options?.maxAgeSeconds || 1800, options?.logger); return await GameMakerComponent.releasesCachePath.read(); } static async findRelease(searchOptions) { searchOptions?.logger?.log('Fetching list of GameMaker releases...'); const releases = await GameMakerComponent.listReleases({ maxAgeSeconds: searchOptions.maxAgeSeconds, logger: searchOptions.logger, }); searchOptions?.logger?.log(`Found ${releases.length} releases...`); const release = releases.find((release) => { if (searchOptions.ideVersion) { return (release.ide.version === cleanVersionString(searchOptions.ideVersion)); } else if (searchOptions.runtimeVersion) { return (release.runtime.version === cleanVersionString(searchOptions.runtimeVersion)); } return false; }); return release; } static get userDirectories() { const prefix = `${process.env.APPDATA}/GameMakerStudio2`; return { lts: `${prefix}-LTS`, stable: prefix, beta: `${prefix}-Beta`, unstable: `${prefix}-Beta`, }; } static get cacheDir() { return stitchConfigDir.join('launcher'); } /** * Ensure that all official runtime feeds are available to the user * by adding missing feeds to the appropriate GameMaker configs. */ static async ensureOfficialRuntimeFeeds(channels = ['lts', 'stable', 'beta']) { const feedConfigs = []; const runtimeUrls = runtimeFeedUrls(); for (const channel of channels) { feedConfigs.push({ Key: channel, Value: runtimeUrls[channel], }); } for (const configFile of await listRuntimeFeedsConfigPaths()) { const existingConfig = await configFile.read({ fallback: [] }); maybeNew: for (const feed of feedConfigs) { for (const existing of existingConfig) { if (existing.Value === feed.Value) { continue maybeNew; } } // Then this is a new feed existingConfig.push(feed); } await configFile.write(existingConfig); } } } __decorate([ MaxAge(60, 30), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], GameMakerComponent, "listWellKnownPaths", null); //# sourceMappingURL=GameMakerComponent.js.map