dmclc
Version:
Dolphin Minecraft Launcher Core
240 lines (239 loc) • 7.5 kB
JavaScript
import crypto from "crypto";
import fsPromises from "fs/promises";
import got from "got";
import { marked } from "marked";
import { Launcher } from "../../../launcher.js";
import { ContentType } from "../ContentService.js";
const contentTypeToModrinth = {
[ContentType.MODPACK]: "modpack",
[ContentType.MOD]: "mod",
[ContentType.RESOURCE_PACK]: "resourcepack",
[ContentType.DATA_PACK]: "datapack",
[ContentType.SHADER]: "shader",
};
const modrinthToContentType = {
"modpack": ContentType.MODPACK,
"mod": ContentType.MOD,
"resourcepack": ContentType.RESOURCE_PACK,
"datapack": ContentType.DATA_PACK,
"shader": ContentType.SHADER,
};
export const ModrinthSortField = {
NEWEST: "newest",
UPDATED: "updated",
RELEVANCE: "relevance",
DOWNLOADS: "downloads",
FOLLOWS: "follows"
};
export class ModrinthContentVersion {
model;
got;
launcher;
dependencyType = "version";
file;
content;
isVersion = true;
constructor(model, got, launcher) {
this.model = model;
this.got = got;
this.launcher = launcher;
for (const i of this.model.files) {
if (i.primary) {
this.file = i;
}
}
this.file = this.model.files[0];
}
async getContent() {
if (!this.content)
return this.content = await ModrinthContent.fromSlugOrID(this.launcher, this.got, this.model.project_id);
return this.content;
}
async getVersionFileName() {
return this.file.filename;
}
async getVersionNumber() {
return this.model.version_number;
}
async getVersionChangelog() {
return await marked(this.model.changelog ?? "");
}
async listDependencies() {
const dependencies = [];
for (const i of this.model.dependencies) {
if (i.dependency_type === "optional" || i.dependency_type === "required") {
if (i.version_id) {
dependencies.push(new ModrinthContentVersion(await this.got("version/" + i.version_id).json(), this.got, this.launcher));
}
else if (i.project_id) {
dependencies.push(await ModrinthContent.fromSlugOrID(this.launcher, this.got, i.project_id));
}
}
}
return dependencies;
}
async getVersionFileURL() {
return this.file.url;
}
async getVersionFileSHA1() {
return this.file.hashes.sha1;
}
}
export class ModrinthContent {
launcher;
got;
model;
constructor(launcher, got, model) {
this.launcher = launcher;
this.got = got;
this.model = model;
}
isVersion = false;
/**
* @internal
* @throws RequestError
*/
static async fromSlugOrID(launcher, got_, slug) {
return new ModrinthContent(launcher, got_, await got_(`project/${slug}`).json());
}
getType() {
return modrinthToContentType[this.model.project_type];
}
async isLibrary() {
return this.model.categories.includes("library");
}
async getBody() {
return await marked(this.model.body ?? "");
}
async getScreenshots() {
return this.model.gallery;
}
async getTitle() {
return this.model.title;
}
async getDescription() {
return this.model.description;
}
async getIconURL() {
return this.model.icon_url ?? "";
}
async getURLs() {
const res = new Map();
if (this.model.wiki_url) {
res.set("wiki", this.model.wiki_url);
}
if (this.model.issues_url) {
res.set("issues", this.model.issues_url);
}
if (this.model.source_url) {
res.set("source", this.model.source_url);
}
if (this.model.discord_url) {
res.set("discord", this.model.discord_url);
}
for (const i of this.model.donation_urls ?? []) {
res.set("donate." + i.platform, i.url);
}
return res;
}
async getOtherInformation() {
const res = new Map();
if (this.model.downloads) {
res.set("downloads", this.model.downloads.toString());
}
if (this.model.followers) {
res.set("followers", this.model.followers.toString());
}
if (this.model.license) {
res.set("license", this.model.license.name);
}
if (this.model.published) {
res.set("published", this.model.published);
}
if (this.model.updated) {
res.set("updated", this.model.updated);
}
return res;
}
async listVersions(forVersion) {
let searchParams = {};
if (forVersion) {
const loaders = [];
forVersion.extras.loaders.forEach((v) => {
loaders.push(v.name);
});
searchParams = {
loaders: JSON.stringify(loaders),
game_versions: JSON.stringify([forVersion.extras.version])
};
}
const versions = await this.got(`project/${this.model.slug}/version`, {
searchParams
}).json();
return versions.map((v) => {
return new ModrinthContentVersion(v, this.got, this.launcher);
});
}
isVanillaOrCanvasShader() {
return this.model.project_type == "shader" && (this.model.loaders.includes("canvas") || this.model.loaders.includes("vanilla"));
}
}
export default class ModrinthContentService {
launcher;
got;
constructor(launcher) {
this.launcher = launcher;
this.got = got.extend({
prefixUrl: "https://api.modrinth.com/v2/",
headers: {
"user-agent": `${this.launcher.name}, using heipiao233/dmclc/${Launcher.version} (heipiao233@outlook.com)`
}
});
}
async getVersionFromFile(path) {
try {
return new ModrinthContentVersion(await this.got(`version_file/${crypto.createHash("sha1").update(await fsPromises.readFile(path)).digest("hex")}?algorithm=sha1`).json(), this.got, this.launcher);
}
catch (e) {
return null;
}
}
getUnsupportedContentTypes() {
return [ContentType.WORLD];
}
getSortFields() {
return ModrinthSortField;
}
getDefaultSortField() {
return ModrinthSortField.RELEVANCE;
}
async searchContent(name, skip, limit, type, sortField, forVersion) {
if (type === ContentType.WORLD)
return [];
const facets = [
["project_type:" + contentTypeToModrinth[type]]
];
if (forVersion) {
facets.push(["versions:" + forVersion.extras.version]);
const loaders = [];
forVersion.extras.loaders.forEach((v) => {
loaders.push("categories:" + v.name);
});
facets.push(loaders);
}
const res = await this.got("search", {
searchParams: {
query: name,
facets: JSON.stringify(facets),
offset: skip,
limit,
index: sortField
}
}).json();
const result = [];
for (const i of res.hits) {
result.push(ModrinthContent.fromSlugOrID(this.launcher, this.got, i.slug));
}
return await Promise.all(result);
}
}