@adhisang/minecraft-modding-mcp 1.1.0 → 1.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 +58 -0
- package/README.md +88 -17
- package/dist/cli.js +1 -16
- package/dist/compat-stdio-transport.d.ts +27 -0
- package/dist/compat-stdio-transport.js +217 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/decompiler/vineflower.d.ts +1 -0
- package/dist/decompiler/vineflower.js +78 -29
- package/dist/index.js +169 -25
- package/dist/mapping-service.d.ts +22 -0
- package/dist/mapping-service.js +309 -30
- package/dist/mixin-parser.d.ts +1 -0
- package/dist/mixin-parser.js +134 -16
- package/dist/mixin-validator.d.ts +93 -2
- package/dist/mixin-validator.js +464 -41
- package/dist/mod-analyzer.d.ts +2 -0
- package/dist/mod-analyzer.js +7 -0
- package/dist/mod-decompile-service.d.ts +6 -0
- package/dist/mod-decompile-service.js +36 -4
- package/dist/mod-search-service.d.ts +1 -0
- package/dist/mod-search-service.js +96 -0
- package/dist/search-hit-accumulator.d.ts +1 -0
- package/dist/search-hit-accumulator.js +3 -0
- package/dist/source-resolver.js +0 -4
- package/dist/source-service.d.ts +91 -4
- package/dist/source-service.js +1211 -120
- package/dist/storage/files-repo.js +35 -8
- package/dist/types.d.ts +1 -0
- package/dist/version-service.js +30 -6
- package/dist/workspace-mapping-service.d.ts +1 -0
- package/dist/workspace-mapping-service.js +24 -0
- package/package.json +1 -1
package/dist/mapping-service.js
CHANGED
|
@@ -246,12 +246,85 @@ function ensurePairIndex(indexes, from, to) {
|
|
|
246
246
|
indexes.set(key, created);
|
|
247
247
|
return created;
|
|
248
248
|
}
|
|
249
|
+
/** Map of proguard primitive type names to JVM type characters. */
|
|
250
|
+
const PROGUARD_PRIMITIVES = {
|
|
251
|
+
void: "V", boolean: "Z", byte: "B", char: "C",
|
|
252
|
+
short: "S", int: "I", long: "J", float: "F", double: "D"
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Convert a single proguard type (e.g. "int", "net.minecraft.Foo", "int[][]")
|
|
256
|
+
* to JVM notation (e.g. "I", "Lnet/minecraft/Foo;", "[[I").
|
|
257
|
+
* `classLookup` maps mojang class names → official class names (for the official descriptor).
|
|
258
|
+
* Pass `undefined` to skip class name translation (for mojang descriptors).
|
|
259
|
+
*/
|
|
260
|
+
function proguardTypeToJvm(type, classLookup) {
|
|
261
|
+
let arrayDepth = 0;
|
|
262
|
+
let base = type;
|
|
263
|
+
while (base.endsWith("[]")) {
|
|
264
|
+
arrayDepth += 1;
|
|
265
|
+
base = base.slice(0, -2);
|
|
266
|
+
}
|
|
267
|
+
const prefix = "[".repeat(arrayDepth);
|
|
268
|
+
const primitive = PROGUARD_PRIMITIVES[base];
|
|
269
|
+
if (primitive) {
|
|
270
|
+
return `${prefix}${primitive}`;
|
|
271
|
+
}
|
|
272
|
+
const translated = classLookup ? (classLookup.get(base) ?? base) : base;
|
|
273
|
+
return `${prefix}L${translated.replace(/\./g, "/")};`;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Parse a proguard method signature (after stripLineInfo) into a JVM descriptor.
|
|
277
|
+
* Input format: "returnType methodName(paramType1,paramType2,...)"
|
|
278
|
+
* Returns `{ name, descriptor }` or `undefined` if parsing fails.
|
|
279
|
+
*/
|
|
280
|
+
function parseProguardMethod(value, classLookup) {
|
|
281
|
+
const match = /^(.+?)\s+([^\s(]+)\((.*)\)$/.exec(value);
|
|
282
|
+
if (!match) {
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
const returnType = match[1].trim();
|
|
286
|
+
const name = match[2].trim();
|
|
287
|
+
const params = match[3].trim();
|
|
288
|
+
if (!name) {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
const paramParts = params ? params.split(",").map((p) => p.trim()) : [];
|
|
292
|
+
const paramDescriptor = paramParts.map((p) => proguardTypeToJvm(p, classLookup)).join("");
|
|
293
|
+
const returnDescriptor = proguardTypeToJvm(returnType, classLookup);
|
|
294
|
+
return { name, descriptor: `(${paramDescriptor})${returnDescriptor}` };
|
|
295
|
+
}
|
|
249
296
|
function parseClientMappings(text) {
|
|
250
297
|
const officialToMojang = createDirectionIndex();
|
|
251
298
|
const mojangToOfficial = createDirectionIndex();
|
|
299
|
+
// Two-pass parsing: first collect class name mappings, then parse members with descriptors.
|
|
300
|
+
const lines = text.split(/\r?\n/);
|
|
301
|
+
// Pass 1: collect class name mappings (mojang → official)
|
|
302
|
+
const mojangToOfficialClass = new Map();
|
|
252
303
|
let classCount = 0;
|
|
304
|
+
for (const rawLine of lines) {
|
|
305
|
+
const line = rawLine.trim();
|
|
306
|
+
if (!line || line.startsWith("#")) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const classMatch = /^(.+?)\s+->\s+(.+):$/.exec(line);
|
|
310
|
+
if (classMatch) {
|
|
311
|
+
const mojangClass = classMatch[1]?.trim() ?? "";
|
|
312
|
+
const officialClass = classMatch[2]?.trim() ?? "";
|
|
313
|
+
if (mojangClass && officialClass) {
|
|
314
|
+
mojangToOfficialClass.set(mojangClass, officialClass);
|
|
315
|
+
classCount += 1;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (classCount === 0) {
|
|
320
|
+
throw createError({
|
|
321
|
+
code: ERROR_CODES.MAPPING_UNAVAILABLE,
|
|
322
|
+
message: "No class mappings could be parsed from client mappings."
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
// Pass 2: build full index with descriptors
|
|
253
326
|
let currentClass;
|
|
254
|
-
for (const rawLine of
|
|
327
|
+
for (const rawLine of lines) {
|
|
255
328
|
const line = rawLine.trim();
|
|
256
329
|
if (!line || line.startsWith("#")) {
|
|
257
330
|
continue;
|
|
@@ -264,7 +337,6 @@ function parseClientMappings(text) {
|
|
|
264
337
|
currentClass = undefined;
|
|
265
338
|
continue;
|
|
266
339
|
}
|
|
267
|
-
classCount += 1;
|
|
268
340
|
currentClass = {
|
|
269
341
|
official: officialClass,
|
|
270
342
|
mojang: mojangClass
|
|
@@ -286,10 +358,14 @@ function parseClientMappings(text) {
|
|
|
286
358
|
continue;
|
|
287
359
|
}
|
|
288
360
|
const mojangMemberSignature = stripLineInfo(leftRaw);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
361
|
+
// Try method parsing with JVM descriptor
|
|
362
|
+
const officialMethod = parseProguardMethod(mojangMemberSignature, mojangToOfficialClass);
|
|
363
|
+
if (officialMethod) {
|
|
364
|
+
const mojangMethod = parseProguardMethod(mojangMemberSignature, undefined);
|
|
365
|
+
const officialDescriptor = officialMethod.descriptor;
|
|
366
|
+
const mojangDescriptor = mojangMethod?.descriptor;
|
|
367
|
+
addLookupEntries(officialToMojang, createMethodSymbolRecord(currentClass.official, rightRaw, officialDescriptor), createMethodSymbolRecord(currentClass.mojang, officialMethod.name, mojangDescriptor));
|
|
368
|
+
addLookupEntries(mojangToOfficial, createMethodSymbolRecord(currentClass.mojang, officialMethod.name, mojangDescriptor), createMethodSymbolRecord(currentClass.official, rightRaw, officialDescriptor));
|
|
293
369
|
continue;
|
|
294
370
|
}
|
|
295
371
|
const fieldName = parseFieldName(mojangMemberSignature);
|
|
@@ -299,12 +375,6 @@ function parseClientMappings(text) {
|
|
|
299
375
|
addLookupEntries(officialToMojang, createFieldSymbolRecord(currentClass.official, rightRaw), createFieldSymbolRecord(currentClass.mojang, fieldName));
|
|
300
376
|
addLookupEntries(mojangToOfficial, createFieldSymbolRecord(currentClass.mojang, fieldName), createFieldSymbolRecord(currentClass.official, rightRaw));
|
|
301
377
|
}
|
|
302
|
-
if (classCount === 0) {
|
|
303
|
-
throw createError({
|
|
304
|
-
code: ERROR_CODES.MAPPING_UNAVAILABLE,
|
|
305
|
-
message: "No class mappings could be parsed from client mappings."
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
378
|
const result = new Map();
|
|
309
379
|
result.set(pairKey("official", "mojang"), officialToMojang);
|
|
310
380
|
result.set(pairKey("mojang", "official"), mojangToOfficial);
|
|
@@ -591,7 +661,7 @@ function normalizeMethodDescriptor(descriptor) {
|
|
|
591
661
|
}
|
|
592
662
|
return normalized;
|
|
593
663
|
}
|
|
594
|
-
function normalizeQuerySymbol(input) {
|
|
664
|
+
function normalizeQuerySymbol(input, signatureMode) {
|
|
595
665
|
if (input.kind !== "class" && input.kind !== "field" && input.kind !== "method") {
|
|
596
666
|
throw invalidInputError('kind must be one of "class", "field", or "method".', {
|
|
597
667
|
kind: input.kind
|
|
@@ -647,12 +717,53 @@ function normalizeQuerySymbol(input) {
|
|
|
647
717
|
querySymbol: toSymbolReference(record)
|
|
648
718
|
};
|
|
649
719
|
}
|
|
650
|
-
const
|
|
720
|
+
const descriptor = signatureMode === "name-only"
|
|
721
|
+
? (input.descriptor?.trim() || "")
|
|
722
|
+
: normalizeMethodDescriptor(input.descriptor);
|
|
723
|
+
const record = createMethodSymbolRecord(owner, normalizeMemberName(normalizedName), descriptor);
|
|
651
724
|
return {
|
|
652
725
|
record,
|
|
653
726
|
querySymbol: toSymbolReference(record)
|
|
654
727
|
};
|
|
655
728
|
}
|
|
729
|
+
function normalizeOwnerHint(ownerHint) {
|
|
730
|
+
const normalized = ownerHint?.trim();
|
|
731
|
+
if (!normalized) {
|
|
732
|
+
return undefined;
|
|
733
|
+
}
|
|
734
|
+
return normalizeMappedSymbolOutput(normalized);
|
|
735
|
+
}
|
|
736
|
+
function normalizeDescriptorHint(descriptorHint) {
|
|
737
|
+
const normalized = descriptorHint?.trim();
|
|
738
|
+
return normalized || undefined;
|
|
739
|
+
}
|
|
740
|
+
function applyDisambiguationHints(candidates, disambiguation) {
|
|
741
|
+
if (!disambiguation || candidates.length <= 1) {
|
|
742
|
+
return candidates;
|
|
743
|
+
}
|
|
744
|
+
let filtered = [...candidates];
|
|
745
|
+
const ownerHint = normalizeOwnerHint(disambiguation.ownerHint);
|
|
746
|
+
if (ownerHint) {
|
|
747
|
+
const ownerMatched = filtered.filter((candidate) => {
|
|
748
|
+
if (candidate.owner) {
|
|
749
|
+
return normalizeMappedSymbolOutput(candidate.owner) === ownerHint;
|
|
750
|
+
}
|
|
751
|
+
const normalizedSymbol = normalizeMappedSymbolOutput(candidate.symbol);
|
|
752
|
+
return normalizedSymbol.startsWith(`${ownerHint}.`);
|
|
753
|
+
});
|
|
754
|
+
if (ownerMatched.length > 0) {
|
|
755
|
+
filtered = ownerMatched;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
const descriptorHint = normalizeDescriptorHint(disambiguation.descriptorHint);
|
|
759
|
+
if (descriptorHint) {
|
|
760
|
+
const descriptorMatched = filtered.filter((candidate) => candidate.descriptor === descriptorHint);
|
|
761
|
+
if (descriptorMatched.length > 0) {
|
|
762
|
+
filtered = descriptorMatched;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return filtered;
|
|
766
|
+
}
|
|
656
767
|
function collectTargetRecords(graph, targetMapping) {
|
|
657
768
|
const merged = new Map();
|
|
658
769
|
for (const [key, pair] of graph.pairs.entries()) {
|
|
@@ -681,6 +792,37 @@ function normalizeIncludedKinds(inputKinds) {
|
|
|
681
792
|
}
|
|
682
793
|
return normalized;
|
|
683
794
|
}
|
|
795
|
+
function inferAmbiguityReasons(candidates, usedMojangClientMappings) {
|
|
796
|
+
if (candidates.length <= 1) {
|
|
797
|
+
return [];
|
|
798
|
+
}
|
|
799
|
+
const reasons = [];
|
|
800
|
+
const owners = [...new Set(candidates.map((c) => c.owner).filter(Boolean))];
|
|
801
|
+
if (owners.length > 1) {
|
|
802
|
+
reasons.push(`Multiple owner classes matched: ${owners.join(", ")}`);
|
|
803
|
+
}
|
|
804
|
+
const matchKinds = [...new Set(candidates.map((c) => c.matchKind))];
|
|
805
|
+
if (matchKinds.length > 1) {
|
|
806
|
+
reasons.push(`Candidates matched at different precision levels: ${matchKinds.join(", ")}`);
|
|
807
|
+
}
|
|
808
|
+
if (usedMojangClientMappings) {
|
|
809
|
+
const hasDescriptor = candidates.some((c) => c.descriptor);
|
|
810
|
+
const missingDescriptor = candidates.some((c) => !c.descriptor);
|
|
811
|
+
if (hasDescriptor && missingDescriptor) {
|
|
812
|
+
reasons.push("Method descriptor was lost through mojang-client-mappings path, causing broader matching.");
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
if (owners.length <= 1) {
|
|
816
|
+
const descriptors = [...new Set(candidates.map((c) => c.descriptor).filter(Boolean))];
|
|
817
|
+
if (descriptors.length > 1) {
|
|
818
|
+
reasons.push(`Overloaded method: ${descriptors.length} variants`);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
if (reasons.length === 0) {
|
|
822
|
+
reasons.push(`${candidates.length} candidates matched with similar confidence scores.`);
|
|
823
|
+
}
|
|
824
|
+
return reasons;
|
|
825
|
+
}
|
|
684
826
|
export class MappingService {
|
|
685
827
|
config;
|
|
686
828
|
versionService;
|
|
@@ -755,17 +897,28 @@ export class MappingService {
|
|
|
755
897
|
};
|
|
756
898
|
}
|
|
757
899
|
const rawCandidates = this.mapCandidatesAlongPath(graph, path, queryRecord);
|
|
758
|
-
const candidates = rawCandidates.map(toResolutionCandidate);
|
|
759
900
|
const warnings = [];
|
|
901
|
+
const disambiguatedCandidates = applyDisambiguationHints(rawCandidates, input.disambiguation);
|
|
902
|
+
if (rawCandidates.length > disambiguatedCandidates.length) {
|
|
903
|
+
warnings.push(`Disambiguation hints narrowed candidates from ${rawCandidates.length} to ${disambiguatedCandidates.length}.`);
|
|
904
|
+
}
|
|
905
|
+
const candidates = disambiguatedCandidates.map(toResolutionCandidate);
|
|
760
906
|
if (queryRecord.kind === "method" &&
|
|
761
907
|
queryRecord.descriptor &&
|
|
762
|
-
pathUsesSource(graph.pairs, path, "mojang-client-mappings")
|
|
908
|
+
pathUsesSource(graph.pairs, path, "mojang-client-mappings") &&
|
|
909
|
+
candidates.length !== 1) {
|
|
763
910
|
warnings.push("Method descriptor could not be preserved through mojang-client-mappings and may have used name-based fallback.");
|
|
764
911
|
}
|
|
765
912
|
if (candidates.length === 0) {
|
|
766
913
|
warnings.push("No mapping candidate matched the input symbol.");
|
|
767
914
|
}
|
|
915
|
+
else if (candidates.length > 1) {
|
|
916
|
+
warnings.push(`Ambiguous mapping: ${candidates.length} candidates matched. Provide a stricter symbol input or disambiguation hints.`);
|
|
917
|
+
}
|
|
768
918
|
const status = candidates.length === 0 ? "not_found" : candidates.length === 1 ? "resolved" : "ambiguous";
|
|
919
|
+
const ambiguityReasons = candidates.length > 1
|
|
920
|
+
? inferAmbiguityReasons(candidates, pathUsesSource(graph.pairs, path, "mojang-client-mappings"))
|
|
921
|
+
: undefined;
|
|
769
922
|
return {
|
|
770
923
|
querySymbol,
|
|
771
924
|
mappingContext,
|
|
@@ -774,7 +927,8 @@ export class MappingService {
|
|
|
774
927
|
resolvedSymbol: status === "resolved" ? candidates[0] : undefined,
|
|
775
928
|
candidates,
|
|
776
929
|
warnings,
|
|
777
|
-
provenance: this.provenanceForPath(graph, path)
|
|
930
|
+
provenance: this.provenanceForPath(graph, path),
|
|
931
|
+
ambiguityReasons
|
|
778
932
|
};
|
|
779
933
|
}
|
|
780
934
|
async ensureMappingAvailable(input) {
|
|
@@ -818,7 +972,9 @@ export class MappingService {
|
|
|
818
972
|
version,
|
|
819
973
|
sourceMapping,
|
|
820
974
|
targetMapping,
|
|
821
|
-
sourcePriority: priority
|
|
975
|
+
sourcePriority: priority,
|
|
976
|
+
nextAction: "Try mapping=official which is always available.",
|
|
977
|
+
suggestedCall: { tool: "resolve-artifact", params: { mapping: "official" } }
|
|
822
978
|
}
|
|
823
979
|
});
|
|
824
980
|
}
|
|
@@ -996,7 +1152,8 @@ export class MappingService {
|
|
|
996
1152
|
}
|
|
997
1153
|
const mapped = this.mapRecordBetweenMappings(graph, classNameMapping, mapping, classByMapping[classNameMapping]);
|
|
998
1154
|
if (mapped.length > 1) {
|
|
999
|
-
|
|
1155
|
+
const competing = mapped.slice(0, 5).map((c) => c.symbol);
|
|
1156
|
+
warnings.push(`Class identity mapping to ${mapping} is ambiguous for "${className}": competing=[${competing.join(", ")}].`);
|
|
1000
1157
|
}
|
|
1001
1158
|
if (mapped.length > 0) {
|
|
1002
1159
|
classByMapping[mapping] = mapped[0];
|
|
@@ -1032,6 +1189,7 @@ export class MappingService {
|
|
|
1032
1189
|
return includeKinds.has("method");
|
|
1033
1190
|
});
|
|
1034
1191
|
const rows = [];
|
|
1192
|
+
let ambiguousRowCount = 0;
|
|
1035
1193
|
const rowSeen = new Set();
|
|
1036
1194
|
const rowKindOrder = {
|
|
1037
1195
|
class: 0,
|
|
@@ -1055,6 +1213,7 @@ export class MappingService {
|
|
|
1055
1213
|
continue;
|
|
1056
1214
|
}
|
|
1057
1215
|
rowSeen.add(key);
|
|
1216
|
+
let rowHadAmbiguity = false;
|
|
1058
1217
|
const row = {
|
|
1059
1218
|
kind: baseRecord.kind,
|
|
1060
1219
|
descriptor: baseRecord.descriptor,
|
|
@@ -1079,7 +1238,9 @@ export class MappingService {
|
|
|
1079
1238
|
}
|
|
1080
1239
|
}
|
|
1081
1240
|
if (filtered.length > 1) {
|
|
1082
|
-
|
|
1241
|
+
const competing = filtered.slice(0, 5).map((c) => c.symbol);
|
|
1242
|
+
warnings.push(`Row mapping to ${mapping} is ambiguous for "${baseRecord.symbol}": competing=[${competing.join(", ")}]. Using highest-ranked candidate.`);
|
|
1243
|
+
rowHadAmbiguity = true;
|
|
1083
1244
|
}
|
|
1084
1245
|
resolved = filtered[0];
|
|
1085
1246
|
}
|
|
@@ -1096,6 +1257,9 @@ export class MappingService {
|
|
|
1096
1257
|
}
|
|
1097
1258
|
row.completeness = Boolean(row.official && row.mojang && row.intermediary && row.yarn);
|
|
1098
1259
|
rows.push(row);
|
|
1260
|
+
if (rowHadAmbiguity) {
|
|
1261
|
+
ambiguousRowCount += 1;
|
|
1262
|
+
}
|
|
1099
1263
|
}
|
|
1100
1264
|
return {
|
|
1101
1265
|
version,
|
|
@@ -1108,7 +1272,8 @@ export class MappingService {
|
|
|
1108
1272
|
yarn: classByMapping.yarn?.symbol
|
|
1109
1273
|
},
|
|
1110
1274
|
rows,
|
|
1111
|
-
warnings
|
|
1275
|
+
warnings,
|
|
1276
|
+
ambiguousRowCount: ambiguousRowCount > 0 ? ambiguousRowCount : undefined
|
|
1112
1277
|
};
|
|
1113
1278
|
}
|
|
1114
1279
|
async checkSymbolExists(input) {
|
|
@@ -1122,7 +1287,6 @@ export class MappingService {
|
|
|
1122
1287
|
}
|
|
1123
1288
|
});
|
|
1124
1289
|
}
|
|
1125
|
-
const { record: queryRecord, querySymbol } = normalizeQuerySymbol(input);
|
|
1126
1290
|
const sourceMapping = input.sourceMapping;
|
|
1127
1291
|
if (!SUPPORTED_MAPPINGS.has(sourceMapping)) {
|
|
1128
1292
|
throw createError({
|
|
@@ -1139,12 +1303,51 @@ export class MappingService {
|
|
|
1139
1303
|
sourceMapping,
|
|
1140
1304
|
sourcePriorityApplied: priority
|
|
1141
1305
|
};
|
|
1306
|
+
const classNameMode = input.nameMode === "auto" ? "auto" : "fqcn";
|
|
1307
|
+
const normalizedQuery = input.kind === "class" && classNameMode === "auto"
|
|
1308
|
+
? (() => {
|
|
1309
|
+
const owner = input.owner?.trim();
|
|
1310
|
+
if (owner) {
|
|
1311
|
+
throw invalidInputError("owner is not allowed when kind=class. Use name as FQCN.", {
|
|
1312
|
+
owner: input.owner,
|
|
1313
|
+
nextAction: 'Provide class as name, e.g. "net.minecraft.server.Main".'
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
if (input.descriptor?.trim()) {
|
|
1317
|
+
throw invalidInputError("descriptor is not allowed when kind=class.", {
|
|
1318
|
+
descriptor: input.descriptor
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
const autoClassName = normalizeMappedSymbolOutput(input.name.trim());
|
|
1322
|
+
if (!autoClassName) {
|
|
1323
|
+
throw invalidInputError("name must be a non-empty string.", {
|
|
1324
|
+
name: input.name
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
return {
|
|
1328
|
+
mode: "auto-class",
|
|
1329
|
+
className: autoClassName,
|
|
1330
|
+
querySymbol: {
|
|
1331
|
+
kind: "class",
|
|
1332
|
+
name: autoClassName,
|
|
1333
|
+
symbol: autoClassName
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
})()
|
|
1337
|
+
: (() => {
|
|
1338
|
+
const { record: queryRecord, querySymbol } = normalizeQuerySymbol(input, input.signatureMode);
|
|
1339
|
+
return {
|
|
1340
|
+
mode: "strict",
|
|
1341
|
+
queryRecord,
|
|
1342
|
+
querySymbol
|
|
1343
|
+
};
|
|
1344
|
+
})();
|
|
1142
1345
|
const graph = await this.loadGraph(version, priority);
|
|
1143
1346
|
const warnings = [...graph.warnings];
|
|
1144
1347
|
const records = collectTargetRecords(graph, sourceMapping);
|
|
1145
1348
|
if (records.length === 0) {
|
|
1146
1349
|
return {
|
|
1147
|
-
querySymbol,
|
|
1350
|
+
querySymbol: normalizedQuery.querySymbol,
|
|
1148
1351
|
mappingContext,
|
|
1149
1352
|
resolved: false,
|
|
1150
1353
|
status: "mapping_unavailable",
|
|
@@ -1152,7 +1355,7 @@ export class MappingService {
|
|
|
1152
1355
|
warnings
|
|
1153
1356
|
};
|
|
1154
1357
|
}
|
|
1155
|
-
const buildOutput = (matched, status) => {
|
|
1358
|
+
const buildOutput = (querySymbol, matched, status) => {
|
|
1156
1359
|
const candidates = matched.map((record) => toResolutionCandidate(toLookupCandidate(record)));
|
|
1157
1360
|
return {
|
|
1158
1361
|
querySymbol,
|
|
@@ -1164,29 +1367,52 @@ export class MappingService {
|
|
|
1164
1367
|
warnings
|
|
1165
1368
|
};
|
|
1166
1369
|
};
|
|
1370
|
+
if (normalizedQuery.mode === "auto-class") {
|
|
1371
|
+
const autoClassName = normalizedQuery.className;
|
|
1372
|
+
if (autoClassName.includes(".")) {
|
|
1373
|
+
const matched = records.filter((record) => record.kind === "class" && record.symbol === autoClassName);
|
|
1374
|
+
const status = matched.length === 1 ? "resolved" : matched.length > 1 ? "ambiguous" : "not_found";
|
|
1375
|
+
return buildOutput(normalizedQuery.querySymbol, matched, status);
|
|
1376
|
+
}
|
|
1377
|
+
const matched = records.filter((record) => record.kind === "class" && record.name === autoClassName);
|
|
1378
|
+
const status = matched.length === 1 ? "resolved" : matched.length > 1 ? "ambiguous" : "not_found";
|
|
1379
|
+
if (status === "ambiguous") {
|
|
1380
|
+
warnings.push(`Multiple class symbols matched short name "${autoClassName}". Provide fully-qualified class name.`);
|
|
1381
|
+
}
|
|
1382
|
+
return buildOutput(normalizedQuery.querySymbol, matched, status);
|
|
1383
|
+
}
|
|
1384
|
+
const { queryRecord, querySymbol } = normalizedQuery;
|
|
1167
1385
|
if (queryRecord.kind === "class") {
|
|
1168
1386
|
const matched = records.filter((record) => record.kind === "class" && record.symbol === queryRecord.symbol);
|
|
1169
1387
|
const status = matched.length === 1 ? "resolved" : matched.length > 1 ? "ambiguous" : "not_found";
|
|
1170
|
-
return buildOutput(matched, status);
|
|
1388
|
+
return buildOutput(querySymbol, matched, status);
|
|
1171
1389
|
}
|
|
1172
1390
|
if (queryRecord.kind === "field") {
|
|
1173
1391
|
const matched = records.filter((record) => record.kind === "field" && record.owner === queryRecord.owner && record.name === queryRecord.name);
|
|
1174
1392
|
const status = matched.length === 1 ? "resolved" : matched.length > 1 ? "ambiguous" : "not_found";
|
|
1175
|
-
return buildOutput(matched, status);
|
|
1393
|
+
return buildOutput(querySymbol, matched, status);
|
|
1176
1394
|
}
|
|
1177
1395
|
const methodCandidates = records.filter((record) => record.kind === "method" && record.owner === queryRecord.owner && record.name === queryRecord.name);
|
|
1396
|
+
// name-only mode: skip descriptor matching, resolve by owner+name
|
|
1397
|
+
if (input.signatureMode === "name-only") {
|
|
1398
|
+
const status = methodCandidates.length === 1 ? "resolved" : methodCandidates.length > 1 ? "ambiguous" : "not_found";
|
|
1399
|
+
if (status === "ambiguous") {
|
|
1400
|
+
warnings.push(`Multiple method overloads matched name "${queryRecord.name}" in owner "${queryRecord.owner}". Provide descriptor for exact match.`);
|
|
1401
|
+
}
|
|
1402
|
+
return buildOutput(querySymbol, methodCandidates, status);
|
|
1403
|
+
}
|
|
1178
1404
|
const descriptorMatched = methodCandidates.filter((record) => record.descriptor === queryRecord.descriptor);
|
|
1179
1405
|
if (descriptorMatched.length === 1) {
|
|
1180
|
-
return buildOutput(descriptorMatched, "resolved");
|
|
1406
|
+
return buildOutput(querySymbol, descriptorMatched, "resolved");
|
|
1181
1407
|
}
|
|
1182
1408
|
if (descriptorMatched.length > 1) {
|
|
1183
|
-
return buildOutput(descriptorMatched, "ambiguous");
|
|
1409
|
+
return buildOutput(querySymbol, descriptorMatched, "ambiguous");
|
|
1184
1410
|
}
|
|
1185
1411
|
if (methodCandidates.some((candidate) => candidate.descriptor == null)) {
|
|
1186
1412
|
warnings.push("Descriptor-level existence checks are unavailable for descriptorless mapping entries.");
|
|
1187
|
-
return buildOutput(methodCandidates, "mapping_unavailable");
|
|
1413
|
+
return buildOutput(querySymbol, methodCandidates, "mapping_unavailable");
|
|
1188
1414
|
}
|
|
1189
|
-
return buildOutput([], "not_found");
|
|
1415
|
+
return buildOutput(querySymbol, [], "not_found");
|
|
1190
1416
|
}
|
|
1191
1417
|
mapRecordBetweenMappings(graph, sourceMapping, targetMapping, record) {
|
|
1192
1418
|
if (sourceMapping === targetMapping) {
|
|
@@ -1308,6 +1534,59 @@ export class MappingService {
|
|
|
1308
1534
|
priority: graph.priority
|
|
1309
1535
|
};
|
|
1310
1536
|
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Probe the mapping graph health for a given version.
|
|
1539
|
+
* Returns availability of mojang mappings, tiny mappings, and member remap paths.
|
|
1540
|
+
*/
|
|
1541
|
+
async checkMappingHealth(input) {
|
|
1542
|
+
const priority = mappingPriorityFromInput(this.config.mappingSourcePriority, input.sourcePriority);
|
|
1543
|
+
const degradations = [];
|
|
1544
|
+
let graph;
|
|
1545
|
+
try {
|
|
1546
|
+
graph = await this.loadGraph(input.version, priority);
|
|
1547
|
+
}
|
|
1548
|
+
catch {
|
|
1549
|
+
return {
|
|
1550
|
+
mojangMappingsAvailable: false,
|
|
1551
|
+
tinyMappingsAvailable: false,
|
|
1552
|
+
memberRemapAvailable: false,
|
|
1553
|
+
degradations: ["Mapping graph could not be loaded."]
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
// Check for mojang-client-mappings pairs
|
|
1557
|
+
let mojangAvailable = false;
|
|
1558
|
+
let tinyAvailable = false;
|
|
1559
|
+
for (const [, record] of graph.pairs) {
|
|
1560
|
+
if (record.source === "mojang-client-mappings")
|
|
1561
|
+
mojangAvailable = true;
|
|
1562
|
+
if (record.source === "loom-cache" || record.source === "maven")
|
|
1563
|
+
tinyAvailable = true;
|
|
1564
|
+
}
|
|
1565
|
+
if (!mojangAvailable) {
|
|
1566
|
+
degradations.push("Mojang client mappings are not available for this version.");
|
|
1567
|
+
}
|
|
1568
|
+
if (!tinyAvailable) {
|
|
1569
|
+
degradations.push("No intermediary/yarn tiny mappings were found for this version.");
|
|
1570
|
+
}
|
|
1571
|
+
// Check if member remap path exists (requestedMapping → official)
|
|
1572
|
+
let memberRemapAvailable = false;
|
|
1573
|
+
if (input.requestedMapping === "official") {
|
|
1574
|
+
memberRemapAvailable = true;
|
|
1575
|
+
}
|
|
1576
|
+
else {
|
|
1577
|
+
const path = namespacePath(graph.pairs, input.requestedMapping, "official");
|
|
1578
|
+
memberRemapAvailable = path != null && path.length > 1;
|
|
1579
|
+
if (!memberRemapAvailable) {
|
|
1580
|
+
degradations.push(`No mapping path from ${input.requestedMapping} to official; member remap will fail.`);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
return {
|
|
1584
|
+
mojangMappingsAvailable: mojangAvailable,
|
|
1585
|
+
tinyMappingsAvailable: tinyAvailable,
|
|
1586
|
+
memberRemapAvailable,
|
|
1587
|
+
degradations
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1311
1590
|
async loadGraph(version, priority) {
|
|
1312
1591
|
const cacheKey = `${version}|${priority}`;
|
|
1313
1592
|
const cached = this.graphCache.get(cacheKey);
|
package/dist/mixin-parser.d.ts
CHANGED