mirror of
https://git.bits.team/Bits/mod-manager.git
synced 2024-11-14 10:28:21 -05:00
Added functionality to mark mods as essential
This commit is contained in:
parent
ab83374735
commit
e913bafb1e
13
package-lock.json
generated
13
package-lock.json
generated
@ -15,7 +15,8 @@
|
|||||||
"commander": "^9.4.0",
|
"commander": "^9.4.0",
|
||||||
"ora": "^6.1.2",
|
"ora": "^6.1.2",
|
||||||
"pino": "^8.3.1",
|
"pino": "^8.3.1",
|
||||||
"string-format": "^2.0.0"
|
"string-format": "^2.0.0",
|
||||||
|
"string-similarity-js": "^2.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18.6.3",
|
"@types/node": "^18.6.3",
|
||||||
@ -546,6 +547,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz",
|
||||||
"integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA=="
|
"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": {
|
"node_modules/strip-ansi": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz",
|
||||||
"integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA=="
|
"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": {
|
"strip-ansi": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
"commander": "^9.4.0",
|
"commander": "^9.4.0",
|
||||||
"ora": "^6.1.2",
|
"ora": "^6.1.2",
|
||||||
"pino": "^8.3.1",
|
"pino": "^8.3.1",
|
||||||
"string-format": "^2.0.0"
|
"string-format": "^2.0.0",
|
||||||
|
"string-similarity-js": "^2.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18.6.3",
|
"@types/node": "^18.6.3",
|
||||||
|
20
src/commands/essential_command.ts
Normal file
20
src/commands/essential_command.ts
Normal 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,10 +8,11 @@ export default class InstallCommand implements Subcommand {
|
|||||||
program.command("install")
|
program.command("install")
|
||||||
.description("Installs the provided mods")
|
.description("Installs the provided mods")
|
||||||
.argument("<mods...>", "The mods to install")
|
.argument("<mods...>", "The mods to install")
|
||||||
.action((mods) => {
|
.option("-e, --essential", "Marks these mods as essential", false)
|
||||||
|
.action(function() {
|
||||||
ModManager.execute(async () => {
|
ModManager.execute(async () => {
|
||||||
for (const mod of mods) {
|
for (const mod of this.args) {
|
||||||
await Mods.install(mod);
|
await Mods.install(mod, this.opts().essential);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -7,7 +7,7 @@ export default class UninstallCommand implements Subcommand {
|
|||||||
registerCommand(program: Command): void {
|
registerCommand(program: Command): void {
|
||||||
program.command("uninstall")
|
program.command("uninstall")
|
||||||
.description("Uninstalls the provided mods")
|
.description("Uninstalls the provided mods")
|
||||||
.argument("<mods...>")
|
.argument("<mods...>", "The mods to uninstall (as names or ids)")
|
||||||
.action((mods) => {
|
.action((mods) => {
|
||||||
ModManager.execute(() => {
|
ModManager.execute(() => {
|
||||||
for (let mod of mods) {
|
for (let mod of mods) {
|
||||||
|
@ -9,6 +9,7 @@ import path from "path";
|
|||||||
import {Logger, pino} from "pino"
|
import {Logger, pino} from "pino"
|
||||||
import {ListCommand} from "./commands/list_command.js";
|
import {ListCommand} from "./commands/list_command.js";
|
||||||
import UninstallCommand from "./commands/uninstall_command.js";
|
import UninstallCommand from "./commands/uninstall_command.js";
|
||||||
|
import EssentialCommand from "./commands/essential_command.js";
|
||||||
|
|
||||||
|
|
||||||
export default class ModManager {
|
export default class ModManager {
|
||||||
@ -21,7 +22,8 @@ export default class ModManager {
|
|||||||
new InitCommand(),
|
new InitCommand(),
|
||||||
new InstallCommand(),
|
new InstallCommand(),
|
||||||
new ListCommand(),
|
new ListCommand(),
|
||||||
new UninstallCommand()
|
new UninstallCommand(),
|
||||||
|
new EssentialCommand()
|
||||||
];
|
];
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
|
2
src/mods/mod.d.ts
vendored
2
src/mods/mod.d.ts
vendored
@ -1,10 +1,12 @@
|
|||||||
declare global {
|
declare global {
|
||||||
|
// DONT FORGET TO UPDATE CONSTRUCTORS WHEN MOD SIGNATURE CHANGES
|
||||||
type Mod = {
|
type Mod = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
fileName: string,
|
fileName: string,
|
||||||
version: string
|
version: string
|
||||||
source: string,
|
source: string,
|
||||||
|
essential: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ export default class Mods {
|
|||||||
new ModrinthSource()
|
new ModrinthSource()
|
||||||
];
|
];
|
||||||
|
|
||||||
public static async install(mod: string): Promise<void> {
|
public static async install(mod: string, essential: boolean): Promise<void> {
|
||||||
let success: boolean = false;
|
let success: boolean = false;
|
||||||
|
|
||||||
// Go through each mod source
|
// Go through each mod source
|
||||||
@ -47,7 +47,7 @@ export default class Mods {
|
|||||||
if (!this.isModInstalled(id)) {
|
if (!this.isModInstalled(id)) {
|
||||||
spinner.updateText(`Installing ${projectName}...`)
|
spinner.updateText(`Installing ${projectName}...`)
|
||||||
try {
|
try {
|
||||||
const modObj: Mod = await source.install(id);
|
const modObj: Mod = await source.install(id, essential);
|
||||||
this.trackMod(modObj);
|
this.trackMod(modObj);
|
||||||
|
|
||||||
spinner.succeed(`Successfully installed ${projectName}`);
|
spinner.succeed(`Successfully installed ${projectName}`);
|
||||||
@ -94,30 +94,17 @@ export default class Mods {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uninstall(mod: string) {
|
static uninstall(mod: string) {
|
||||||
let mods: Array<Mod> = this.getTrackedMods();
|
|
||||||
|
|
||||||
// Replace underscores with spaces
|
|
||||||
mod = mod.replaceAll("_", " ");
|
|
||||||
|
|
||||||
// Find mod to uninstall
|
// Find mod to uninstall
|
||||||
const spinner = new PrintUtils.Spinner(`Uninstalling ${mod}...`)
|
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 a matching mod is found, remove it
|
||||||
if (modToUninstall != undefined) {
|
if (modToUninstall != undefined) {
|
||||||
|
let mods: Array<Mod> = this.getTrackedMods();
|
||||||
|
|
||||||
// Remove mod from list and uninstall it
|
// Remove mod from list and uninstall it
|
||||||
unlinkSync(path.join(this.MODS_FOLDER_PATH, modToUninstall.fileName));
|
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);
|
this.writeFile(mods);
|
||||||
spinner.succeed(`${modToUninstall.name} successfully uninstalled!`)
|
spinner.succeed(`${modToUninstall.name} successfully uninstalled!`)
|
||||||
} else {
|
} 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
export default interface ModSource {
|
export default interface ModSource {
|
||||||
search(query: string): Promise<string>;
|
search(query: string): Promise<string>;
|
||||||
|
|
||||||
install(id: string): Promise<Mod>;
|
install(id: string, essential: boolean): Promise<Mod>;
|
||||||
|
|
||||||
getSourceName(): string;
|
getSourceName(): string;
|
||||||
|
|
||||||
|
@ -124,10 +124,11 @@ export default class ModrinthSource implements ModSource {
|
|||||||
* }
|
* }
|
||||||
* ]
|
* ]
|
||||||
* @param id the id of the mod
|
* @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 DownloadError if an error occurs when downloading
|
||||||
* @throws ModNotFoundError if there are no versions available for the current Minecraft Version
|
* @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 mcVersion = await MinecraftUtils.getCurrentMinecraftVersion();
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
@ -161,7 +162,8 @@ export default class ModrinthSource implements ModSource {
|
|||||||
id: id,
|
id: id,
|
||||||
fileName: fileName,
|
fileName: fileName,
|
||||||
version: modVersion,
|
version: modVersion,
|
||||||
source: this.getSourceName()
|
source: this.getSourceName(),
|
||||||
|
essential: essential
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
import { stringSimilarity } from "string-similarity-js";
|
||||||
|
|
||||||
export default class Util {
|
export default class Util {
|
||||||
|
private static readonly SIMILARITY_THRESHOLD: number = 0.8;
|
||||||
|
|
||||||
static isArrayEmpty(array: Array<any> | undefined): boolean {
|
static isArrayEmpty(array: Array<any> | undefined): boolean {
|
||||||
return array === undefined || array.length == 0;
|
return array === undefined || array.length == 0;
|
||||||
}
|
}
|
||||||
@ -9,4 +13,10 @@ export default class Util {
|
|||||||
// uppercase the first character
|
// uppercase the first character
|
||||||
.replace(/^./, function(str){ return str.toUpperCase(); })
|
.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user