Added functionality to mark mods as essential

This commit is contained in:
Kallum Jones 2022-08-04 16:04:21 +01:00
parent ab83374735
commit e913bafb1e
No known key found for this signature in database
GPG Key ID: D7F4589C4D7F81A9
11 changed files with 106 additions and 29 deletions

13
package-lock.json generated
View File

@ -15,7 +15,8 @@
"commander": "^9.4.0",
"ora": "^6.1.2",
"pino": "^8.3.1",
"string-format": "^2.0.0"
"string-format": "^2.0.0",
"string-similarity-js": "^2.1.4"
},
"devDependencies": {
"@types/node": "^18.6.3",
@ -546,6 +547,11 @@
"resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz",
"integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA=="
},
"node_modules/string-similarity-js": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/string-similarity-js/-/string-similarity-js-2.1.4.tgz",
"integrity": "sha512-uApODZNjCHGYROzDSAdCmAHf60L/pMDHnP/yk6TAbvGg7JSPZlSto/ceCI7hZEqzc53/juU2aOJFkM2yUVTMTA=="
},
"node_modules/strip-ansi": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
@ -946,6 +952,11 @@
"resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz",
"integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA=="
},
"string-similarity-js": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/string-similarity-js/-/string-similarity-js-2.1.4.tgz",
"integrity": "sha512-uApODZNjCHGYROzDSAdCmAHf60L/pMDHnP/yk6TAbvGg7JSPZlSto/ceCI7hZEqzc53/juU2aOJFkM2yUVTMTA=="
},
"strip-ansi": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",

View File

@ -17,7 +17,8 @@
"commander": "^9.4.0",
"ora": "^6.1.2",
"pino": "^8.3.1",
"string-format": "^2.0.0"
"string-format": "^2.0.0",
"string-similarity-js": "^2.1.4"
},
"devDependencies": {
"@types/node": "^18.6.3",

View File

@ -0,0 +1,20 @@
import Subcommand from "./subcommand.js";
import {Command} from "commander";
import ModManager from "../mod-manager.js";
import Mods from "../mods/mods.js";
export default class EssentialCommand implements Subcommand {
registerCommand(program: Command): void {
program.command("essential")
.description("Marks mods as essential")
.argument("<mods...>", "The mods to mark as essential (as names or ids)")
.action((mods) => {
ModManager.execute(() => {
for (let mod of mods) {
Mods.markEssential(mod)
}
})
})
}
}

View File

@ -8,10 +8,11 @@ export default class InstallCommand implements Subcommand {
program.command("install")
.description("Installs the provided mods")
.argument("<mods...>", "The mods to install")
.action((mods) => {
.option("-e, --essential", "Marks these mods as essential", false)
.action(function() {
ModManager.execute(async () => {
for (const mod of mods) {
await Mods.install(mod);
for (const mod of this.args) {
await Mods.install(mod, this.opts().essential);
}
})
});

View File

@ -7,7 +7,7 @@ export default class UninstallCommand implements Subcommand {
registerCommand(program: Command): void {
program.command("uninstall")
.description("Uninstalls the provided mods")
.argument("<mods...>")
.argument("<mods...>", "The mods to uninstall (as names or ids)")
.action((mods) => {
ModManager.execute(() => {
for (let mod of mods) {

View File

@ -9,6 +9,7 @@ import path from "path";
import {Logger, pino} from "pino"
import {ListCommand} from "./commands/list_command.js";
import UninstallCommand from "./commands/uninstall_command.js";
import EssentialCommand from "./commands/essential_command.js";
export default class ModManager {
@ -21,7 +22,8 @@ export default class ModManager {
new InitCommand(),
new InstallCommand(),
new ListCommand(),
new UninstallCommand()
new UninstallCommand(),
new EssentialCommand()
];
static init() {

2
src/mods/mod.d.ts vendored
View File

@ -1,10 +1,12 @@
declare global {
// DONT FORGET TO UPDATE CONSTRUCTORS WHEN MOD SIGNATURE CHANGES
type Mod = {
id: string
name: string
fileName: string,
version: string
source: string,
essential: boolean
}
}

View File

@ -15,7 +15,7 @@ export default class Mods {
new ModrinthSource()
];
public static async install(mod: string): Promise<void> {
public static async install(mod: string, essential: boolean): Promise<void> {
let success: boolean = false;
// Go through each mod source
@ -47,7 +47,7 @@ export default class Mods {
if (!this.isModInstalled(id)) {
spinner.updateText(`Installing ${projectName}...`)
try {
const modObj: Mod = await source.install(id);
const modObj: Mod = await source.install(id, essential);
this.trackMod(modObj);
spinner.succeed(`Successfully installed ${projectName}`);
@ -94,30 +94,17 @@ export default class Mods {
}
static uninstall(mod: string) {
let mods: Array<Mod> = this.getTrackedMods();
// Replace underscores with spaces
mod = mod.replaceAll("_", " ");
// Find mod to uninstall
const spinner = new PrintUtils.Spinner(`Uninstalling ${mod}...`)
let modToUninstall: Mod | undefined = undefined;
for (let modEle of mods) {
const id = modEle.id.toLowerCase();
const name = modEle.name.toLowerCase();
const query = mod.toLowerCase();
if (id == query || name == query) {
modToUninstall = modEle;
break;
}
}
const modToUninstall = this.findMod(mod);
// IF a matching mod is found, remove it
if (modToUninstall != undefined) {
let mods: Array<Mod> = this.getTrackedMods();
// Remove mod from list and uninstall it
unlinkSync(path.join(this.MODS_FOLDER_PATH, modToUninstall.fileName));
mods = mods.filter(item => item !== modToUninstall);
mods = mods.filter(item => !Mods.areModsEqual(item, modToUninstall));
this.writeFile(mods);
spinner.succeed(`${modToUninstall.name} successfully uninstalled!`)
} else {
@ -127,5 +114,46 @@ export default class Mods {
}
static areModsEqual(mod1: Mod, mod2: Mod): boolean {
return mod1.id === mod2.id;
}
static markEssential(mod: string) {
const modToMark = this.findMod(mod);
if (modToMark != undefined) {
let mods = this.getTrackedMods();
// Remove mod from list
mods = mods.filter(item => !Mods.areModsEqual(item, modToMark));
// Mark is as essential, and read it
modToMark.essential = true;
mods.push(modToMark)
this.writeFile(mods);
PrintUtils.success(`Marked ${modToMark.name} as essential`)
} else {
PrintUtils.error(`${mod} not found.`)
}
}
private static findMod(mod: string): Mod | undefined {
// Replace underscores with spaces
mod = mod.replaceAll("_", " ");
let mods: Array<Mod> = this.getTrackedMods();
for (let modEle of mods) {
const id = modEle.id.toLowerCase();
const name = modEle.name.toLowerCase();
const query = mod.toLowerCase();
if (id == query || Util.areStringsSimilar(mod, name)) {
return modEle;
}
}
return undefined;
}
}

View File

@ -1,7 +1,7 @@
export default interface ModSource {
search(query: string): Promise<string>;
install(id: string): Promise<Mod>;
install(id: string, essential: boolean): Promise<Mod>;
getSourceName(): string;

View File

@ -124,10 +124,11 @@ export default class ModrinthSource implements ModSource {
* }
* ]
* @param id the id of the mod
* @param essential whether this mod is essential or not
* @throws DownloadError if an error occurs when downloading
* @throws ModNotFoundError if there are no versions available for the current Minecraft Version
*/
async install(id: string): Promise<Mod> {
async install(id: string, essential: boolean): Promise<Mod> {
const mcVersion = await MinecraftUtils.getCurrentMinecraftVersion();
const params = {
@ -161,7 +162,8 @@ export default class ModrinthSource implements ModSource {
id: id,
fileName: fileName,
version: modVersion,
source: this.getSourceName()
source: this.getSourceName(),
essential: essential
};
} catch (e) {

View File

@ -1,4 +1,8 @@
import { stringSimilarity } from "string-similarity-js";
export default class Util {
private static readonly SIMILARITY_THRESHOLD: number = 0.8;
static isArrayEmpty(array: Array<any> | undefined): boolean {
return array === undefined || array.length == 0;
}
@ -9,4 +13,10 @@ export default class Util {
// uppercase the first character
.replace(/^./, function(str){ return str.toUpperCase(); })
}
static areStringsSimilar(master: string, compare: string): boolean {
master = master.toLowerCase();
compare = compare.toLowerCase();
return stringSimilarity(master, compare) >= this.SIMILARITY_THRESHOLD;
}
}