@adhisang/minecraft-modding-mcp 7.0.0-rc.0 → 7.0.0-rc.2
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 +131 -49
- package/README.md +2 -0
- package/dist/cache-registry.d.ts +10 -0
- package/dist/cache-registry.js +44 -3
- package/dist/entry-tools/batch-class-members-service.js +8 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +21 -0
- package/dist/entry-tools/inspect-minecraft/internal.d.ts +4 -0
- package/dist/entry-tools/manage-cache-service.d.ts +2 -2
- package/dist/entry-tools/validate-project/cases/mixin.js +26 -6
- package/dist/entry-tools/validate-project/cases/project-summary.d.ts +7 -7
- package/dist/entry-tools/validate-project-service.d.ts +2 -2
- package/dist/entry-tools/verify-mixin-target-service.js +19 -1
- package/dist/error-mapping.d.ts +118 -0
- package/dist/error-mapping.js +177 -7
- package/dist/index.js +3 -1
- package/dist/json-rpc-framing.d.ts +53 -1
- package/dist/json-rpc-framing.js +190 -25
- package/dist/mapping/loaders/tiny-loom-selection.js +10 -3
- package/dist/mapping/parsers/tiny.js +4 -0
- package/dist/mapping-service.js +24 -3
- package/dist/maven-resolver.d.ts +61 -0
- package/dist/maven-resolver.js +95 -4
- package/dist/maven-token.d.ts +65 -0
- package/dist/maven-token.js +120 -0
- package/dist/mcp-helpers.js +14 -3
- package/dist/minecraft-explorer-service.d.ts +13 -1
- package/dist/minecraft-explorer-service.js +200 -7
- package/dist/repo-downloader.d.ts +260 -0
- package/dist/repo-downloader.js +1083 -14
- package/dist/source/artifact-resolver.d.ts +97 -3
- package/dist/source/artifact-resolver.js +228 -24
- package/dist/source/class-source.d.ts +15 -0
- package/dist/source/class-source.js +163 -18
- package/dist/source/indexer.js +14 -0
- package/dist/source/nested-jars.d.ts +16 -1
- package/dist/source/nested-jars.js +66 -3
- package/dist/source/workspace-target.js +39 -20
- package/dist/source-jar-reader.d.ts +30 -2
- package/dist/source-jar-reader.js +39 -8
- package/dist/source-resolver.d.ts +48 -0
- package/dist/source-resolver.js +735 -81
- package/dist/source-service.d.ts +10 -1
- package/dist/stdio-supervisor.d.ts +75 -1
- package/dist/stdio-supervisor.js +240 -14
- package/dist/storage/artifacts-repo.d.ts +2 -0
- package/dist/storage/artifacts-repo.js +22 -0
- package/dist/storage/db.js +32 -10
- package/dist/tool-guidance.js +17 -7
- package/dist/tool-schemas.d.ts +10 -2
- package/dist/tool-schemas.js +4 -0
- package/dist/v1-parity-schemas.js +16 -0
- package/dist/version-diff-service.js +25 -4
- package/dist/version-service.js +23 -8
- package/dist/warning-details.js +3 -1
- package/dist/workspace-mapping-service.d.ts +8 -1
- package/dist/workspace-mapping-service.js +28 -19
- package/docs/tool-reference.md +31 -7
- package/package.json +6 -4
|
@@ -2,6 +2,74 @@ import type { SourceService } from "../source-service.js";
|
|
|
2
2
|
import type { ArtifactContentsSummary, ProbeMinecraftArtifactInput, ProbeMinecraftArtifactOutput, ResolveArtifactInput, ResolveArtifactOutput } from "../source-service.js";
|
|
3
3
|
import type { AccessTransformerNamespace, ArtifactProvenance, ArtifactScope, ArtifactTargetKind, ResolvedSourceArtifact, RuntimeLoader, RuntimeValidationProvenance, SourceMapping } from "../types.js";
|
|
4
4
|
import type { WorkspaceProjectLoader } from "../workspace-mapping-service.js";
|
|
5
|
+
/**
|
|
6
|
+
* Does this artifactId, on its own, name the Minecraft runtime rather than a
|
|
7
|
+
* library that merely shares Minecraft's group?
|
|
8
|
+
*
|
|
9
|
+
* This is the rule itself, held in one place so the two ways an artifact can be
|
|
10
|
+
* named cannot drift apart. `coordinateNamesMinecraftRuntime` applies it to a
|
|
11
|
+
* parsed Maven coordinate; `extractVersionFromPath` in
|
|
12
|
+
* `minecraft-explorer-service.ts` applies it to the artifactId DIRECTORY of a jar
|
|
13
|
+
* sitting in a dependency store, which is all a `kind:"jar"` target ever offers.
|
|
14
|
+
* When those two disagreed, `net.minecraft:launchwrapper:1.12` was a library on
|
|
15
|
+
* the coordinate route and the Minecraft runtime on the path route.
|
|
16
|
+
*/
|
|
17
|
+
export declare function artifactIdNamesMinecraftRuntime(artifactId: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Does this Maven coordinate name the Minecraft runtime itself?
|
|
20
|
+
*
|
|
21
|
+
* Only then does its version segment carry a real Minecraft version, and only then
|
|
22
|
+
* may a version or namespace derived from the artifact be read as Minecraft's.
|
|
23
|
+
* `net.minecraft:launchwrapper:1.12` is a real library Mojang publishes on
|
|
24
|
+
* libraries.minecraft.net: judging by group alone read it as the runtime, so its
|
|
25
|
+
* own release number was reported as a Minecraft version and a class miss inside it
|
|
26
|
+
* was blamed on Minecraft obfuscation.
|
|
27
|
+
*
|
|
28
|
+
* A coordinate that cannot be parsed is not shown to name the runtime, so it
|
|
29
|
+
* answers `false`. That matches what both call sites did with a parse failure
|
|
30
|
+
* before this helper existed: the resolver left its group unread and fell through
|
|
31
|
+
* to "not Minecraft", and `isDependencyLikeArtifact` treated it as a library.
|
|
32
|
+
*/
|
|
33
|
+
export declare function coordinateNamesMinecraftRuntime(coordinate: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Does this artifact hold a third-party library rather than the Minecraft runtime?
|
|
36
|
+
*
|
|
37
|
+
* Callers use this to decide whether any Minecraft version derived from the
|
|
38
|
+
* artifact's jar path or resolver metadata may be believed, and whether a
|
|
39
|
+
* class-not-found miss may be blamed on Minecraft obfuscation.
|
|
40
|
+
*
|
|
41
|
+
* `provenance.dependencyResolution` alone cannot answer it. That marker is written
|
|
42
|
+
* only when the same `resolveArtifact` call handled a `kind:"dependency"` target,
|
|
43
|
+
* yet `synthesizeDependencyTarget` rewrites such a target into `{kind:"coordinate"}`
|
|
44
|
+
* BEFORE `buildProvenance` runs — so a dependency-route artifact and one reached by
|
|
45
|
+
* naming the same Maven coordinate directly persist an identical `target` and
|
|
46
|
+
* `resolvedFrom.coordinate`. Keyed off the marker, the question "is this a
|
|
47
|
+
* dependency?" was really answering "was it reached by a dependency target in this
|
|
48
|
+
* very call?", and a coordinate target — or a later reuse of the stored artifact by
|
|
49
|
+
* `artifactId` — got the vanilla answer for a dependency jar.
|
|
50
|
+
*
|
|
51
|
+
* The coordinate is route-invariant, so it decides instead, through the same
|
|
52
|
+
* `coordinateNamesMinecraftRuntime` the resolver's own obfuscation gate uses: a
|
|
53
|
+
* coordinate that does not name the Minecraft runtime names a library whose paths
|
|
54
|
+
* and version segment say nothing about Minecraft. Sharing the one helper is
|
|
55
|
+
* deliberate — the two questions are the same question asked in opposite polarity,
|
|
56
|
+
* and answering them differently is how `net.minecraft:launchwrapper` came to be
|
|
57
|
+
* read as the runtime on one side while being a library on the other.
|
|
58
|
+
*
|
|
59
|
+
* One limit remains, and it predates this predicate: an artifact reached by
|
|
60
|
+
* `kind:"jar"` records no coordinate at all, so it falls to the absent case and
|
|
61
|
+
* keeps the path-derived behaviour it has always had. The absent case is vanilla
|
|
62
|
+
* because that is the pre-existing default for the routes that record no coordinate
|
|
63
|
+
* — not because a vanilla artifact necessarily lacks one; resolving
|
|
64
|
+
* `net.minecraft:client:26.1` by coordinate does store it, and the runtime-name
|
|
65
|
+
* check is what classifies it. (`extractVersionFromPath` in
|
|
66
|
+
* `minecraft-explorer-service.ts` refuses on its own for a jar served out of a
|
|
67
|
+
* dependency cache, which is what covers the no-coordinate dependency jar.)
|
|
68
|
+
*/
|
|
69
|
+
export declare function isDependencyLikeArtifact(input: {
|
|
70
|
+
provenance?: ArtifactProvenance;
|
|
71
|
+
coordinate?: string;
|
|
72
|
+
}): boolean;
|
|
5
73
|
export type VersionSourceDiscovery = {
|
|
6
74
|
searchedPaths: string[];
|
|
7
75
|
candidateArtifacts: string[];
|
|
@@ -42,6 +110,16 @@ export type MappingFallbackSuggestion = {
|
|
|
42
110
|
nextAction: string;
|
|
43
111
|
_suggestedCallPrimaryDropped?: boolean;
|
|
44
112
|
};
|
|
113
|
+
/**
|
|
114
|
+
* Does this jar path look like it holds Minecraft itself?
|
|
115
|
+
*
|
|
116
|
+
* Loom stages Minecraft into maven-shaped local stores — its own `minecraftMaven`
|
|
117
|
+
* repository, and from there Gradle's dependency cache — so a path alone cannot be
|
|
118
|
+
* dismissed as a third-party dependency just because it sits in one. This is the
|
|
119
|
+
* override that keeps such a jar's version readable; see `extractVersionFromPath`
|
|
120
|
+
* in `minecraft-explorer-service.ts`.
|
|
121
|
+
*/
|
|
122
|
+
export declare function looksLikeMinecraftArtifactPath(path: string): boolean;
|
|
45
123
|
export declare function hasExactVersionToken(path: string, version: string): boolean;
|
|
46
124
|
/**
|
|
47
125
|
* True when a path carries the NeoForge/Forge loader version that corresponds
|
|
@@ -66,12 +144,17 @@ export declare function hasLoaderRuntimeVersionToken(path: string, mcVersion: st
|
|
|
66
144
|
*/
|
|
67
145
|
export declare function inferRuntimeJarMinecraftVersion(path: string): string | undefined;
|
|
68
146
|
/**
|
|
69
|
-
* Loader a runtime jar belongs to, read from its path.
|
|
147
|
+
* Loader a runtime jar belongs to, read from the tool-written part of its path.
|
|
70
148
|
*
|
|
71
149
|
* A Loom cache holds NeoForge-patched jars under a `/neoforge/` segment
|
|
72
150
|
* (`caches/fabric-loom/1.21.10/neoforge/21.10.50-beta/minecraft-merged-mojang-at-patched.jar`).
|
|
73
151
|
* Serving one of those to a Fabric workspace silently validated a Fabric access
|
|
74
|
-
* widener against NeoForge bytecode, so the loader has to travel with the jar
|
|
152
|
+
* widener against NeoForge bytecode, so the loader has to travel with the jar —
|
|
153
|
+
* which is why NeoForge is still asked first and still wins inside a Loom cache.
|
|
154
|
+
*
|
|
155
|
+
* Fabric is asked before Forge because its markers name a specific tool
|
|
156
|
+
* (`fabric-loom`, `loom-cache`) while Forge's are a bare `forge` token and the
|
|
157
|
+
* three letters `srg`: inside a Loom cache the specific marker is the true one.
|
|
75
158
|
*/
|
|
76
159
|
export declare function inferRuntimeJarLoader(path: string): RuntimeLoader;
|
|
77
160
|
/**
|
|
@@ -169,10 +252,21 @@ export declare function resolveBinaryFallbackArtifact(svc: SourceService, input:
|
|
|
169
252
|
/** Forwarded from the caller; unset keeps the historical decompile-on-demand behaviour. */
|
|
170
253
|
allowDecompile?: boolean;
|
|
171
254
|
}): Promise<ResolvedSourceArtifact | undefined>;
|
|
255
|
+
/**
|
|
256
|
+
* Describe what an artifact's indexed contents are.
|
|
257
|
+
*
|
|
258
|
+
* `sourceKind` is a derivation question — was the text produced by decompiling
|
|
259
|
+
* bytecode? — and only the persisted `isDecompiled` flag answers it. `origin`
|
|
260
|
+
* records provenance (where the bytes came from) and is deliberately not read:
|
|
261
|
+
* the two axes disagree in practice, most visibly for a Jar-in-Jar shell, whose
|
|
262
|
+
* row keeps origin "decompiled" while ingest clears the derivation flag. It
|
|
263
|
+
* stays in the input shape only because every caller already carries it
|
|
264
|
+
* alongside the fields that are read.
|
|
265
|
+
*/
|
|
172
266
|
export declare function buildArtifactContentsSummary(_svc: SourceService, input: {
|
|
173
267
|
origin: ResolvedSourceArtifact["origin"];
|
|
174
268
|
sourceJarPath?: string;
|
|
175
|
-
isDecompiled
|
|
269
|
+
isDecompiled: boolean;
|
|
176
270
|
qualityFlags: string[];
|
|
177
271
|
}): ArtifactContentsSummary;
|
|
178
272
|
export declare function buildMappingFallbackSuggestedCall(svc: SourceService, args: {
|
|
@@ -13,6 +13,121 @@ import { artifactIdForJar, resolveSourceTarget as resolveSourceTargetInternal }
|
|
|
13
13
|
import { resolveTinyRemapperJar } from "../tiny-remapper-resolver.js";
|
|
14
14
|
import { isUnobfuscatedVersion } from "../version-service.js";
|
|
15
15
|
import { dedupeQualityFlags, normalizeMapping, normalizeOptionalString, normalizePathStyle } from "./shared-utils.js";
|
|
16
|
+
/**
|
|
17
|
+
* The Maven group under which the Minecraft runtime artifact itself is published
|
|
18
|
+
* (`net.minecraft:client`, `net.minecraft:server`, the Loom merged jars).
|
|
19
|
+
*
|
|
20
|
+
* The group is necessary but NOT sufficient: Mojang also publishes ordinary
|
|
21
|
+
* libraries here, `net.minecraft:launchwrapper:1.12` being the long-standing one.
|
|
22
|
+
* Use `coordinateNamesMinecraftRuntime` rather than comparing against this
|
|
23
|
+
* constant directly.
|
|
24
|
+
*/
|
|
25
|
+
const MINECRAFT_ARTIFACT_GROUP_ID = "net.minecraft";
|
|
26
|
+
/**
|
|
27
|
+
* ArtifactIds under MINECRAFT_ARTIFACT_GROUP_ID that name the runtime itself, as
|
|
28
|
+
* opposed to a library that merely shares the group. `client` and `server` are the
|
|
29
|
+
* published runtime jars; every Loom-staged jar this codebase recognizes elsewhere
|
|
30
|
+
* (`minecraft-merged`, `minecraft-common`, `minecraft-clientonly`,
|
|
31
|
+
* `minecraft-client`, `minecraft-server` — see `looksLikeMinecraftArtifactPath`)
|
|
32
|
+
* starts with `minecraft`, so that prefix covers them without having to enumerate
|
|
33
|
+
* Loom's naming.
|
|
34
|
+
*/
|
|
35
|
+
const MINECRAFT_RUNTIME_ARTIFACT_IDS = new Set(["client", "server"]);
|
|
36
|
+
/**
|
|
37
|
+
* Does this artifactId, on its own, name the Minecraft runtime rather than a
|
|
38
|
+
* library that merely shares Minecraft's group?
|
|
39
|
+
*
|
|
40
|
+
* This is the rule itself, held in one place so the two ways an artifact can be
|
|
41
|
+
* named cannot drift apart. `coordinateNamesMinecraftRuntime` applies it to a
|
|
42
|
+
* parsed Maven coordinate; `extractVersionFromPath` in
|
|
43
|
+
* `minecraft-explorer-service.ts` applies it to the artifactId DIRECTORY of a jar
|
|
44
|
+
* sitting in a dependency store, which is all a `kind:"jar"` target ever offers.
|
|
45
|
+
* When those two disagreed, `net.minecraft:launchwrapper:1.12` was a library on
|
|
46
|
+
* the coordinate route and the Minecraft runtime on the path route.
|
|
47
|
+
*/
|
|
48
|
+
export function artifactIdNamesMinecraftRuntime(artifactId) {
|
|
49
|
+
const normalized = artifactId.trim().toLowerCase();
|
|
50
|
+
return MINECRAFT_RUNTIME_ARTIFACT_IDS.has(normalized) || normalized.startsWith("minecraft");
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Does this Maven coordinate name the Minecraft runtime itself?
|
|
54
|
+
*
|
|
55
|
+
* Only then does its version segment carry a real Minecraft version, and only then
|
|
56
|
+
* may a version or namespace derived from the artifact be read as Minecraft's.
|
|
57
|
+
* `net.minecraft:launchwrapper:1.12` is a real library Mojang publishes on
|
|
58
|
+
* libraries.minecraft.net: judging by group alone read it as the runtime, so its
|
|
59
|
+
* own release number was reported as a Minecraft version and a class miss inside it
|
|
60
|
+
* was blamed on Minecraft obfuscation.
|
|
61
|
+
*
|
|
62
|
+
* A coordinate that cannot be parsed is not shown to name the runtime, so it
|
|
63
|
+
* answers `false`. That matches what both call sites did with a parse failure
|
|
64
|
+
* before this helper existed: the resolver left its group unread and fell through
|
|
65
|
+
* to "not Minecraft", and `isDependencyLikeArtifact` treated it as a library.
|
|
66
|
+
*/
|
|
67
|
+
export function coordinateNamesMinecraftRuntime(coordinate) {
|
|
68
|
+
let parsed;
|
|
69
|
+
try {
|
|
70
|
+
parsed = parseCoordinate(coordinate);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (parsed.groupId !== MINECRAFT_ARTIFACT_GROUP_ID) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return artifactIdNamesMinecraftRuntime(parsed.artifactId);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Does this artifact hold a third-party library rather than the Minecraft runtime?
|
|
82
|
+
*
|
|
83
|
+
* Callers use this to decide whether any Minecraft version derived from the
|
|
84
|
+
* artifact's jar path or resolver metadata may be believed, and whether a
|
|
85
|
+
* class-not-found miss may be blamed on Minecraft obfuscation.
|
|
86
|
+
*
|
|
87
|
+
* `provenance.dependencyResolution` alone cannot answer it. That marker is written
|
|
88
|
+
* only when the same `resolveArtifact` call handled a `kind:"dependency"` target,
|
|
89
|
+
* yet `synthesizeDependencyTarget` rewrites such a target into `{kind:"coordinate"}`
|
|
90
|
+
* BEFORE `buildProvenance` runs — so a dependency-route artifact and one reached by
|
|
91
|
+
* naming the same Maven coordinate directly persist an identical `target` and
|
|
92
|
+
* `resolvedFrom.coordinate`. Keyed off the marker, the question "is this a
|
|
93
|
+
* dependency?" was really answering "was it reached by a dependency target in this
|
|
94
|
+
* very call?", and a coordinate target — or a later reuse of the stored artifact by
|
|
95
|
+
* `artifactId` — got the vanilla answer for a dependency jar.
|
|
96
|
+
*
|
|
97
|
+
* The coordinate is route-invariant, so it decides instead, through the same
|
|
98
|
+
* `coordinateNamesMinecraftRuntime` the resolver's own obfuscation gate uses: a
|
|
99
|
+
* coordinate that does not name the Minecraft runtime names a library whose paths
|
|
100
|
+
* and version segment say nothing about Minecraft. Sharing the one helper is
|
|
101
|
+
* deliberate — the two questions are the same question asked in opposite polarity,
|
|
102
|
+
* and answering them differently is how `net.minecraft:launchwrapper` came to be
|
|
103
|
+
* read as the runtime on one side while being a library on the other.
|
|
104
|
+
*
|
|
105
|
+
* One limit remains, and it predates this predicate: an artifact reached by
|
|
106
|
+
* `kind:"jar"` records no coordinate at all, so it falls to the absent case and
|
|
107
|
+
* keeps the path-derived behaviour it has always had. The absent case is vanilla
|
|
108
|
+
* because that is the pre-existing default for the routes that record no coordinate
|
|
109
|
+
* — not because a vanilla artifact necessarily lacks one; resolving
|
|
110
|
+
* `net.minecraft:client:26.1` by coordinate does store it, and the runtime-name
|
|
111
|
+
* check is what classifies it. (`extractVersionFromPath` in
|
|
112
|
+
* `minecraft-explorer-service.ts` refuses on its own for a jar served out of a
|
|
113
|
+
* dependency cache, which is what covers the no-coordinate dependency jar.)
|
|
114
|
+
*/
|
|
115
|
+
export function isDependencyLikeArtifact(input) {
|
|
116
|
+
if (input.provenance?.dependencyResolution != null) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
const coordinate = normalizeOptionalString(input.coordinate) ??
|
|
120
|
+
normalizeOptionalString(input.provenance?.resolvedFrom?.coordinate);
|
|
121
|
+
if (!coordinate) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
// Polarity: this predicate is TRUE for a library, so it is the negation of the
|
|
125
|
+
// runtime test. An unparseable coordinate answers `false` there and therefore
|
|
126
|
+
// `true` here — it cannot be shown to name the Minecraft runtime, so treat it as
|
|
127
|
+
// a dependency and suppress a Minecraft version we could not justify, rather than
|
|
128
|
+
// asserting one on the strength of a string we failed to read.
|
|
129
|
+
return !coordinateNamesMinecraftRuntime(coordinate);
|
|
130
|
+
}
|
|
16
131
|
const VERSION_TOKEN_REGEX_CACHE = new Map();
|
|
17
132
|
const LOADER_VERSION_TOKEN_REGEX_CACHE = new Map();
|
|
18
133
|
const MAX_HELPER_REGEX_CACHE = 128;
|
|
@@ -32,10 +147,16 @@ function escapeRegexLiteral(value) {
|
|
|
32
147
|
function hasPartialNetMinecraftCoverage(qualityFlags) {
|
|
33
148
|
return qualityFlags.includes("partial-source-no-net-minecraft");
|
|
34
149
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Does this jar path look like it holds Minecraft itself?
|
|
152
|
+
*
|
|
153
|
+
* Loom stages Minecraft into maven-shaped local stores — its own `minecraftMaven`
|
|
154
|
+
* repository, and from there Gradle's dependency cache — so a path alone cannot be
|
|
155
|
+
* dismissed as a third-party dependency just because it sits in one. This is the
|
|
156
|
+
* override that keeps such a jar's version readable; see `extractVersionFromPath`
|
|
157
|
+
* in `minecraft-explorer-service.ts`.
|
|
158
|
+
*/
|
|
159
|
+
export function looksLikeMinecraftArtifactPath(path) {
|
|
39
160
|
const normalizedPath = normalizePathStyle(path).toLowerCase();
|
|
40
161
|
return (normalizedPath.includes("/minecraftmaven/") ||
|
|
41
162
|
normalizedPath.includes("/net/minecraft/") ||
|
|
@@ -46,6 +167,9 @@ function looksLikeMinecraftSourceArtifact(path, hasMinecraftNamespace) {
|
|
|
46
167
|
normalizedPath.includes("minecraft-client") ||
|
|
47
168
|
normalizedPath.includes("minecraft-server"));
|
|
48
169
|
}
|
|
170
|
+
function looksLikeMinecraftSourceArtifact(path, hasMinecraftNamespace) {
|
|
171
|
+
return hasMinecraftNamespace || looksLikeMinecraftArtifactPath(path);
|
|
172
|
+
}
|
|
49
173
|
export function hasExactVersionToken(path, version) {
|
|
50
174
|
const normalizedPath = normalizePathStyle(path).toLowerCase();
|
|
51
175
|
const normalizedVersion = version.trim().toLowerCase();
|
|
@@ -96,24 +220,64 @@ export function inferRuntimeJarMinecraftVersion(path) {
|
|
|
96
220
|
return RUNTIME_JAR_VERSION_REGEX.exec(normalizePathStyle(path))?.[1];
|
|
97
221
|
}
|
|
98
222
|
/**
|
|
99
|
-
*
|
|
223
|
+
* Segment that opens a directory tree a BUILD TOOL wrote: a dot-directory
|
|
224
|
+
* (`.gradle`, `.gradle-user-home`, `.m2`), Gradle's `caches` root, or a project
|
|
225
|
+
* `build` directory. Everything below the deepest such segment was laid out by a
|
|
226
|
+
* tool and names what it holds; everything above it is the user's own directory
|
|
227
|
+
* names and names nothing.
|
|
228
|
+
*/
|
|
229
|
+
const RUNTIME_JAR_LAYOUT_ROOT_RE = /^(?:\.[^/]*|caches|build)$/;
|
|
230
|
+
/**
|
|
231
|
+
* The part of a runtime jar path that a build tool wrote: everything below the
|
|
232
|
+
* deepest layout root, or the bare file name when the path never enters one.
|
|
233
|
+
*
|
|
234
|
+
* Judged over the WHOLE path, an ancestor directory decided the loader — a
|
|
235
|
+
* Fabric workspace checked out under `~/dev/forge/` was served "forge", and
|
|
236
|
+
* `moddev-notes` or `srg-test` anywhere above the jar was enough to name a
|
|
237
|
+
* loader for an artifact that names none.
|
|
238
|
+
*/
|
|
239
|
+
function runtimeJarLayoutRelativePath(path) {
|
|
240
|
+
const segments = normalizePathStyle(path)
|
|
241
|
+
.toLowerCase()
|
|
242
|
+
.split("/")
|
|
243
|
+
.filter((segment) => segment.length > 0);
|
|
244
|
+
for (let index = segments.length - 2; index >= 0; index -= 1) {
|
|
245
|
+
if (RUNTIME_JAR_LAYOUT_ROOT_RE.test(segments[index] ?? "")) {
|
|
246
|
+
return segments.slice(index + 1).join("/");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return segments[segments.length - 1] ?? "";
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Loader a runtime jar belongs to, read from the tool-written part of its path.
|
|
100
253
|
*
|
|
101
254
|
* A Loom cache holds NeoForge-patched jars under a `/neoforge/` segment
|
|
102
255
|
* (`caches/fabric-loom/1.21.10/neoforge/21.10.50-beta/minecraft-merged-mojang-at-patched.jar`).
|
|
103
256
|
* Serving one of those to a Fabric workspace silently validated a Fabric access
|
|
104
|
-
* widener against NeoForge bytecode, so the loader has to travel with the jar
|
|
257
|
+
* widener against NeoForge bytecode, so the loader has to travel with the jar —
|
|
258
|
+
* which is why NeoForge is still asked first and still wins inside a Loom cache.
|
|
259
|
+
*
|
|
260
|
+
* Fabric is asked before Forge because its markers name a specific tool
|
|
261
|
+
* (`fabric-loom`, `loom-cache`) while Forge's are a bare `forge` token and the
|
|
262
|
+
* three letters `srg`: inside a Loom cache the specific marker is the true one.
|
|
105
263
|
*/
|
|
106
264
|
export function inferRuntimeJarLoader(path) {
|
|
107
|
-
const
|
|
108
|
-
if (
|
|
265
|
+
const layoutPath = runtimeJarLayoutRelativePath(path);
|
|
266
|
+
if (layoutPath.includes("neoforge") ||
|
|
267
|
+
layoutPath.includes("neoform") ||
|
|
268
|
+
layoutPath.includes("moddev")) {
|
|
109
269
|
return "neoforge";
|
|
110
270
|
}
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (lower.includes("fabric-loom") || lower.includes("loom-cache") || lower.includes("intermediary")) {
|
|
271
|
+
if (layoutPath.includes("fabric-loom") ||
|
|
272
|
+
layoutPath.includes("loom-cache") ||
|
|
273
|
+
layoutPath.includes("intermediary")) {
|
|
115
274
|
return "fabric";
|
|
116
275
|
}
|
|
276
|
+
if (/(^|[/\-_])forge([/\-_.]|$)/.test(layoutPath) ||
|
|
277
|
+
layoutPath.includes("forge_gradle") ||
|
|
278
|
+
layoutPath.includes("srg")) {
|
|
279
|
+
return "forge";
|
|
280
|
+
}
|
|
117
281
|
return "unknown";
|
|
118
282
|
}
|
|
119
283
|
/**
|
|
@@ -1032,8 +1196,19 @@ async function computeBinaryRemapGate(svc, input) {
|
|
|
1032
1196
|
warnings: baseline.warnings
|
|
1033
1197
|
};
|
|
1034
1198
|
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Describe what an artifact's indexed contents are.
|
|
1201
|
+
*
|
|
1202
|
+
* `sourceKind` is a derivation question — was the text produced by decompiling
|
|
1203
|
+
* bytecode? — and only the persisted `isDecompiled` flag answers it. `origin`
|
|
1204
|
+
* records provenance (where the bytes came from) and is deliberately not read:
|
|
1205
|
+
* the two axes disagree in practice, most visibly for a Jar-in-Jar shell, whose
|
|
1206
|
+
* row keeps origin "decompiled" while ingest clears the derivation flag. It
|
|
1207
|
+
* stays in the input shape only because every caller already carries it
|
|
1208
|
+
* alongside the fields that are read.
|
|
1209
|
+
*/
|
|
1035
1210
|
export function buildArtifactContentsSummary(_svc, input) {
|
|
1036
|
-
const sourceKind = input.isDecompiled ||
|
|
1211
|
+
const sourceKind = input.isDecompiled || !normalizeOptionalString(input.sourceJarPath)
|
|
1037
1212
|
? "decompiled-binary"
|
|
1038
1213
|
: "source-jar";
|
|
1039
1214
|
const sourceCoverage = hasPartialNetMinecraftCoverage(input.qualityFlags) ? "partial" : "full";
|
|
@@ -1210,17 +1385,16 @@ export async function resolveArtifact(svc, input) {
|
|
|
1210
1385
|
let resolvedTarget = { kind, value };
|
|
1211
1386
|
let resolvedVersion;
|
|
1212
1387
|
let versionSourceDiscovery;
|
|
1213
|
-
let runtimeNamesUnobfuscated = false;
|
|
1214
1388
|
if (kind === "version") {
|
|
1215
1389
|
const versionJar = await svc.versionService.resolveVersionJar(value);
|
|
1216
1390
|
resolvedVersion = versionJar.version;
|
|
1217
|
-
runtimeNamesUnobfuscated = isUnobfuscatedVersion(resolvedVersion);
|
|
1218
1391
|
resolvedTarget = {
|
|
1219
1392
|
kind: "jar",
|
|
1220
1393
|
value: versionJar.jarPath
|
|
1221
1394
|
};
|
|
1222
1395
|
warnings.push(`Resolved Minecraft ${versionJar.version} from ${versionJar.clientJarUrl}.`);
|
|
1223
1396
|
}
|
|
1397
|
+
let coordinateIsMinecraftRuntime = false;
|
|
1224
1398
|
if (kind === "coordinate") {
|
|
1225
1399
|
try {
|
|
1226
1400
|
resolvedVersion = parseCoordinate(value).version;
|
|
@@ -1228,15 +1402,32 @@ export async function resolveArtifact(svc, input) {
|
|
|
1228
1402
|
catch {
|
|
1229
1403
|
// coordinate validity is validated by resolver
|
|
1230
1404
|
}
|
|
1405
|
+
// Swallows its own parse failure and answers false, which is what the
|
|
1406
|
+
// unread group left behind here before.
|
|
1407
|
+
coordinateIsMinecraftRuntime = coordinateNamesMinecraftRuntime(value);
|
|
1231
1408
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1409
|
+
// `isUnobfuscatedVersion` answers "does this MINECRAFT version ship unobfuscated
|
|
1410
|
+
// names in its runtime jar?", so it may only be asked about a string that really is
|
|
1411
|
+
// a Minecraft version. A kind="version" target always carries one. A coordinate
|
|
1412
|
+
// carries one only when it names the Minecraft runtime artifact itself
|
|
1413
|
+
// (net.minecraft:client:26.1); for any other coordinate the version segment is a
|
|
1414
|
+
// third-party library's own release number — including a library published under
|
|
1415
|
+
// Minecraft's own group, such as net.minecraft:launchwrapper, which is why the
|
|
1416
|
+
// group alone cannot answer this and `coordinateNamesMinecraftRuntime` checks the
|
|
1417
|
+
// artifactId too. Without this gate a dependency such as org.jetbrains:annotations:26.0.2
|
|
1418
|
+
// parses as an "unobfuscated Minecraft version" and short-circuits
|
|
1419
|
+
// applyMappingPipeline into reporting mappingApplied="mojang" with no remap
|
|
1420
|
+
// performed, no verification, and no dependency-mapping-unverified warning.
|
|
1421
|
+
// A kind="dependency" target is excluded outright regardless of its group,
|
|
1422
|
+
// because binary remap is force-disabled for it (see forceBinaryRemapDisabled
|
|
1423
|
+
// below), so its mapping is never actually enforced either way.
|
|
1424
|
+
const versionNamesMinecraft = kind === "version" ||
|
|
1425
|
+
(kind === "coordinate" && !dependencyOrigin && coordinateIsMinecraftRuntime);
|
|
1426
|
+
const minecraftVersion = versionNamesMinecraft ? resolvedVersion : undefined;
|
|
1427
|
+
const runtimeNamesUnobfuscated = minecraftVersion !== undefined && isUnobfuscatedVersion(minecraftVersion);
|
|
1235
1428
|
let effectiveMapping = mapping;
|
|
1236
|
-
if ((mapping === "intermediary" || mapping === "yarn") &&
|
|
1237
|
-
|
|
1238
|
-
isUnobfuscatedVersion(resolvedVersion)) {
|
|
1239
|
-
warnings.push(`Version ${resolvedVersion} is unobfuscated; ${mapping} mappings are not applicable. Using the obfuscated namespace label for the deobfuscated runtime names.`);
|
|
1429
|
+
if ((mapping === "intermediary" || mapping === "yarn") && runtimeNamesUnobfuscated) {
|
|
1430
|
+
warnings.push(`Version ${minecraftVersion} is unobfuscated; ${mapping} mappings are not applicable. Using the obfuscated namespace label for the deobfuscated runtime names.`);
|
|
1240
1431
|
effectiveMapping = "obfuscated";
|
|
1241
1432
|
}
|
|
1242
1433
|
if (kind === "version" &&
|
|
@@ -1382,7 +1573,10 @@ export async function resolveArtifact(svc, input) {
|
|
|
1382
1573
|
resolved.sourceJarPath === versionSourceDiscovery.selectedSourceJarPath) {
|
|
1383
1574
|
provenance.companionSourceJars = versionSourceDiscovery.companionSourceJarPaths;
|
|
1384
1575
|
}
|
|
1385
|
-
if (dependencyOrigin &&
|
|
1576
|
+
if (dependencyOrigin &&
|
|
1577
|
+
dependencyRequestedMapping &&
|
|
1578
|
+
dependencyRequestedMapping !== "obfuscated" &&
|
|
1579
|
+
mappingDecision.qualityFlags.includes("dependency-mapping-unverified")) {
|
|
1386
1580
|
const coord = resolved.coordinate ?? value;
|
|
1387
1581
|
warnings.push(`Dependency artifact ${coord} mapping "${dependencyRequestedMapping}" is not enforced (binary remap is disabled for non-vanilla artifacts); the JAR is returned in its native namespace and mappingApplied is reported as "obfuscated" with qualityFlag "dependency-mapping-unverified". Caller must validate symbol availability.`);
|
|
1388
1582
|
}
|
|
@@ -1393,7 +1587,17 @@ export async function resolveArtifact(svc, input) {
|
|
|
1393
1587
|
resolved.requestedMapping = effectiveMapping;
|
|
1394
1588
|
resolved.mappingApplied = mappingDecision.mappingApplied;
|
|
1395
1589
|
resolved.provenance = provenance;
|
|
1396
|
-
|
|
1590
|
+
// MERGE, never assign. The mapping pipeline builds its list from an empty
|
|
1591
|
+
// array and knows only what mapping did, so anything the resolver already
|
|
1592
|
+
// observed about the artifact itself - "binary-jar-no-classes" is the first
|
|
1593
|
+
// such flag - is invisible to it. Overwriting the list here would drop those
|
|
1594
|
+
// observations before dedupe, before indexing, and before the response is
|
|
1595
|
+
// built, so the caller would never learn what the resolver saw. Same shape
|
|
1596
|
+
// as the binary-fallback path above.
|
|
1597
|
+
resolved.qualityFlags = dedupeQualityFlags([
|
|
1598
|
+
...(resolved.qualityFlags ?? []),
|
|
1599
|
+
...mappingDecision.qualityFlags
|
|
1600
|
+
]);
|
|
1397
1601
|
if (versionSourceDiscovery?.candidateArtifacts.length) {
|
|
1398
1602
|
resolved.qualityFlags.push("source-jar-found");
|
|
1399
1603
|
}
|
|
@@ -65,6 +65,21 @@ export declare function buildClassSourceNotFoundError(svc: SourceService, input:
|
|
|
65
65
|
mappingApplied: SourceMapping;
|
|
66
66
|
requestedMapping: SourceMapping;
|
|
67
67
|
qualityFlags: string[];
|
|
68
|
+
/**
|
|
69
|
+
* The mapping the CALLER wrote in the request, undefined when they omitted it.
|
|
70
|
+
* Distinct from `requestedMapping`, which falls back to the artifact's own
|
|
71
|
+
* namespace: only the caller-supplied value can tell whether the obfuscated
|
|
72
|
+
* namespace hint would be re-asking for an argument that is already present.
|
|
73
|
+
*/
|
|
74
|
+
callerSuppliedMapping?: SourceMapping;
|
|
75
|
+
/**
|
|
76
|
+
* Whether the REQUESTED artifact is a native dependency, as decided by
|
|
77
|
+
* `isDependencyLikeArtifact`. Such artifacts are handed
|
|
78
|
+
* `mappingApplied: "obfuscated"` by substitution rather than by being an
|
|
79
|
+
* obfuscated index, which the obfuscated namespace hint must not mistake for
|
|
80
|
+
* a missing mapping argument.
|
|
81
|
+
*/
|
|
82
|
+
nativeDependency?: boolean;
|
|
68
83
|
attemptedBinaryFallback: boolean;
|
|
69
84
|
filePath?: string;
|
|
70
85
|
targetKind?: string;
|