bedrock-launcher-version-symlink-generator
Version:
A CLI to create symlinks for each of the version folders in Bedrock Launcher, so you can access the folders by version number instead of by UUID.
72 lines (71 loc) • 3.47 kB
JavaScript
import chalk from "chalk";
import { exec } from "child_process";
import { existsSync, mkdirSync, readdirSync, readFileSync } from "fs";
import path from "path";
/**
* The path to the user folder.
*
* This is the same folder you are sent to if your type `%AppData%/../../` in the `WIN+R` Run dialog.
*/
export const userFolderPath = import.meta.dirname.split(path.sep).slice(0, 3).join(path.sep);
// Check if the user folder path is valid.
if (!new RegExp(`[A-Z]:${path.sep === "\\" ? "\\\\" : path.sep}Users${path.sep === "\\" ? "\\\\" : path.sep}`).test(userFolderPath)) {
console.error(chalk.red(`Invalid user folder path, please make sure this package is installed globally: "${userFolderPath}"`));
process.exit(1);
}
/**
* The path to the Bedrock Launcher data folder.
*/
export const mcBedrockFolderPath = path.join(userFolderPath, "AppData/Roaming/.minecraft_bedrock");
// Check if Bedrock Launcher is installed.
if (!existsSync(mcBedrockFolderPath)) {
console.error(chalk.red(`Bedrock Launcher is not installed.`));
process.exit(1);
}
/**
* The path to the Bedrock Launcher versions folder.
*/
const mcBedrockVersionsFolderPath = path.join(mcBedrockFolderPath, "versions");
/**
* The list of folders for all the currently installed Minecraft Bedrock Edition versions.
*/
const bedrockLauncherVersionFolders = readdirSync(mcBedrockVersionsFolderPath).filter((versionFolder) => versionFolder.split("-").length === 5);
/**
* The list of version numbers.
*/
const versionNumbers = bedrockLauncherVersionFolders.map((versionFolder) => {
const AppxManifestXML = path.join(mcBedrockVersionsFolderPath, versionFolder, "AppxManifest.xml");
const AppxManifestXMLContent = readFileSync(AppxManifestXML, "utf-8");
const AppxManifestXMLVersion = AppxManifestXMLContent.match(/\<Identity Name="(?:Microsoft\.MinecraftUWP|Microsoft\.MinecraftWindowsBeta)" Publisher="[^"]*" Version="([\d\.]+)"/)?.[1];
if (!AppxManifestXMLVersion) {
return "Unable to determine version.";
}
const AppxManifestXMLEdition = AppxManifestXMLContent.match(/\<DisplayName\>(Minecraft for Windows|Minecraft Windows Preview)\<\/DisplayName>/)?.[1];
const versionSegments = AppxManifestXMLVersion.split(".");
return `${Number(versionSegments[0])}.${Number(versionSegments[1])}.${Number(versionSegments[2]?.slice(0, -2))}.${Number(versionSegments[2]?.slice(-2))}_${AppxManifestXMLEdition === "Minecraft for Windows" ? "Release" : "Preview"}`;
});
mkdirSync(path.join(mcBedrockFolderPath, "versionSymlinks"), { recursive: true });
for (let i = 0; i < bedrockLauncherVersionFolders.length; i++) {
if (versionNumbers[i] === "Unable to determine version.") {
console.warn(chalk.yellow(`Unable to determine version for folder "${bedrockLauncherVersionFolders[i]}". Skipping...`));
continue;
}
await runCommmand(`mklink /J "${path.join(mcBedrockFolderPath, "versionSymlinks", versionNumbers[i])}" "${path.join(mcBedrockVersionsFolderPath, bedrockLauncherVersionFolders[i])}"`).then((r) => {
if (r.err !== null) {
throw r.err;
}
});
}
/**
* Runs a command.
*
* @param {string} command The command to run.
* @returns A promise that resolves with the results of the command.
*/
export async function runCommmand(command) {
return new Promise((resolve) => {
exec(command, (err, stdout, stderr) => {
resolve({ err, stdout, stderr });
});
});
}