Created
June 16, 2020 18:30
-
-
Save jfmherokiller/7aef5612e29052ffce4315ce2d5caac7 to your computer and use it in GitHub Desktop.
steam patched satisfactory modmanager
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
var __importDefault = (this && this.__importDefault) || function (mod) { | |
return (mod && mod.__esModule) ? mod : { "default": mod }; | |
}; | |
var __importStar = (this && this.__importStar) || function (mod) { | |
if (mod && mod.__esModule) return mod; | |
var result = {}; | |
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | |
result["default"] = mod; | |
return result; | |
}; | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
const path_1 = __importDefault(require("path")); | |
const platform_folders_1 = require("platform-folders"); | |
const fs_1 = __importDefault(require("fs")); | |
const semver_1 = require("semver"); | |
const crypto_1 = require("crypto"); | |
const MH = __importStar(require("./modHandler")); | |
const SH = __importStar(require("./smlHandler")); | |
const BH = __importStar(require("./bootstrapperHandler")); | |
const manifest_1 = require("./manifest"); | |
const utils_1 = require("./utils"); | |
const logging_1 = require("./logging"); | |
const errors_1 = require("./errors"); | |
function getConfigFolderPath(configName) { | |
const configPath = path_1.default.join(utils_1.configFolder, configName); | |
utils_1.ensureExists(configPath); | |
return configPath; | |
} | |
exports.getConfigFolderPath = getConfigFolderPath; | |
const VANILLA_CONFIG_NAME = 'vanilla'; | |
const MODDED_CONFIG_NAME = 'modded'; | |
const DEVELOPMENT_CONFIG_NAME = 'development'; | |
const CacheRelativePath = '.cache'; | |
function getInstallHash(satisfactoryPath) { | |
return crypto_1.createHash('sha256').update(satisfactoryPath, 'utf8').digest('hex'); | |
} | |
exports.getInstallHash = getInstallHash; | |
function getManifestFolderPath(satisfactoryPath) { | |
return path_1.default.join(utils_1.manifestsDir, getInstallHash(satisfactoryPath)); | |
} | |
exports.getManifestFolderPath = getManifestFolderPath; | |
class SatisfactoryInstall { | |
constructor(name, version, installLocation, mainGameAppName) { | |
this.installLocation = installLocation; | |
this._manifestHandler = new manifest_1.ManifestHandler(getManifestFolderPath(installLocation)); | |
this.name = name; | |
this.version = version; | |
this.mainGameAppName = mainGameAppName; | |
} | |
async _getInstalledMismatches(items) { | |
const installedSML = SH.getSMLVersion(this.installLocation); | |
const installedBootstrapper = BH.getBootstrapperVersion(this.installLocation); | |
const installedMods = await MH.getInstalledMods(SH.getModsDir(this.installLocation)); | |
const mismatches = { | |
install: {}, | |
uninstall: [], | |
}; | |
if (installedSML !== items[SH.SMLModID]) { | |
if (!items[SH.SMLModID] || (installedSML && items[SH.SMLModID])) { | |
mismatches.uninstall.push(SH.SMLModID); | |
} | |
if (items[SH.SMLModID]) { | |
mismatches.install[SH.SMLModID] = items[SH.SMLModID]; | |
} | |
} | |
if (installedBootstrapper !== items[BH.BootstrapperModID]) { | |
if (!items[BH.BootstrapperModID] || (installedBootstrapper && items[BH.BootstrapperModID])) { | |
mismatches.uninstall.push(BH.BootstrapperModID); | |
} | |
if (items[BH.BootstrapperModID]) { | |
mismatches.install[BH.BootstrapperModID] = items[BH.BootstrapperModID]; | |
} | |
} | |
const allMods = utils_1.mergeArrays(Object.keys(items) | |
.filter((item) => item !== SH.SMLModID && item !== BH.BootstrapperModID), installedMods.map((mod) => mod.mod_id)); | |
allMods.forEach((mod) => { | |
var _a; | |
const installedModVersion = (_a = installedMods | |
.find((installedMod) => installedMod.mod_id === mod)) === null || _a === void 0 ? void 0 : _a.version; | |
if (installedModVersion !== items[mod]) { | |
if (!items[mod] || (installedModVersion && items[mod])) { | |
mismatches.uninstall.push(mod); | |
} | |
if (items[mod]) { | |
mismatches.install[mod] = items[mod]; | |
} | |
} | |
}); | |
return mismatches; | |
} | |
async validateInstall() { | |
const items = this._manifestHandler.getItemsList(); | |
logging_1.debug(`Items: ${JSON.stringify(items)}`); | |
const mismatches = await this._getInstalledMismatches(items); | |
logging_1.debug(`Mismatches: ${JSON.stringify(mismatches)}`); | |
const modsDir = SH.getModsDir(this.installLocation); | |
await Promise.all(mismatches.uninstall.map((id) => { | |
if (id !== SH.SMLModID && id !== BH.BootstrapperModID) { | |
if (modsDir) { | |
logging_1.debug(`Removing ${id} from Satisfactory install`); | |
return MH.uninstallMod(id, modsDir); | |
} | |
} | |
return Promise.resolve(); | |
})); | |
if (mismatches.uninstall.includes(SH.SMLModID)) { | |
logging_1.debug('Removing SML from Satisfactory install'); | |
await SH.uninstallSML(this.installLocation); | |
} | |
if (mismatches.uninstall.includes(BH.BootstrapperModID)) { | |
logging_1.debug('Removing Bootstrapper from Satisfactory install'); | |
await BH.uninstallBootstrapper(this.installLocation); | |
} | |
if (mismatches.install[SH.SMLModID]) { | |
logging_1.debug('Copying SML to Satisfactory install'); | |
await SH.installSML(mismatches.install[SH.SMLModID], this.installLocation); | |
} | |
if (mismatches.install[BH.BootstrapperModID]) { | |
logging_1.debug('Copying Bootstrapper to Satisfactory install'); | |
await BH.installBootstrapper(mismatches.install[BH.BootstrapperModID], this.installLocation); | |
} | |
await Object.entries(mismatches.install).forEachAsync(async (modInstall) => { | |
const modInstallID = modInstall[0]; | |
const modInstallVersion = modInstall[1]; | |
if (modInstallID !== SH.SMLModID && modInstallID !== BH.BootstrapperModID) { | |
if (modsDir) { | |
logging_1.debug(`Copying ${modInstallID}@${modInstallVersion} to Satisfactory install`); | |
await MH.installMod(modInstallID, modInstallVersion, modsDir); | |
} | |
} | |
}); | |
} | |
async manifestMutate(install, uninstall, update) { | |
if (!await SatisfactoryInstall.isGameRunning()) { | |
logging_1.debug(`install: [${install.map((item) => (item.version ? `${item.id}@${item.version}` : item.id)).join(', ')}], uninstall: [${uninstall.join(', ')}], update: [${update.join(', ')}]`); | |
const currentManifest = this._manifestHandler.readManifest(); | |
const currentLockfile = this._manifestHandler.readLockfile(); | |
try { | |
await this._manifestHandler.setSatisfactoryVersion(this.version); | |
await this._manifestHandler.mutate(install, uninstall, update); | |
await this.validateInstall(); | |
} | |
catch (e) { | |
await this._manifestHandler.writeManifest(currentManifest); | |
await this._manifestHandler.writeLockfile(currentLockfile); | |
if (e instanceof errors_1.ModRemovedByAuthor) { | |
if (update.includes(e.modID)) { | |
update.remove(e.modID); | |
uninstall.push(e.modID); | |
logging_1.info(`Uninstalling mod ${e.modID}, it was removed from ficsit.app`); | |
await this.manifestMutate(install, uninstall, update); | |
return; | |
} | |
update.push(e.modID); | |
logging_1.info(`Trying to update mod ${e.modID}, the installed version was removed from ficsit.app`); | |
if (e.version) { | |
MH.removeModFromCache(e.modID, e.version); | |
} | |
await this.manifestMutate(install, uninstall, update); | |
return; | |
} | |
e.message = `${e.message}\nAll changes were discarded.`; | |
logging_1.error(e); | |
await this.validateInstall(); | |
throw e; | |
} | |
} | |
else { | |
throw new errors_1.GameRunningError('Satisfactory is running. Please close it and wait until it fully shuts down.'); | |
} | |
} | |
async loadConfig(configName) { | |
const currentManifest = this._manifestHandler.readManifest(); | |
const currentLockfile = this._manifestHandler.readLockfile(); | |
let manifest; | |
let lockfile; | |
try { | |
manifest = JSON.parse(fs_1.default.readFileSync(path_1.default.join(getConfigFolderPath(configName), 'manifest.json'), 'utf8')); | |
manifest.satisfactoryVersion = this.version; | |
} | |
catch (e) { | |
throw new errors_1.InvalidConfigError(`Config ${configName} is invalid`); | |
} | |
if (fs_1.default.existsSync(path_1.default.join(getConfigFolderPath(configName), `lock-${getInstallHash(this.installLocation)}.json`))) { | |
try { | |
lockfile = JSON.parse(fs_1.default.readFileSync(path_1.default.join(getConfigFolderPath(configName), `lock-${getInstallHash(this.installLocation)}.json`), 'utf8')); | |
} | |
catch (e) { | |
throw new errors_1.InvalidConfigError(`Config ${configName} is invalid`); | |
} | |
} | |
else { | |
lockfile = {}; | |
} | |
this._manifestHandler.writeManifest(manifest); | |
this._manifestHandler.writeLockfile(lockfile); | |
try { | |
await this.manifestMutate([], [], []); | |
} | |
catch (e) { | |
// Something invalid was found. Revert and pass the error forward | |
this._manifestHandler.writeManifest(currentManifest); | |
this._manifestHandler.writeLockfile(currentLockfile); | |
await this.validateInstall(); | |
throw new errors_1.InvalidConfigError(`Error while loading config: ${e}`); | |
} | |
} | |
async saveConfig(configName) { | |
if (configName.toLowerCase() === VANILLA_CONFIG_NAME) { | |
throw new errors_1.InvalidConfigError('Cannot modify vanilla config. Use Modded config or create a new config'); | |
} | |
const manifest = this._manifestHandler.readManifest(); | |
delete manifest.satisfactoryVersion; | |
fs_1.default.writeFileSync(path_1.default.join(getConfigFolderPath(configName), 'manifest.json'), JSON.stringify(manifest)); | |
fs_1.default.writeFileSync(path_1.default.join(getConfigFolderPath(configName), `lock-${getInstallHash(this.installLocation)}.json`), JSON.stringify(this._manifestHandler.readLockfile())); | |
} | |
async _installItem(id, version) { | |
return this.manifestMutate([{ id, version }], [], []); | |
} | |
async _uninstallItem(item) { | |
return this.manifestMutate([], [item], []); | |
} | |
async _updateItem(item) { | |
return this.manifestMutate([], [], [item]); | |
} | |
async installMod(modID, version) { | |
var _a; | |
if (!(await this._getInstalledMods()).some((mod) => mod.mod_id === modID)) { | |
logging_1.info(`Installing ${modID}${version ? `@${version}` : ''}`); | |
await this._installItem(modID, version); | |
} | |
else { | |
logging_1.info(`${modID} is already installed with version ${(_a = (await this._getInstalledMods()).find((mod) => mod.mod_id === modID)) === null || _a === void 0 ? void 0 : _a.version}`); | |
} | |
} | |
async installFicsitAppMod(modVersion) { | |
return this.installMod(modVersion.mod_id); | |
} | |
async uninstallMod(modID) { | |
logging_1.info(`Uninstalling ${modID}`); | |
return this._uninstallItem(modID); | |
} | |
async uninstallFicsitAppMod(mod) { | |
return this.uninstallMod(mod.id); | |
} | |
async updateMod(modID) { | |
logging_1.info(`Updating ${modID}`); | |
await this._updateItem(modID); | |
} | |
async updateFicsitAppMod(mod) { | |
return this.updateMod(mod.id); | |
} | |
async _getInstalledMods() { | |
return MH.getInstalledMods(SH.getModsDir(this.installLocation)); | |
} | |
get mods() { | |
return utils_1.filterObject(this._manifestHandler.getItemsList(), (id) => id !== SH.SMLModID && id !== BH.BootstrapperModID); | |
} | |
get manifestMods() { | |
return this._manifestHandler.readManifest().items | |
.filter((item) => item.id !== SH.SMLModID && item.id !== BH.BootstrapperModID) | |
.map((item) => item.id); | |
} | |
async installSML(version) { | |
return this._installItem(SH.SMLModID, version); | |
} | |
async uninstallSML() { | |
return this._uninstallItem(SH.SMLModID); | |
} | |
async updateSML() { | |
logging_1.info('Updating SML to latest version'); | |
await this._updateItem(SH.SMLModID); | |
} | |
async _getInstalledSMLVersion() { | |
return SH.getSMLVersion(this.installLocation); | |
} | |
get smlVersion() { | |
return this._manifestHandler.getItemsList()[SH.SMLModID]; | |
} | |
get isSMLInstalledDev() { | |
return this._manifestHandler.readManifest().items.some((item) => item.id === SH.SMLModID); | |
} | |
async updateBootstrapper() { | |
logging_1.info('Updating bootstrapper to latest version'); | |
await this._updateItem(BH.BootstrapperModID); | |
} | |
async clearCache() { | |
if (!await SatisfactoryInstall.isGameRunning()) { | |
MH.clearCache(); | |
utils_1.deleteFolderRecursive(path_1.default.join(this.installLocation, CacheRelativePath)); | |
} | |
else { | |
throw new errors_1.GameRunningError('Satisfactory is running. Please close it and wait until it fully shuts down.'); | |
} | |
} | |
static isGameRunning() { | |
return utils_1.isRunning('FactoryGame-Win64-Shipping.exe'); // TODO: cross platform | |
} | |
get bootstrapperVersion() { | |
return this._manifestHandler.getItemsList()[BH.BootstrapperModID]; | |
} | |
async _getInstalledBootstrapperVersion() { | |
return BH.getBootstrapperVersion(this.installLocation); | |
} | |
get launchPath() { | |
return `steam://rungameid/526870`; | |
} | |
get binariesDir() { | |
return path_1.default.join(this.installLocation, 'FactoryGame', 'Binaries', 'Win64'); // TODO: other platforms | |
} | |
get displayName() { | |
return `${this.name} (${this.version})`; | |
} | |
get modsDir() { | |
return SH.getModsDir(this.installLocation); | |
} | |
} | |
exports.SatisfactoryInstall = SatisfactoryInstall; | |
function getConfigs() { | |
return utils_1.dirs(utils_1.configFolder).sort(); | |
} | |
exports.getConfigs = getConfigs; | |
function deleteConfig(name) { | |
if (name.toLowerCase() === VANILLA_CONFIG_NAME || name.toLowerCase() === MODDED_CONFIG_NAME || name.toLowerCase() === DEVELOPMENT_CONFIG_NAME) { | |
throw new errors_1.InvalidConfigError(`Cannot delete ${name} config (it is part of the default set of configs)`); | |
} | |
if (fs_1.default.existsSync(getConfigFolderPath(name))) { | |
utils_1.deleteFolderRecursive(getConfigFolderPath(name)); | |
} | |
} | |
exports.deleteConfig = deleteConfig; | |
if (!fs_1.default.existsSync(path_1.default.join(getConfigFolderPath(VANILLA_CONFIG_NAME), 'manifest.json'))) { | |
fs_1.default.writeFileSync(path_1.default.join(getConfigFolderPath(VANILLA_CONFIG_NAME), 'manifest.json'), JSON.stringify({ items: new Array() })); | |
} | |
if (!fs_1.default.existsSync(path_1.default.join(getConfigFolderPath(VANILLA_CONFIG_NAME), 'lock.json'))) { | |
fs_1.default.writeFileSync(path_1.default.join(getConfigFolderPath(VANILLA_CONFIG_NAME), 'lock.json'), JSON.stringify({})); | |
} | |
if (!fs_1.default.existsSync(path_1.default.join(getConfigFolderPath(MODDED_CONFIG_NAME), 'manifest.json'))) { | |
fs_1.default.writeFileSync(path_1.default.join(getConfigFolderPath(MODDED_CONFIG_NAME), 'manifest.json'), JSON.stringify({ items: new Array() })); | |
} | |
if (!fs_1.default.existsSync(path_1.default.join(getConfigFolderPath(MODDED_CONFIG_NAME), 'lock.json'))) { | |
fs_1.default.writeFileSync(path_1.default.join(getConfigFolderPath(MODDED_CONFIG_NAME), 'lock.json'), JSON.stringify({})); | |
} | |
if (!fs_1.default.existsSync(path_1.default.join(getConfigFolderPath(DEVELOPMENT_CONFIG_NAME), 'manifest.json'))) { | |
fs_1.default.writeFileSync(path_1.default.join(getConfigFolderPath(DEVELOPMENT_CONFIG_NAME), 'manifest.json'), JSON.stringify({ items: [{ id: SH.SMLModID }] })); | |
} | |
if (!fs_1.default.existsSync(path_1.default.join(getConfigFolderPath(DEVELOPMENT_CONFIG_NAME), 'lock.json'))) { | |
fs_1.default.writeFileSync(path_1.default.join(getConfigFolderPath(DEVELOPMENT_CONFIG_NAME), 'lock.json'), JSON.stringify({})); | |
} | |
const EpicManifestsFolder = path_1.default.join(platform_folders_1.getDataFolders()[0], 'Epic', 'EpicGamesLauncher', 'Data', 'Manifests'); // TODO: other platforms | |
const UEInstalledManifest = path_1.default.join(platform_folders_1.getDataFolders()[0], 'Epic', 'UnrealEngineLauncher', 'LauncherInstalled.dat'); // TODO: other platforms | |
const MyPath = "D:\\SteamLibrary\\steamapps\\common\\Satisfactory\\"; | |
async function getInstalls() { | |
let foundInstalls = new Array(); | |
if (fs_1.default.existsSync(EpicManifestsFolder)) { | |
fs_1.default.readdirSync(EpicManifestsFolder).forEach((fileName) => { | |
if (fileName.endsWith('.item')) { | |
const filePath = path_1.default.join(EpicManifestsFolder, fileName); | |
try { | |
const jsonString = fs_1.default.readFileSync(filePath, 'utf8'); | |
const manifest = JSON.parse(jsonString); | |
if (manifest.CatalogNamespace === 'crab') { | |
try { | |
const gameManifestString = fs_1.default.readFileSync(path_1.default.join(manifest.ManifestLocation, `${manifest.InstallationGuid}.mancpn`), 'utf8'); | |
const gameManifest = JSON.parse(gameManifestString); | |
if (gameManifest.AppName === manifest.MainGameAppName | |
&& gameManifest.CatalogItemId === manifest.CatalogItemId | |
&& gameManifest.CatalogNamespace === manifest.CatalogNamespace) { | |
const installWithSamePath = foundInstalls.find((install) => install.installLocation === manifest.InstallLocation); | |
if (installWithSamePath) { | |
if (parseInt(manifest.AppVersionString, 10) > parseInt(installWithSamePath.version, 10)) { | |
installWithSamePath.version = manifest.AppVersionString; | |
} | |
} | |
else { | |
foundInstalls.push(new SatisfactoryInstall(manifest.DisplayName, manifest.AppVersionString, manifest.InstallLocation, manifest.MainGameAppName)); | |
} | |
} | |
else { | |
logging_1.warn(`Epic install info points to invalid folder ${manifest.InstallLocation}. If you moved your install to an external drive, try verifying the game in Epic and restarting your PC.`); | |
} | |
} | |
catch (e) { | |
logging_1.warn(`Epic install info points to invalid folder ${manifest.InstallLocation}. If you moved your install to an external drive, try verifying the game in Epic and restarting your PC.`); | |
} | |
} | |
} | |
catch (e) { | |
logging_1.info(`Found invalid manifest: ${fileName}`); | |
} | |
} | |
}); | |
} | |
if (foundInstalls.length === 0) { | |
logging_1.warn('No Satisfactory installs found'); | |
if(fs_1.default.existsSync(MyPath)) | |
{ | |
foundInstalls.push(new SatisfactoryInstall("Default","124884",MyPath,"test")); | |
} | |
} | |
let installedManifest = { InstallationList: [] }; | |
if (fs_1.default.existsSync(UEInstalledManifest)) { | |
try { | |
installedManifest = JSON.parse(fs_1.default.readFileSync(UEInstalledManifest, 'utf8')); | |
//foundInstalls = foundInstalls.filter((install) => installedManifest.InstallationList.some((manifestInstall) => manifestInstall.InstallLocation === install.installLocation)); // Filter out old .items left over by Epic | |
if (foundInstalls.length === 0) { | |
logging_1.warn('UE manifest filtered all installs.'); | |
} | |
} | |
catch (e) { | |
logging_1.info('Invalid UE manifest. The game might appear multiple times.'); | |
} | |
} | |
else { | |
logging_1.info('Invalid UE manifest. The game might appear multiple times.'); | |
} | |
foundInstalls.sort((a, b) => { | |
const semverCmp = semver_1.compare(semver_1.valid(semver_1.coerce(a.version)) || '0.0.0', semver_1.valid(semver_1.coerce(b.version)) || '0.0.0'); | |
if (semverCmp === 0) { | |
return a.name.localeCompare(b.name); | |
} | |
return semverCmp; | |
}); | |
return foundInstalls; | |
} | |
exports.getInstalls = getInstalls; | |
//# sourceMappingURL=satisfactoryInstall.js.map |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment