@adhisang/minecraft-modding-mcp 3.1.0 → 3.2.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/CHANGELOG.md +62 -34
- package/README.md +79 -100
- package/dist/access-transformer-parser.d.ts +17 -0
- package/dist/access-transformer-parser.js +97 -0
- package/dist/concurrency.d.ts +1 -0
- package/dist/concurrency.js +24 -0
- package/dist/config.js +19 -11
- package/dist/decompiler/vineflower.js +22 -21
- package/dist/entry-tools/analyze-mod-service.d.ts +4 -4
- package/dist/entry-tools/analyze-symbol-service.d.ts +22 -20
- package/dist/entry-tools/analyze-symbol-service.js +6 -3
- package/dist/entry-tools/inspect-minecraft-service.d.ts +166 -149
- package/dist/entry-tools/inspect-minecraft-service.js +318 -55
- package/dist/entry-tools/validate-project-service.d.ts +153 -16
- package/dist/entry-tools/validate-project-service.js +360 -23
- package/dist/gradle-paths.d.ts +4 -0
- package/dist/gradle-paths.js +57 -0
- package/dist/index.js +274 -13
- package/dist/mapping-pipeline-service.d.ts +3 -1
- package/dist/mapping-pipeline-service.js +16 -1
- package/dist/mapping-service.d.ts +5 -0
- package/dist/mapping-service.js +200 -84
- package/dist/minecraft-explorer-service.d.ts +13 -0
- package/dist/minecraft-explorer-service.js +8 -4
- package/dist/mixin-validator.d.ts +33 -2
- package/dist/mixin-validator.js +197 -11
- package/dist/mod-analyzer.d.ts +1 -0
- package/dist/mod-analyzer.js +17 -1
- package/dist/mod-decompile-service.js +4 -4
- package/dist/mod-remap-service.js +1 -54
- package/dist/mod-search-service.d.ts +1 -0
- package/dist/mod-search-service.js +84 -51
- package/dist/response-utils.d.ts +35 -0
- package/dist/response-utils.js +113 -0
- package/dist/source-jar-reader.d.ts +16 -0
- package/dist/source-jar-reader.js +103 -1
- package/dist/source-resolver.js +9 -10
- package/dist/source-service.d.ts +24 -2
- package/dist/source-service.js +1052 -139
- package/dist/tool-contract-manifest.js +74 -74
- package/dist/types.d.ts +17 -0
- package/dist/workspace-mapping-service.d.ts +13 -0
- package/dist/workspace-mapping-service.js +146 -14
- package/package.json +1 -1
package/dist/mapping-service.js
CHANGED
|
@@ -3,8 +3,9 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import fastGlob from "fast-glob";
|
|
5
5
|
import { createError, ERROR_CODES } from "./errors.js";
|
|
6
|
+
import { buildVersionSourceSearchRoots, normalizeOptionalProjectPath } from "./gradle-paths.js";
|
|
6
7
|
import { defaultDownloadPath, downloadToCache } from "./repo-downloader.js";
|
|
7
|
-
import {
|
|
8
|
+
import { collectMatchedJarEntriesAsUtf8 } from "./source-jar-reader.js";
|
|
8
9
|
import { VersionService, isUnobfuscatedVersion } from "./version-service.js";
|
|
9
10
|
const SUPPORTED_MAPPINGS = new Set([
|
|
10
11
|
"obfuscated",
|
|
@@ -19,6 +20,7 @@ const MATCH_RANK = {
|
|
|
19
20
|
};
|
|
20
21
|
const DESCRIPTOR_FALLBACK_CONFIDENCE = 0.85;
|
|
21
22
|
const MAX_CANDIDATES = 200;
|
|
23
|
+
const GLOB_SPECIAL_CHARS = /[\\!*+?()[\]{}@|]/g;
|
|
22
24
|
function createDirectionIndex() {
|
|
23
25
|
return {
|
|
24
26
|
exact: new Map(),
|
|
@@ -420,7 +422,7 @@ function parseClientMappings(text) {
|
|
|
420
422
|
}
|
|
421
423
|
function normalizeTinyNamespace(namespace) {
|
|
422
424
|
const normalized = namespace.trim().toLowerCase();
|
|
423
|
-
if (normalized === "obfuscated") {
|
|
425
|
+
if (normalized === "obfuscated" || normalized === "official") {
|
|
424
426
|
return "obfuscated";
|
|
425
427
|
}
|
|
426
428
|
if (normalized === "mojang") {
|
|
@@ -590,6 +592,12 @@ function mappingSourceOrder(priority) {
|
|
|
590
592
|
}
|
|
591
593
|
return ["loom-cache", "maven"];
|
|
592
594
|
}
|
|
595
|
+
function requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) {
|
|
596
|
+
return sourceMapping !== "intermediary" &&
|
|
597
|
+
sourceMapping !== "yarn" &&
|
|
598
|
+
targetMapping !== "intermediary" &&
|
|
599
|
+
targetMapping !== "yarn";
|
|
600
|
+
}
|
|
593
601
|
function namespacePath(graph, sourceMapping, targetMapping) {
|
|
594
602
|
if (sourceMapping === targetMapping) {
|
|
595
603
|
return [sourceMapping];
|
|
@@ -709,7 +717,7 @@ function normalizeMethodDescriptor(descriptor) {
|
|
|
709
717
|
}
|
|
710
718
|
return normalized;
|
|
711
719
|
}
|
|
712
|
-
function normalizeQuerySymbol(input, signatureMode) {
|
|
720
|
+
function normalizeQuerySymbol(input, signatureMode, options) {
|
|
713
721
|
if (input.kind !== "class" && input.kind !== "field" && input.kind !== "method") {
|
|
714
722
|
throw invalidInputError('kind must be one of "class", "field", or "method".', {
|
|
715
723
|
kind: input.kind
|
|
@@ -735,7 +743,7 @@ function normalizeQuerySymbol(input, signatureMode) {
|
|
|
735
743
|
});
|
|
736
744
|
}
|
|
737
745
|
const className = normalizeMappedSymbolOutput(normalizedName);
|
|
738
|
-
if (!className.includes(".")) {
|
|
746
|
+
if (!className.includes(".") && !options?.allowShortClassName) {
|
|
739
747
|
throw invalidInputError("name must be a fully qualified class name when kind=class.", {
|
|
740
748
|
name: input.name
|
|
741
749
|
});
|
|
@@ -805,13 +813,32 @@ function applyDisambiguationHints(candidates, disambiguation) {
|
|
|
805
813
|
}
|
|
806
814
|
const descriptorHint = normalizeDescriptorHint(disambiguation.descriptorHint);
|
|
807
815
|
if (descriptorHint) {
|
|
808
|
-
const descriptorMatched = filtered.filter((candidate) => candidate.descriptor === descriptorHint);
|
|
816
|
+
const descriptorMatched = filtered.filter((candidate) => candidate.descriptor != null && candidate.descriptor === descriptorHint);
|
|
809
817
|
if (descriptorMatched.length > 0) {
|
|
810
818
|
filtered = descriptorMatched;
|
|
811
819
|
}
|
|
812
820
|
}
|
|
813
821
|
return filtered;
|
|
814
822
|
}
|
|
823
|
+
function projectLookupCandidateDescriptor(candidate, sourceDescriptor, targetDescriptor) {
|
|
824
|
+
// Tiny mappings preserve method descriptors verbatim, so single-hop tiny paths often
|
|
825
|
+
// return the source descriptor even though the final symbol is already in the target
|
|
826
|
+
// namespace. Multi-hop paths that already produced a target-side descriptor are left
|
|
827
|
+
// unchanged by design.
|
|
828
|
+
if (candidate.kind !== "method" ||
|
|
829
|
+
!candidate.descriptor ||
|
|
830
|
+
!targetDescriptor ||
|
|
831
|
+
candidate.descriptor !== sourceDescriptor) {
|
|
832
|
+
return candidate;
|
|
833
|
+
}
|
|
834
|
+
return {
|
|
835
|
+
...candidate,
|
|
836
|
+
descriptor: targetDescriptor
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
function effectiveLoomSearchProjectPath(projectPath) {
|
|
840
|
+
return normalizeOptionalProjectPath(projectPath) ?? normalizeOptionalProjectPath(process.cwd());
|
|
841
|
+
}
|
|
815
842
|
function collectTargetRecords(graph, targetMapping) {
|
|
816
843
|
return [...(graph.recordsByTarget.get(targetMapping) ?? [])];
|
|
817
844
|
}
|
|
@@ -905,7 +932,9 @@ export class MappingService {
|
|
|
905
932
|
}
|
|
906
933
|
});
|
|
907
934
|
}
|
|
908
|
-
const { record: queryRecord, querySymbol } = normalizeQuerySymbol(input, input.signatureMode
|
|
935
|
+
const { record: queryRecord, querySymbol } = normalizeQuerySymbol(input, input.signatureMode, {
|
|
936
|
+
allowShortClassName: input.kind === "class" && input.sourceMapping === "obfuscated"
|
|
937
|
+
});
|
|
909
938
|
const sourceMapping = input.sourceMapping;
|
|
910
939
|
const targetMapping = input.targetMapping;
|
|
911
940
|
if (!SUPPORTED_MAPPINGS.has(sourceMapping) || !SUPPORTED_MAPPINGS.has(targetMapping)) {
|
|
@@ -945,7 +974,7 @@ export class MappingService {
|
|
|
945
974
|
warnings: []
|
|
946
975
|
};
|
|
947
976
|
}
|
|
948
|
-
const graph = await this.loadGraph(version, priority);
|
|
977
|
+
const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath);
|
|
949
978
|
const path = namespacePath(graph, sourceMapping, targetMapping);
|
|
950
979
|
if (!path) {
|
|
951
980
|
return {
|
|
@@ -960,7 +989,15 @@ export class MappingService {
|
|
|
960
989
|
]
|
|
961
990
|
};
|
|
962
991
|
}
|
|
963
|
-
const
|
|
992
|
+
const descriptorProjection = queryRecord.kind === "method" && queryRecord.descriptor
|
|
993
|
+
? this.projectMethodDescriptorToTarget(graph, path, queryRecord.descriptor)
|
|
994
|
+
: undefined;
|
|
995
|
+
const projectedDescriptor = descriptorProjection?.complete ? descriptorProjection.descriptor : undefined;
|
|
996
|
+
const rawCandidates = this
|
|
997
|
+
.mapCandidatesAlongPath(graph, path, queryRecord)
|
|
998
|
+
.map((candidate) => queryRecord.kind === "method" && queryRecord.descriptor
|
|
999
|
+
? projectLookupCandidateDescriptor(candidate, queryRecord.descriptor, projectedDescriptor)
|
|
1000
|
+
: candidate);
|
|
964
1001
|
const warnings = [];
|
|
965
1002
|
const disambiguatedCandidates = applyDisambiguationHints(rawCandidates, input.disambiguation);
|
|
966
1003
|
if (rawCandidates.length > disambiguatedCandidates.length) {
|
|
@@ -1029,7 +1066,7 @@ export class MappingService {
|
|
|
1029
1066
|
warnings: []
|
|
1030
1067
|
};
|
|
1031
1068
|
}
|
|
1032
|
-
const graph = await this.loadGraph(version, priority);
|
|
1069
|
+
const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath);
|
|
1033
1070
|
const path = namespacePath(graph, sourceMapping, targetMapping);
|
|
1034
1071
|
if (!path) {
|
|
1035
1072
|
throw createError({
|
|
@@ -1113,7 +1150,7 @@ export class MappingService {
|
|
|
1113
1150
|
warnings: []
|
|
1114
1151
|
};
|
|
1115
1152
|
}
|
|
1116
|
-
const graph = await this.loadGraph(version, priority);
|
|
1153
|
+
const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath);
|
|
1117
1154
|
const path = namespacePath(graph, sourceMapping, targetMapping);
|
|
1118
1155
|
if (!path) {
|
|
1119
1156
|
return {
|
|
@@ -1129,12 +1166,16 @@ export class MappingService {
|
|
|
1129
1166
|
};
|
|
1130
1167
|
}
|
|
1131
1168
|
const warnings = [];
|
|
1169
|
+
const descriptorProjection = this.projectMethodDescriptorToTarget(graph, path, descriptor);
|
|
1170
|
+
const projectedDescriptor = descriptorProjection.complete ? descriptorProjection.descriptor : undefined;
|
|
1132
1171
|
const rawCandidates = this
|
|
1133
1172
|
.mapCandidatesAlongPath(graph, path, queryRecord)
|
|
1134
|
-
.filter((candidate) => candidate.kind === "method")
|
|
1173
|
+
.filter((candidate) => candidate.kind === "method")
|
|
1174
|
+
.map((candidate) => projectLookupCandidateDescriptor(candidate, descriptor, projectedDescriptor));
|
|
1135
1175
|
const candidates = rawCandidates.map(toResolutionCandidate);
|
|
1136
1176
|
const limitedCandidates = limitResolutionCandidates(candidates, input.maxCandidates);
|
|
1137
|
-
const
|
|
1177
|
+
const strictDescriptor = projectedDescriptor ?? descriptor;
|
|
1178
|
+
const strictCandidates = rawCandidates.filter((candidate) => candidate.descriptor === strictDescriptor);
|
|
1138
1179
|
if (strictCandidates.length === 1) {
|
|
1139
1180
|
const resolved = toResolutionCandidate(strictCandidates[0]);
|
|
1140
1181
|
return {
|
|
@@ -1164,8 +1205,10 @@ export class MappingService {
|
|
|
1164
1205
|
provenance: this.provenanceForPath(graph, path)
|
|
1165
1206
|
};
|
|
1166
1207
|
}
|
|
1167
|
-
if (
|
|
1168
|
-
warnings.push(
|
|
1208
|
+
if (descriptorProjection.hadClassReferences && !descriptorProjection.complete) {
|
|
1209
|
+
warnings.push(pathUsesSource(graph.pairs, path, "mojang-client-mappings")
|
|
1210
|
+
? "Method descriptor could not be preserved through mojang-client-mappings and exact resolution is unavailable."
|
|
1211
|
+
: "Method descriptor could not be fully remapped across the mapping path and exact resolution is unavailable.");
|
|
1169
1212
|
return {
|
|
1170
1213
|
querySymbol,
|
|
1171
1214
|
mappingContext,
|
|
@@ -1214,7 +1257,7 @@ export class MappingService {
|
|
|
1214
1257
|
});
|
|
1215
1258
|
}
|
|
1216
1259
|
const priority = mappingPriorityFromInput(this.config.mappingSourcePriority, input.sourcePriority);
|
|
1217
|
-
const graph = await this.loadGraph(version, priority);
|
|
1260
|
+
const graph = await this.loadGraph(version, priority, "full");
|
|
1218
1261
|
const warnings = [...graph.warnings];
|
|
1219
1262
|
const includeKinds = normalizeIncludedKinds(input.includeKinds);
|
|
1220
1263
|
const pathCache = new Map();
|
|
@@ -1246,7 +1289,7 @@ export class MappingService {
|
|
|
1246
1289
|
classByMapping[mapping] = mapped[0];
|
|
1247
1290
|
}
|
|
1248
1291
|
}
|
|
1249
|
-
const baseMapping =
|
|
1292
|
+
const baseMapping = classNameMapping;
|
|
1250
1293
|
const baseClass = classByMapping[baseMapping];
|
|
1251
1294
|
if (!baseClass) {
|
|
1252
1295
|
return {
|
|
@@ -1435,7 +1478,7 @@ export class MappingService {
|
|
|
1435
1478
|
querySymbol
|
|
1436
1479
|
};
|
|
1437
1480
|
})();
|
|
1438
|
-
const graph = await this.loadGraph(version, priority);
|
|
1481
|
+
const graph = await this.loadGraph(version, priority, sourceMapping === "mojang" ? "obfuscated-mojang-only" : "full");
|
|
1439
1482
|
const warnings = [...graph.warnings];
|
|
1440
1483
|
const records = collectTargetRecords(graph, sourceMapping);
|
|
1441
1484
|
if (records.length === 0) {
|
|
@@ -1616,6 +1659,33 @@ export class MappingService {
|
|
|
1616
1659
|
descriptor: item.record.descriptor
|
|
1617
1660
|
}));
|
|
1618
1661
|
}
|
|
1662
|
+
projectMethodDescriptorToTarget(graph, path, descriptor) {
|
|
1663
|
+
let hadClassReferences = false;
|
|
1664
|
+
let complete = true;
|
|
1665
|
+
const classProjectionCache = new Map();
|
|
1666
|
+
const projectedDescriptor = descriptor.replace(/L([^;]+);/g, (fullMatch, internalName) => {
|
|
1667
|
+
hadClassReferences = true;
|
|
1668
|
+
const cached = classProjectionCache.get(internalName);
|
|
1669
|
+
if (cached) {
|
|
1670
|
+
return `L${cached};`;
|
|
1671
|
+
}
|
|
1672
|
+
const projectedClassCandidates = this
|
|
1673
|
+
.mapCandidatesAlongPath(graph, path, createClassSymbolRecord(internalName.replace(/\//g, ".")))
|
|
1674
|
+
.filter((candidate) => candidate.kind === "class");
|
|
1675
|
+
if (projectedClassCandidates.length !== 1) {
|
|
1676
|
+
complete = false;
|
|
1677
|
+
return fullMatch;
|
|
1678
|
+
}
|
|
1679
|
+
const projectedInternalName = projectedClassCandidates[0].symbol.replace(/\./g, "/");
|
|
1680
|
+
classProjectionCache.set(internalName, projectedInternalName);
|
|
1681
|
+
return `L${projectedInternalName};`;
|
|
1682
|
+
});
|
|
1683
|
+
return {
|
|
1684
|
+
descriptor: projectedDescriptor,
|
|
1685
|
+
hadClassReferences,
|
|
1686
|
+
complete
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1619
1689
|
provenanceForPath(graph, path) {
|
|
1620
1690
|
if (path.length <= 1) {
|
|
1621
1691
|
return undefined;
|
|
@@ -1638,9 +1708,21 @@ export class MappingService {
|
|
|
1638
1708
|
async checkMappingHealth(input) {
|
|
1639
1709
|
const priority = mappingPriorityFromInput(this.config.mappingSourcePriority, input.sourcePriority);
|
|
1640
1710
|
const degradations = [];
|
|
1711
|
+
if (isUnobfuscatedVersion(input.version)) {
|
|
1712
|
+
const requestFulfillable = input.requestedMapping === "obfuscated" || input.requestedMapping === "mojang";
|
|
1713
|
+
if (!requestFulfillable) {
|
|
1714
|
+
degradations.push(`Version ${input.version} is unobfuscated; ${input.requestedMapping} mappings are not applicable.`);
|
|
1715
|
+
}
|
|
1716
|
+
return {
|
|
1717
|
+
mojangMappingsAvailable: true,
|
|
1718
|
+
tinyMappingsAvailable: false,
|
|
1719
|
+
memberRemapAvailable: requestFulfillable,
|
|
1720
|
+
degradations
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1641
1723
|
let graph;
|
|
1642
1724
|
try {
|
|
1643
|
-
graph = await this.loadGraph(input.version, priority);
|
|
1725
|
+
graph = await this.loadGraph(input.version, priority, "full");
|
|
1644
1726
|
}
|
|
1645
1727
|
catch {
|
|
1646
1728
|
return {
|
|
@@ -1684,8 +1766,9 @@ export class MappingService {
|
|
|
1684
1766
|
degradations
|
|
1685
1767
|
};
|
|
1686
1768
|
}
|
|
1687
|
-
async loadGraph(version, priority) {
|
|
1688
|
-
const
|
|
1769
|
+
async loadGraph(version, priority, mode, projectPath) {
|
|
1770
|
+
const effectiveProjectPath = effectiveLoomSearchProjectPath(projectPath);
|
|
1771
|
+
const cacheKey = `${version}|${priority}|${mode}|${effectiveProjectPath ?? ""}`;
|
|
1689
1772
|
const cached = this.graphCache.get(cacheKey);
|
|
1690
1773
|
if (cached) {
|
|
1691
1774
|
this.graphCache.delete(cacheKey);
|
|
@@ -1696,7 +1779,7 @@ export class MappingService {
|
|
|
1696
1779
|
if (existingLock) {
|
|
1697
1780
|
return existingLock;
|
|
1698
1781
|
}
|
|
1699
|
-
const buildPromise = this.buildGraph(version, priority);
|
|
1782
|
+
const buildPromise = this.buildGraph(version, priority, mode, effectiveProjectPath);
|
|
1700
1783
|
this.buildLocks.set(cacheKey, buildPromise);
|
|
1701
1784
|
try {
|
|
1702
1785
|
const built = await buildPromise;
|
|
@@ -1708,11 +1791,12 @@ export class MappingService {
|
|
|
1708
1791
|
this.buildLocks.delete(cacheKey);
|
|
1709
1792
|
}
|
|
1710
1793
|
}
|
|
1711
|
-
async buildGraph(version, priority) {
|
|
1794
|
+
async buildGraph(version, priority, mode, projectPath) {
|
|
1712
1795
|
if (isUnobfuscatedVersion(version)) {
|
|
1713
1796
|
return {
|
|
1714
1797
|
version,
|
|
1715
1798
|
priority,
|
|
1799
|
+
mode,
|
|
1716
1800
|
pairs: new Map(),
|
|
1717
1801
|
adjacency: new Map(),
|
|
1718
1802
|
pathCache: new Map(),
|
|
@@ -1725,6 +1809,7 @@ export class MappingService {
|
|
|
1725
1809
|
const graph = {
|
|
1726
1810
|
version,
|
|
1727
1811
|
priority,
|
|
1812
|
+
mode,
|
|
1728
1813
|
pairs: new Map(),
|
|
1729
1814
|
adjacency: new Map(),
|
|
1730
1815
|
pathCache: new Map(),
|
|
@@ -1734,27 +1819,29 @@ export class MappingService {
|
|
|
1734
1819
|
const mojangLoad = await this.loadMojangPairs(version);
|
|
1735
1820
|
graph.warnings.push(...mojangLoad.warnings);
|
|
1736
1821
|
this.mergePairs(graph.pairs, mojangLoad.pairs, "mojang-client-mappings", mojangLoad.mappingArtifact);
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
const
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1822
|
+
if (mode === "full") {
|
|
1823
|
+
let tinyLoaded = false;
|
|
1824
|
+
const deferredTinyWarnings = [];
|
|
1825
|
+
for (const source of mappingSourceOrder(priority)) {
|
|
1826
|
+
const tinyLoad = source === "loom-cache"
|
|
1827
|
+
? await this.loadTinyPairsFromLoom(version, projectPath)
|
|
1828
|
+
: await this.loadTinyPairsFromMaven(version);
|
|
1829
|
+
if (tinyLoad.pairs.size === 0) {
|
|
1830
|
+
deferredTinyWarnings.push(...tinyLoad.warnings);
|
|
1831
|
+
continue;
|
|
1832
|
+
}
|
|
1833
|
+
tinyLoaded = true;
|
|
1834
|
+
this.mergePairs(graph.pairs, tinyLoad.pairs, source, tinyLoad.mappingArtifact);
|
|
1835
|
+
graph.warnings.push(...tinyLoad.warnings);
|
|
1836
|
+
if (deferredTinyWarnings.length > 0) {
|
|
1837
|
+
graph.warnings.push(`Used ${source === "maven" ? "Maven" : "Loom cache"} tiny mappings for "${version}" after an earlier source lookup returned no data.`);
|
|
1838
|
+
}
|
|
1839
|
+
break;
|
|
1746
1840
|
}
|
|
1747
|
-
tinyLoaded
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
if (deferredTinyWarnings.length > 0) {
|
|
1751
|
-
graph.warnings.push(`Used ${source === "maven" ? "Maven" : "Loom cache"} tiny mappings for "${version}" after an earlier source lookup returned no data.`);
|
|
1841
|
+
if (!tinyLoaded) {
|
|
1842
|
+
graph.warnings.push(...deferredTinyWarnings);
|
|
1843
|
+
graph.warnings.push("No intermediary/yarn tiny mappings were found for this version.");
|
|
1752
1844
|
}
|
|
1753
|
-
break;
|
|
1754
|
-
}
|
|
1755
|
-
if (!tinyLoaded) {
|
|
1756
|
-
graph.warnings.push(...deferredTinyWarnings);
|
|
1757
|
-
graph.warnings.push("No intermediary/yarn tiny mappings were found for this version.");
|
|
1758
1845
|
}
|
|
1759
1846
|
graph.adjacency = buildAdjacency(graph.pairs);
|
|
1760
1847
|
graph.recordsByTarget = buildTargetRecordIndex(graph.pairs);
|
|
@@ -1835,46 +1922,67 @@ export class MappingService {
|
|
|
1835
1922
|
};
|
|
1836
1923
|
}
|
|
1837
1924
|
}
|
|
1838
|
-
async loadTinyPairsFromLoom(version) {
|
|
1839
|
-
const
|
|
1840
|
-
const candidates = fastGlob.sync(patterns, {
|
|
1841
|
-
cwd: process.cwd(),
|
|
1842
|
-
absolute: true,
|
|
1843
|
-
onlyFiles: true
|
|
1844
|
-
});
|
|
1845
|
-
const byVersion = candidates
|
|
1846
|
-
.filter((p) => p.replaceAll("\\", "/").includes(`/${version}/`))
|
|
1847
|
-
.sort((left, right) => left.localeCompare(right));
|
|
1848
|
-
if (byVersion.length === 0) {
|
|
1849
|
-
return {
|
|
1850
|
-
pairs: new Map(),
|
|
1851
|
-
warnings: [`No Loom tiny mapping files matched version "${version}".`],
|
|
1852
|
-
mappingArtifact: "loom-cache:none"
|
|
1853
|
-
};
|
|
1854
|
-
}
|
|
1925
|
+
async loadTinyPairsFromLoom(version, projectPath) {
|
|
1926
|
+
const searchRoots = buildVersionSourceSearchRoots(effectiveLoomSearchProjectPath(projectPath));
|
|
1855
1927
|
const merged = new Map();
|
|
1856
|
-
|
|
1928
|
+
const discoveredPaths = new Set();
|
|
1929
|
+
for (const root of searchRoots) {
|
|
1930
|
+
let discovered = [];
|
|
1931
|
+
const versionRoot = join(root, version);
|
|
1857
1932
|
try {
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1933
|
+
discovered = existsSync(versionRoot)
|
|
1934
|
+
? await fastGlob.glob(["**/*.tiny", "**/*.tinyv2"], {
|
|
1935
|
+
cwd: versionRoot,
|
|
1936
|
+
absolute: true,
|
|
1937
|
+
onlyFiles: true
|
|
1938
|
+
})
|
|
1939
|
+
: await fastGlob.glob([`${version.replace(GLOB_SPECIAL_CHARS, "\\$&")}/**/*.tiny`, `${version.replace(GLOB_SPECIAL_CHARS, "\\$&")}/**/*.tinyv2`], {
|
|
1940
|
+
cwd: root,
|
|
1941
|
+
absolute: true,
|
|
1942
|
+
onlyFiles: true
|
|
1943
|
+
});
|
|
1869
1944
|
}
|
|
1870
1945
|
catch {
|
|
1871
|
-
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
const byVersion = discovered
|
|
1949
|
+
.filter((path) => path.replaceAll("\\", "/").includes(`/${version}/`))
|
|
1950
|
+
.sort((left, right) => left.localeCompare(right));
|
|
1951
|
+
if (byVersion.length === 0) {
|
|
1952
|
+
continue;
|
|
1953
|
+
}
|
|
1954
|
+
for (const path of byVersion) {
|
|
1955
|
+
discoveredPaths.add(path);
|
|
1956
|
+
try {
|
|
1957
|
+
const content = await readFile(path, "utf8");
|
|
1958
|
+
const parsed = parseTinyMappings(content);
|
|
1959
|
+
for (const [key, index] of parsed.entries()) {
|
|
1960
|
+
const existing = merged.get(key);
|
|
1961
|
+
if (!existing) {
|
|
1962
|
+
merged.set(key, index);
|
|
1963
|
+
}
|
|
1964
|
+
else {
|
|
1965
|
+
mergeDirectionIndexes(existing, index);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
catch {
|
|
1970
|
+
// best effort: skip unreadable or invalid files
|
|
1971
|
+
}
|
|
1872
1972
|
}
|
|
1873
1973
|
}
|
|
1974
|
+
const orderedPaths = [...discoveredPaths].sort((left, right) => left.localeCompare(right));
|
|
1975
|
+
if (orderedPaths.length > 0) {
|
|
1976
|
+
return {
|
|
1977
|
+
pairs: merged,
|
|
1978
|
+
warnings: [],
|
|
1979
|
+
mappingArtifact: orderedPaths[0]
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1874
1982
|
return {
|
|
1875
|
-
pairs:
|
|
1876
|
-
warnings: [],
|
|
1877
|
-
mappingArtifact:
|
|
1983
|
+
pairs: new Map(),
|
|
1984
|
+
warnings: [`No Loom tiny mapping files matched version "${version}".`],
|
|
1985
|
+
mappingArtifact: "loom-cache:none"
|
|
1878
1986
|
};
|
|
1879
1987
|
}
|
|
1880
1988
|
async loadTinyPairsFromMaven(version) {
|
|
@@ -1930,15 +2038,11 @@ export class MappingService {
|
|
|
1930
2038
|
};
|
|
1931
2039
|
}
|
|
1932
2040
|
async parseTinyFromJar(jarPath) {
|
|
1933
|
-
const
|
|
1934
|
-
const tinyEntries = entries
|
|
1935
|
-
.filter((entry) => entry.toLowerCase().endsWith(".tiny") || entry.toLowerCase().endsWith(".tinyv2"))
|
|
1936
|
-
.sort((left, right) => left.localeCompare(right));
|
|
2041
|
+
const tinyEntries = (await collectMatchedJarEntriesAsUtf8(jarPath, (entry) => entry.toLowerCase().endsWith(".tiny") || entry.toLowerCase().endsWith(".tinyv2"), { continueOnError: true })).sort((left, right) => left.filePath.localeCompare(right.filePath));
|
|
1937
2042
|
const merged = new Map();
|
|
1938
2043
|
for (const entry of tinyEntries) {
|
|
1939
2044
|
try {
|
|
1940
|
-
const
|
|
1941
|
-
const parsed = parseTinyMappings(text);
|
|
2045
|
+
const parsed = parseTinyMappings(entry.content);
|
|
1942
2046
|
for (const [key, index] of parsed.entries()) {
|
|
1943
2047
|
const existing = merged.get(key);
|
|
1944
2048
|
if (!existing) {
|
|
@@ -1990,6 +2094,19 @@ export class MappingService {
|
|
|
1990
2094
|
this.graphCache.delete(oldestKey);
|
|
1991
2095
|
}
|
|
1992
2096
|
}
|
|
2097
|
+
releaseGraphCacheEntry(version, sourcePriority) {
|
|
2098
|
+
const normalizedVersion = version.trim();
|
|
2099
|
+
if (!normalizedVersion) {
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
2102
|
+
const priority = mappingPriorityFromInput(this.config.mappingSourcePriority, sourcePriority);
|
|
2103
|
+
const prefix = `${normalizedVersion}|${priority}|`;
|
|
2104
|
+
for (const key of this.graphCache.keys()) {
|
|
2105
|
+
if (key.startsWith(prefix)) {
|
|
2106
|
+
this.graphCache.delete(key);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
1993
2110
|
}
|
|
1994
2111
|
// ---------------------------------------------------------------------------
|
|
1995
2112
|
// Standalone: Tiny v2 mapping file resolution for remapping
|
|
@@ -2018,14 +2135,13 @@ async function fetchYarnCoordinatesStandalone(version, fetchFn = globalThis.fetc
|
|
|
2018
2135
|
}
|
|
2019
2136
|
}
|
|
2020
2137
|
async function extractTinyFromJar(jarPath, outputPath) {
|
|
2021
|
-
const
|
|
2022
|
-
const tinyEntry =
|
|
2138
|
+
const matchedEntries = await collectMatchedJarEntriesAsUtf8(jarPath, (entry) => entry === "mappings/mappings.tiny" || entry.toLowerCase().endsWith(".tiny"), { maxEntries: 1 });
|
|
2139
|
+
const tinyEntry = matchedEntries[0];
|
|
2023
2140
|
if (!tinyEntry) {
|
|
2024
2141
|
return false;
|
|
2025
2142
|
}
|
|
2026
|
-
const content = await readJarEntryAsUtf8(jarPath, tinyEntry);
|
|
2027
2143
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
2028
|
-
await writeFile(outputPath, content, "utf8");
|
|
2144
|
+
await writeFile(outputPath, tinyEntry.content, "utf8");
|
|
2029
2145
|
return true;
|
|
2030
2146
|
}
|
|
2031
2147
|
/**
|
|
@@ -25,12 +25,25 @@ export interface GetSignatureInput {
|
|
|
25
25
|
includeInherited?: boolean;
|
|
26
26
|
}
|
|
27
27
|
export interface GetSignatureOutput {
|
|
28
|
+
classAccessFlags?: number;
|
|
28
29
|
constructors: SignatureMember[];
|
|
29
30
|
methods: SignatureMember[];
|
|
30
31
|
fields: SignatureMember[];
|
|
31
32
|
warnings: string[];
|
|
32
33
|
context: ResponseContext;
|
|
33
34
|
}
|
|
35
|
+
export declare function modifierPrefix(flags: number, category: "method" | "field"): string;
|
|
36
|
+
export declare function parseFieldType(descriptor: string, position?: number, options?: {
|
|
37
|
+
allowVoid?: boolean;
|
|
38
|
+
invalidVoidMessage?: string;
|
|
39
|
+
}): {
|
|
40
|
+
type: string;
|
|
41
|
+
next: number;
|
|
42
|
+
};
|
|
43
|
+
export declare function parseMethodDescriptor(descriptor: string): {
|
|
44
|
+
args: string[];
|
|
45
|
+
returnType: string;
|
|
46
|
+
};
|
|
34
47
|
export declare class MinecraftExplorerService {
|
|
35
48
|
private readonly config;
|
|
36
49
|
private readonly signatureCache;
|
|
@@ -21,7 +21,7 @@ const ACC_SYNTHETIC = 0x1000;
|
|
|
21
21
|
function lower(value) {
|
|
22
22
|
return value.toLocaleLowerCase();
|
|
23
23
|
}
|
|
24
|
-
function modifierPrefix(flags, category) {
|
|
24
|
+
export function modifierPrefix(flags, category) {
|
|
25
25
|
const parts = [];
|
|
26
26
|
if ((flags & ACC_PUBLIC) !== 0) {
|
|
27
27
|
parts.push("public");
|
|
@@ -68,7 +68,7 @@ function modifierPrefix(flags, category) {
|
|
|
68
68
|
}
|
|
69
69
|
return parts.join(" ");
|
|
70
70
|
}
|
|
71
|
-
function parseFieldType(descriptor, position = 0, options = {}) {
|
|
71
|
+
export function parseFieldType(descriptor, position = 0, options = {}) {
|
|
72
72
|
if (position >= descriptor.length) {
|
|
73
73
|
throw createError({
|
|
74
74
|
code: ERROR_CODES.INVALID_INPUT,
|
|
@@ -127,7 +127,7 @@ function parseFieldType(descriptor, position = 0, options = {}) {
|
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
-
function parseMethodDescriptor(descriptor) {
|
|
130
|
+
export function parseMethodDescriptor(descriptor) {
|
|
131
131
|
if (!descriptor.startsWith("(")) {
|
|
132
132
|
throw createError({
|
|
133
133
|
code: ERROR_CODES.INVALID_INPUT,
|
|
@@ -372,7 +372,7 @@ function parseClassFile(buffer) {
|
|
|
372
372
|
});
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
|
-
reader.readU2();
|
|
375
|
+
const accessFlags = reader.readU2();
|
|
376
376
|
const thisClassIndex = reader.readU2();
|
|
377
377
|
const superClassIndex = reader.readU2();
|
|
378
378
|
const interfacesCount = reader.readU2();
|
|
@@ -404,6 +404,7 @@ function parseClassFile(buffer) {
|
|
|
404
404
|
const attributesCount = reader.readU2();
|
|
405
405
|
readAttributes(reader, cp, attributesCount);
|
|
406
406
|
return {
|
|
407
|
+
accessFlags,
|
|
407
408
|
internalName: readClassName(cp, thisClassIndex),
|
|
408
409
|
superInternalName: readOptionalClassName(cp, superClassIndex),
|
|
409
410
|
interfaceInternalNames,
|
|
@@ -442,6 +443,7 @@ export class MinecraftExplorerService {
|
|
|
442
443
|
const cached = this.signatureCache.get(cacheKey);
|
|
443
444
|
if (cached) {
|
|
444
445
|
return {
|
|
446
|
+
classAccessFlags: cached.classAccessFlags,
|
|
445
447
|
constructors: cached.constructors,
|
|
446
448
|
methods: cached.methods,
|
|
447
449
|
fields: cached.fields,
|
|
@@ -619,6 +621,7 @@ export class MinecraftExplorerService {
|
|
|
619
621
|
const fields = dedupeMembers(hierarchyClasses.flatMap((classFile) => toMembers(classFile, "field", (member) => shouldIncludeMember(member))));
|
|
620
622
|
const methods = dedupeMembers(hierarchyClasses.flatMap((classFile) => toMembers(classFile, "method", (member) => member.name !== "<init>" && shouldIncludeMember(member))));
|
|
621
623
|
const output = {
|
|
624
|
+
classAccessFlags: parsed.accessFlags,
|
|
622
625
|
constructors,
|
|
623
626
|
methods,
|
|
624
627
|
fields,
|
|
@@ -626,6 +629,7 @@ export class MinecraftExplorerService {
|
|
|
626
629
|
};
|
|
627
630
|
this.signatureCache.set(cacheKey, output);
|
|
628
631
|
return {
|
|
632
|
+
classAccessFlags: output.classAccessFlags,
|
|
629
633
|
constructors: output.constructors,
|
|
630
634
|
methods: output.methods,
|
|
631
635
|
fields: output.fields,
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
import type { SignatureMember } from "./minecraft-explorer-service.js";
|
|
6
6
|
import type { ParsedMixin } from "./mixin-parser.js";
|
|
7
7
|
import type { ParsedAccessWidener, AccessWidenerEntry } from "./access-widener-parser.js";
|
|
8
|
-
import type {
|
|
8
|
+
import type { ParsedAccessTransformer, AccessTransformerEntry } from "./access-transformer-parser.js";
|
|
9
|
+
import type { AccessTransformerNamespace, RuntimeValidationProvenance, SourceMapping } from "./types.js";
|
|
9
10
|
export type MappingHealthReport = {
|
|
10
11
|
jarAvailable: boolean;
|
|
11
12
|
jarPath: string;
|
|
@@ -131,6 +132,7 @@ export type MixinValidationResult = {
|
|
|
131
132
|
};
|
|
132
133
|
export type ResolvedTargetMembers = {
|
|
133
134
|
className: string;
|
|
135
|
+
classAccessFlags?: number;
|
|
134
136
|
constructors: SignatureMember[];
|
|
135
137
|
methods: SignatureMember[];
|
|
136
138
|
fields: SignatureMember[];
|
|
@@ -143,12 +145,36 @@ export type AccessWidenerValidationResult = {
|
|
|
143
145
|
valid: boolean;
|
|
144
146
|
issue?: string;
|
|
145
147
|
suggestions?: string[];
|
|
148
|
+
resolvedInRuntime?: boolean;
|
|
149
|
+
resolvedRuntimeAccess?: "public" | "protected" | "private" | "package-private";
|
|
150
|
+
resolvedRuntimeJvmDescriptor?: string;
|
|
151
|
+
resolvedRuntimeJavaSignature?: string;
|
|
146
152
|
}>;
|
|
147
153
|
summary: {
|
|
148
154
|
total: number;
|
|
149
155
|
valid: number;
|
|
150
156
|
invalid: number;
|
|
151
157
|
};
|
|
158
|
+
provenance?: RuntimeValidationProvenance<SourceMapping>;
|
|
159
|
+
warnings: string[];
|
|
160
|
+
};
|
|
161
|
+
export type AccessTransformerValidationResult = {
|
|
162
|
+
valid: boolean;
|
|
163
|
+
entries: Array<AccessTransformerEntry & {
|
|
164
|
+
valid: boolean;
|
|
165
|
+
issue?: string;
|
|
166
|
+
suggestions?: string[];
|
|
167
|
+
resolvedInRuntime?: boolean;
|
|
168
|
+
resolvedRuntimeAccess?: "public" | "protected" | "private" | "package-private";
|
|
169
|
+
resolvedRuntimeJvmDescriptor?: string;
|
|
170
|
+
resolvedRuntimeJavaSignature?: string;
|
|
171
|
+
}>;
|
|
172
|
+
summary: {
|
|
173
|
+
total: number;
|
|
174
|
+
valid: number;
|
|
175
|
+
invalid: number;
|
|
176
|
+
};
|
|
177
|
+
provenance?: RuntimeValidationProvenance<AccessTransformerNamespace>;
|
|
152
178
|
warnings: string[];
|
|
153
179
|
};
|
|
154
180
|
export declare function levenshteinDistance(a: string, b: string): number;
|
|
@@ -169,4 +195,9 @@ export declare function validateParsedMixin(parsed: ParsedMixin, targetMembers:
|
|
|
169
195
|
projectPath?: string;
|
|
170
196
|
mapping?: string;
|
|
171
197
|
}, warningMode?: "full" | "aggregated", healthReport?: MappingHealthReport, symbolExistsButSignatureFailed?: Set<string>): MixinValidationResult;
|
|
172
|
-
export declare function validateParsedAccessWidener(parsed: ParsedAccessWidener, membersByClass: Map<string, ResolvedTargetMembers>, warnings: string[]
|
|
198
|
+
export declare function validateParsedAccessWidener(parsed: ParsedAccessWidener, membersByClass: Map<string, ResolvedTargetMembers>, warnings: string[], options?: {
|
|
199
|
+
includeRuntimeEvidence?: boolean;
|
|
200
|
+
}): AccessWidenerValidationResult;
|
|
201
|
+
export declare function validateParsedAccessTransformer(parsed: ParsedAccessTransformer, membersByClass: Map<string, ResolvedTargetMembers>, warnings: string[], options?: {
|
|
202
|
+
includeRuntimeEvidence?: boolean;
|
|
203
|
+
}): AccessTransformerValidationResult;
|