@creeperhost/modlens-mcp 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +938 -0
- package/dist/access-flags.d.ts +58 -0
- package/dist/access-flags.d.ts.map +1 -0
- package/dist/access-flags.js +79 -0
- package/dist/access-flags.js.map +1 -0
- package/dist/access-flags.test.d.ts +2 -0
- package/dist/access-flags.test.d.ts.map +1 -0
- package/dist/access-flags.test.js +189 -0
- package/dist/access-flags.test.js.map +1 -0
- package/dist/batch-ingest.d.ts +2 -0
- package/dist/batch-ingest.d.ts.map +1 -0
- package/dist/batch-ingest.js +54 -0
- package/dist/batch-ingest.js.map +1 -0
- package/dist/batch-resolve-mixins.d.ts +2 -0
- package/dist/batch-resolve-mixins.d.ts.map +1 -0
- package/dist/batch-resolve-mixins.js +36 -0
- package/dist/batch-resolve-mixins.js.map +1 -0
- package/dist/cache.d.ts +16 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +36 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1318 -0
- package/dist/cli.js.map +1 -0
- package/dist/curseforge.d.ts +53 -0
- package/dist/curseforge.d.ts.map +1 -0
- package/dist/curseforge.js +104 -0
- package/dist/curseforge.js.map +1 -0
- package/dist/db-backend.d.ts +17 -0
- package/dist/db-backend.d.ts.map +1 -0
- package/dist/db-backend.js +40 -0
- package/dist/db-backend.js.map +1 -0
- package/dist/db.d.ts +11 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +55 -0
- package/dist/db.js.map +1 -0
- package/dist/embed-queue.d.ts +12 -0
- package/dist/embed-queue.d.ts.map +1 -0
- package/dist/embed-queue.js +103 -0
- package/dist/embed-queue.js.map +1 -0
- package/dist/embeddings.d.ts +21 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +55 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/env-path.d.ts +19 -0
- package/dist/env-path.d.ts.map +1 -0
- package/dist/env-path.js +40 -0
- package/dist/env-path.js.map +1 -0
- package/dist/fetch-utils.d.ts +24 -0
- package/dist/fetch-utils.d.ts.map +1 -0
- package/dist/fetch-utils.js +73 -0
- package/dist/fetch-utils.js.map +1 -0
- package/dist/fetch-utils.test.d.ts +2 -0
- package/dist/fetch-utils.test.d.ts.map +1 -0
- package/dist/fetch-utils.test.js +133 -0
- package/dist/fetch-utils.test.js.map +1 -0
- package/dist/generated/sqlite/browser.d.ts +55 -0
- package/dist/generated/sqlite/browser.d.ts.map +1 -0
- package/dist/generated/sqlite/browser.js +18 -0
- package/dist/generated/sqlite/browser.js.map +1 -0
- package/dist/generated/sqlite/client.d.ts +72 -0
- package/dist/generated/sqlite/client.d.ts.map +1 -0
- package/dist/generated/sqlite/client.js +33 -0
- package/dist/generated/sqlite/client.js.map +1 -0
- package/dist/generated/sqlite/commonInputTypes.d.ts +387 -0
- package/dist/generated/sqlite/commonInputTypes.d.ts.map +1 -0
- package/dist/generated/sqlite/commonInputTypes.js +11 -0
- package/dist/generated/sqlite/commonInputTypes.js.map +1 -0
- package/dist/generated/sqlite/enums.d.ts +2 -0
- package/dist/generated/sqlite/enums.d.ts.map +1 -0
- package/dist/generated/sqlite/enums.js +11 -0
- package/dist/generated/sqlite/enums.js.map +1 -0
- package/dist/generated/sqlite/internal/class.d.ts +226 -0
- package/dist/generated/sqlite/internal/class.d.ts.map +1 -0
- package/dist/generated/sqlite/internal/class.js +84 -0
- package/dist/generated/sqlite/internal/class.js.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespace.d.ts +1344 -0
- package/dist/generated/sqlite/internal/prismaNamespace.d.ts.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespace.js +220 -0
- package/dist/generated/sqlite/internal/prismaNamespace.js.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.d.ts +204 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.d.ts.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.js +191 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.js.map +1 -0
- package/dist/generated/sqlite/models/DocEntry.d.ts +1223 -0
- package/dist/generated/sqlite/models/DocEntry.d.ts.map +1 -0
- package/dist/generated/sqlite/models/DocEntry.js +2 -0
- package/dist/generated/sqlite/models/DocEntry.js.map +1 -0
- package/dist/generated/sqlite/models/McSourceFile.d.ts +1289 -0
- package/dist/generated/sqlite/models/McSourceFile.d.ts.map +1 -0
- package/dist/generated/sqlite/models/McSourceFile.js +2 -0
- package/dist/generated/sqlite/models/McSourceFile.js.map +1 -0
- package/dist/generated/sqlite/models/McVersion.d.ts +1315 -0
- package/dist/generated/sqlite/models/McVersion.d.ts.map +1 -0
- package/dist/generated/sqlite/models/McVersion.js +2 -0
- package/dist/generated/sqlite/models/McVersion.js.map +1 -0
- package/dist/generated/sqlite/models/McVersionDiff.d.ts +1061 -0
- package/dist/generated/sqlite/models/McVersionDiff.d.ts.map +1 -0
- package/dist/generated/sqlite/models/McVersionDiff.js +2 -0
- package/dist/generated/sqlite/models/McVersionDiff.js.map +1 -0
- package/dist/generated/sqlite/models/Mod.d.ts +2396 -0
- package/dist/generated/sqlite/models/Mod.d.ts.map +1 -0
- package/dist/generated/sqlite/models/Mod.js +2 -0
- package/dist/generated/sqlite/models/Mod.js.map +1 -0
- package/dist/generated/sqlite/models/ModClass.d.ts +1254 -0
- package/dist/generated/sqlite/models/ModClass.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModClass.js +2 -0
- package/dist/generated/sqlite/models/ModClass.js.map +1 -0
- package/dist/generated/sqlite/models/ModSourceFile.d.ts +1283 -0
- package/dist/generated/sqlite/models/ModSourceFile.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModSourceFile.js +2 -0
- package/dist/generated/sqlite/models/ModSourceFile.js.map +1 -0
- package/dist/generated/sqlite/models/ModTag.d.ts +1278 -0
- package/dist/generated/sqlite/models/ModTag.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModTag.js +2 -0
- package/dist/generated/sqlite/models/ModTag.js.map +1 -0
- package/dist/generated/sqlite/models/ModVersionDiff.d.ts +1073 -0
- package/dist/generated/sqlite/models/ModVersionDiff.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModVersionDiff.js +2 -0
- package/dist/generated/sqlite/models/ModVersionDiff.js.map +1 -0
- package/dist/generated/sqlite/models/Primer.d.ts +1319 -0
- package/dist/generated/sqlite/models/Primer.d.ts.map +1 -0
- package/dist/generated/sqlite/models/Primer.js +2 -0
- package/dist/generated/sqlite/models/Primer.js.map +1 -0
- package/dist/generated/sqlite/models.d.ts +12 -0
- package/dist/generated/sqlite/models.d.ts.map +1 -0
- package/dist/generated/sqlite/models.js +2 -0
- package/dist/generated/sqlite/models.js.map +1 -0
- package/dist/jar.d.ts +7 -0
- package/dist/jar.d.ts.map +1 -0
- package/dist/jar.js +52 -0
- package/dist/jar.js.map +1 -0
- package/dist/java-tools.d.ts +46 -0
- package/dist/java-tools.d.ts.map +1 -0
- package/dist/java-tools.js +263 -0
- package/dist/java-tools.js.map +1 -0
- package/dist/launcher.d.ts +3 -0
- package/dist/launcher.d.ts.map +1 -0
- package/dist/launcher.js +105 -0
- package/dist/launcher.js.map +1 -0
- package/dist/mappings.d.ts +95 -0
- package/dist/mappings.d.ts.map +1 -0
- package/dist/mappings.js +1094 -0
- package/dist/mappings.js.map +1 -0
- package/dist/mappings.test.d.ts +2 -0
- package/dist/mappings.test.d.ts.map +1 -0
- package/dist/mappings.test.js +161 -0
- package/dist/mappings.test.js.map +1 -0
- package/dist/minecraft.d.ts +21 -0
- package/dist/minecraft.d.ts.map +1 -0
- package/dist/minecraft.js +70 -0
- package/dist/minecraft.js.map +1 -0
- package/dist/modpacks-ch.d.ts +224 -0
- package/dist/modpacks-ch.d.ts.map +1 -0
- package/dist/modpacks-ch.js +146 -0
- package/dist/modpacks-ch.js.map +1 -0
- package/dist/modrinth.d.ts +67 -0
- package/dist/modrinth.d.ts.map +1 -0
- package/dist/modrinth.js +88 -0
- package/dist/modrinth.js.map +1 -0
- package/dist/platform-adapter.d.ts +23 -0
- package/dist/platform-adapter.d.ts.map +1 -0
- package/dist/platform-adapter.js +2 -0
- package/dist/platform-adapter.js.map +1 -0
- package/dist/platform.d.ts +47 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +175 -0
- package/dist/platform.js.map +1 -0
- package/dist/processor.d.ts +51 -0
- package/dist/processor.d.ts.map +1 -0
- package/dist/processor.js +655 -0
- package/dist/processor.js.map +1 -0
- package/dist/processor.test.d.ts +2 -0
- package/dist/processor.test.d.ts.map +1 -0
- package/dist/processor.test.js +629 -0
- package/dist/processor.test.js.map +1 -0
- package/dist/repositories/embeddings-sqlite.d.ts +38 -0
- package/dist/repositories/embeddings-sqlite.d.ts.map +1 -0
- package/dist/repositories/embeddings-sqlite.js +208 -0
- package/dist/repositories/embeddings-sqlite.js.map +1 -0
- package/dist/repositories/embeddings.d.ts +40 -0
- package/dist/repositories/embeddings.d.ts.map +1 -0
- package/dist/repositories/embeddings.js +136 -0
- package/dist/repositories/embeddings.js.map +1 -0
- package/dist/repositories/index.d.ts +2 -0
- package/dist/repositories/index.d.ts.map +1 -0
- package/dist/repositories/index.js +15 -0
- package/dist/repositories/index.js.map +1 -0
- package/dist/repositories/mcVersion.d.ts +16 -0
- package/dist/repositories/mcVersion.d.ts.map +1 -0
- package/dist/repositories/mcVersion.js +52 -0
- package/dist/repositories/mcVersion.js.map +1 -0
- package/dist/repositories/mod.d.ts +241 -0
- package/dist/repositories/mod.d.ts.map +1 -0
- package/dist/repositories/mod.js +342 -0
- package/dist/repositories/mod.js.map +1 -0
- package/dist/repositories/packs.d.ts +44 -0
- package/dist/repositories/packs.d.ts.map +1 -0
- package/dist/repositories/packs.js +138 -0
- package/dist/repositories/packs.js.map +1 -0
- package/dist/search-adapter.d.ts +34 -0
- package/dist/search-adapter.d.ts.map +1 -0
- package/dist/search-adapter.js +195 -0
- package/dist/search-adapter.js.map +1 -0
- package/dist/search.d.ts +6 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +54 -0
- package/dist/search.js.map +1 -0
- package/dist/search.test.d.ts +2 -0
- package/dist/search.test.d.ts.map +1 -0
- package/dist/search.test.js +71 -0
- package/dist/search.test.js.map +1 -0
- package/dist/security.d.ts +98 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +446 -0
- package/dist/security.js.map +1 -0
- package/dist/security.test.d.ts +2 -0
- package/dist/security.test.d.ts.map +1 -0
- package/dist/security.test.js +687 -0
- package/dist/security.test.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1465 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +1113 -0
- package/dist/setup.js.map +1 -0
- package/dist/tools/bytecode.d.ts +129 -0
- package/dist/tools/bytecode.d.ts.map +1 -0
- package/dist/tools/bytecode.js +679 -0
- package/dist/tools/bytecode.js.map +1 -0
- package/dist/tools/catalog.d.ts +54 -0
- package/dist/tools/catalog.d.ts.map +1 -0
- package/dist/tools/catalog.js +249 -0
- package/dist/tools/catalog.js.map +1 -0
- package/dist/tools/compat-check.d.ts +23 -0
- package/dist/tools/compat-check.d.ts.map +1 -0
- package/dist/tools/compat-check.js +250 -0
- package/dist/tools/compat-check.js.map +1 -0
- package/dist/tools/compat-check.test.d.ts +2 -0
- package/dist/tools/compat-check.test.d.ts.map +1 -0
- package/dist/tools/compat-check.test.js +179 -0
- package/dist/tools/compat-check.test.js.map +1 -0
- package/dist/tools/diagnostics.d.ts +10 -0
- package/dist/tools/diagnostics.d.ts.map +1 -0
- package/dist/tools/diagnostics.js +133 -0
- package/dist/tools/diagnostics.js.map +1 -0
- package/dist/tools/diagnostics.test.d.ts +2 -0
- package/dist/tools/diagnostics.test.d.ts.map +1 -0
- package/dist/tools/diagnostics.test.js +168 -0
- package/dist/tools/diagnostics.test.js.map +1 -0
- package/dist/tools/docs.d.ts +19 -0
- package/dist/tools/docs.d.ts.map +1 -0
- package/dist/tools/docs.js +230 -0
- package/dist/tools/docs.js.map +1 -0
- package/dist/tools/embed-registry.d.ts +125 -0
- package/dist/tools/embed-registry.d.ts.map +1 -0
- package/dist/tools/embed-registry.js +560 -0
- package/dist/tools/embed-registry.js.map +1 -0
- package/dist/tools/gradle.d.ts +16 -0
- package/dist/tools/gradle.d.ts.map +1 -0
- package/dist/tools/gradle.js +182 -0
- package/dist/tools/gradle.js.map +1 -0
- package/dist/tools/graphify.d.ts +144 -0
- package/dist/tools/graphify.d.ts.map +1 -0
- package/dist/tools/graphify.js +798 -0
- package/dist/tools/graphify.js.map +1 -0
- package/dist/tools/graphify.test.d.ts +2 -0
- package/dist/tools/graphify.test.d.ts.map +1 -0
- package/dist/tools/graphify.test.js +39 -0
- package/dist/tools/graphify.test.js.map +1 -0
- package/dist/tools/ingest.d.ts +85 -0
- package/dist/tools/ingest.d.ts.map +1 -0
- package/dist/tools/ingest.js +477 -0
- package/dist/tools/ingest.js.map +1 -0
- package/dist/tools/ingest.test.d.ts +2 -0
- package/dist/tools/ingest.test.d.ts.map +1 -0
- package/dist/tools/ingest.test.js +340 -0
- package/dist/tools/ingest.test.js.map +1 -0
- package/dist/tools/kubejs.d.ts +37 -0
- package/dist/tools/kubejs.d.ts.map +1 -0
- package/dist/tools/kubejs.js +245 -0
- package/dist/tools/kubejs.js.map +1 -0
- package/dist/tools/mappings.d.ts +17 -0
- package/dist/tools/mappings.d.ts.map +1 -0
- package/dist/tools/mappings.js +85 -0
- package/dist/tools/mappings.js.map +1 -0
- package/dist/tools/mc-fts.d.ts +101 -0
- package/dist/tools/mc-fts.d.ts.map +1 -0
- package/dist/tools/mc-fts.js +279 -0
- package/dist/tools/mc-fts.js.map +1 -0
- package/dist/tools/mcmeta.d.ts +41 -0
- package/dist/tools/mcmeta.d.ts.map +1 -0
- package/dist/tools/mcmeta.js +365 -0
- package/dist/tools/mcmeta.js.map +1 -0
- package/dist/tools/mixin-scan.d.ts +113 -0
- package/dist/tools/mixin-scan.d.ts.map +1 -0
- package/dist/tools/mixin-scan.js +433 -0
- package/dist/tools/mixin-scan.js.map +1 -0
- package/dist/tools/mod-data.d.ts +83 -0
- package/dist/tools/mod-data.d.ts.map +1 -0
- package/dist/tools/mod-data.js +630 -0
- package/dist/tools/mod-data.js.map +1 -0
- package/dist/tools/mod-tags.d.ts +59 -0
- package/dist/tools/mod-tags.d.ts.map +1 -0
- package/dist/tools/mod-tags.js +279 -0
- package/dist/tools/mod-tags.js.map +1 -0
- package/dist/tools/mod-tags.test.d.ts +2 -0
- package/dist/tools/mod-tags.test.d.ts.map +1 -0
- package/dist/tools/mod-tags.test.js +75 -0
- package/dist/tools/mod-tags.test.js.map +1 -0
- package/dist/tools/modpacks-ch.d.ts +257 -0
- package/dist/tools/modpacks-ch.d.ts.map +1 -0
- package/dist/tools/modpacks-ch.js +516 -0
- package/dist/tools/modpacks-ch.js.map +1 -0
- package/dist/tools/packtools.d.ts +113 -0
- package/dist/tools/packtools.d.ts.map +1 -0
- package/dist/tools/packtools.js +809 -0
- package/dist/tools/packtools.js.map +1 -0
- package/dist/tools/packtools.test.d.ts +2 -0
- package/dist/tools/packtools.test.d.ts.map +1 -0
- package/dist/tools/packtools.test.js +99 -0
- package/dist/tools/packtools.test.js.map +1 -0
- package/dist/tools/platform.d.ts +68 -0
- package/dist/tools/platform.d.ts.map +1 -0
- package/dist/tools/platform.js +452 -0
- package/dist/tools/platform.js.map +1 -0
- package/dist/tools/primers.d.ts +36 -0
- package/dist/tools/primers.d.ts.map +1 -0
- package/dist/tools/primers.js +451 -0
- package/dist/tools/primers.js.map +1 -0
- package/dist/tools/reports.d.ts +19 -0
- package/dist/tools/reports.d.ts.map +1 -0
- package/dist/tools/reports.js +573 -0
- package/dist/tools/reports.js.map +1 -0
- package/dist/tools/source.d.ts +10 -0
- package/dist/tools/source.d.ts.map +1 -0
- package/dist/tools/source.js +101 -0
- package/dist/tools/source.js.map +1 -0
- package/dist/tools/vanilla-data.d.ts +127 -0
- package/dist/tools/vanilla-data.d.ts.map +1 -0
- package/dist/tools/vanilla-data.js +824 -0
- package/dist/tools/vanilla-data.js.map +1 -0
- package/dist/tools/vanilla.d.ts +171 -0
- package/dist/tools/vanilla.d.ts.map +1 -0
- package/dist/tools/vanilla.js +612 -0
- package/dist/tools/vanilla.js.map +1 -0
- package/dist/tools/version-diff.d.ts +113 -0
- package/dist/tools/version-diff.d.ts.map +1 -0
- package/dist/tools/version-diff.js +519 -0
- package/dist/tools/version-diff.js.map +1 -0
- package/dist/tools/version-diff.test.d.ts +2 -0
- package/dist/tools/version-diff.test.d.ts.map +1 -0
- package/dist/tools/version-diff.test.js +102 -0
- package/dist/tools/version-diff.test.js.map +1 -0
- package/dist/validate.d.ts +19 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +54 -0
- package/dist/validate.js.map +1 -0
- package/dist/validate.test.d.ts +2 -0
- package/dist/validate.test.d.ts.map +1 -0
- package/dist/validate.test.js +73 -0
- package/dist/validate.test.js.map +1 -0
- package/package.json +62 -0
- package/prisma/backends/schema.sqlite.prisma +215 -0
- package/prisma/backends/template.db +0 -0
- package/prisma/schema.prisma +303 -0
- package/scripts/backup.mjs +151 -0
- package/scripts/build-template-db.mjs +38 -0
- package/scripts/check-atm10.mjs +27 -0
- package/scripts/check-sync.mjs +13 -0
- package/scripts/create-fts-index.mjs +50 -0
- package/scripts/enable-pgvector.mjs +90 -0
- package/scripts/enable-sqlite-vec.mjs +158 -0
- package/scripts/migrate-backend.mjs +188 -0
- package/scripts/migrate-embed-provenance.mjs +80 -0
- package/scripts/migrate-metadata-source.mjs +62 -0
- package/scripts/migrate-mod-source-files.mjs +67 -0
- package/scripts/post-update.mjs +77 -0
- package/scripts/verify-atm10.mjs +63 -0
package/dist/mappings.js
ADDED
|
@@ -0,0 +1,1094 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mapping utilities for Minecraft: download, parse, and translate symbols
|
|
3
|
+
* across official / intermediary / yarn / mojmap namespaces.
|
|
4
|
+
*
|
|
5
|
+
* Also supports Parchment parameter names + javadocs layered on top of mojmap.
|
|
6
|
+
*
|
|
7
|
+
* Data sources:
|
|
8
|
+
* - Intermediary: https://maven.fabricmc.net/net/fabricmc/intermediary/{v}/intermediary-{v}-v2.jar
|
|
9
|
+
* - Yarn: https://maven.fabricmc.net/net/fabricmc/yarn/{yarnVer}/yarn-{yarnVer}-v2.jar
|
|
10
|
+
* - Mojmap: Mojang's client_mappings ProGuard file (inverted to official→named)
|
|
11
|
+
* - Parchment: https://maven.parchmentmc.org/org/parchmentmc/data/parchment-{v}/{build}/parchment-{v}-{build}-checked.zip
|
|
12
|
+
*/
|
|
13
|
+
import { readFile, writeFile } from "fs/promises";
|
|
14
|
+
import { join } from "path";
|
|
15
|
+
import AdmZip from "adm-zip";
|
|
16
|
+
import { CACHE_ROOT, exists, ensureDir } from "./cache.js";
|
|
17
|
+
import { spawn } from "child_process";
|
|
18
|
+
// ── Cache dirs ────────────────────────────────────────────────────────────────
|
|
19
|
+
export const MAPPINGS_DIR = join(CACHE_ROOT, "mappings");
|
|
20
|
+
export const PARCHMENT_DIR = join(CACHE_ROOT, "parchment");
|
|
21
|
+
const TOOLS_DIR = join(CACHE_ROOT, "tools");
|
|
22
|
+
const TINY_REMAPPER_VERSION = "0.10.3";
|
|
23
|
+
export const TINY_REMAPPER_PATH = join(TOOLS_DIR, "tiny-remapper.jar");
|
|
24
|
+
const TINY_REMAPPER_URL = `https://maven.fabricmc.net/net/fabricmc/tiny-remapper/${TINY_REMAPPER_VERSION}/tiny-remapper-${TINY_REMAPPER_VERSION}-fat.jar`;
|
|
25
|
+
const SPECIAL_SOURCE_VERSION = "1.11.4";
|
|
26
|
+
export const SPECIAL_SOURCE_PATH = join(TOOLS_DIR, "SpecialSource.jar");
|
|
27
|
+
const SPECIAL_SOURCE_URL = `https://maven.minecraftforge.net/net/md-5/SpecialSource/${SPECIAL_SOURCE_VERSION}/SpecialSource-${SPECIAL_SOURCE_VERSION}-shaded.jar`;
|
|
28
|
+
// ── In-memory caches ──────────────────────────────────────────────────────────
|
|
29
|
+
const tinyIndexCache = new Map();
|
|
30
|
+
const mojmapCache = new Map(); // version → (official→named)
|
|
31
|
+
const parchmentCache = new Map();
|
|
32
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
33
|
+
async function downloadToFile(url, dest) {
|
|
34
|
+
await ensureDir(dest);
|
|
35
|
+
const res = await fetch(url);
|
|
36
|
+
if (!res.ok)
|
|
37
|
+
throw new Error(`Download failed ${url}: ${res.status}`);
|
|
38
|
+
const buf = await res.arrayBuffer();
|
|
39
|
+
await writeFile(dest, Buffer.from(buf));
|
|
40
|
+
}
|
|
41
|
+
async function extractTinyFromJar(jarPath, entry, dest) {
|
|
42
|
+
const zip = new AdmZip(jarPath);
|
|
43
|
+
const e = zip.getEntry(entry);
|
|
44
|
+
if (!e)
|
|
45
|
+
throw new Error(`Entry '${entry}' not found in ${jarPath}`);
|
|
46
|
+
await ensureDir(dest);
|
|
47
|
+
await writeFile(dest, e.getData());
|
|
48
|
+
}
|
|
49
|
+
// ── Tiny V2 parser ────────────────────────────────────────────────────────────
|
|
50
|
+
export function parseTinyV2(content) {
|
|
51
|
+
const lines = content.split("\n");
|
|
52
|
+
const header = lines[0].split("\t");
|
|
53
|
+
const ns0 = header[3] ?? "official";
|
|
54
|
+
const ns1 = header[4] ?? "intermediary";
|
|
55
|
+
const classes = new Map();
|
|
56
|
+
const fields = new Map();
|
|
57
|
+
const methods = new Map();
|
|
58
|
+
let curClass = "";
|
|
59
|
+
let curFields = null;
|
|
60
|
+
let curMethods = null;
|
|
61
|
+
for (let i = 1; i < lines.length; i++) {
|
|
62
|
+
const line = lines[i];
|
|
63
|
+
if (!line || line.startsWith("#"))
|
|
64
|
+
continue;
|
|
65
|
+
const depth = (line.match(/^\t*/)?.[0].length) ?? 0;
|
|
66
|
+
const parts = line.trimStart().split("\t");
|
|
67
|
+
if (depth === 0 && parts[0] === "c") {
|
|
68
|
+
curClass = parts[1];
|
|
69
|
+
const toClass = parts[2] ?? parts[1];
|
|
70
|
+
classes.set(curClass, toClass);
|
|
71
|
+
curFields = new Map();
|
|
72
|
+
fields.set(curClass, curFields);
|
|
73
|
+
curMethods = new Map();
|
|
74
|
+
methods.set(curClass, curMethods);
|
|
75
|
+
}
|
|
76
|
+
else if (depth === 1 && parts[0] === "f" && curFields) {
|
|
77
|
+
const [, desc, fromName, toName] = parts;
|
|
78
|
+
curFields.set(`${fromName}:${desc}`, toName ?? fromName);
|
|
79
|
+
}
|
|
80
|
+
else if (depth === 1 && parts[0] === "m" && curMethods) {
|
|
81
|
+
const [, desc, fromName, toName] = parts;
|
|
82
|
+
curMethods.set(`${fromName}${desc}`, toName ?? fromName);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { ns: [ns0, ns1], classes, fields, methods };
|
|
86
|
+
}
|
|
87
|
+
// ── ProGuard parser (named → official, inverted to official → named) ──────────
|
|
88
|
+
function parseProGuardClasses(content) {
|
|
89
|
+
const map = new Map();
|
|
90
|
+
for (const line of content.split("\n")) {
|
|
91
|
+
if (line.startsWith("#") || line.startsWith(" ") || line.startsWith("\t") || !line.trim())
|
|
92
|
+
continue;
|
|
93
|
+
const m = line.match(/^(.+) -> (.+):$/);
|
|
94
|
+
if (m) {
|
|
95
|
+
const named = m[1].trim().replace(/\./g, "/");
|
|
96
|
+
const obf = m[2].trim().replace(/\./g, "/");
|
|
97
|
+
map.set(obf, named); // official(obf) → named
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return map;
|
|
101
|
+
}
|
|
102
|
+
// ── Fabric Meta ───────────────────────────────────────────────────────────────
|
|
103
|
+
async function resolveYarnVersion(mcVersion) {
|
|
104
|
+
try {
|
|
105
|
+
const res = await fetch(`https://meta.fabricmc.net/v2/versions/yarn/${encodeURIComponent(mcVersion)}`);
|
|
106
|
+
if (!res.ok)
|
|
107
|
+
return null;
|
|
108
|
+
const versions = await res.json();
|
|
109
|
+
const stable = versions.filter(v => v.stable);
|
|
110
|
+
const list = stable.length > 0 ? stable : versions;
|
|
111
|
+
if (list.length === 0)
|
|
112
|
+
return null;
|
|
113
|
+
list.sort((a, b) => {
|
|
114
|
+
const ba = parseInt(a.version.split("+build.")[1] ?? "0");
|
|
115
|
+
const bb = parseInt(b.version.split("+build.")[1] ?? "0");
|
|
116
|
+
return bb - ba;
|
|
117
|
+
});
|
|
118
|
+
return list[0].version;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// ── Mapping index loaders ─────────────────────────────────────────────────────
|
|
125
|
+
async function getIntermediaryIndex(version) {
|
|
126
|
+
const key = `intermediary-${version}`;
|
|
127
|
+
if (tinyIndexCache.has(key))
|
|
128
|
+
return tinyIndexCache.get(key) ?? null;
|
|
129
|
+
const tinyPath = join(MAPPINGS_DIR, `intermediary-${version}.tiny`);
|
|
130
|
+
if (!(await exists(tinyPath))) {
|
|
131
|
+
const jarPath = join(MAPPINGS_DIR, `intermediary-${version}.jar`);
|
|
132
|
+
const url = `https://maven.fabricmc.net/net/fabricmc/intermediary/${encodeURIComponent(version)}/intermediary-${encodeURIComponent(version)}-v2.jar`;
|
|
133
|
+
try {
|
|
134
|
+
await downloadToFile(url, jarPath);
|
|
135
|
+
await extractTinyFromJar(jarPath, "mappings/mappings.tiny", tinyPath);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
tinyIndexCache.set(key, null);
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const index = parseTinyV2(await readFile(tinyPath, "utf8"));
|
|
144
|
+
tinyIndexCache.set(key, index);
|
|
145
|
+
return index;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
tinyIndexCache.set(key, null);
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function getYarnIndex(version) {
|
|
153
|
+
const key = `yarn-${version}`;
|
|
154
|
+
if (tinyIndexCache.has(key))
|
|
155
|
+
return tinyIndexCache.get(key) ?? null;
|
|
156
|
+
const tinyPath = join(MAPPINGS_DIR, `yarn-${version}.tiny`);
|
|
157
|
+
if (!(await exists(tinyPath))) {
|
|
158
|
+
const yarnVersion = await resolveYarnVersion(version);
|
|
159
|
+
if (!yarnVersion) {
|
|
160
|
+
tinyIndexCache.set(key, null);
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const jarPath = join(MAPPINGS_DIR, `yarn-${version}.jar`);
|
|
164
|
+
const url = `https://maven.fabricmc.net/net/fabricmc/yarn/${encodeURIComponent(yarnVersion)}/yarn-${encodeURIComponent(yarnVersion)}-v2.jar`;
|
|
165
|
+
try {
|
|
166
|
+
await downloadToFile(url, jarPath);
|
|
167
|
+
await extractTinyFromJar(jarPath, "mappings/mappings.tiny", tinyPath);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
tinyIndexCache.set(key, null);
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const index = parseTinyV2(await readFile(tinyPath, "utf8"));
|
|
176
|
+
tinyIndexCache.set(key, index);
|
|
177
|
+
return index;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
tinyIndexCache.set(key, null);
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function getMojmapClassMap(version) {
|
|
185
|
+
if (mojmapCache.has(version))
|
|
186
|
+
return mojmapCache.get(version) ?? null;
|
|
187
|
+
const cachePath = join(MAPPINGS_DIR, `mojmap-classes-${version}.json`);
|
|
188
|
+
if (!(await exists(cachePath))) {
|
|
189
|
+
try {
|
|
190
|
+
const manifestRes = await fetch("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json");
|
|
191
|
+
if (!manifestRes.ok) {
|
|
192
|
+
mojmapCache.set(version, null);
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const manifest = await manifestRes.json();
|
|
196
|
+
const entry = manifest.versions.find(v => v.id === version);
|
|
197
|
+
if (!entry) {
|
|
198
|
+
mojmapCache.set(version, null);
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
const metaRes = await fetch(entry.url);
|
|
202
|
+
if (!metaRes.ok) {
|
|
203
|
+
mojmapCache.set(version, null);
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
const meta = await metaRes.json();
|
|
207
|
+
const mappingsUrl = meta.downloads?.client_mappings?.url;
|
|
208
|
+
if (!mappingsUrl) {
|
|
209
|
+
mojmapCache.set(version, null);
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
const pgPath = join(MAPPINGS_DIR, `proguard-${version}.txt`);
|
|
213
|
+
await downloadToFile(mappingsUrl, pgPath);
|
|
214
|
+
const classMap = parseProGuardClasses(await readFile(pgPath, "utf8"));
|
|
215
|
+
await ensureDir(cachePath);
|
|
216
|
+
await writeFile(cachePath, JSON.stringify(Object.fromEntries(classMap)));
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
mojmapCache.set(version, null);
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const raw = JSON.parse(await readFile(cachePath, "utf8"));
|
|
225
|
+
const map = new Map(Object.entries(raw));
|
|
226
|
+
mojmapCache.set(version, map);
|
|
227
|
+
return map;
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
mojmapCache.set(version, null);
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// ── Index lookup helper ───────────────────────────────────────────────────────
|
|
235
|
+
export function lookupInIndex(idx, symbol, reverse) {
|
|
236
|
+
const notFound = { found: false, source: symbol, type: "unknown" };
|
|
237
|
+
// Class lookup
|
|
238
|
+
if (!reverse) {
|
|
239
|
+
const target = idx.classes.get(symbol);
|
|
240
|
+
if (target)
|
|
241
|
+
return { found: true, source: symbol, target, type: "class" };
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
for (const [from, to] of idx.classes) {
|
|
245
|
+
if (to === symbol)
|
|
246
|
+
return { found: true, source: symbol, target: from, type: "class" };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// Method lookup
|
|
250
|
+
for (const [className, methodMap] of idx.methods) {
|
|
251
|
+
if (!reverse) {
|
|
252
|
+
for (const [key, toName] of methodMap) {
|
|
253
|
+
if (key === symbol || key.startsWith(symbol + "(") || key.startsWith(symbol + "()")) {
|
|
254
|
+
const toClass = idx.classes.get(className) ?? className;
|
|
255
|
+
return { found: true, source: symbol, target: toName, type: "method", containingClass: toClass };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
for (const [fromKey, toName] of methodMap) {
|
|
261
|
+
if (toName === symbol) {
|
|
262
|
+
return { found: true, source: symbol, target: fromKey.split("(")[0], type: "method", containingClass: className };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Field lookup
|
|
268
|
+
for (const [className, fieldMap] of idx.fields) {
|
|
269
|
+
if (!reverse) {
|
|
270
|
+
for (const [key, toName] of fieldMap) {
|
|
271
|
+
const fieldName = key.split(":")[0];
|
|
272
|
+
if (fieldName === symbol) {
|
|
273
|
+
const toClass = idx.classes.get(className) ?? className;
|
|
274
|
+
return { found: true, source: symbol, target: toName, type: "field", containingClass: toClass };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
for (const [fromKey, toName] of fieldMap) {
|
|
280
|
+
if (toName === symbol) {
|
|
281
|
+
return { found: true, source: symbol, target: fromKey.split(":")[0], type: "field", containingClass: className };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return notFound;
|
|
287
|
+
}
|
|
288
|
+
// ── SRG/MCP mappings (legacy Forge 1.7.10–1.15) ──────────────────────────────
|
|
289
|
+
/**
|
|
290
|
+
* Known stable MCP channels per MC version.
|
|
291
|
+
* Format: "stable_{num}-{mcVersion}" or "snapshot_{date}-{mcVersion}"
|
|
292
|
+
* Source: https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp_stable/maven-metadata.xml
|
|
293
|
+
*
|
|
294
|
+
* Versions without a dedicated stable channel fall back to the nearest
|
|
295
|
+
* available channel for the same major.minor series.
|
|
296
|
+
*/
|
|
297
|
+
const MCP_CHANNELS = {
|
|
298
|
+
// 1.7
|
|
299
|
+
"1.7.10": "stable_12-1.7.10",
|
|
300
|
+
// 1.8
|
|
301
|
+
"1.8": "stable_18-1.8",
|
|
302
|
+
"1.8.8": "stable_20-1.8.8",
|
|
303
|
+
"1.8.9": "stable_22-1.8.9",
|
|
304
|
+
// 1.9
|
|
305
|
+
"1.9": "stable_24-1.9",
|
|
306
|
+
"1.9.2": "stable_24-1.9", // no dedicated stable; use 1.9's
|
|
307
|
+
"1.9.4": "stable_26-1.9.4",
|
|
308
|
+
// 1.10
|
|
309
|
+
"1.10": "stable_29-1.10.2", // no dedicated stable; use 1.10.2's
|
|
310
|
+
"1.10.2": "stable_29-1.10.2",
|
|
311
|
+
// 1.11
|
|
312
|
+
"1.11": "stable_32-1.11",
|
|
313
|
+
"1.11.1": "stable_32-1.11", // no dedicated stable; use 1.11's
|
|
314
|
+
"1.11.2": "stable_32-1.11",
|
|
315
|
+
// 1.12
|
|
316
|
+
"1.12": "stable_39-1.12",
|
|
317
|
+
"1.12.1": "stable_39-1.12",
|
|
318
|
+
"1.12.2": "stable_39-1.12",
|
|
319
|
+
// 1.13 (TSRG era starts — getSrgIndex falls back to mcp_config)
|
|
320
|
+
"1.13": "stable_43-1.13",
|
|
321
|
+
"1.13.1": "stable_45-1.13.1",
|
|
322
|
+
"1.13.2": "stable_47-1.13.2",
|
|
323
|
+
// 1.14
|
|
324
|
+
"1.14": "stable_49-1.14",
|
|
325
|
+
"1.14.1": "stable_51-1.14.1",
|
|
326
|
+
"1.14.2": "stable_53-1.14.2",
|
|
327
|
+
"1.14.3": "stable_56-1.14.3",
|
|
328
|
+
"1.14.4": "stable_58-1.14.4",
|
|
329
|
+
// 1.15
|
|
330
|
+
"1.15": "stable_60-1.15",
|
|
331
|
+
};
|
|
332
|
+
const srgCache = new Map();
|
|
333
|
+
const mcpCache = new Map();
|
|
334
|
+
function parseSrg(content) {
|
|
335
|
+
const classes = new Map();
|
|
336
|
+
const methods = new Map();
|
|
337
|
+
const fields = new Map();
|
|
338
|
+
for (const line of content.split("\n")) {
|
|
339
|
+
const parts = line.trim().split(" ");
|
|
340
|
+
if (parts[0] === "CL:") {
|
|
341
|
+
classes.set(parts[1], parts[2]);
|
|
342
|
+
}
|
|
343
|
+
else if (parts[0] === "FD:") {
|
|
344
|
+
fields.set(parts[1], parts[2]);
|
|
345
|
+
}
|
|
346
|
+
else if (parts[0] === "MD:") {
|
|
347
|
+
methods.set(parts[1] + " " + parts[2], parts[3]);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return { classes, methods, fields };
|
|
351
|
+
}
|
|
352
|
+
function parseTsrg(content) {
|
|
353
|
+
const classes = new Map();
|
|
354
|
+
const methods = new Map();
|
|
355
|
+
const fields = new Map();
|
|
356
|
+
let curObf = "";
|
|
357
|
+
let curSrg = "";
|
|
358
|
+
for (const line of content.split("\n")) {
|
|
359
|
+
if (!line || line.startsWith("#"))
|
|
360
|
+
continue;
|
|
361
|
+
if (!line.startsWith("\t")) {
|
|
362
|
+
const [obf, srg] = line.trim().split(" ");
|
|
363
|
+
if (obf && srg) {
|
|
364
|
+
curObf = obf;
|
|
365
|
+
curSrg = srg;
|
|
366
|
+
classes.set(obf, srg);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
const parts = line.trim().split(" ");
|
|
371
|
+
if (parts.length === 3) {
|
|
372
|
+
// method: obfName obfDesc srgName
|
|
373
|
+
methods.set(curObf + "/" + parts[0] + " " + parts[1], curSrg + "/" + parts[2]);
|
|
374
|
+
}
|
|
375
|
+
else if (parts.length === 2) {
|
|
376
|
+
// field: obfName srgName
|
|
377
|
+
fields.set(curObf + "/" + parts[0], curSrg + "/" + parts[1]);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return { classes, methods, fields };
|
|
382
|
+
}
|
|
383
|
+
function parseMcpCsv(csv) {
|
|
384
|
+
const map = new Map();
|
|
385
|
+
const lines = csv.split("\n");
|
|
386
|
+
// Skip header: searge,name,side,desc
|
|
387
|
+
for (let i = 1; i < lines.length; i++) {
|
|
388
|
+
const line = lines[i].trim();
|
|
389
|
+
if (!line)
|
|
390
|
+
continue;
|
|
391
|
+
const comma1 = line.indexOf(",");
|
|
392
|
+
const comma2 = line.indexOf(",", comma1 + 1);
|
|
393
|
+
if (comma1 < 0 || comma2 < 0)
|
|
394
|
+
continue;
|
|
395
|
+
const searge = line.substring(0, comma1);
|
|
396
|
+
const name = line.substring(comma1 + 1, comma2);
|
|
397
|
+
if (searge && name)
|
|
398
|
+
map.set(searge, name);
|
|
399
|
+
}
|
|
400
|
+
return map;
|
|
401
|
+
}
|
|
402
|
+
async function getSrgIndex(version) {
|
|
403
|
+
const key = `srg-${version}`;
|
|
404
|
+
if (srgCache.has(key))
|
|
405
|
+
return srgCache.get(key) ?? null;
|
|
406
|
+
const srgPath = join(MAPPINGS_DIR, `srg-${version}.srg`);
|
|
407
|
+
if (!(await exists(srgPath))) {
|
|
408
|
+
// Try joined.srg format first (1.7.10–1.12.2)
|
|
409
|
+
const srgZipPath = join(MAPPINGS_DIR, `mcp-${version}-srg.zip`);
|
|
410
|
+
const srgUrl = `https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp/${version}/mcp-${version}-srg.zip`;
|
|
411
|
+
try {
|
|
412
|
+
await downloadToFile(srgUrl, srgZipPath);
|
|
413
|
+
const zip = new AdmZip(srgZipPath);
|
|
414
|
+
const entry = zip.getEntry("joined.srg");
|
|
415
|
+
if (entry) {
|
|
416
|
+
await ensureDir(srgPath);
|
|
417
|
+
await writeFile(srgPath, entry.getData());
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
srgCache.set(key, null);
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
// Try MCPConfig TSRG format (1.13+)
|
|
426
|
+
const tsrgZipPath = join(MAPPINGS_DIR, `mcp_config-${version}.zip`);
|
|
427
|
+
const tsrgUrl = `https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp_config/${version}/mcp_config-${version}.zip`;
|
|
428
|
+
try {
|
|
429
|
+
await downloadToFile(tsrgUrl, tsrgZipPath);
|
|
430
|
+
const zip = new AdmZip(tsrgZipPath);
|
|
431
|
+
const entry = zip.getEntry("config/joined.tsrg");
|
|
432
|
+
if (entry) {
|
|
433
|
+
const content = entry.getData().toString("utf8");
|
|
434
|
+
const idx = parseTsrg(content);
|
|
435
|
+
srgCache.set(key, idx);
|
|
436
|
+
return idx;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch { /* fall through */ }
|
|
440
|
+
srgCache.set(key, null);
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
const content = await readFile(srgPath, "utf8");
|
|
446
|
+
const idx = parseSrg(content);
|
|
447
|
+
srgCache.set(key, idx);
|
|
448
|
+
return idx;
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
srgCache.set(key, null);
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
async function getMcpNames(version) {
|
|
456
|
+
const key = `mcp-${version}`;
|
|
457
|
+
if (mcpCache.has(key))
|
|
458
|
+
return mcpCache.get(key) ?? null;
|
|
459
|
+
const channel = MCP_CHANNELS[version];
|
|
460
|
+
if (!channel) {
|
|
461
|
+
mcpCache.set(key, null);
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
const cachedMethods = join(MAPPINGS_DIR, `mcp-methods-${version}.csv`);
|
|
465
|
+
const cachedFields = join(MAPPINGS_DIR, `mcp-fields-${version}.csv`);
|
|
466
|
+
if (!(await exists(cachedMethods)) || !(await exists(cachedFields))) {
|
|
467
|
+
const zipPath = join(MAPPINGS_DIR, `mcp_${channel}.zip`);
|
|
468
|
+
const url = `https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp_stable/${channel}/mcp_stable-${channel}.zip`;
|
|
469
|
+
try {
|
|
470
|
+
await downloadToFile(url, zipPath);
|
|
471
|
+
const zip = new AdmZip(zipPath);
|
|
472
|
+
const methodsEntry = zip.getEntry("methods.csv");
|
|
473
|
+
const fieldsEntry = zip.getEntry("fields.csv");
|
|
474
|
+
if (!methodsEntry || !fieldsEntry) {
|
|
475
|
+
mcpCache.set(key, null);
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
await ensureDir(cachedMethods);
|
|
479
|
+
await writeFile(cachedMethods, methodsEntry.getData());
|
|
480
|
+
await ensureDir(cachedFields);
|
|
481
|
+
await writeFile(cachedFields, fieldsEntry.getData());
|
|
482
|
+
}
|
|
483
|
+
catch {
|
|
484
|
+
mcpCache.set(key, null);
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
try {
|
|
489
|
+
const methodsCsv = await readFile(cachedMethods, "utf8");
|
|
490
|
+
const fieldsCsv = await readFile(cachedFields, "utf8");
|
|
491
|
+
const names = {
|
|
492
|
+
methods: parseMcpCsv(methodsCsv),
|
|
493
|
+
fields: parseMcpCsv(fieldsCsv),
|
|
494
|
+
};
|
|
495
|
+
mcpCache.set(key, names);
|
|
496
|
+
return names;
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
mcpCache.set(key, null);
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Translate a SRG name (func_12345_a / field_12345_b) to its MCP human-readable name.
|
|
505
|
+
*/
|
|
506
|
+
function translateSrgToMcp(symbol, mcpNames) {
|
|
507
|
+
const notFound = { found: false, source: symbol, type: "unknown" };
|
|
508
|
+
// Method: func_12345_a
|
|
509
|
+
if (symbol.startsWith("func_")) {
|
|
510
|
+
const target = mcpNames.methods.get(symbol);
|
|
511
|
+
return target ? { found: true, source: symbol, target, type: "method" } : notFound;
|
|
512
|
+
}
|
|
513
|
+
// Field: field_12345_b
|
|
514
|
+
if (symbol.startsWith("field_")) {
|
|
515
|
+
const target = mcpNames.fields.get(symbol);
|
|
516
|
+
return target ? { found: true, source: symbol, target, type: "field" } : notFound;
|
|
517
|
+
}
|
|
518
|
+
// Try both maps as a fallback
|
|
519
|
+
const mTarget = mcpNames.methods.get(symbol);
|
|
520
|
+
if (mTarget)
|
|
521
|
+
return { found: true, source: symbol, target: mTarget, type: "method" };
|
|
522
|
+
const fTarget = mcpNames.fields.get(symbol);
|
|
523
|
+
if (fTarget)
|
|
524
|
+
return { found: true, source: symbol, target: fTarget, type: "field" };
|
|
525
|
+
return notFound;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Reverse-translate an MCP name back to its SRG name.
|
|
529
|
+
*/
|
|
530
|
+
function translateMcpToSrg(symbol, mcpNames) {
|
|
531
|
+
const notFound = { found: false, source: symbol, type: "unknown" };
|
|
532
|
+
for (const [srg, mcp] of mcpNames.methods) {
|
|
533
|
+
if (mcp === symbol)
|
|
534
|
+
return { found: true, source: symbol, target: srg, type: "method" };
|
|
535
|
+
}
|
|
536
|
+
for (const [srg, mcp] of mcpNames.fields) {
|
|
537
|
+
if (mcp === symbol)
|
|
538
|
+
return { found: true, source: symbol, target: srg, type: "field" };
|
|
539
|
+
}
|
|
540
|
+
return notFound;
|
|
541
|
+
}
|
|
542
|
+
// ── Detect unobfuscated versions (MC 26.1+ ships without obfuscation) ─────────
|
|
543
|
+
function isUnobfuscated(version) {
|
|
544
|
+
// 26.1+ versioning uses the new unobfuscated scheme
|
|
545
|
+
return /^(?:2[6-9]\.|[3-9]\d\.)/.test(version);
|
|
546
|
+
}
|
|
547
|
+
// ── Public translation API ────────────────────────────────────────────────────
|
|
548
|
+
export async function translateSymbol(symbol, from, to, version) {
|
|
549
|
+
if (from === to)
|
|
550
|
+
return { found: true, source: symbol, target: symbol, type: "unknown" };
|
|
551
|
+
if (isUnobfuscated(version)) {
|
|
552
|
+
return {
|
|
553
|
+
found: true,
|
|
554
|
+
source: symbol,
|
|
555
|
+
target: symbol,
|
|
556
|
+
type: "class",
|
|
557
|
+
note: `Version ${version} uses an unobfuscated JAR — all names are already in human-readable (mojmap-equivalent) form. No translation needed.`,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
const normalized = symbol.replace(/\./g, "/");
|
|
561
|
+
const notFound = { found: false, source: symbol, type: "unknown" };
|
|
562
|
+
try {
|
|
563
|
+
// Direct single-step routes
|
|
564
|
+
if (from === "official" && to === "intermediary") {
|
|
565
|
+
const idx = await getIntermediaryIndex(version);
|
|
566
|
+
if (!idx)
|
|
567
|
+
return { ...notFound, note: "Intermediary mappings not available" };
|
|
568
|
+
return lookupInIndex(idx, normalized, false);
|
|
569
|
+
}
|
|
570
|
+
if (from === "intermediary" && to === "official") {
|
|
571
|
+
const idx = await getIntermediaryIndex(version);
|
|
572
|
+
if (!idx)
|
|
573
|
+
return { ...notFound, note: "Intermediary mappings not available" };
|
|
574
|
+
return lookupInIndex(idx, normalized, true);
|
|
575
|
+
}
|
|
576
|
+
if (from === "intermediary" && to === "yarn") {
|
|
577
|
+
const idx = await getYarnIndex(version);
|
|
578
|
+
if (!idx)
|
|
579
|
+
return { ...notFound, note: "Yarn mappings not available" };
|
|
580
|
+
return lookupInIndex(idx, normalized, false);
|
|
581
|
+
}
|
|
582
|
+
if (from === "yarn" && to === "intermediary") {
|
|
583
|
+
const idx = await getYarnIndex(version);
|
|
584
|
+
if (!idx)
|
|
585
|
+
return { ...notFound, note: "Yarn mappings not available" };
|
|
586
|
+
return lookupInIndex(idx, normalized, true);
|
|
587
|
+
}
|
|
588
|
+
if (from === "official" && to === "mojmap") {
|
|
589
|
+
const map = await getMojmapClassMap(version);
|
|
590
|
+
if (!map)
|
|
591
|
+
return { ...notFound, note: "Mojmap not available for this version" };
|
|
592
|
+
const target = map.get(normalized);
|
|
593
|
+
return target ? { found: true, source: symbol, target, type: "class" } : notFound;
|
|
594
|
+
}
|
|
595
|
+
if (from === "mojmap" && to === "official") {
|
|
596
|
+
const map = await getMojmapClassMap(version);
|
|
597
|
+
if (!map)
|
|
598
|
+
return { ...notFound, note: "Mojmap not available for this version" };
|
|
599
|
+
for (const [off, named] of map)
|
|
600
|
+
if (named === normalized)
|
|
601
|
+
return { found: true, source: symbol, target: off, type: "class" };
|
|
602
|
+
return notFound;
|
|
603
|
+
}
|
|
604
|
+
// SRG/MCP direct routes (legacy Forge)
|
|
605
|
+
if (from === "srg" && to === "mcp") {
|
|
606
|
+
const names = await getMcpNames(version);
|
|
607
|
+
if (!names)
|
|
608
|
+
return { ...notFound, note: `MCP names not available for ${version}. Known versions: ${Object.keys(MCP_CHANNELS).join(", ")}` };
|
|
609
|
+
return translateSrgToMcp(symbol, names);
|
|
610
|
+
}
|
|
611
|
+
if (from === "mcp" && to === "srg") {
|
|
612
|
+
const names = await getMcpNames(version);
|
|
613
|
+
if (!names)
|
|
614
|
+
return { ...notFound, note: `MCP names not available for ${version}` };
|
|
615
|
+
return translateMcpToSrg(symbol, names);
|
|
616
|
+
}
|
|
617
|
+
if (from === "official" && to === "srg") {
|
|
618
|
+
const idx = await getSrgIndex(version);
|
|
619
|
+
if (!idx)
|
|
620
|
+
return { ...notFound, note: `SRG mappings not available for ${version}` };
|
|
621
|
+
const cls = idx.classes.get(normalized);
|
|
622
|
+
if (cls)
|
|
623
|
+
return { found: true, source: symbol, target: cls, type: "class" };
|
|
624
|
+
for (const [from, to] of idx.fields)
|
|
625
|
+
if (from.endsWith("/" + normalized))
|
|
626
|
+
return { found: true, source: symbol, target: to.split("/").pop(), type: "field" };
|
|
627
|
+
for (const [from, to] of idx.methods)
|
|
628
|
+
if (from.startsWith(normalized) || from.includes("/" + normalized + " "))
|
|
629
|
+
return { found: true, source: symbol, target: to.split("/").pop(), type: "method" };
|
|
630
|
+
return notFound;
|
|
631
|
+
}
|
|
632
|
+
if (from === "srg" && to === "official") {
|
|
633
|
+
const idx = await getSrgIndex(version);
|
|
634
|
+
if (!idx)
|
|
635
|
+
return { ...notFound, note: `SRG mappings not available for ${version}` };
|
|
636
|
+
for (const [obf, srg] of idx.classes)
|
|
637
|
+
if (srg === normalized)
|
|
638
|
+
return { found: true, source: symbol, target: obf, type: "class" };
|
|
639
|
+
for (const [obf, srg] of idx.fields)
|
|
640
|
+
if (srg.endsWith("/" + symbol))
|
|
641
|
+
return { found: true, source: symbol, target: obf.split("/").pop(), type: "field" };
|
|
642
|
+
for (const [obf, srg] of idx.methods)
|
|
643
|
+
if (srg.endsWith("/" + symbol))
|
|
644
|
+
return { found: true, source: symbol, target: obf.split("/")[0]?.split(" ")[0]?.split("/").pop(), type: "method" };
|
|
645
|
+
return notFound;
|
|
646
|
+
}
|
|
647
|
+
// Chained routes via intermediary hub
|
|
648
|
+
const chain = async (steps) => {
|
|
649
|
+
let current = normalized;
|
|
650
|
+
let result = notFound;
|
|
651
|
+
for (const [f, t] of steps) {
|
|
652
|
+
result = await translateSymbol(current, f, t, version);
|
|
653
|
+
if (!result.found || !result.target)
|
|
654
|
+
return result;
|
|
655
|
+
current = result.target;
|
|
656
|
+
}
|
|
657
|
+
return { ...result, source: symbol };
|
|
658
|
+
};
|
|
659
|
+
if (from === "official" && to === "yarn")
|
|
660
|
+
return chain([["official", "intermediary"], ["intermediary", "yarn"]]);
|
|
661
|
+
if (from === "yarn" && to === "official")
|
|
662
|
+
return chain([["yarn", "intermediary"], ["intermediary", "official"]]);
|
|
663
|
+
if (from === "mojmap" && to === "intermediary")
|
|
664
|
+
return chain([["mojmap", "official"], ["official", "intermediary"]]);
|
|
665
|
+
if (from === "intermediary" && to === "mojmap")
|
|
666
|
+
return chain([["intermediary", "official"], ["official", "mojmap"]]);
|
|
667
|
+
if (from === "yarn" && to === "mojmap")
|
|
668
|
+
return chain([["yarn", "official"], ["official", "mojmap"]]);
|
|
669
|
+
if (from === "mojmap" && to === "yarn")
|
|
670
|
+
return chain([["mojmap", "official"], ["official", "yarn"]]);
|
|
671
|
+
// Chained routes involving SRG/MCP
|
|
672
|
+
if (from === "official" && to === "mcp")
|
|
673
|
+
return chain([["official", "srg"], ["srg", "mcp"]]);
|
|
674
|
+
if (from === "mcp" && to === "official")
|
|
675
|
+
return chain([["mcp", "srg"], ["srg", "official"]]);
|
|
676
|
+
return { ...notFound, note: `Unsupported translation: ${from} → ${to}` };
|
|
677
|
+
}
|
|
678
|
+
catch (err) {
|
|
679
|
+
return { ...notFound, note: `Translation error: ${err instanceof Error ? err.message : String(err)}` };
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
// ── TinyRemapper ──────────────────────────────────────────────────────────────
|
|
683
|
+
export async function ensureTinyRemapper() {
|
|
684
|
+
if (await exists(TINY_REMAPPER_PATH))
|
|
685
|
+
return TINY_REMAPPER_PATH;
|
|
686
|
+
await downloadToFile(TINY_REMAPPER_URL, TINY_REMAPPER_PATH);
|
|
687
|
+
return TINY_REMAPPER_PATH;
|
|
688
|
+
}
|
|
689
|
+
/** Run java with the given args, return stdout. */
|
|
690
|
+
async function runJava(args) {
|
|
691
|
+
const javaExe = process.env.JAVA_HOME
|
|
692
|
+
? join(process.env.JAVA_HOME, "bin", process.platform === "win32" ? "java.exe" : "java")
|
|
693
|
+
: "java";
|
|
694
|
+
return new Promise((resolve, reject) => {
|
|
695
|
+
const proc = spawn(javaExe, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
696
|
+
const out = [];
|
|
697
|
+
const err = [];
|
|
698
|
+
proc.stdout.on("data", (d) => out.push(d));
|
|
699
|
+
proc.stderr.on("data", (d) => err.push(d));
|
|
700
|
+
proc.on("close", (code) => {
|
|
701
|
+
if (code === 0)
|
|
702
|
+
resolve(Buffer.concat(out).toString());
|
|
703
|
+
else
|
|
704
|
+
reject(new Error(Buffer.concat(err).toString().slice(0, 1000)));
|
|
705
|
+
});
|
|
706
|
+
proc.on("error", reject);
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Remap a mod JAR using TinyRemapper.
|
|
711
|
+
* For unobfuscated versions (26.1+) no remapping is required or possible.
|
|
712
|
+
* For older versions, remaps official→yarn or official→mojmap in two steps.
|
|
713
|
+
*/
|
|
714
|
+
export async function remapJar(inputJar, outputJar, version, toMapping) {
|
|
715
|
+
if (isUnobfuscated(version)) {
|
|
716
|
+
return { outputJar: inputJar, note: `Version ${version} is already unobfuscated — no remapping needed. The input JAR IS the output.` };
|
|
717
|
+
}
|
|
718
|
+
const trJar = await ensureTinyRemapper();
|
|
719
|
+
const intIdx = await getIntermediaryIndex(version);
|
|
720
|
+
if (!intIdx)
|
|
721
|
+
throw new Error(`Intermediary mappings not available for ${version}`);
|
|
722
|
+
const intTiny = join(MAPPINGS_DIR, `intermediary-${version}.tiny`);
|
|
723
|
+
const stepOneOut = outputJar.replace(/\.jar$/, "-step1.jar");
|
|
724
|
+
// Step 1: official → intermediary
|
|
725
|
+
await runJava(["-jar", trJar, inputJar, stepOneOut, intTiny, "official", "intermediary"]);
|
|
726
|
+
// Step 2: intermediary → named (yarn or mojmap)
|
|
727
|
+
let step2Tiny;
|
|
728
|
+
if (toMapping === "yarn") {
|
|
729
|
+
await getYarnIndex(version); // ensure downloaded
|
|
730
|
+
step2Tiny = join(MAPPINGS_DIR, `yarn-${version}.tiny`);
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
// Mojmap
|
|
734
|
+
await getMojmapClassMap(version); // ensure downloaded
|
|
735
|
+
step2Tiny = join(MAPPINGS_DIR, `mojmap-${version}.tiny`);
|
|
736
|
+
// For mojmap, the proguard inverted tiny doesn't exist yet — build it
|
|
737
|
+
if (!(await exists(step2Tiny))) {
|
|
738
|
+
throw new Error(`Mojmap tiny file not found at ${step2Tiny}. Mojmap remapping via TinyRemapper requires a converted Tiny v2 file. Use a tool like mapping-io to generate it.`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
await runJava(["-jar", trJar, stepOneOut, outputJar, step2Tiny, "intermediary", "named"]);
|
|
742
|
+
// Cleanup temp file
|
|
743
|
+
const { unlink } = await import("fs/promises");
|
|
744
|
+
await unlink(stepOneOut).catch(() => { });
|
|
745
|
+
return { outputJar };
|
|
746
|
+
}
|
|
747
|
+
// ── Parchment ─────────────────────────────────────────────────────────────────
|
|
748
|
+
async function resolveParchmentVersion(mcVersion) {
|
|
749
|
+
const metaUrl = `https://maven.parchmentmc.org/org/parchmentmc/data/parchment-${mcVersion}/maven-metadata.xml`;
|
|
750
|
+
try {
|
|
751
|
+
const res = await fetch(metaUrl);
|
|
752
|
+
if (!res.ok)
|
|
753
|
+
return null;
|
|
754
|
+
const xml = await res.text();
|
|
755
|
+
const m = xml.match(/<(?:latest|release)>([^<]+)<\/(?:latest|release)>/);
|
|
756
|
+
return m ? m[1].trim() : null;
|
|
757
|
+
}
|
|
758
|
+
catch {
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
export async function getParchmentData(mcVersion) {
|
|
763
|
+
if (parchmentCache.has(mcVersion))
|
|
764
|
+
return parchmentCache.get(mcVersion) ?? null;
|
|
765
|
+
const cachePath = join(PARCHMENT_DIR, `${mcVersion}.json`);
|
|
766
|
+
if (!(await exists(cachePath))) {
|
|
767
|
+
const parchmentVersion = await resolveParchmentVersion(mcVersion);
|
|
768
|
+
if (!parchmentVersion) {
|
|
769
|
+
parchmentCache.set(mcVersion, null);
|
|
770
|
+
return null;
|
|
771
|
+
}
|
|
772
|
+
const zipUrl = `https://maven.parchmentmc.org/org/parchmentmc/data/parchment-${mcVersion}/${parchmentVersion}/parchment-${mcVersion}-${parchmentVersion}-checked.zip`;
|
|
773
|
+
const zipPath = join(PARCHMENT_DIR, `${mcVersion}-${parchmentVersion}.zip`);
|
|
774
|
+
try {
|
|
775
|
+
await downloadToFile(zipUrl, zipPath);
|
|
776
|
+
const zip = new AdmZip(zipPath);
|
|
777
|
+
const entry = zip.getEntry("parchment.json");
|
|
778
|
+
if (!entry) {
|
|
779
|
+
parchmentCache.set(mcVersion, null);
|
|
780
|
+
return null;
|
|
781
|
+
}
|
|
782
|
+
await ensureDir(cachePath);
|
|
783
|
+
await writeFile(cachePath, entry.getData());
|
|
784
|
+
}
|
|
785
|
+
catch {
|
|
786
|
+
parchmentCache.set(mcVersion, null);
|
|
787
|
+
return null;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
try {
|
|
791
|
+
const raw = JSON.parse(await readFile(cachePath, "utf8"));
|
|
792
|
+
const data = { classes: new Map() };
|
|
793
|
+
for (const cls of raw.classes ?? []) {
|
|
794
|
+
const fieldMap = new Map();
|
|
795
|
+
const methodMap = new Map();
|
|
796
|
+
for (const f of cls.fields ?? [])
|
|
797
|
+
fieldMap.set(`${f.name}:${f.descriptor}`, { name: f.name, javadoc: f.javadoc });
|
|
798
|
+
for (const m of cls.methods ?? [])
|
|
799
|
+
methodMap.set(`${m.name}${m.descriptor}`, {
|
|
800
|
+
name: m.name, descriptor: m.descriptor, javadoc: m.javadoc,
|
|
801
|
+
parameters: (m.parameters ?? []).map(p => ({ index: p.index, name: p.name, javadoc: p.javadoc })),
|
|
802
|
+
});
|
|
803
|
+
data.classes.set(cls.name, { name: cls.name, javadoc: cls.javadoc, fields: fieldMap, methods: methodMap });
|
|
804
|
+
}
|
|
805
|
+
parchmentCache.set(mcVersion, data);
|
|
806
|
+
return data;
|
|
807
|
+
}
|
|
808
|
+
catch {
|
|
809
|
+
parchmentCache.set(mcVersion, null);
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
export async function getParchmentClass(className, mcVersion) {
|
|
814
|
+
const data = await getParchmentData(mcVersion);
|
|
815
|
+
if (!data)
|
|
816
|
+
return null;
|
|
817
|
+
return data.classes.get(className.replace(/\./g, "/")) ?? null;
|
|
818
|
+
}
|
|
819
|
+
export async function listAvailableParchmentVersions(mcVersion) {
|
|
820
|
+
const metaUrl = `https://maven.parchmentmc.org/org/parchmentmc/data/parchment-${mcVersion}/maven-metadata.xml`;
|
|
821
|
+
try {
|
|
822
|
+
const res = await fetch(metaUrl);
|
|
823
|
+
if (!res.ok)
|
|
824
|
+
return [];
|
|
825
|
+
const xml = await res.text();
|
|
826
|
+
const matches = [...xml.matchAll(/<version>([^<]+)<\/version>/g)];
|
|
827
|
+
return matches.map(m => m[1]).filter(v => !v.includes("SNAPSHOT"));
|
|
828
|
+
}
|
|
829
|
+
catch {
|
|
830
|
+
return [];
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
// ── SpecialSource (legacy Forge remapping tool) ───────────────────────────────
|
|
834
|
+
export async function ensureSpecialSource() {
|
|
835
|
+
if (await exists(SPECIAL_SOURCE_PATH))
|
|
836
|
+
return SPECIAL_SOURCE_PATH;
|
|
837
|
+
await downloadToFile(SPECIAL_SOURCE_URL, SPECIAL_SOURCE_PATH);
|
|
838
|
+
return SPECIAL_SOURCE_PATH;
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Generate a combined SRG mapping (notch→mcp) for a given MC version.
|
|
842
|
+
* Classes use SRG names (already human-readable: net/minecraft/...).
|
|
843
|
+
* Methods/fields use MCP names where available, SRG names as fallback.
|
|
844
|
+
* Returns the path to the generated .srg file.
|
|
845
|
+
*/
|
|
846
|
+
export async function generateCombinedSrg(version) {
|
|
847
|
+
const combined = join(MAPPINGS_DIR, `combined-${version}.srg`);
|
|
848
|
+
if (await exists(combined))
|
|
849
|
+
return combined;
|
|
850
|
+
const srgIdx = await getSrgIndex(version);
|
|
851
|
+
if (!srgIdx)
|
|
852
|
+
return null;
|
|
853
|
+
const mcpNames = await getMcpNames(version);
|
|
854
|
+
const lines = [];
|
|
855
|
+
// Class mappings: CL: notch srg
|
|
856
|
+
for (const [notch, srg] of srgIdx.classes) {
|
|
857
|
+
lines.push(`CL: ${notch} ${srg}`);
|
|
858
|
+
}
|
|
859
|
+
// Field mappings: FD: notchClass/notchField srgClass/mcpField
|
|
860
|
+
for (const [notchQualified, srgQualified] of srgIdx.fields) {
|
|
861
|
+
if (mcpNames) {
|
|
862
|
+
const srgFieldName = srgQualified.split("/").pop();
|
|
863
|
+
const mcpField = mcpNames.fields.get(srgFieldName);
|
|
864
|
+
if (mcpField) {
|
|
865
|
+
const srgClassPath = srgQualified.substring(0, srgQualified.lastIndexOf("/"));
|
|
866
|
+
lines.push(`FD: ${notchQualified} ${srgClassPath}/${mcpField}`);
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
lines.push(`FD: ${notchQualified} ${srgQualified}`);
|
|
871
|
+
}
|
|
872
|
+
// Method mappings: MD: notchClass/notchMethod notchDesc srgClass/mcpMethod srgDesc
|
|
873
|
+
for (const [notchKey, srgQualified] of srgIdx.methods) {
|
|
874
|
+
// notchKey = "notchClass/notchMethod notchDesc"
|
|
875
|
+
// srgQualified = "srgClass/srgMethod" (no desc stored — reuse notchDesc)
|
|
876
|
+
const spaceIdx = notchKey.indexOf(" ");
|
|
877
|
+
const notchDesc = notchKey.substring(spaceIdx + 1);
|
|
878
|
+
if (mcpNames) {
|
|
879
|
+
const srgMethodName = srgQualified.split("/").pop();
|
|
880
|
+
const mcpMethod = mcpNames.methods.get(srgMethodName);
|
|
881
|
+
if (mcpMethod) {
|
|
882
|
+
const srgClassPath = srgQualified.substring(0, srgQualified.lastIndexOf("/"));
|
|
883
|
+
// Remap desc class references too
|
|
884
|
+
const remappedDesc = remapDescriptor(notchDesc, srgIdx.classes);
|
|
885
|
+
lines.push(`MD: ${notchKey} ${srgClassPath}/${mcpMethod} ${remappedDesc}`);
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
// Fallback: SRG name, remap desc
|
|
890
|
+
const remappedDesc = remapDescriptor(notchDesc, srgIdx.classes);
|
|
891
|
+
lines.push(`MD: ${notchKey} ${srgQualified} ${remappedDesc}`);
|
|
892
|
+
}
|
|
893
|
+
await ensureDir(combined);
|
|
894
|
+
await writeFile(combined, lines.join("\n"));
|
|
895
|
+
return combined;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Remap class references in a method descriptor using the class mapping.
|
|
899
|
+
* e.g. (La;Lb;)Lc; → (Lnet/minecraft/Foo;Lnet/minecraft/Bar;)Lnet/minecraft/Baz;
|
|
900
|
+
*/
|
|
901
|
+
function remapDescriptor(desc, classMap) {
|
|
902
|
+
return desc.replace(/L([^;]+);/g, (_m, cls) => {
|
|
903
|
+
const mapped = classMap.get(cls);
|
|
904
|
+
return mapped ? `L${mapped};` : `L${cls};`;
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Versions that have SRG+MCP mappings available (legacy Forge era 1.7.10–1.15).
|
|
909
|
+
*/
|
|
910
|
+
export function hasSrgMappings(version) {
|
|
911
|
+
return version in MCP_CHANNELS;
|
|
912
|
+
}
|
|
913
|
+
// ── RetroMCP (pre-1.7.10 Tiny v2 mappings from MCPHackers) ──────────────────
|
|
914
|
+
/**
|
|
915
|
+
* Base URL for RetroMCP resource ZIPs.
|
|
916
|
+
* Override with RETROMCP_BASE_URL env var when hosting on a private Maven.
|
|
917
|
+
*/
|
|
918
|
+
const RETROMCP_BASE_URL = process.env.RETROMCP_BASE_URL ?? "https://mcphackers.org/versionsV2";
|
|
919
|
+
/**
|
|
920
|
+
* Known versions with RetroMCP mappings (Tiny v2 format, "official" → "named").
|
|
921
|
+
* Maps version ID → resource ZIP filename (appended to RETROMCP_BASE_URL).
|
|
922
|
+
* Source: https://mcphackers.org/versionsV3/versions.json
|
|
923
|
+
*
|
|
924
|
+
* Note: Multiple sub-versions may share the same resource ZIP.
|
|
925
|
+
*/
|
|
926
|
+
const RETROMCP_VERSIONS = {
|
|
927
|
+
// Releases
|
|
928
|
+
"1.5.2": "1.5.2.zip",
|
|
929
|
+
"1.2.5": "1.2.5.zip",
|
|
930
|
+
"1.2.4": "1.2.4.zip",
|
|
931
|
+
"1.2.3": "1.2.3.zip",
|
|
932
|
+
"1.1": "1.1.zip",
|
|
933
|
+
"1.0": "1.0.0.zip",
|
|
934
|
+
// Beta
|
|
935
|
+
"b1.8.1": "b1.8.zip",
|
|
936
|
+
"b1.8": "b1.8.zip",
|
|
937
|
+
"b1.7.3": "b1.7.zip",
|
|
938
|
+
"b1.7.2": "b1.7.zip",
|
|
939
|
+
"b1.7": "b1.7.zip",
|
|
940
|
+
"b1.6.6": "b1.6.zip",
|
|
941
|
+
"b1.6.5": "b1.6.zip",
|
|
942
|
+
"b1.6.4": "b1.6.zip",
|
|
943
|
+
"b1.6.3": "b1.6.zip",
|
|
944
|
+
"b1.6.2": "b1.6.zip",
|
|
945
|
+
"b1.6.1": "b1.6.zip",
|
|
946
|
+
"b1.6": "b1.6.zip",
|
|
947
|
+
"b1.5_01": "b1.5_01.zip",
|
|
948
|
+
"b1.4_01": "b1.4_01.zip",
|
|
949
|
+
"b1.3_01": "b1.3_01.zip",
|
|
950
|
+
"b1.2_02": "b1.2.zip",
|
|
951
|
+
"b1.2_01": "b1.2.zip",
|
|
952
|
+
"b1.2": "b1.2.zip",
|
|
953
|
+
"b1.1_02": "b1.1.zip",
|
|
954
|
+
"b1.1_01": "b1.1.zip",
|
|
955
|
+
// Alpha
|
|
956
|
+
"a1.2.6": "a1.2.6.zip",
|
|
957
|
+
"a1.2.5": "a1.2.5.zip",
|
|
958
|
+
"a1.2.3_04": "a1.2.3.zip",
|
|
959
|
+
"a1.2.3_02": "a1.2.3.zip",
|
|
960
|
+
"a1.2.3": "a1.2.3.zip",
|
|
961
|
+
"a1.1.2_01": "a1.1.2_01.zip",
|
|
962
|
+
"a1.1.2": "a1.1.1.zip",
|
|
963
|
+
"a1.1.1": "a1.1.1.zip",
|
|
964
|
+
"a1.0.17_04": "a1.0.17.zip",
|
|
965
|
+
"a1.0.17_02": "a1.0.17.zip",
|
|
966
|
+
"a1.0.16_02": "a1.0.16_02.zip",
|
|
967
|
+
"a1.0.16_01": "a1.0.16.zip",
|
|
968
|
+
"a1.0.16": "a1.0.16.zip",
|
|
969
|
+
"a1.0.15": "a1.0.15.zip",
|
|
970
|
+
"a1.0.14-1659": "a1.0.14.zip",
|
|
971
|
+
"a1.0.11": "a1.0.11.zip",
|
|
972
|
+
"a1.0.5_01": "a1.0.5_01.zip",
|
|
973
|
+
"a1.0.4": "a1.0.4.zip",
|
|
974
|
+
};
|
|
975
|
+
/**
|
|
976
|
+
* Check whether a version has RetroMCP Tiny v2 mappings available (pre-1.7.10).
|
|
977
|
+
*/
|
|
978
|
+
export function hasRetroMcpMappings(version) {
|
|
979
|
+
return version in RETROMCP_VERSIONS;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Download the RetroMCP resource ZIP and extract conf/mappings.tiny.
|
|
983
|
+
* Returns path to the extracted .tiny file or null on failure.
|
|
984
|
+
*/
|
|
985
|
+
async function getRetroMcpTinyPath(version) {
|
|
986
|
+
const zipName = RETROMCP_VERSIONS[version];
|
|
987
|
+
if (!zipName)
|
|
988
|
+
return null;
|
|
989
|
+
const tinyPath = join(MAPPINGS_DIR, `retromcp-${version}.tiny`);
|
|
990
|
+
if (await exists(tinyPath))
|
|
991
|
+
return tinyPath;
|
|
992
|
+
const zipPath = join(MAPPINGS_DIR, `retromcp-${zipName}`);
|
|
993
|
+
const url = `${RETROMCP_BASE_URL}/${zipName}`;
|
|
994
|
+
try {
|
|
995
|
+
if (!(await exists(zipPath))) {
|
|
996
|
+
await downloadToFile(url, zipPath);
|
|
997
|
+
}
|
|
998
|
+
const zip = new AdmZip(zipPath);
|
|
999
|
+
// RetroMCP ZIPs may store mappings at root or under conf/
|
|
1000
|
+
const entry = zip.getEntry("mappings.tiny") ?? zip.getEntry("conf/mappings.tiny");
|
|
1001
|
+
if (!entry)
|
|
1002
|
+
return null;
|
|
1003
|
+
await ensureDir(tinyPath);
|
|
1004
|
+
await writeFile(tinyPath, entry.getData());
|
|
1005
|
+
return tinyPath;
|
|
1006
|
+
}
|
|
1007
|
+
catch {
|
|
1008
|
+
return null;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Remap a vanilla MC JAR from obfuscated → named using TinyRemapper + RetroMCP mappings.
|
|
1013
|
+
* Returns the path to the remapped JAR, or null if mappings aren't available.
|
|
1014
|
+
*/
|
|
1015
|
+
export async function remapMcJarTiny(jarPath, version) {
|
|
1016
|
+
const remappedPath = jarPath.replace(/\.jar$/, "-mapped.jar");
|
|
1017
|
+
if (await exists(remappedPath))
|
|
1018
|
+
return remappedPath;
|
|
1019
|
+
const tinyPath = await getRetroMcpTinyPath(version);
|
|
1020
|
+
if (!tinyPath)
|
|
1021
|
+
return null;
|
|
1022
|
+
const trJar = await ensureTinyRemapper();
|
|
1023
|
+
// RetroMCP Tiny v2 namespaces: named (human-readable), client (obfuscated), server (obfuscated)
|
|
1024
|
+
// MC client JAR uses "client" obfuscation, so remap client → named
|
|
1025
|
+
await runJava(["-jar", trJar, jarPath, remappedPath, tinyPath, "client", "named"]);
|
|
1026
|
+
return remappedPath;
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Remap a vanilla MC JAR from obfuscated → SRG+MCP names using SpecialSource.
|
|
1030
|
+
* Returns the path to the remapped JAR.
|
|
1031
|
+
*/
|
|
1032
|
+
export async function remapMcJar(jarPath, version) {
|
|
1033
|
+
const remappedPath = jarPath.replace(/\.jar$/, "-mapped.jar");
|
|
1034
|
+
if (await exists(remappedPath))
|
|
1035
|
+
return remappedPath;
|
|
1036
|
+
const srgPath = await generateCombinedSrg(version);
|
|
1037
|
+
if (!srgPath)
|
|
1038
|
+
return null;
|
|
1039
|
+
const ssJar = await ensureSpecialSource();
|
|
1040
|
+
await runJava(["-jar", ssJar, "--in-jar", jarPath, "--out-jar", remappedPath, "--srg-in", srgPath]);
|
|
1041
|
+
return remappedPath;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Post-decompile: replace SRG method/field names (func_12345/field_12345) with MCP names.
|
|
1045
|
+
* Modifies .java files in-place in the given directory.
|
|
1046
|
+
*/
|
|
1047
|
+
export async function applyMcpNamesToSource(sourceDir, version) {
|
|
1048
|
+
const mcpNames = await getMcpNames(version);
|
|
1049
|
+
if (!mcpNames)
|
|
1050
|
+
return { replaced: 0, files: 0 };
|
|
1051
|
+
// Build a combined replacement map (srg→mcp for both methods and fields)
|
|
1052
|
+
const replacements = new Map();
|
|
1053
|
+
for (const [srg, mcp] of mcpNames.methods)
|
|
1054
|
+
replacements.set(srg, mcp);
|
|
1055
|
+
for (const [srg, mcp] of mcpNames.fields)
|
|
1056
|
+
replacements.set(srg, mcp);
|
|
1057
|
+
if (replacements.size === 0)
|
|
1058
|
+
return { replaced: 0, files: 0 };
|
|
1059
|
+
// Build a regex that matches any SRG identifier in one pass
|
|
1060
|
+
const srgPattern = /\b(func_\d+_[a-zA-Z_]+|field_\d+_[a-zA-Z_]+)\b/g;
|
|
1061
|
+
const { readdir, readFile: rf, writeFile: wf } = await import("fs/promises");
|
|
1062
|
+
const { join: pjoin } = await import("path");
|
|
1063
|
+
let totalReplaced = 0;
|
|
1064
|
+
let totalFiles = 0;
|
|
1065
|
+
async function walkAndReplace(dir) {
|
|
1066
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
1067
|
+
for (const entry of entries) {
|
|
1068
|
+
const fullPath = pjoin(dir, entry.name);
|
|
1069
|
+
if (entry.isDirectory()) {
|
|
1070
|
+
await walkAndReplace(fullPath);
|
|
1071
|
+
}
|
|
1072
|
+
else if (entry.name.endsWith(".java")) {
|
|
1073
|
+
const content = await rf(fullPath, "utf8");
|
|
1074
|
+
let count = 0;
|
|
1075
|
+
const replaced = content.replace(srgPattern, (match) => {
|
|
1076
|
+
const mcp = replacements.get(match);
|
|
1077
|
+
if (mcp) {
|
|
1078
|
+
count++;
|
|
1079
|
+
return mcp;
|
|
1080
|
+
}
|
|
1081
|
+
return match;
|
|
1082
|
+
});
|
|
1083
|
+
if (count > 0) {
|
|
1084
|
+
await wf(fullPath, replaced);
|
|
1085
|
+
totalReplaced += count;
|
|
1086
|
+
totalFiles++;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
await walkAndReplace(sourceDir);
|
|
1092
|
+
return { replaced: totalReplaced, files: totalFiles };
|
|
1093
|
+
}
|
|
1094
|
+
//# sourceMappingURL=mappings.js.map
|